diff -urwN linux-3.10/drivers/mmc/card/block.c linux-rpi-3.10.y/drivers/mmc/card/block.c --- linux-3.10/drivers/mmc/card/block.c 2013-06-30 23:13:29.000000000 +0100 +++ linux-rpi-3.10.y/drivers/mmc/card/block.c 2013-07-06 15:25:50.000000000 +0100 @@ -1294,7 +1294,7 @@ brq->data.blocks = 1; } - if (brq->data.blocks > 1 || do_rel_wr) { + if (brq->data.blocks > 1 || do_rel_wr || card->host->caps2 & MMC_CAP2_FORCE_MULTIBLOCK) { /* SPI multiblock writes terminate using a special * token, not a STOP_TRANSMISSION request. */ diff -urwN linux-3.10/drivers/mmc/core/sd.c linux-rpi-3.10.y/drivers/mmc/core/sd.c --- linux-3.10/drivers/mmc/core/sd.c 2013-06-30 23:13:29.000000000 +0100 +++ linux-rpi-3.10.y/drivers/mmc/core/sd.c 2013-07-06 15:25:50.000000000 +0100 @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include @@ -58,6 +60,15 @@ __res & __mask; \ }) +// timeout for tries +static const unsigned long retry_timeout_ms= 10*1000; + +// try at least 10 times, even if timeout is reached +static const int retry_min_tries= 10; + +// delay between tries +static const unsigned long retry_delay_ms= 10; + /* * Given the decoded CSD structure, decode the raw CID to our CID structure. */ @@ -210,12 +221,62 @@ } /* - * Fetch and process SD Status register. + * Fetch and process SD Configuration Register. + */ +static int mmc_read_scr(struct mmc_card *card) +{ + unsigned long timeout_at; + int err, tries; + + timeout_at= jiffies + msecs_to_jiffies( retry_timeout_ms ); + tries= 0; + + while( tries < retry_min_tries || time_before( jiffies, timeout_at ) ) + { + unsigned long delay_at; + tries++; + + err = mmc_app_send_scr(card, card->raw_scr); + if( !err ) + break; // success!!! + + touch_nmi_watchdog(); // we are still alive! + + // delay + delay_at= jiffies + msecs_to_jiffies( retry_delay_ms ); + while( time_before( jiffies, delay_at ) ) + { + mdelay( 1 ); + touch_nmi_watchdog(); // we are still alive! + } + } + + if( err) + { + pr_err("%s: failed to read SD Configuration register (SCR) after %d tries during %lu ms, error %d\n", mmc_hostname(card->host), tries, retry_timeout_ms, err ); + return err; + } + + if( tries > 1 ) + { + pr_info("%s: could read SD Configuration register (SCR) at the %dth attempt\n", mmc_hostname(card->host), tries ); + } + + err = mmc_decode_scr(card); + if (err) + return err; + + return err; +} + +/* + * Fetch and process SD Status Register. */ static int mmc_read_ssr(struct mmc_card *card) { + unsigned long timeout_at; unsigned int au, es, et, eo; - int err, i; + int err, i, tries; u32 *ssr; if (!(card->csd.cmdclass & CCC_APP_SPEC)) { @@ -228,14 +289,40 @@ if (!ssr) return -ENOMEM; + timeout_at= jiffies + msecs_to_jiffies( retry_timeout_ms ); + tries= 0; + + while( tries < retry_min_tries || time_before( jiffies, timeout_at ) ) + { + unsigned long delay_at; + tries++; + err = mmc_app_sd_status(card, ssr); - if (err) { - pr_warning("%s: problem reading SD Status " - "register.\n", mmc_hostname(card->host)); - err = 0; + if( !err ) + break; // sucess!!! + + touch_nmi_watchdog(); // we are still alive! + + // delay + delay_at= jiffies + msecs_to_jiffies( retry_delay_ms ); + while( time_before( jiffies, delay_at ) ) + { + mdelay( 1 ); + touch_nmi_watchdog(); // we are still alive! + } + } + + if( err) + { + pr_err("%s: failed to read SD Status register (SSR) after %d tries during %lu ms, error %d\n", mmc_hostname(card->host), tries, retry_timeout_ms, err ); goto out; } + if( tries > 1 ) + { + pr_info("%s: read SD Status register (SSR) after %d attempts\n", mmc_hostname(card->host), tries ); + } + for (i = 0; i < 16; i++) ssr[i] = be32_to_cpu(ssr[i]); @@ -808,13 +895,9 @@ if (!reinit) { /* - * Fetch SCR from card. + * Fetch and decode SD Configuration register. */ - err = mmc_app_send_scr(card, card->raw_scr); - if (err) - return err; - - err = mmc_decode_scr(card); + err = mmc_read_scr(card); if (err) return err; diff -urwN linux-3.10/drivers/mmc/host/Kconfig linux-rpi-3.10.y/drivers/mmc/host/Kconfig --- linux-3.10/drivers/mmc/host/Kconfig 2013-06-30 23:13:29.000000000 +0100 +++ linux-rpi-3.10.y/drivers/mmc/host/Kconfig 2013-07-06 15:25:50.000000000 +0100 @@ -249,6 +249,27 @@ YMMV. +config MMC_SDHCI_BCM2708 + tristate "SDHCI support on BCM2708" + depends on MMC_SDHCI && MACH_BCM2708 + select MMC_SDHCI_IO_ACCESSORS + help + This selects the Secure Digital Host Controller Interface (SDHCI) + often referrered to as the eMMC block. + + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + +config MMC_SDHCI_BCM2708_DMA + bool "DMA support on BCM2708 Arasan controller" + depends on MMC_SDHCI_BCM2708 + help + Enable DMA support on the Arasan SDHCI controller in Broadcom 2708 + based chips. + + If unsure, say N. + config MMC_SDHCI_BCM2835 tristate "SDHCI platform support for the BCM2835 SD/MMC Controller" depends on ARCH_BCM2835 diff -urwN linux-3.10/drivers/mmc/host/Makefile linux-rpi-3.10.y/drivers/mmc/host/Makefile --- linux-3.10/drivers/mmc/host/Makefile 2013-06-30 23:13:29.000000000 +0100 +++ linux-rpi-3.10.y/drivers/mmc/host/Makefile 2013-07-06 15:25:50.000000000 +0100 @@ -15,6 +15,7 @@ obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o obj-$(CONFIG_MMC_SDHCI_SIRF) += sdhci-sirf.o obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o +obj-$(CONFIG_MMC_SDHCI_BCM2708) += sdhci-bcm2708.o obj-$(CONFIG_MMC_WBSD) += wbsd.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o obj-$(CONFIG_MMC_OMAP) += omap.o diff -urwN linux-3.10/drivers/mmc/host/sdhci-bcm2708.c linux-rpi-3.10.y/drivers/mmc/host/sdhci-bcm2708.c --- linux-3.10/drivers/mmc/host/sdhci-bcm2708.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-rpi-3.10.y/drivers/mmc/host/sdhci-bcm2708.c 2013-07-06 15:25:50.000000000 +0100 @@ -0,0 +1,1420 @@ +/* + * sdhci-bcm2708.c Support for SDHCI device on BCM2708 + * Copyright (c) 2010 Broadcom + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Supports: + * SDHCI platform device - Arasan SD controller in BCM2708 + * + * Inspired by sdhci-pci.c, by Pierre Ossman + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sdhci.h" + +/*****************************************************************************\ + * * + * Configuration * + * * +\*****************************************************************************/ + +#define DRIVER_NAME "bcm2708_sdhci" + +/* for the time being insist on DMA mode - PIO seems not to work */ +#ifndef CONFIG_MMC_SDHCI_BCM2708_DMA +#warning Non-DMA (PIO) version of this driver currently unavailable +#endif +#undef CONFIG_MMC_SDHCI_BCM2708_DMA +#define CONFIG_MMC_SDHCI_BCM2708_DMA y + +#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA +/* #define CHECK_DMA_USE */ +#endif +//#define LOG_REGISTERS + +#define USE_SCHED_TIME +#define USE_SPACED_WRITES_2CLK 1 /* space consecutive register writes */ +#define USE_SOFTWARE_TIMEOUTS 1 /* not hardware timeouts */ +#define SOFTWARE_ERASE_TIMEOUT_SEC 30 + +#define SDHCI_BCM_DMA_CHAN 4 /* this default is normally overriden */ +#define SDHCI_BCM_DMA_WAITS 0 /* delays slowing DMA transfers: 0-31 */ +/* We are worried that SD card DMA use may be blocking the AXI bus for others */ + +/*! TODO: obtain these from the physical address */ +#define DMA_SDHCI_BASE 0x7e300000 /* EMMC register block on Videocore */ +#define DMA_SDHCI_BUFFER (DMA_SDHCI_BASE + SDHCI_BUFFER) + +#define BCM2708_SDHCI_SLEEP_TIMEOUT 1000 /* msecs */ + +/* Mhz clock that the EMMC core is running at. Should match the platform clockman settings */ +#define BCM2708_EMMC_CLOCK_FREQ 50000000 + +#define REG_EXRDFIFO_EN 0x80 +#define REG_EXRDFIFO_CFG 0x84 + +int cycle_delay=2; + +/*****************************************************************************\ + * * + * Debug * + * * +\*****************************************************************************/ + + + +#define DBG(f, x...) \ + pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x) +// printk(KERN_INFO DRIVER_NAME " [%s()]: " f, __func__,## x)//GRAYG + + +/*****************************************************************************\ + * * + * High Precision Time * + * * +\*****************************************************************************/ + +#ifdef USE_SCHED_TIME + +#include + +typedef unsigned long hptime_t; + +#define FMT_HPT "lu" + +static inline hptime_t hptime(void) +{ + return frc_clock_ticks32(); +} + +#define HPTIME_CLK_NS 1000ul + +#else + +typedef unsigned long hptime_t; + +#define FMT_HPT "lu" + +static inline hptime_t hptime(void) +{ + return jiffies; +} + +#define HPTIME_CLK_NS (1000000000ul/HZ) + +#endif + +static inline unsigned long int since_ns(hptime_t t) +{ + return (unsigned long)((hptime() - t) * HPTIME_CLK_NS); +} + +static bool allow_highspeed = 1; +static int emmc_clock_freq = BCM2708_EMMC_CLOCK_FREQ; +static bool sync_after_dma = 1; +static bool missing_status = 1; +static bool spurious_crc_acmd51 = 0; +bool enable_llm = 1; +bool extra_messages = 0; + +#if 0 +static void hptime_test(void) +{ + hptime_t now; + hptime_t later; + + now = hptime(); + msleep(10); + later = hptime(); + + printk(KERN_INFO DRIVER_NAME": 10ms = %"FMT_HPT" clks " + "(from %"FMT_HPT" to %"FMT_HPT") = %luns\n", + later-now, now, later, + (unsigned long)(HPTIME_CLK_NS * (later - now))); + + now = hptime(); + msleep(1000); + later = hptime(); + + printk(KERN_INFO DRIVER_NAME": 1s = %"FMT_HPT" clks " + "(from %"FMT_HPT" to %"FMT_HPT") = %luns\n", + later-now, now, later, + (unsigned long)(HPTIME_CLK_NS * (later - now))); +} +#endif + +/*****************************************************************************\ + * * + * SDHCI core callbacks * + * * +\*****************************************************************************/ + + +#ifdef CHECK_DMA_USE +/*#define CHECK_DMA_REG_USE*/ +#endif + +#ifdef CHECK_DMA_REG_USE +/* we don't expect anything to be using these registers during a + DMA (except the IRQ status) - so check */ +static void check_dma_reg_use(struct sdhci_host *host, int reg); +#else +#define check_dma_reg_use(host, reg) +#endif + + +static inline u32 sdhci_bcm2708_raw_readl(struct sdhci_host *host, int reg) +{ + return readl(host->ioaddr + reg); +} + +u32 sdhci_bcm2708_readl(struct sdhci_host *host, int reg) +{ + u32 l = sdhci_bcm2708_raw_readl(host, reg); + +#ifdef LOG_REGISTERS + printk(KERN_ERR "%s: readl from 0x%02x, value 0x%08x\n", + mmc_hostname(host->mmc), reg, l); +#endif + check_dma_reg_use(host, reg); + + return l; +} + +u16 sdhci_bcm2708_readw(struct sdhci_host *host, int reg) +{ + u32 l = sdhci_bcm2708_raw_readl(host, reg & ~3); + u32 w = l >> (reg << 3 & 0x18) & 0xffff; + +#ifdef LOG_REGISTERS + printk(KERN_ERR "%s: readw from 0x%02x, value 0x%04x\n", + mmc_hostname(host->mmc), reg, w); +#endif + check_dma_reg_use(host, reg); + + return (u16)w; +} + +u8 sdhci_bcm2708_readb(struct sdhci_host *host, int reg) +{ + u32 l = sdhci_bcm2708_raw_readl(host, reg & ~3); + u32 b = l >> (reg << 3 & 0x18) & 0xff; + +#ifdef LOG_REGISTERS + printk(KERN_ERR "%s: readb from 0x%02x, value 0x%02x\n", + mmc_hostname(host->mmc), reg, b); +#endif + check_dma_reg_use(host, reg); + + return (u8)b; +} + + +static void sdhci_bcm2708_raw_writel(struct sdhci_host *host, u32 val, int reg) +{ + u32 ier; + +#if USE_SPACED_WRITES_2CLK + static bool timeout_disabled = false; + unsigned int ns_2clk = 0; + + /* The Arasan has a bugette whereby it may lose the content of + * successive writes to registers that are within two SD-card clock + * cycles of each other (a clock domain crossing problem). + * It seems, however, that the data register does not have this problem. + * (Which is just as well - otherwise we'd have to nobble the DMA engine + * too) + */ + if (reg != SDHCI_BUFFER && host->clock != 0) { + /* host->clock is the clock freq in Hz */ + static hptime_t last_write_hpt; + hptime_t now = hptime(); + ns_2clk = cycle_delay*1000000/(host->clock/1000); + + if (now == last_write_hpt || now == last_write_hpt+1) { + /* we can't guarantee any significant time has + * passed - we'll have to wait anyway ! */ + ndelay(ns_2clk); + } else + { + /* we must have waited at least this many ns: */ + unsigned int ns_wait = HPTIME_CLK_NS * + (last_write_hpt - now - 1); + if (ns_wait < ns_2clk) + ndelay(ns_2clk - ns_wait); + } + last_write_hpt = now; + } +#if USE_SOFTWARE_TIMEOUTS + /* The Arasan is clocked for timeouts using the SD clock which is too + * fast for ERASE commands and causes issues. So we disable timeouts + * for ERASE */ + if (host->cmd != NULL && host->cmd->opcode == MMC_ERASE && + reg == (SDHCI_COMMAND & ~3)) { + mod_timer(&host->timer, + jiffies + SOFTWARE_ERASE_TIMEOUT_SEC * HZ); + ier = readl(host->ioaddr + SDHCI_SIGNAL_ENABLE); + ier &= ~SDHCI_INT_DATA_TIMEOUT; + writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE); + timeout_disabled = true; + ndelay(ns_2clk); + } else if (timeout_disabled) { + ier = readl(host->ioaddr + SDHCI_SIGNAL_ENABLE); + ier |= SDHCI_INT_DATA_TIMEOUT; + writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE); + timeout_disabled = false; + ndelay(ns_2clk); + } +#endif + writel(val, host->ioaddr + reg); +#else + void __iomem * regaddr = host->ioaddr + reg; + + writel(val, regaddr); + + if (reg != SDHCI_BUFFER && reg != SDHCI_INT_STATUS && host->clock != 0) + { + int timeout = 100000; + while (val != readl(regaddr) && --timeout > 0) + continue; + + if (timeout <= 0) + printk(KERN_ERR "%s: writing 0x%X to reg 0x%X " + "always gives 0x%X\n", + mmc_hostname(host->mmc), + val, reg, readl(regaddr)); + BUG_ON(timeout <= 0); + } +#endif +} + + +void sdhci_bcm2708_writel(struct sdhci_host *host, u32 val, int reg) +{ +#ifdef LOG_REGISTERS + printk(KERN_ERR "%s: writel to 0x%02x, value 0x%08x\n", + mmc_hostname(host->mmc), reg, val); +#endif + check_dma_reg_use(host, reg); + + sdhci_bcm2708_raw_writel(host, val, reg); +} + +void sdhci_bcm2708_writew(struct sdhci_host *host, u16 val, int reg) +{ + static u32 shadow = 0; + + u32 p = reg == SDHCI_COMMAND ? shadow : + sdhci_bcm2708_raw_readl(host, reg & ~3); + u32 s = reg << 3 & 0x18; + u32 l = val << s; + u32 m = 0xffff << s; + +#ifdef LOG_REGISTERS + printk(KERN_ERR "%s: writew to 0x%02x, value 0x%04x\n", + mmc_hostname(host->mmc), reg, val); +#endif + + if (reg == SDHCI_TRANSFER_MODE) + shadow = (p & ~m) | l; + else { + check_dma_reg_use(host, reg); + sdhci_bcm2708_raw_writel(host, (p & ~m) | l, reg & ~3); + } +} + +void sdhci_bcm2708_writeb(struct sdhci_host *host, u8 val, int reg) +{ + u32 p = sdhci_bcm2708_raw_readl(host, reg & ~3); + u32 s = reg << 3 & 0x18; + u32 l = val << s; + u32 m = 0xff << s; + +#ifdef LOG_REGISTERS + printk(KERN_ERR "%s: writeb to 0x%02x, value 0x%02x\n", + mmc_hostname(host->mmc), reg, val); +#endif + + check_dma_reg_use(host, reg); + sdhci_bcm2708_raw_writel(host, (p & ~m) | l, reg & ~3); +} + +static unsigned int sdhci_bcm2708_get_max_clock(struct sdhci_host *host) +{ + return emmc_clock_freq; +} + +/*****************************************************************************\ + * * + * DMA Operation * + * * +\*****************************************************************************/ + +struct sdhci_bcm2708_priv { + int dma_chan; + int dma_irq; + void __iomem *dma_chan_base; + struct bcm2708_dma_cb *cb_base; /* DMA control blocks */ + dma_addr_t cb_handle; + /* tracking scatter gather progress */ + unsigned sg_ix; /* scatter gather list index */ + unsigned sg_done; /* bytes in current sg_ix done */ +#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA + unsigned char dma_wanted; /* DMA transfer requested */ + unsigned char dma_waits; /* wait states in DMAs */ +#ifdef CHECK_DMA_USE + unsigned char dmas_pending; /* no of unfinished DMAs */ + hptime_t when_started; + hptime_t when_reset; + hptime_t when_stopped; +#endif +#endif + /* signalling the end of a transfer */ + void (*complete)(struct sdhci_host *); +}; + +#define SDHCI_HOST_PRIV(host) \ + (struct sdhci_bcm2708_priv *)((struct sdhci_host *)(host)+1) + + + +#ifdef CHECK_DMA_REG_USE +static void check_dma_reg_use(struct sdhci_host *host, int reg) +{ + struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); + if (host_priv->dma_wanted && reg != SDHCI_INT_STATUS) { + printk(KERN_INFO"%s: accessing register 0x%x during DMA\n", + mmc_hostname(host->mmc), reg); + } +} +#endif + + + +#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA + +static void sdhci_clear_set_irqgen(struct sdhci_host *host, u32 clear, u32 set) +{ + u32 ier; + + ier = sdhci_bcm2708_raw_readl(host, SDHCI_SIGNAL_ENABLE); + ier &= ~clear; + ier |= set; + /* change which requests generate IRQs - makes no difference to + the content of SDHCI_INT_STATUS, or the need to acknowledge IRQs */ + sdhci_bcm2708_raw_writel(host, ier, SDHCI_SIGNAL_ENABLE); +} + +static void sdhci_signal_irqs(struct sdhci_host *host, u32 irqs) +{ + sdhci_clear_set_irqgen(host, 0, irqs); +} + +static void sdhci_unsignal_irqs(struct sdhci_host *host, u32 irqs) +{ + sdhci_clear_set_irqgen(host, irqs, 0); +} + + + +static void schci_bcm2708_cb_read(struct sdhci_bcm2708_priv *host, + int ix, + dma_addr_t dma_addr, unsigned len, + int /*bool*/ is_last) +{ + struct bcm2708_dma_cb *cb = &host->cb_base[ix]; + unsigned char dmawaits = host->dma_waits; + + cb->info = BCM2708_DMA_PER_MAP(BCM2708_DMA_DREQ_EMMC) | + BCM2708_DMA_WAITS(dmawaits) | + BCM2708_DMA_S_DREQ | + BCM2708_DMA_D_WIDTH | + BCM2708_DMA_D_INC; + cb->src = DMA_SDHCI_BUFFER; /* DATA register DMA address */ + cb->dst = dma_addr; + cb->length = len; + cb->stride = 0; + + if (is_last) { + cb->info |= BCM2708_DMA_INT_EN | + BCM2708_DMA_WAIT_RESP; + cb->next = 0; + } else + cb->next = host->cb_handle + + (ix+1)*sizeof(struct bcm2708_dma_cb); + + cb->pad[0] = 0; + cb->pad[1] = 0; +} + +static void schci_bcm2708_cb_write(struct sdhci_bcm2708_priv *host, + int ix, + dma_addr_t dma_addr, unsigned len, + int /*bool*/ is_last) +{ + struct bcm2708_dma_cb *cb = &host->cb_base[ix]; + unsigned char dmawaits = host->dma_waits; + + /* We can make arbitrarily large writes as long as we specify DREQ to + pace the delivery of bytes to the Arasan hardware */ + cb->info = BCM2708_DMA_PER_MAP(BCM2708_DMA_DREQ_EMMC) | + BCM2708_DMA_WAITS(dmawaits) | + BCM2708_DMA_D_DREQ | + BCM2708_DMA_S_WIDTH | + BCM2708_DMA_S_INC; + cb->src = dma_addr; + cb->dst = DMA_SDHCI_BUFFER; /* DATA register DMA address */ + cb->length = len; + cb->stride = 0; + + if (is_last) { + cb->info |= BCM2708_DMA_INT_EN | + BCM2708_DMA_WAIT_RESP; + cb->next = 0; + } else + cb->next = host->cb_handle + + (ix+1)*sizeof(struct bcm2708_dma_cb); + + cb->pad[0] = 0; + cb->pad[1] = 0; +} + + +static void schci_bcm2708_dma_go(struct sdhci_host *host) +{ + struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); + void __iomem *dma_chan_base = host_priv->dma_chan_base; + + BUG_ON(host_priv->dma_wanted); +#ifdef CHECK_DMA_USE + if (host_priv->dma_wanted) + printk(KERN_ERR "%s: DMA already in progress - " + "now %"FMT_HPT", last started %lu " + "reset %lu stopped %lu\n", + mmc_hostname(host->mmc), + hptime(), since_ns(host_priv->when_started), + since_ns(host_priv->when_reset), + since_ns(host_priv->when_stopped)); + else if (host_priv->dmas_pending > 0) + printk(KERN_INFO "%s: note - new DMA when %d reset DMAs " + "already in progress - " + "now %"FMT_HPT", started %lu reset %lu stopped %lu\n", + mmc_hostname(host->mmc), + host_priv->dmas_pending, + hptime(), since_ns(host_priv->when_started), + since_ns(host_priv->when_reset), + since_ns(host_priv->when_stopped)); + host_priv->dmas_pending += 1; + host_priv->when_started = hptime(); +#endif + host_priv->dma_wanted = 1; + DBG("PDMA go - base %p handle %08X\n", dma_chan_base, + host_priv->cb_handle); + bcm_dma_start(dma_chan_base, host_priv->cb_handle); +} + + +static void +sdhci_platdma_read(struct sdhci_host *host, dma_addr_t dma_addr, size_t len) +{ + struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); + + DBG("PDMA to read %d bytes\n", len); + host_priv->sg_done += len; + schci_bcm2708_cb_read(host_priv, 0, dma_addr, len, 1/*TRUE*/); + schci_bcm2708_dma_go(host); +} + + +static void +sdhci_platdma_write(struct sdhci_host *host, dma_addr_t dma_addr, size_t len) +{ + struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); + + DBG("PDMA to write %d bytes\n", len); + //BUG_ON(0 != (len & 0x1ff)); + + host_priv->sg_done += len; + schci_bcm2708_cb_write(host_priv, 0, dma_addr, len, 1/*TRUE*/); + schci_bcm2708_dma_go(host); +} + +/*! space is avaiable to receive into or data is available to write + Platform DMA exported function +*/ +void +sdhci_bcm2708_platdma_avail(struct sdhci_host *host, unsigned int *ref_intmask, + void(*completion_callback)(struct sdhci_host *host)) +{ + struct mmc_data *data = host->data; + struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); + int sg_ix; + size_t bytes; + dma_addr_t addr; + + BUG_ON(NULL == data); + BUG_ON(0 == data->blksz); + + host_priv->complete = completion_callback; + + sg_ix = host_priv->sg_ix; + BUG_ON(sg_ix >= data->sg_len); + + /* we can DMA blocks larger than blksz - it may hang the DMA + channel but we are its only user */ + bytes = sg_dma_len(&data->sg[sg_ix]) - host_priv->sg_done; + addr = sg_dma_address(&data->sg[sg_ix]) + host_priv->sg_done; + + if (bytes > 0) { + /* We're going to poll for read/write available state until + we finish this DMA + */ + + if (data->flags & MMC_DATA_READ) { + if (*ref_intmask & SDHCI_INT_DATA_AVAIL) { + sdhci_unsignal_irqs(host, SDHCI_INT_DATA_AVAIL | + SDHCI_INT_SPACE_AVAIL); + sdhci_platdma_read(host, addr, bytes); + } + } else { + if (*ref_intmask & SDHCI_INT_SPACE_AVAIL) { + sdhci_unsignal_irqs(host, SDHCI_INT_DATA_AVAIL | + SDHCI_INT_SPACE_AVAIL); + sdhci_platdma_write(host, addr, bytes); + } + } + } + /* else: + we have run out of bytes that need transferring (e.g. we may be in + the middle of the last DMA transfer), or + it is also possible that we've been called when another IRQ is + signalled, even though we've turned off signalling of our own IRQ */ + + *ref_intmask &= ~SDHCI_INT_DATA_END; + /* don't let the main sdhci driver act on this .. we'll deal with it + when we respond to the DMA - if one is currently in progress */ +} + +/* is it possible to DMA the given mmc_data structure? + Platform DMA exported function +*/ +int /*bool*/ +sdhci_bcm2708_platdma_dmaable(struct sdhci_host *host, struct mmc_data *data) +{ + struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); + int ok = bcm_sg_suitable_for_dma(data->sg, data->sg_len); + + if (!ok) + DBG("Reverting to PIO - bad cache alignment\n"); + + else { + host_priv->sg_ix = 0; /* first SG index */ + host_priv->sg_done = 0; /* no bytes done */ + } + + return ok; +} + +#include //GRAYG +/*! the current SD transacton has been abandonned + We need to tidy up if we were in the middle of a DMA + Platform DMA exported function +*/ +void +sdhci_bcm2708_platdma_reset(struct sdhci_host *host, struct mmc_data *data) +{ + struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); +// unsigned long flags; + + BUG_ON(NULL == host); + +// spin_lock_irqsave(&host->lock, flags); + + if (host_priv->dma_wanted) { + if (NULL == data) { + printk(KERN_ERR "%s: ongoing DMA reset - no data!\n", + mmc_hostname(host->mmc)); + BUG_ON(NULL == data); + } else { + struct scatterlist *sg; + int sg_len; + int sg_todo; + int rc; + unsigned long cs; + + sg = data->sg; + sg_len = data->sg_len; + sg_todo = sg_dma_len(&sg[host_priv->sg_ix]); + + cs = readl(host_priv->dma_chan_base + BCM2708_DMA_CS); + + if (!(BCM2708_DMA_ACTIVE & cs)) + { + if (extra_messages) + printk(KERN_INFO "%s: missed completion of " + "cmd %d DMA (%d/%d [%d]/[%d]) - " + "ignoring it\n", + mmc_hostname(host->mmc), + host->last_cmdop, + host_priv->sg_done, sg_todo, + host_priv->sg_ix+1, sg_len); + } + else + printk(KERN_INFO "%s: resetting ongoing cmd %d" + "DMA before %d/%d [%d]/[%d] complete\n", + mmc_hostname(host->mmc), + host->last_cmdop, + host_priv->sg_done, sg_todo, + host_priv->sg_ix+1, sg_len); +#ifdef CHECK_DMA_USE + printk(KERN_INFO "%s: now %"FMT_HPT" started %lu " + "last reset %lu last stopped %lu\n", + mmc_hostname(host->mmc), + hptime(), since_ns(host_priv->when_started), + since_ns(host_priv->when_reset), + since_ns(host_priv->when_stopped)); + { unsigned long info, debug; + void __iomem *base; + unsigned long pend0, pend1, pend2; + + base = host_priv->dma_chan_base; + cs = readl(base + BCM2708_DMA_CS); + info = readl(base + BCM2708_DMA_INFO); + debug = readl(base + BCM2708_DMA_DEBUG); + printk(KERN_INFO "%s: DMA%d CS=%08lX TI=%08lX " + "DEBUG=%08lX\n", + mmc_hostname(host->mmc), + host_priv->dma_chan, + cs, info, debug); + pend0 = readl(__io_address(ARM_IRQ_PEND0)); + pend1 = readl(__io_address(ARM_IRQ_PEND1)); + pend2 = readl(__io_address(ARM_IRQ_PEND2)); + + printk(KERN_INFO "%s: PEND0=%08lX " + "PEND1=%08lX PEND2=%08lX\n", + mmc_hostname(host->mmc), + pend0, pend1, pend2); + + //gintsts = readl(__io_address(GINTSTS)); + //gintmsk = readl(__io_address(GINTMSK)); + //printk(KERN_INFO "%s: USB GINTSTS=%08lX" + // "GINTMSK=%08lX\n", + // mmc_hostname(host->mmc), gintsts, gintmsk); + } +#endif + rc = bcm_dma_abort(host_priv->dma_chan_base); + BUG_ON(rc != 0); + } + host_priv->dma_wanted = 0; +#ifdef CHECK_DMA_USE + host_priv->when_reset = hptime(); +#endif + } + +// spin_unlock_irqrestore(&host->lock, flags); +} + + +static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host, + u32 dma_cs) +{ + struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); + struct mmc_data *data; + struct scatterlist *sg; + int sg_len; + int sg_ix; + int sg_todo; +// unsigned long flags; + + BUG_ON(NULL == host); + +// spin_lock_irqsave(&host->lock, flags); + data = host->data; + +#ifdef CHECK_DMA_USE + if (host_priv->dmas_pending <= 0) + DBG("on completion no DMA in progress - " + "now %"FMT_HPT" started %lu reset %lu stopped %lu\n", + hptime(), since_ns(host_priv->when_started), + since_ns(host_priv->when_reset), + since_ns(host_priv->when_stopped)); + else if (host_priv->dmas_pending > 1) + DBG("still %d DMA in progress after completion - " + "now %"FMT_HPT" started %lu reset %lu stopped %lu\n", + host_priv->dmas_pending - 1, + hptime(), since_ns(host_priv->when_started), + since_ns(host_priv->when_reset), + since_ns(host_priv->when_stopped)); + BUG_ON(host_priv->dmas_pending <= 0); + host_priv->dmas_pending -= 1; + host_priv->when_stopped = hptime(); +#endif + host_priv->dma_wanted = 0; + + if (NULL == data) { + DBG("PDMA unused completion - status 0x%X\n", dma_cs); +// spin_unlock_irqrestore(&host->lock, flags); + return; + } + sg = data->sg; + sg_len = data->sg_len; + sg_todo = sg_dma_len(&sg[host_priv->sg_ix]); + + DBG("PDMA complete %d/%d [%d]/[%d]..\n", + host_priv->sg_done, sg_todo, + host_priv->sg_ix+1, sg_len); + + BUG_ON(host_priv->sg_done > sg_todo); + + if (host_priv->sg_done >= sg_todo) { + host_priv->sg_ix++; + host_priv->sg_done = 0; + } + + sg_ix = host_priv->sg_ix; + if (sg_ix < sg_len) { + u32 irq_mask; + /* Set off next DMA if we've got the capacity */ + + if (data->flags & MMC_DATA_READ) + irq_mask = SDHCI_INT_DATA_AVAIL; + else + irq_mask = SDHCI_INT_SPACE_AVAIL; + + /* We have to use the interrupt status register on the BCM2708 + rather than the SDHCI_PRESENT_STATE register because latency + in the glue logic means that the information retrieved from + the latter is not always up-to-date w.r.t the DMA engine - + it may not indicate that a read or a write is ready yet */ + if (sdhci_bcm2708_raw_readl(host, SDHCI_INT_STATUS) & + irq_mask) { + size_t bytes = sg_dma_len(&sg[sg_ix]) - + host_priv->sg_done; + dma_addr_t addr = sg_dma_address(&data->sg[sg_ix]) + + host_priv->sg_done; + + /* acknowledge interrupt */ + sdhci_bcm2708_raw_writel(host, irq_mask, + SDHCI_INT_STATUS); + + BUG_ON(0 == bytes); + + if (data->flags & MMC_DATA_READ) + sdhci_platdma_read(host, addr, bytes); + else + sdhci_platdma_write(host, addr, bytes); + } else { + DBG("PDMA - wait avail\n"); + /* may generate an IRQ if already present */ + sdhci_signal_irqs(host, SDHCI_INT_DATA_AVAIL | + SDHCI_INT_SPACE_AVAIL); + } + } else { + if (sync_after_dma) { + /* On the Arasan controller the stop command (which will be + scheduled after this completes) does not seem to work + properly if we allow it to be issued when we are + transferring data to/from the SD card. + We get CRC and DEND errors unless we wait for + the SD controller to finish reading/writing to the card. */ + u32 state_mask; + int timeout=30*5000; + + DBG("PDMA over - sync card\n"); + if (data->flags & MMC_DATA_READ) + state_mask = SDHCI_DOING_READ; + else + state_mask = SDHCI_DOING_WRITE; + + while (0 != (sdhci_bcm2708_raw_readl(host, SDHCI_PRESENT_STATE) + & state_mask) && --timeout > 0) + { + udelay(1); + continue; + } + if (timeout <= 0) + printk(KERN_ERR"%s: final %s to SD card still " + "running\n", + mmc_hostname(host->mmc), + data->flags & MMC_DATA_READ? "read": "write"); + } + if (host_priv->complete) { + (*host_priv->complete)(host); + DBG("PDMA %s complete\n", + data->flags & MMC_DATA_READ?"read":"write"); + sdhci_signal_irqs(host, SDHCI_INT_DATA_AVAIL | + SDHCI_INT_SPACE_AVAIL); + } + } +// spin_unlock_irqrestore(&host->lock, flags); +} + +static irqreturn_t sdhci_bcm2708_dma_irq(int irq, void *dev_id) +{ + irqreturn_t result = IRQ_NONE; + struct sdhci_host *host = dev_id; + struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); + u32 dma_cs; /* control and status register */ + + BUG_ON(NULL == dev_id); + BUG_ON(NULL == host_priv->dma_chan_base); + + sdhci_spin_lock(host); + + dma_cs = readl(host_priv->dma_chan_base + BCM2708_DMA_CS); + + if (dma_cs & BCM2708_DMA_ERR) { + unsigned long debug; + debug = readl(host_priv->dma_chan_base + + BCM2708_DMA_DEBUG); + printk(KERN_ERR "%s: DMA error - CS %lX DEBUG %lX\n", + mmc_hostname(host->mmc), (unsigned long)dma_cs, + (unsigned long)debug); + /* reset error */ + writel(debug, host_priv->dma_chan_base + + BCM2708_DMA_DEBUG); + } + if (dma_cs & BCM2708_DMA_INT) { + /* acknowledge interrupt */ + writel(BCM2708_DMA_INT, + host_priv->dma_chan_base + BCM2708_DMA_CS); + + dsb(); /* ARM data synchronization (push) operation */ + + if (!host_priv->dma_wanted) { + /* ignore this interrupt - it was reset */ + if (extra_messages) + printk(KERN_INFO "%s: DMA IRQ %X ignored - " + "results were reset\n", + mmc_hostname(host->mmc), dma_cs); +#ifdef CHECK_DMA_USE + printk(KERN_INFO "%s: now %"FMT_HPT + " started %lu reset %lu stopped %lu\n", + mmc_hostname(host->mmc), hptime(), + since_ns(host_priv->when_started), + since_ns(host_priv->when_reset), + since_ns(host_priv->when_stopped)); + host_priv->dmas_pending--; +#endif + } else + sdhci_bcm2708_dma_complete_irq(host, dma_cs); + + result = IRQ_HANDLED; + } + sdhci_spin_unlock(host); + + return result; +} +#endif /* CONFIG_MMC_SDHCI_BCM2708_DMA */ + + +/***************************************************************************** \ + * * + * Device Attributes * + * * +\*****************************************************************************/ + + +/** + * Show the DMA-using status + */ +static ssize_t attr_dma_show(struct device *_dev, + struct device_attribute *attr, char *buf) +{ + struct sdhci_host *host = (struct sdhci_host *)dev_get_drvdata(_dev); + + if (host) { + int use_dma = (host->flags & SDHCI_USE_PLATDMA? 1:0); + return sprintf(buf, "%d\n", use_dma); + } else + return -EINVAL; +} + +/** + * Set the DMA-using status + */ +static ssize_t attr_dma_store(struct device *_dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sdhci_host *host = (struct sdhci_host *)dev_get_drvdata(_dev); + + if (host) { +#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA + int on = simple_strtol(buf, NULL, 0); + if (on) { + host->flags |= SDHCI_USE_PLATDMA; + sdhci_bcm2708_writel(host, 1, REG_EXRDFIFO_EN); + printk(KERN_INFO "%s: DMA enabled\n", + mmc_hostname(host->mmc)); + } else { + host->flags &= ~(SDHCI_USE_PLATDMA | SDHCI_REQ_USE_DMA); + sdhci_bcm2708_writel(host, 0, REG_EXRDFIFO_EN); + printk(KERN_INFO "%s: DMA disabled\n", + mmc_hostname(host->mmc)); + } +#endif + return count; + } else + return -EINVAL; +} + +static DEVICE_ATTR(use_dma, S_IRUGO | S_IWUGO, attr_dma_show, attr_dma_store); + + +/** + * Show the DMA wait states used + */ +static ssize_t attr_dmawait_show(struct device *_dev, + struct device_attribute *attr, char *buf) +{ + struct sdhci_host *host = (struct sdhci_host *)dev_get_drvdata(_dev); + + if (host) { + struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); + int dmawait = host_priv->dma_waits; + return sprintf(buf, "%d\n", dmawait); + } else + return -EINVAL; +} + +/** + * Set the DMA wait state used + */ +static ssize_t attr_dmawait_store(struct device *_dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sdhci_host *host = (struct sdhci_host *)dev_get_drvdata(_dev); + + if (host) { +#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA + struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); + int dma_waits = simple_strtol(buf, NULL, 0); + if (dma_waits >= 0 && dma_waits < 32) + host_priv->dma_waits = dma_waits; + else + printk(KERN_ERR "%s: illegal dma_waits value - %d", + mmc_hostname(host->mmc), dma_waits); +#endif + return count; + } else + return -EINVAL; +} + +static DEVICE_ATTR(dma_wait, S_IRUGO | S_IWUGO, + attr_dmawait_show, attr_dmawait_store); + + +/** + * Show the DMA-using status + */ +static ssize_t attr_status_show(struct device *_dev, + struct device_attribute *attr, char *buf) +{ + struct sdhci_host *host = (struct sdhci_host *)dev_get_drvdata(_dev); + + if (host) { + struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); + return sprintf(buf, + "present: yes\n" + "power: %s\n" + "clock: %u Hz\n" +#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA + "dma: %s (%d waits)\n", +#else + "dma: unconfigured\n", +#endif + "always on", + host->clock +#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA + , (host->flags & SDHCI_USE_PLATDMA)? "on": "off" + , host_priv->dma_waits +#endif + ); + } else + return -EINVAL; +} + +static DEVICE_ATTR(status, S_IRUGO, attr_status_show, NULL); + +/***************************************************************************** \ + * * + * Power Management * + * * +\*****************************************************************************/ + + +#ifdef CONFIG_PM +static int sdhci_bcm2708_suspend(struct platform_device *dev, pm_message_t state) +{ + struct sdhci_host *host = (struct sdhci_host *) + platform_get_drvdata(dev); + int ret = 0; + + if (host->mmc) { + ret = mmc_suspend_host(host->mmc); + } + + return ret; +} + +static int sdhci_bcm2708_resume(struct platform_device *dev) +{ + struct sdhci_host *host = (struct sdhci_host *) + platform_get_drvdata(dev); + int ret = 0; + + if (host->mmc) { + ret = mmc_resume_host(host->mmc); + } + + return ret; +} +#endif + + +/*****************************************************************************\ + * * + * Device quirk functions. Implemented as local ops because the flags * + * field is out of space with newer kernels. This implementation can be * + * back ported to older kernels as well. * +\****************************************************************************/ +static unsigned int sdhci_bcm2708_quirk_extra_ints(struct sdhci_host *host) +{ + return 1; +} + +static unsigned int sdhci_bcm2708_quirk_spurious_crc_acmd51(struct sdhci_host *host) +{ + return 1; +} + +static unsigned int sdhci_bcm2708_quirk_voltage_broken(struct sdhci_host *host) +{ + return 1; +} + +static unsigned int sdhci_bcm2708_uhs_broken(struct sdhci_host *host) +{ + return 1; +} + +static unsigned int sdhci_bcm2708_missing_status(struct sdhci_host *host) +{ + return 1; +} + +/***************************************************************************** \ + * * + * Device ops * + * * +\*****************************************************************************/ + +static struct sdhci_ops sdhci_bcm2708_ops = { +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS + .read_l = sdhci_bcm2708_readl, + .read_w = sdhci_bcm2708_readw, + .read_b = sdhci_bcm2708_readb, + .write_l = sdhci_bcm2708_writel, + .write_w = sdhci_bcm2708_writew, + .write_b = sdhci_bcm2708_writeb, +#else +#error The BCM2708 SDHCI driver needs CONFIG_MMC_SDHCI_IO_ACCESSORS to be set +#endif + .get_max_clock = sdhci_bcm2708_get_max_clock, + +#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA + // Platform DMA operations + .pdma_able = sdhci_bcm2708_platdma_dmaable, + .pdma_avail = sdhci_bcm2708_platdma_avail, + .pdma_reset = sdhci_bcm2708_platdma_reset, +#endif + .extra_ints = sdhci_bcm2708_quirk_extra_ints, + .voltage_broken = sdhci_bcm2708_quirk_voltage_broken, + .uhs_broken = sdhci_bcm2708_uhs_broken, +}; + +/*****************************************************************************\ + * * + * Device probing/removal * + * * +\*****************************************************************************/ + +static int sdhci_bcm2708_probe(struct platform_device *pdev) +{ + struct sdhci_host *host; + struct resource *iomem; + struct sdhci_bcm2708_priv *host_priv; + int ret; + + BUG_ON(pdev == NULL); + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iomem) { + ret = -ENOMEM; + goto err; + } + + if (resource_size(iomem) != 0x100) + dev_err(&pdev->dev, "Invalid iomem size. You may " + "experience problems.\n"); + + if (pdev->dev.parent) + host = sdhci_alloc_host(pdev->dev.parent, + sizeof(struct sdhci_bcm2708_priv)); + else + host = sdhci_alloc_host(&pdev->dev, + sizeof(struct sdhci_bcm2708_priv)); + + if (IS_ERR(host)) { + ret = PTR_ERR(host); + goto err; + } + if (missing_status) { + sdhci_bcm2708_ops.missing_status = sdhci_bcm2708_missing_status; + } + + if( spurious_crc_acmd51 ) { + sdhci_bcm2708_ops.spurious_crc_acmd51 = sdhci_bcm2708_quirk_spurious_crc_acmd51; + } + + + printk("sdhci: %s low-latency mode\n",enable_llm?"Enable":"Disable"); + + host->hw_name = "BCM2708_Arasan"; + host->ops = &sdhci_bcm2708_ops; + host->irq = platform_get_irq(pdev, 0); + host->second_irq = 0; + + host->quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | + SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | + SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | + SDHCI_QUIRK_MISSING_CAPS | + SDHCI_QUIRK_NO_HISPD_BIT | + (sync_after_dma ? 0:SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12); + + +#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA + host->flags = SDHCI_USE_PLATDMA; +#endif + + if (!request_mem_region(iomem->start, resource_size(iomem), + mmc_hostname(host->mmc))) { + dev_err(&pdev->dev, "cannot request region\n"); + ret = -EBUSY; + goto err_request; + } + + host->ioaddr = ioremap(iomem->start, resource_size(iomem)); + if (!host->ioaddr) { + dev_err(&pdev->dev, "failed to remap registers\n"); + ret = -ENOMEM; + goto err_remap; + } + + host_priv = SDHCI_HOST_PRIV(host); + +#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA + host_priv->dma_wanted = 0; +#ifdef CHECK_DMA_USE + host_priv->dmas_pending = 0; + host_priv->when_started = 0; + host_priv->when_reset = 0; + host_priv->when_stopped = 0; +#endif + host_priv->sg_ix = 0; + host_priv->sg_done = 0; + host_priv->complete = NULL; + host_priv->dma_waits = SDHCI_BCM_DMA_WAITS; + + host_priv->cb_base = dma_alloc_writecombine(&pdev->dev, SZ_4K, + &host_priv->cb_handle, + GFP_KERNEL); + if (!host_priv->cb_base) { + dev_err(&pdev->dev, "cannot allocate DMA CBs\n"); + ret = -ENOMEM; + goto err_alloc_cb; + } + + ret = bcm_dma_chan_alloc(BCM_DMA_FEATURE_FAST, + &host_priv->dma_chan_base, + &host_priv->dma_irq); + if (ret < 0) { + dev_err(&pdev->dev, "couldn't allocate a DMA channel\n"); + goto err_add_dma; + } + host_priv->dma_chan = ret; + + ret = request_irq(host_priv->dma_irq, sdhci_bcm2708_dma_irq,0,//IRQF_SHARED, + DRIVER_NAME " (dma)", host); + if (ret) { + dev_err(&pdev->dev, "cannot set DMA IRQ\n"); + goto err_add_dma_irq; + } + host->second_irq = host_priv->dma_irq; + DBG("DMA CBs %p handle %08X DMA%d %p DMA IRQ %d\n", + host_priv->cb_base, (unsigned)host_priv->cb_handle, + host_priv->dma_chan, host_priv->dma_chan_base, + host_priv->dma_irq); + + if (allow_highspeed) + host->mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; + + /* single block writes cause data loss with some SD cards! */ + host->mmc->caps2 |= MMC_CAP2_FORCE_MULTIBLOCK; +#endif + + ret = sdhci_add_host(host); + if (ret) + goto err_add_host; + + platform_set_drvdata(pdev, host); + ret = device_create_file(&pdev->dev, &dev_attr_use_dma); + ret = device_create_file(&pdev->dev, &dev_attr_dma_wait); + ret = device_create_file(&pdev->dev, &dev_attr_status); + +#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA + /* enable extension fifo for paced DMA transfers */ + sdhci_bcm2708_writel(host, 1, REG_EXRDFIFO_EN); + sdhci_bcm2708_writel(host, 4, REG_EXRDFIFO_CFG); +#endif + + printk(KERN_INFO "%s: BCM2708 SDHC host at 0x%08llx DMA %d IRQ %d\n", + mmc_hostname(host->mmc), (unsigned long long)iomem->start, + host_priv->dma_chan, host_priv->dma_irq); + + return 0; + +err_add_host: +#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA + free_irq(host_priv->dma_irq, host); +err_add_dma_irq: + bcm_dma_chan_free(host_priv->dma_chan); +err_add_dma: + dma_free_writecombine(&pdev->dev, SZ_4K, host_priv->cb_base, + host_priv->cb_handle); +err_alloc_cb: +#endif + iounmap(host->ioaddr); +err_remap: + release_mem_region(iomem->start, resource_size(iomem)); +err_request: + sdhci_free_host(host); +err: + dev_err(&pdev->dev, "probe failed, err %d\n", ret); + return ret; +} + +static int sdhci_bcm2708_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); + int dead; + u32 scratch; + + dead = 0; + scratch = sdhci_bcm2708_readl(host, SDHCI_INT_STATUS); + if (scratch == (u32)-1) + dead = 1; + + device_remove_file(&pdev->dev, &dev_attr_status); + device_remove_file(&pdev->dev, &dev_attr_dma_wait); + device_remove_file(&pdev->dev, &dev_attr_use_dma); + +#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA + free_irq(host_priv->dma_irq, host); + dma_free_writecombine(&pdev->dev, SZ_4K, host_priv->cb_base, + host_priv->cb_handle); +#endif + sdhci_remove_host(host, dead); + iounmap(host->ioaddr); + release_mem_region(iomem->start, resource_size(iomem)); + sdhci_free_host(host); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver sdhci_bcm2708_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = sdhci_bcm2708_probe, + .remove = sdhci_bcm2708_remove, + +#ifdef CONFIG_PM + .suspend = sdhci_bcm2708_suspend, + .resume = sdhci_bcm2708_resume, +#endif + +}; + +/*****************************************************************************\ + * * + * Driver init/exit * + * * +\*****************************************************************************/ + +static int __init sdhci_drv_init(void) +{ + return platform_driver_register(&sdhci_bcm2708_driver); +} + +static void __exit sdhci_drv_exit(void) +{ + platform_driver_unregister(&sdhci_bcm2708_driver); +} + +module_init(sdhci_drv_init); +module_exit(sdhci_drv_exit); + +module_param(allow_highspeed, bool, 0444); +module_param(emmc_clock_freq, int, 0444); +module_param(sync_after_dma, bool, 0444); +module_param(missing_status, bool, 0444); +module_param(spurious_crc_acmd51, bool, 0444); +module_param(enable_llm, bool, 0444); +module_param(cycle_delay, int, 0444); +module_param(extra_messages, bool, 0444); + +MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver"); +MODULE_AUTHOR("Broadcom "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:"DRIVER_NAME); + +MODULE_PARM_DESC(allow_highspeed, "Allow high speed transfers modes"); +MODULE_PARM_DESC(emmc_clock_freq, "Specify the speed of emmc clock"); +MODULE_PARM_DESC(sync_after_dma, "Block in driver until dma complete"); +MODULE_PARM_DESC(missing_status, "Use the missing status quirk"); +MODULE_PARM_DESC(spurious_crc_acmd51, "Use the spurious crc quirk for reading SCR (ACMD51)"); +MODULE_PARM_DESC(enable_llm, "Enable low-latency mode"); +MODULE_PARM_DESC(extra_messages, "Enable more sdcard warning messages"); + + diff -urwN linux-3.10/drivers/mmc/host/sdhci.c linux-rpi-3.10.y/drivers/mmc/host/sdhci.c --- linux-3.10/drivers/mmc/host/sdhci.c 2013-06-30 23:13:29.000000000 +0100 +++ linux-rpi-3.10.y/drivers/mmc/host/sdhci.c 2013-07-06 15:25:50.000000000 +0100 @@ -28,6 +28,7 @@ #include #include #include +#include #include #include "sdhci.h" @@ -123,6 +124,91 @@ * Low level functions * * * \*****************************************************************************/ +extern bool enable_llm; +static int sdhci_locked=0; +void sdhci_spin_lock(struct sdhci_host *host) +{ + spin_lock(&host->lock); +#ifdef CONFIG_PREEMPT + if(enable_llm) + { + disable_irq_nosync(host->irq); + if(host->second_irq) + disable_irq_nosync(host->second_irq); + local_irq_enable(); + } +#endif +} + +void sdhci_spin_unlock(struct sdhci_host *host) +{ +#ifdef CONFIG_PREEMPT + if(enable_llm) + { + local_irq_disable(); + if(host->second_irq) + enable_irq(host->second_irq); + enable_irq(host->irq); + } +#endif + spin_unlock(&host->lock); +} + +void sdhci_spin_lock_irqsave(struct sdhci_host *host,unsigned long *flags) +{ +#ifdef CONFIG_PREEMPT + if(enable_llm) + { + while(sdhci_locked) + { + preempt_schedule(); + } + spin_lock_irqsave(&host->lock,*flags); + disable_irq(host->irq); + if(host->second_irq) + disable_irq(host->second_irq); + local_irq_enable(); + } + else +#endif + spin_lock_irqsave(&host->lock,*flags); +} + +void sdhci_spin_unlock_irqrestore(struct sdhci_host *host,unsigned long flags) +{ +#ifdef CONFIG_PREEMPT + if(enable_llm) + { + local_irq_disable(); + if(host->second_irq) + enable_irq(host->second_irq); + enable_irq(host->irq); + } +#endif + spin_unlock_irqrestore(&host->lock,flags); +} + +static void sdhci_spin_enable_schedule(struct sdhci_host *host) +{ +#ifdef CONFIG_PREEMPT + if(enable_llm) + { + sdhci_locked = 1; + preempt_enable(); + } +#endif +} + +static void sdhci_spin_disable_schedule(struct sdhci_host *host) +{ +#ifdef CONFIG_PREEMPT + if(enable_llm) + { + preempt_disable(); + sdhci_locked = 0; + } +#endif +} static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set) { @@ -288,7 +374,7 @@ struct sdhci_host *host = container_of(led, struct sdhci_host, led); unsigned long flags; - spin_lock_irqsave(&host->lock, flags); + sdhci_spin_lock_irqsave(host, &flags); if (host->runtime_suspended) goto out; @@ -298,7 +384,7 @@ else sdhci_activate_led(host); out: - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); } #endif @@ -315,7 +401,7 @@ u32 uninitialized_var(scratch); u8 *buf; - DBG("PIO reading\n"); + DBG("PIO reading %db\n", host->data->blksz); blksize = host->data->blksz; chunk = 0; @@ -360,7 +446,7 @@ u32 scratch; u8 *buf; - DBG("PIO writing\n"); + DBG("PIO writing %db\n", host->data->blksz); blksize = host->data->blksz; chunk = 0; @@ -399,19 +485,28 @@ local_irq_restore(flags); } -static void sdhci_transfer_pio(struct sdhci_host *host) +static void sdhci_transfer_pio(struct sdhci_host *host, u32 intstate) { u32 mask; + u32 state = 0; + u32 intmask; + int available; BUG_ON(!host->data); if (host->blocks == 0) return; - if (host->data->flags & MMC_DATA_READ) + if (host->data->flags & MMC_DATA_READ) { mask = SDHCI_DATA_AVAILABLE; - else + intmask = SDHCI_INT_DATA_AVAIL; + } else { mask = SDHCI_SPACE_AVAILABLE; + intmask = SDHCI_INT_SPACE_AVAIL; + } + + /* initially we can see whether we can procede using intstate */ + available = (intstate & intmask); /* * Some controllers (JMicron JMB38x) mess up the buffer bits @@ -422,7 +517,7 @@ (host->data->blocks == 1)) mask = ~0; - while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { + while (available) { if (host->quirks & SDHCI_QUIRK_PIO_NEEDS_DELAY) udelay(100); @@ -434,9 +529,11 @@ host->blocks--; if (host->blocks == 0) break; + state = sdhci_readl(host, SDHCI_PRESENT_STATE); + available = state & mask; } - DBG("PIO transfer complete.\n"); + DBG("PIO transfer complete - %d blocks left.\n", host->blocks); } static char *sdhci_kmap_atomic(struct scatterlist *sg, unsigned long *flags) @@ -709,7 +806,9 @@ u32 pio_irqs = SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL; u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR; - if (host->flags & SDHCI_REQ_USE_DMA) + /* platform DMA will begin on receipt of PIO irqs */ + if ((host->flags & SDHCI_REQ_USE_DMA) && + !(host->flags & SDHCI_USE_PLATDMA)) sdhci_clear_set_irqs(host, pio_irqs, dma_irqs); else sdhci_clear_set_irqs(host, dma_irqs, pio_irqs); @@ -741,44 +840,25 @@ host->data_early = 0; host->data->bytes_xfered = 0; - if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA | SDHCI_USE_PLATDMA)) host->flags |= SDHCI_REQ_USE_DMA; /* * FIXME: This doesn't account for merging when mapping the * scatterlist. */ - if (host->flags & SDHCI_REQ_USE_DMA) { - int broken, i; - struct scatterlist *sg; - - broken = 0; - if (host->flags & SDHCI_USE_ADMA) { - if (host->quirks & SDHCI_QUIRK_32BIT_ADMA_SIZE) - broken = 1; - } else { - if (host->quirks & SDHCI_QUIRK_32BIT_DMA_SIZE) - broken = 1; - } - - if (unlikely(broken)) { - for_each_sg(data->sg, sg, data->sg_len, i) { - if (sg->length & 0x3) { - DBG("Reverting to PIO because of " - "transfer size (%d)\n", - sg->length); - host->flags &= ~SDHCI_REQ_USE_DMA; - break; - } - } - } - } /* * The assumption here being that alignment is the same after * translation to device address space. */ - if (host->flags & SDHCI_REQ_USE_DMA) { + if ((host->flags & (SDHCI_REQ_USE_DMA | SDHCI_USE_PLATDMA)) == + (SDHCI_REQ_USE_DMA | SDHCI_USE_PLATDMA)) { + + if (! sdhci_platdma_dmaable(host, data)) + host->flags &= ~SDHCI_REQ_USE_DMA; + + } else if (host->flags & SDHCI_REQ_USE_DMA) { int broken, i; struct scatterlist *sg; @@ -837,7 +917,8 @@ */ WARN_ON(1); host->flags &= ~SDHCI_REQ_USE_DMA; - } else { + } else + if (!(host->flags & SDHCI_USE_PLATDMA)) { WARN_ON(sg_cnt != 1); sdhci_writel(host, sg_dma_address(data->sg), SDHCI_DMA_ADDRESS); @@ -853,11 +934,13 @@ if (host->version >= SDHCI_SPEC_200) { ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl &= ~SDHCI_CTRL_DMA_MASK; + if (! (host->flags & SDHCI_USE_PLATDMA)) { if ((host->flags & SDHCI_REQ_USE_DMA) && (host->flags & SDHCI_USE_ADMA)) ctrl |= SDHCI_CTRL_ADMA32; else ctrl |= SDHCI_CTRL_SDMA; + } sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } @@ -909,7 +992,8 @@ if (data->flags & MMC_DATA_READ) mode |= SDHCI_TRNS_READ; - if (host->flags & SDHCI_REQ_USE_DMA) + if ((host->flags & SDHCI_REQ_USE_DMA) && + !(host->flags & SDHCI_USE_PLATDMA)) mode |= SDHCI_TRNS_DMA; sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); @@ -925,13 +1009,16 @@ host->data = NULL; if (host->flags & SDHCI_REQ_USE_DMA) { - if (host->flags & SDHCI_USE_ADMA) - sdhci_adma_table_post(host, data); - else { + /* we may have to abandon an ongoing platform DMA */ + if (host->flags & SDHCI_USE_PLATDMA) + sdhci_platdma_reset(host, data); + + if (host->flags & (SDHCI_USE_PLATDMA | SDHCI_USE_SDMA)) { dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, (data->flags & MMC_DATA_READ) ? DMA_FROM_DEVICE : DMA_TO_DEVICE); - } + } else if (host->flags & SDHCI_USE_ADMA) + sdhci_adma_table_post(host, data); } /* @@ -984,6 +1071,12 @@ if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY)) mask |= SDHCI_DATA_INHIBIT; + if(host->ops->missing_status && (cmd->opcode == MMC_SEND_STATUS)) { + timeout = 5000; // Really obscenely large delay to send the status, due to bug in controller + // which might cause the STATUS command to get stuck when a data operation is in flow + mask |= SDHCI_DATA_INHIBIT; + } + /* We shouldn't wait for data inihibit for stop commands, even though they might use busy signaling */ if (host->mrq->data && (cmd == host->mrq->data->stop)) @@ -999,12 +1092,20 @@ return; } timeout--; + sdhci_spin_enable_schedule(host); mdelay(1); + sdhci_spin_disable_schedule(host); } + DBG("send cmd %d - wait 0x%X irq 0x%x\n", cmd->opcode, mask, + sdhci_readl(host, SDHCI_INT_STATUS)); mod_timer(&host->timer, jiffies + 10 * HZ); host->cmd = cmd; + if (host->last_cmdop == MMC_APP_CMD) + host->last_cmdop = -cmd->opcode; + else + host->last_cmdop = cmd->opcode; sdhci_prepare_data(host, cmd); @@ -1220,7 +1321,9 @@ return; } timeout--; + sdhci_spin_enable_schedule(host); mdelay(1); + sdhci_spin_disable_schedule(host); } clk |= SDHCI_CLOCK_CARD_EN; @@ -1316,7 +1419,7 @@ sdhci_runtime_pm_get(host); - spin_lock_irqsave(&host->lock, flags); + sdhci_spin_lock_irqsave(host, &flags); WARN_ON(host->mrq != NULL); @@ -1374,9 +1477,9 @@ mmc->card->type == MMC_TYPE_MMC ? MMC_SEND_TUNING_BLOCK_HS200 : MMC_SEND_TUNING_BLOCK; - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); sdhci_execute_tuning(mmc, tuning_opcode); - spin_lock_irqsave(&host->lock, flags); + sdhci_spin_lock_irqsave(host, &flags); /* Restore original mmc_request structure */ host->mrq = mrq; @@ -1390,7 +1493,7 @@ } mmiowb(); - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); } static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) @@ -1399,10 +1502,10 @@ int vdd_bit = -1; u8 ctrl; - spin_lock_irqsave(&host->lock, flags); + sdhci_spin_lock_irqsave(host, &flags); if (host->flags & SDHCI_DEVICE_DEAD) { - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); if (host->vmmc && ios->power_mode == MMC_POWER_OFF) mmc_regulator_set_ocr(host->mmc, host->vmmc, 0); return; @@ -1429,9 +1532,9 @@ vdd_bit = sdhci_set_power(host, ios->vdd); if (host->vmmc && vdd_bit != -1) { - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd_bit); - spin_lock_irqsave(&host->lock, flags); + sdhci_spin_lock_irqsave(host, &flags); } if (host->ops->platform_send_init_74_clocks) @@ -1470,7 +1573,7 @@ else ctrl &= ~SDHCI_CTRL_HISPD; - if (host->version >= SDHCI_SPEC_300) { + if (host->version >= SDHCI_SPEC_300 && !(host->ops->uhs_broken)) { u16 clk, ctrl_2; /* In case of UHS-I modes, set High Speed Enable */ @@ -1569,7 +1672,7 @@ sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA); mmiowb(); - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); } static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) @@ -1617,7 +1720,7 @@ unsigned long flags; int is_readonly; - spin_lock_irqsave(&host->lock, flags); + sdhci_spin_lock_irqsave(host, &flags); if (host->flags & SDHCI_DEVICE_DEAD) is_readonly = 0; @@ -1627,7 +1730,7 @@ is_readonly = !(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_WRITE_PROTECT); - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); /* This quirk needs to be replaced by a callback-function later */ return host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT ? @@ -1700,9 +1803,9 @@ struct sdhci_host *host = mmc_priv(mmc); unsigned long flags; - spin_lock_irqsave(&host->lock, flags); + sdhci_spin_lock_irqsave(host, &flags); sdhci_enable_sdio_irq_nolock(host, enable); - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); } static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, @@ -2046,7 +2149,7 @@ struct sdhci_host *host = mmc_priv(mmc); unsigned long flags; - spin_lock_irqsave(&host->lock, flags); + sdhci_spin_lock_irqsave(host, &flags); /* Check host->mrq first in case we are runtime suspended */ if (host->mrq && @@ -2063,7 +2166,7 @@ tasklet_schedule(&host->finish_tasklet); } - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); } static const struct mmc_host_ops sdhci_ops = { @@ -2102,14 +2205,14 @@ host = (struct sdhci_host*)param; - spin_lock_irqsave(&host->lock, flags); + sdhci_spin_lock_irqsave(host, &flags); /* * If this tasklet gets rescheduled while running, it will * be run again afterwards but without any active request. */ if (!host->mrq) { - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); return; } @@ -2147,7 +2250,7 @@ #endif mmiowb(); - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); mmc_request_done(host->mmc, mrq); sdhci_runtime_pm_put(host); @@ -2160,11 +2263,11 @@ host = (struct sdhci_host*)data; - spin_lock_irqsave(&host->lock, flags); + sdhci_spin_lock_irqsave(host, &flags); if (host->mrq) { pr_err("%s: Timeout waiting for hardware " - "interrupt.\n", mmc_hostname(host->mmc)); + "interrupt - cmd%d.\n", mmc_hostname(host->mmc), host->last_cmdop); sdhci_dumpregs(host); if (host->data) { @@ -2181,7 +2284,7 @@ } mmiowb(); - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); } static void sdhci_tuning_timer(unsigned long data) @@ -2191,11 +2294,11 @@ host = (struct sdhci_host *)data; - spin_lock_irqsave(&host->lock, flags); + sdhci_spin_lock_irqsave(host, &flags); host->flags |= SDHCI_NEEDS_RETUNING; - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); } /*****************************************************************************\ @@ -2209,10 +2312,13 @@ BUG_ON(intmask == 0); if (!host->cmd) { + if (!(host->ops->extra_ints)) { pr_err("%s: Got command interrupt 0x%08x even " "though no command operation was in progress.\n", mmc_hostname(host->mmc), (unsigned)intmask); sdhci_dumpregs(host); + } else + DBG("cmd irq 0x%08x cmd complete\n", (unsigned)intmask); return; } @@ -2282,6 +2388,19 @@ static void sdhci_show_adma_error(struct sdhci_host *host) { } #endif +static void sdhci_data_end(struct sdhci_host *host) +{ + if (host->cmd) { + /* + * Data managed to finish before the + * command completed. Make sure we do + * things in the proper order. + */ + host->data_early = 1; + } else + sdhci_finish_data(host); +} + static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) { u32 command; @@ -2311,23 +2430,39 @@ } } + if (!(host->ops->extra_ints)) { pr_err("%s: Got data interrupt 0x%08x even " "though no data operation was in progress.\n", mmc_hostname(host->mmc), (unsigned)intmask); sdhci_dumpregs(host); + } else + DBG("data irq 0x%08x but no data\n", (unsigned)intmask); return; } if (intmask & SDHCI_INT_DATA_TIMEOUT) host->data->error = -ETIMEDOUT; - else if (intmask & SDHCI_INT_DATA_END_BIT) + else if (intmask & SDHCI_INT_DATA_END_BIT) { + DBG("end error in cmd %d\n", host->last_cmdop); + if (host->ops->spurious_crc_acmd51 && + host->last_cmdop == -SD_APP_SEND_SCR) { + DBG("ignoring spurious data_end_bit error\n"); + intmask = SDHCI_INT_DATA_AVAIL|SDHCI_INT_DATA_END; + } else host->data->error = -EILSEQ; - else if ((intmask & SDHCI_INT_DATA_CRC) && + } else if ((intmask & SDHCI_INT_DATA_CRC) && SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) - != MMC_BUS_TEST_R) + != MMC_BUS_TEST_R) { + DBG("crc error in cmd %d\n", host->last_cmdop); + if (host->ops->spurious_crc_acmd51 && + host->last_cmdop == -SD_APP_SEND_SCR) { + DBG("ignoring spurious data_crc_bit error\n"); + intmask = SDHCI_INT_DATA_AVAIL|SDHCI_INT_DATA_END; + } else { host->data->error = -EILSEQ; - else if (intmask & SDHCI_INT_ADMA_ERROR) { + } + } else if (intmask & SDHCI_INT_ADMA_ERROR) { pr_err("%s: ADMA error\n", mmc_hostname(host->mmc)); sdhci_show_adma_error(host); host->data->error = -EIO; @@ -2335,11 +2470,18 @@ host->ops->adma_workaround(host, intmask); } - if (host->data->error) + if (host->data->error) { + DBG("finish request early on error %d\n", host->data->error); sdhci_finish_data(host); - else { - if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) - sdhci_transfer_pio(host); + } else { + if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) { + if (host->flags & SDHCI_REQ_USE_DMA) { + /* possible only in PLATDMA mode */ + sdhci_platdma_avail(host, &intmask, + &sdhci_data_end); + } else + sdhci_transfer_pio(host, intmask); + } /* * We currently don't do anything fancy with DMA @@ -2368,18 +2510,8 @@ sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS); } - if (intmask & SDHCI_INT_DATA_END) { - if (host->cmd) { - /* - * Data managed to finish before the - * command completed. Make sure we do - * things in the proper order. - */ - host->data_early = 1; - } else { - sdhci_finish_data(host); - } - } + if (intmask & SDHCI_INT_DATA_END) + sdhci_data_end(host); } } @@ -2390,10 +2522,10 @@ u32 intmask, unexpected = 0; int cardint = 0, max_loops = 16; - spin_lock(&host->lock); + sdhci_spin_lock(host); if (host->runtime_suspended) { - spin_unlock(&host->lock); + sdhci_spin_unlock(host); pr_warning("%s: got irq while runtime suspended\n", mmc_hostname(host->mmc)); return IRQ_HANDLED; @@ -2435,6 +2567,22 @@ tasklet_schedule(&host->card_tasklet); } + if (intmask & SDHCI_INT_ERROR_MASK & ~SDHCI_INT_ERROR) + DBG("controller reports error 0x%x -" + "%s%s%s%s%s%s%s%s%s%s", + intmask, + intmask & SDHCI_INT_TIMEOUT? " timeout": "", + intmask & SDHCI_INT_CRC ? " crc": "", + intmask & SDHCI_INT_END_BIT? " endbit": "", + intmask & SDHCI_INT_INDEX? " index": "", + intmask & SDHCI_INT_DATA_TIMEOUT? " data_timeout": "", + intmask & SDHCI_INT_DATA_CRC? " data_crc": "", + intmask & SDHCI_INT_DATA_END_BIT? " data_endbit": "", + intmask & SDHCI_INT_BUS_POWER? " buspower": "", + intmask & SDHCI_INT_ACMD12ERR? " acmd12": "", + intmask & SDHCI_INT_ADMA_ERROR? " adma": "" + ); + if (intmask & SDHCI_INT_CMD_MASK) { sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK, SDHCI_INT_STATUS); @@ -2449,7 +2597,13 @@ intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK); - intmask &= ~SDHCI_INT_ERROR; + if (intmask & SDHCI_INT_ERROR_MASK) { + /* collect any uncovered errors */ + sdhci_writel(host, intmask & SDHCI_INT_ERROR_MASK, + SDHCI_INT_STATUS); + } + + intmask &= ~SDHCI_INT_ERROR_MASK; if (intmask & SDHCI_INT_BUS_POWER) { pr_err("%s: Card is consuming too much power!\n", @@ -2475,7 +2629,7 @@ if (intmask && --max_loops) goto again; out: - spin_unlock(&host->lock); + sdhci_spin_unlock(host); if (unexpected) { pr_err("%s: Unexpected interrupt 0x%08x.\n", @@ -2569,7 +2723,8 @@ { int ret; - if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA | + SDHCI_USE_PLATDMA)) { if (host->ops->enable_dma) host->ops->enable_dma(host); } @@ -2636,15 +2791,15 @@ host->flags &= ~SDHCI_NEEDS_RETUNING; } - spin_lock_irqsave(&host->lock, flags); + sdhci_spin_lock_irqsave(host, &flags); sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); synchronize_irq(host->irq); - spin_lock_irqsave(&host->lock, flags); + sdhci_spin_lock_irqsave(host, &flags); host->runtime_suspended = true; - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); return ret; } @@ -2670,16 +2825,16 @@ sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios); if ((host_flags & SDHCI_PV_ENABLED) && !(host->quirks2 & SDHCI_QUIRK2_PRESET_VALUE_BROKEN)) { - spin_lock_irqsave(&host->lock, flags); + sdhci_spin_lock_irqsave(host, &flags); sdhci_enable_preset_value(host, true); - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); } /* Set the re-tuning expiration flag */ if (host->flags & SDHCI_USING_RETUNING_TIMER) host->flags |= SDHCI_NEEDS_RETUNING; - spin_lock_irqsave(&host->lock, flags); + sdhci_spin_lock_irqsave(host, &flags); host->runtime_suspended = false; @@ -2690,7 +2845,7 @@ /* Enable Card Detection */ sdhci_enable_card_detection(host); - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); return ret; } @@ -2785,14 +2940,16 @@ host->flags &= ~SDHCI_USE_ADMA; } - if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { + if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA | + SDHCI_USE_PLATDMA)) { if (host->ops->enable_dma) { if (host->ops->enable_dma(host)) { pr_warning("%s: No suitable DMA " "available. Falling back to PIO.\n", mmc_hostname(mmc)); host->flags &= - ~(SDHCI_USE_SDMA | SDHCI_USE_ADMA); + ~(SDHCI_USE_SDMA | SDHCI_USE_ADMA | + SDHCI_USE_PLATDMA); } } } @@ -3080,6 +3237,12 @@ SDHCI_MAX_CURRENT_MULTIPLIER; } + if(host->ops->voltage_broken) { + ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34; + // Cannot support UHS modes if we are stuck at 3.3V; + mmc->caps &= ~(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_DDR50); + } + mmc->ocr_avail = ocr_avail; mmc->ocr_avail_sdio = ocr_avail; if (host->ocr_avail_sdio) @@ -3174,7 +3337,7 @@ host->tuning_timer.function = sdhci_tuning_timer; } - ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, + ret = request_irq(host->irq, sdhci_irq, 0,//IRQF_SHARED, mmc_hostname(mmc), host); if (ret) { pr_err("%s: Failed to request IRQ %d: %d\n", @@ -3210,6 +3373,7 @@ pr_info("%s: SDHCI controller on %s [%s] using %s\n", mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)), + (host->flags & SDHCI_USE_PLATDMA) ? "platform's DMA" : (host->flags & SDHCI_USE_ADMA) ? "ADMA" : (host->flags & SDHCI_USE_SDMA) ? "DMA" : "PIO"); @@ -3237,7 +3401,7 @@ unsigned long flags; if (dead) { - spin_lock_irqsave(&host->lock, flags); + sdhci_spin_lock_irqsave(host, &flags); host->flags |= SDHCI_DEVICE_DEAD; @@ -3249,7 +3413,7 @@ tasklet_schedule(&host->finish_tasklet); } - spin_unlock_irqrestore(&host->lock, flags); + sdhci_spin_unlock_irqrestore(host, flags); } sdhci_disable_card_detection(host); diff -urwN linux-3.10/drivers/mmc/host/sdhci.h linux-rpi-3.10.y/drivers/mmc/host/sdhci.h --- linux-3.10/drivers/mmc/host/sdhci.h 2013-06-30 23:13:29.000000000 +0100 +++ linux-rpi-3.10.y/drivers/mmc/host/sdhci.h 2013-07-06 15:25:50.000000000 +0100 @@ -289,6 +289,20 @@ void (*platform_reset_enter)(struct sdhci_host *host, u8 mask); void (*platform_reset_exit)(struct sdhci_host *host, u8 mask); int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs); + + int (*pdma_able)(struct sdhci_host *host, + struct mmc_data *data); + void (*pdma_avail)(struct sdhci_host *host, + unsigned int *ref_intmask, + void(*complete)(struct sdhci_host *)); + void (*pdma_reset)(struct sdhci_host *host, + struct mmc_data *data); + unsigned int (*extra_ints)(struct sdhci_host *host); + unsigned int (*spurious_crc_acmd51)(struct sdhci_host *host); + unsigned int (*voltage_broken)(struct sdhci_host *host); + unsigned int (*uhs_broken)(struct sdhci_host *host); + unsigned int (*missing_status)(struct sdhci_host *host); + void (*hw_reset)(struct sdhci_host *host); void (*platform_suspend)(struct sdhci_host *host); void (*platform_resume)(struct sdhci_host *host); @@ -399,9 +413,38 @@ extern void sdhci_enable_irq_wakeups(struct sdhci_host *host); #endif +static inline int /*bool*/ +sdhci_platdma_dmaable(struct sdhci_host *host, struct mmc_data *data) +{ + if (host->ops->pdma_able) + return host->ops->pdma_able(host, data); + else + return 1; +} +static inline void +sdhci_platdma_avail(struct sdhci_host *host, unsigned int *ref_intmask, + void(*completion_callback)(struct sdhci_host *)) +{ + if (host->ops->pdma_avail) + host->ops->pdma_avail(host, ref_intmask, completion_callback); +} + +static inline void +sdhci_platdma_reset(struct sdhci_host *host, struct mmc_data *data) +{ + if (host->ops->pdma_reset) + host->ops->pdma_reset(host, data); +} + #ifdef CONFIG_PM_RUNTIME extern int sdhci_runtime_suspend_host(struct sdhci_host *host); extern int sdhci_runtime_resume_host(struct sdhci_host *host); #endif +extern void sdhci_spin_lock_irqsave(struct sdhci_host *host,unsigned long *flags); +extern void sdhci_spin_unlock_irqrestore(struct sdhci_host *host,unsigned long flags); +extern void sdhci_spin_lock(struct sdhci_host *host); +extern void sdhci_spin_unlock(struct sdhci_host *host); + + #endif /* __SDHCI_HW_H */ diff -urN linux-3.10/include/linux/mmc/host.h linux-rpi-3.10.y/include/linux/mmc/host.h --- linux-3.10/include/linux/mmc/host.h 2013-06-30 23:13:29.000000000 +0100 +++ linux-rpi-3.10.y/include/linux/mmc/host.h 2013-07-06 15:25:50.000000000 +0100 @@ -281,6 +281,7 @@ #define MMC_CAP2_PACKED_CMD (MMC_CAP2_PACKED_RD | \ MMC_CAP2_PACKED_WR) #define MMC_CAP2_NO_PRESCAN_POWERUP (1 << 14) /* Don't power up before scan */ +#define MMC_CAP2_FORCE_MULTIBLOCK (1 << 31) /* Always use multiblock transfers */ mmc_pm_flag_t pm_caps; /* supported pm features */ diff -urN linux-3.10/include/linux/mmc/sdhci.h linux-rpi-3.10.y/include/linux/mmc/sdhci.h --- linux-3.10/include/linux/mmc/sdhci.h 2013-06-30 23:13:29.000000000 +0100 +++ linux-rpi-3.10.y/include/linux/mmc/sdhci.h 2013-07-06 15:25:50.000000000 +0100 @@ -97,6 +97,7 @@ #define SDHCI_QUIRK2_PRESET_VALUE_BROKEN (1<<3) int irq; /* Device IRQ */ + int second_irq; /* Additional IRQ to disable/enable in low-latency mode */ void __iomem *ioaddr; /* Mapped address */ const struct sdhci_ops *ops; /* Low level hw interface */ @@ -128,6 +129,7 @@ #define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */ #define SDHCI_HS200_NEEDS_TUNING (1<<10) /* HS200 needs tuning */ #define SDHCI_USING_RETUNING_TIMER (1<<11) /* Host is using a retuning timer for the card */ +#define SDHCI_USE_PLATDMA (1<<12) /* Host uses 3rd party DMA */ unsigned int version; /* SDHCI spec. version */ @@ -142,6 +144,7 @@ struct mmc_request *mrq; /* Current request */ struct mmc_command *cmd; /* Current command */ + int last_cmdop; /* Opcode of last cmd sent */ struct mmc_data *data; /* Current data request */ unsigned int data_early:1; /* Data finished before cmd */