diff options
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/Kconfig | 28 | ||||
-rw-r--r-- | drivers/mmc/Makefile | 6 | ||||
-rw-r--r-- | drivers/mmc/at91_mci.c | 357 | ||||
-rw-r--r-- | drivers/mmc/au1xmmc.c | 4 | ||||
-rw-r--r-- | drivers/mmc/imxmmc.c | 8 | ||||
-rw-r--r-- | drivers/mmc/mmc.c | 330 | ||||
-rw-r--r-- | drivers/mmc/mmc.h | 4 | ||||
-rw-r--r-- | drivers/mmc/mmc_block.c | 191 | ||||
-rw-r--r-- | drivers/mmc/mmc_queue.c | 75 | ||||
-rw-r--r-- | drivers/mmc/mmc_queue.h | 3 | ||||
-rw-r--r-- | drivers/mmc/mmc_sysfs.c | 47 | ||||
-rw-r--r-- | drivers/mmc/mmci.c | 19 | ||||
-rw-r--r-- | drivers/mmc/omap.c | 284 | ||||
-rw-r--r-- | drivers/mmc/omap.h | 55 | ||||
-rw-r--r-- | drivers/mmc/pxamci.c | 12 | ||||
-rw-r--r-- | drivers/mmc/sdhci.c | 55 | ||||
-rw-r--r-- | drivers/mmc/sdhci.h | 7 | ||||
-rw-r--r-- | drivers/mmc/tifm_sd.c | 936 | ||||
-rw-r--r-- | drivers/mmc/wbsd.c | 17 | ||||
-rw-r--r-- | drivers/mmc/wbsd.h | 5 |
20 files changed, 1942 insertions, 501 deletions
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 45bcf098e762..4224686fdf2a 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -21,7 +21,7 @@ config MMC_DEBUG config MMC_BLOCK tristate "MMC block device driver" - depends on MMC + depends on MMC && BLOCK default y help Say Y here to enable the MMC block device driver support. @@ -40,7 +40,7 @@ config MMC_ARMMMCI If unsure, say N. config MMC_PXA - tristate "Intel PXA255 Multimedia Card Interface support" + tristate "Intel PXA25x/26x/27x Multimedia Card Interface support" depends on ARCH_PXA && MMC help This selects the Intel(R) PXA(R) Multimedia card Interface. @@ -91,11 +91,11 @@ config MMC_AU1X If unsure, say N. -config MMC_AT91RM9200 - tristate "AT91RM9200 SD/MMC Card Interface support" - depends on ARCH_AT91RM9200 && MMC +config MMC_AT91 + tristate "AT91 SD/MMC Card Interface support" + depends on ARCH_AT91 && MMC help - This selects the AT91RM9200 MCI controller. + This selects the AT91 MCI controller. If unsure, say N. @@ -109,4 +109,20 @@ config MMC_IMX If unsure, say N. +config MMC_TIFM_SD + tristate "TI Flash Media MMC/SD Interface support (EXPERIMENTAL)" + depends on MMC && EXPERIMENTAL + select TIFM_CORE + help + Say Y here if you want to be able to access MMC/SD cards with + the Texas Instruments(R) Flash Media card reader, found in many + laptops. + This option 'selects' (turns on, enables) 'TIFM_CORE', but you + probably also need appropriate card reader host adapter, such as + 'Misc devices: TI Flash Media PCI74xx/PCI76xx host adapter support + (TIFM_7XX1)'. + + To compile this driver as a module, choose M here: the + module will be called tifm_sd. + endmenu diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index d2957e35cc6f..83ffb9326a54 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -22,9 +22,11 @@ obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_WBSD) += wbsd.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o obj-$(CONFIG_MMC_OMAP) += omap.o -obj-$(CONFIG_MMC_AT91RM9200) += at91_mci.o +obj-$(CONFIG_MMC_AT91) += at91_mci.o +obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o -mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o +mmc_core-y := mmc.o mmc_sysfs.o +mmc_core-$(CONFIG_BLOCK) += mmc_queue.o ifeq ($(CONFIG_MMC_DEBUG),y) EXTRA_CFLAGS += -DDEBUG diff --git a/drivers/mmc/at91_mci.c b/drivers/mmc/at91_mci.c index 6b7638b84290..08a33c33f6ed 100644 --- a/drivers/mmc/at91_mci.c +++ b/drivers/mmc/at91_mci.c @@ -1,5 +1,5 @@ /* - * linux/drivers/mmc/at91_mci.c - ATMEL AT91RM9200 MCI Driver + * linux/drivers/mmc/at91_mci.c - ATMEL AT91 MCI Driver * * Copyright (C) 2005 Cougar Creek Computing Devices Ltd, All Rights Reserved * @@ -11,7 +11,7 @@ */ /* - This is the AT91RM9200 MCI driver that has been tested with both MMC cards + This is the AT91 MCI driver that has been tested with both MMC cards and SD-cards. Boards that support write protect are now supported. The CCAT91SBC001 board does not support SD cards. @@ -38,8 +38,8 @@ controller to manage the transfers. A read is done from the controller directly to the scatterlist passed in from the request. - Due to a bug in the controller, when a read is completed, all the words are byte - swapped in the scatterlist buffers. + Due to a bug in the AT91RM9200 controller, when a read is completed, all the words are byte + swapped in the scatterlist buffers. AT91SAM926x are not affected by this bug. The sequence of read interrupts is: ENDRX, RXBUFF, CMDRDY @@ -72,42 +72,27 @@ #include <asm/irq.h> #include <asm/mach/mmc.h> #include <asm/arch/board.h> +#include <asm/arch/cpu.h> #include <asm/arch/gpio.h> -#include <asm/arch/at91rm9200_mci.h> -#include <asm/arch/at91rm9200_pdc.h> +#include <asm/arch/at91_mci.h> +#include <asm/arch/at91_pdc.h> #define DRIVER_NAME "at91_mci" #undef SUPPORT_4WIRE -static struct clk *mci_clk; +#define FL_SENT_COMMAND (1 << 0) +#define FL_SENT_STOP (1 << 1) -#define FL_SENT_COMMAND (1 << 0) -#define FL_SENT_STOP (1 << 1) +#define AT91_MCI_ERRORS (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE \ + | AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE \ + | AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE) +#define at91_mci_read(host, reg) __raw_readl((host)->baseaddr + (reg)) +#define at91_mci_write(host, reg, val) __raw_writel((val), (host)->baseaddr + (reg)) /* - * Read from a MCI register. - */ -static inline unsigned long at91_mci_read(unsigned int reg) -{ - void __iomem *mci_base = (void __iomem *)AT91_VA_BASE_MCI; - - return __raw_readl(mci_base + reg); -} - -/* - * Write to a MCI register. - */ -static inline void at91_mci_write(unsigned int reg, unsigned long value) -{ - void __iomem *mci_base = (void __iomem *)AT91_VA_BASE_MCI; - - __raw_writel(value, mci_base + reg); -} - -/* * Low level type for this driver */ struct at91mci_host @@ -116,9 +101,14 @@ struct at91mci_host struct mmc_command *cmd; struct mmc_request *request; + void __iomem *baseaddr; + int irq; + struct at91_mmc_data *board; int present; + struct clk *mci_clk; + /* * Flag indicating when the command has been sent. This is used to * work out whether or not to send the stop @@ -158,7 +148,6 @@ static inline void at91mci_sg_to_dma(struct at91mci_host *host, struct mmc_data for (i = 0; i < len; i++) { struct scatterlist *sg; int amount; - int index; unsigned int *sgbuffer; sg = &data->sg[i]; @@ -166,10 +155,15 @@ static inline void at91mci_sg_to_dma(struct at91mci_host *host, struct mmc_data sgbuffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset; amount = min(size, sg->length); size -= amount; - amount /= 4; - for (index = 0; index < amount; index++) - *dmabuf++ = swab32(sgbuffer[index]); + if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ + int index; + + for (index = 0; index < (amount / 4); index++) + *dmabuf++ = swab32(sgbuffer[index]); + } + else + memcpy(dmabuf, sgbuffer, amount); kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ); @@ -217,13 +211,13 @@ static void at91mci_pre_dma_read(struct at91mci_host *host) /* Check to see if this needs filling */ if (i == 0) { - if (at91_mci_read(AT91_PDC_RCR) != 0) { + if (at91_mci_read(host, AT91_PDC_RCR) != 0) { pr_debug("Transfer active in current\n"); continue; } } else { - if (at91_mci_read(AT91_PDC_RNCR) != 0) { + if (at91_mci_read(host, AT91_PDC_RNCR) != 0) { pr_debug("Transfer active in next\n"); continue; } @@ -240,12 +234,12 @@ static void at91mci_pre_dma_read(struct at91mci_host *host) pr_debug("dma address = %08X, length = %d\n", sg->dma_address, sg->length); if (i == 0) { - at91_mci_write(AT91_PDC_RPR, sg->dma_address); - at91_mci_write(AT91_PDC_RCR, sg->length / 4); + at91_mci_write(host, AT91_PDC_RPR, sg->dma_address); + at91_mci_write(host, AT91_PDC_RCR, sg->length / 4); } else { - at91_mci_write(AT91_PDC_RNPR, sg->dma_address); - at91_mci_write(AT91_PDC_RNCR, sg->length / 4); + at91_mci_write(host, AT91_PDC_RNPR, sg->dma_address); + at91_mci_write(host, AT91_PDC_RNCR, sg->length / 4); } } @@ -276,8 +270,6 @@ static void at91mci_post_dma_read(struct at91mci_host *host) while (host->in_use_index < host->transfer_index) { unsigned int *buffer; - int index; - int len; struct scatterlist *sg; @@ -295,11 +287,13 @@ static void at91mci_post_dma_read(struct at91mci_host *host) data->bytes_xfered += sg->length; - len = sg->length / 4; + if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ + int index; - for (index = 0; index < len; index++) { - buffer[index] = swab32(buffer[index]); + for (index = 0; index < (sg->length / 4); index++) + buffer[index] = swab32(buffer[index]); } + kunmap_atomic(buffer, KM_BIO_SRC_IRQ); flush_dcache_page(sg->page); } @@ -308,8 +302,8 @@ static void at91mci_post_dma_read(struct at91mci_host *host) if (host->transfer_index < data->sg_len) at91mci_pre_dma_read(host); else { - at91_mci_write(AT91_MCI_IER, AT91_MCI_RXBUFF); - at91_mci_write(AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS); + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); + at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS); } pr_debug("post dma read done\n"); @@ -326,11 +320,11 @@ static void at91_mci_handle_transmitted(struct at91mci_host *host) pr_debug("Handling the transmit\n"); /* Disable the transfer */ - at91_mci_write(AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS); + at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS); /* Now wait for cmd ready */ - at91_mci_write(AT91_MCI_IDR, AT91_MCI_TXBUFE); - at91_mci_write(AT91_MCI_IER, AT91_MCI_NOTBUSY); + at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE); + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); cmd = host->cmd; if (!cmd) return; @@ -344,21 +338,23 @@ static void at91_mci_handle_transmitted(struct at91mci_host *host) /* * Enable the controller */ -static void at91_mci_enable(void) +static void at91_mci_enable(struct at91mci_host *host) { - at91_mci_write(AT91_MCI_CR, AT91_MCI_MCIEN); - at91_mci_write(AT91_MCI_IDR, 0xFFFFFFFF); - at91_mci_write(AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC); - at91_mci_write(AT91_MCI_MR, 0x834A); - at91_mci_write(AT91_MCI_SDCR, 0x0); + at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); + at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); + at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC); + at91_mci_write(host, AT91_MCI_MR, AT91_MCI_PDCMODE | 0x34a); + + /* use Slot A or B (only one at same time) */ + at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b); } /* * Disable the controller */ -static void at91_mci_disable(void) +static void at91_mci_disable(struct at91mci_host *host) { - at91_mci_write(AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST); + at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST); } /* @@ -378,13 +374,13 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ /* Not sure if this is needed */ #if 0 - if ((at91_mci_read(AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) { + if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) { pr_debug("Clearing timeout\n"); - at91_mci_write(AT91_MCI_ARGR, 0); - at91_mci_write(AT91_MCI_CMDR, AT91_MCI_OPDCMD); - while (!(at91_mci_read(AT91_MCI_SR) & AT91_MCI_CMDRDY)) { + at91_mci_write(host, AT91_MCI_ARGR, 0); + at91_mci_write(host, AT91_MCI_CMDR, AT91_MCI_OPDCMD); + while (!(at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_CMDRDY)) { /* spin */ - pr_debug("Clearing: SR = %08X\n", at91_mci_read(AT91_MCI_SR)); + pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR)); } } #endif @@ -431,32 +427,32 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ /* * Set the arguments and send the command */ - pr_debug("Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08lX)\n", - cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(AT91_MCI_MR)); + pr_debug("Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08X)\n", + cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(host, AT91_MCI_MR)); if (!data) { - at91_mci_write(AT91_PDC_PTCR, AT91_PDC_TXTDIS | AT91_PDC_RXTDIS); - at91_mci_write(AT91_PDC_RPR, 0); - at91_mci_write(AT91_PDC_RCR, 0); - at91_mci_write(AT91_PDC_RNPR, 0); - at91_mci_write(AT91_PDC_RNCR, 0); - at91_mci_write(AT91_PDC_TPR, 0); - at91_mci_write(AT91_PDC_TCR, 0); - at91_mci_write(AT91_PDC_TNPR, 0); - at91_mci_write(AT91_PDC_TNCR, 0); - - at91_mci_write(AT91_MCI_ARGR, cmd->arg); - at91_mci_write(AT91_MCI_CMDR, cmdr); + at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_TXTDIS | AT91_PDC_RXTDIS); + at91_mci_write(host, AT91_PDC_RPR, 0); + at91_mci_write(host, AT91_PDC_RCR, 0); + at91_mci_write(host, AT91_PDC_RNPR, 0); + at91_mci_write(host, AT91_PDC_RNCR, 0); + at91_mci_write(host, AT91_PDC_TPR, 0); + at91_mci_write(host, AT91_PDC_TCR, 0); + at91_mci_write(host, AT91_PDC_TNPR, 0); + at91_mci_write(host, AT91_PDC_TNCR, 0); + + at91_mci_write(host, AT91_MCI_ARGR, cmd->arg); + at91_mci_write(host, AT91_MCI_CMDR, cmdr); return AT91_MCI_CMDRDY; } - mr = at91_mci_read(AT91_MCI_MR) & 0x7fff; /* zero block length and PDC mode */ - at91_mci_write(AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE); + mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; /* zero block length and PDC mode */ + at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE); /* * Disable the PDC controller */ - at91_mci_write(AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS); + at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS); if (cmdr & AT91_MCI_TRCMD_START) { data->bytes_xfered = 0; @@ -485,8 +481,8 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ pr_debug("Transmitting %d bytes\n", host->total_length); - at91_mci_write(AT91_PDC_TPR, host->physical_address); - at91_mci_write(AT91_PDC_TCR, host->total_length / 4); + at91_mci_write(host, AT91_PDC_TPR, host->physical_address); + at91_mci_write(host, AT91_PDC_TCR, host->total_length / 4); ier = AT91_MCI_TXBUFE; } } @@ -496,14 +492,14 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ * the data sheet says */ - at91_mci_write(AT91_MCI_ARGR, cmd->arg); - at91_mci_write(AT91_MCI_CMDR, cmdr); + at91_mci_write(host, AT91_MCI_ARGR, cmd->arg); + at91_mci_write(host, AT91_MCI_CMDR, cmdr); if (cmdr & AT91_MCI_TRCMD_START) { if (cmdr & AT91_MCI_TRDIR) - at91_mci_write(AT91_PDC_PTCR, AT91_PDC_RXTEN); + at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_RXTEN); else - at91_mci_write(AT91_PDC_PTCR, AT91_PDC_TXTEN); + at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_TXTEN); } return ier; } @@ -520,7 +516,7 @@ static void at91mci_process_command(struct at91mci_host *host, struct mmc_comman pr_debug("setting ier to %08X\n", ier); /* Stop on errors or the required value */ - at91_mci_write(AT91_MCI_IER, 0xffff0000 | ier); + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier); } /* @@ -548,19 +544,19 @@ static void at91mci_completed_command(struct at91mci_host *host) struct mmc_command *cmd = host->cmd; unsigned int status; - at91_mci_write(AT91_MCI_IDR, 0xffffffff); + at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); - cmd->resp[0] = at91_mci_read(AT91_MCI_RSPR(0)); - cmd->resp[1] = at91_mci_read(AT91_MCI_RSPR(1)); - cmd->resp[2] = at91_mci_read(AT91_MCI_RSPR(2)); - cmd->resp[3] = at91_mci_read(AT91_MCI_RSPR(3)); + cmd->resp[0] = at91_mci_read(host, AT91_MCI_RSPR(0)); + cmd->resp[1] = at91_mci_read(host, AT91_MCI_RSPR(1)); + cmd->resp[2] = at91_mci_read(host, AT91_MCI_RSPR(2)); + cmd->resp[3] = at91_mci_read(host, AT91_MCI_RSPR(3)); if (host->buffer) { dma_free_coherent(NULL, host->total_length, host->buffer, host->physical_address); host->buffer = NULL; } - status = at91_mci_read(AT91_MCI_SR); + status = at91_mci_read(host, AT91_MCI_SR); pr_debug("Status = %08X [%08X %08X %08X %08X]\n", status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); @@ -611,18 +607,18 @@ static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { int clkdiv; struct at91mci_host *host = mmc_priv(mmc); - unsigned long at91_master_clock = clk_get_rate(mci_clk); + unsigned long at91_master_clock = clk_get_rate(host->mci_clk); host->bus_mode = ios->bus_mode; if (ios->clock == 0) { /* Disable the MCI controller */ - at91_mci_write(AT91_MCI_CR, AT91_MCI_MCIDIS); + at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS); clkdiv = 0; } else { /* Enable the MCI controller */ - at91_mci_write(AT91_MCI_CR, AT91_MCI_MCIEN); + at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); if ((at91_master_clock % (ios->clock * 2)) == 0) clkdiv = ((at91_master_clock / ios->clock) / 2) - 1; @@ -634,25 +630,25 @@ static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } if (ios->bus_width == MMC_BUS_WIDTH_4 && host->board->wire4) { pr_debug("MMC: Setting controller bus width to 4\n"); - at91_mci_write(AT91_MCI_SDCR, at91_mci_read(AT91_MCI_SDCR) | AT91_MCI_SDCBUS); + at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) | AT91_MCI_SDCBUS); } else { pr_debug("MMC: Setting controller bus width to 1\n"); - at91_mci_write(AT91_MCI_SDCR, at91_mci_read(AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); + at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); } /* Set the clock divider */ - at91_mci_write(AT91_MCI_MR, (at91_mci_read(AT91_MCI_MR) & ~AT91_MCI_CLKDIV) | clkdiv); + at91_mci_write(host, AT91_MCI_MR, (at91_mci_read(host, AT91_MCI_MR) & ~AT91_MCI_CLKDIV) | clkdiv); /* maybe switch power to the card */ if (host->board->vcc_pin) { switch (ios->power_mode) { case MMC_POWER_OFF: - at91_set_gpio_output(host->board->vcc_pin, 0); + at91_set_gpio_value(host->board->vcc_pin, 0); break; case MMC_POWER_UP: case MMC_POWER_ON: - at91_set_gpio_output(host->board->vcc_pin, 1); + at91_set_gpio_value(host->board->vcc_pin, 1); break; } } @@ -661,43 +657,44 @@ static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) /* * Handle an interrupt */ -static irqreturn_t at91_mci_irq(int irq, void *devid, struct pt_regs *regs) +static irqreturn_t at91_mci_irq(int irq, void *devid) { struct at91mci_host *host = devid; int completed = 0; + unsigned int int_status, int_mask; - unsigned int int_status; + int_status = at91_mci_read(host, AT91_MCI_SR); + int_mask = at91_mci_read(host, AT91_MCI_IMR); + + pr_debug("MCI irq: status = %08X, %08X, %08X\n", int_status, int_mask, + int_status & int_mask); + + int_status = int_status & int_mask; - int_status = at91_mci_read(AT91_MCI_SR); - pr_debug("MCI irq: status = %08X, %08lX, %08lX\n", int_status, at91_mci_read(AT91_MCI_IMR), - int_status & at91_mci_read(AT91_MCI_IMR)); - - if ((int_status & at91_mci_read(AT91_MCI_IMR)) & 0xffff0000) + if (int_status & AT91_MCI_ERRORS) { completed = 1; + + if (int_status & AT91_MCI_UNRE) + pr_debug("MMC: Underrun error\n"); + if (int_status & AT91_MCI_OVRE) + pr_debug("MMC: Overrun error\n"); + if (int_status & AT91_MCI_DTOE) + pr_debug("MMC: Data timeout\n"); + if (int_status & AT91_MCI_DCRCE) + pr_debug("MMC: CRC error in data\n"); + if (int_status & AT91_MCI_RTOE) + pr_debug("MMC: Response timeout\n"); + if (int_status & AT91_MCI_RENDE) + pr_debug("MMC: Response end bit error\n"); + if (int_status & AT91_MCI_RCRCE) + pr_debug("MMC: Response CRC error\n"); + if (int_status & AT91_MCI_RDIRE) + pr_debug("MMC: Response direction error\n"); + if (int_status & AT91_MCI_RINDE) + pr_debug("MMC: Response index error\n"); + } else { + /* Only continue processing if no errors */ - int_status &= at91_mci_read(AT91_MCI_IMR); - - if (int_status & AT91_MCI_UNRE) - pr_debug("MMC: Underrun error\n"); - if (int_status & AT91_MCI_OVRE) - pr_debug("MMC: Overrun error\n"); - if (int_status & AT91_MCI_DTOE) - pr_debug("MMC: Data timeout\n"); - if (int_status & AT91_MCI_DCRCE) - pr_debug("MMC: CRC error in data\n"); - if (int_status & AT91_MCI_RTOE) - pr_debug("MMC: Response timeout\n"); - if (int_status & AT91_MCI_RENDE) - pr_debug("MMC: Response end bit error\n"); - if (int_status & AT91_MCI_RCRCE) - pr_debug("MMC: Response CRC error\n"); - if (int_status & AT91_MCI_RDIRE) - pr_debug("MMC: Response direction error\n"); - if (int_status & AT91_MCI_RINDE) - pr_debug("MMC: Response index error\n"); - - /* Only continue processing if no errors */ - if (!completed) { if (int_status & AT91_MCI_TXBUFE) { pr_debug("TX buffer empty\n"); at91_mci_handle_transmitted(host); @@ -705,12 +702,11 @@ static irqreturn_t at91_mci_irq(int irq, void *devid, struct pt_regs *regs) if (int_status & AT91_MCI_RXBUFF) { pr_debug("RX buffer full\n"); - at91_mci_write(AT91_MCI_IER, AT91_MCI_CMDRDY); + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY); } - if (int_status & AT91_MCI_ENDTX) { + if (int_status & AT91_MCI_ENDTX) pr_debug("Transmit has ended\n"); - } if (int_status & AT91_MCI_ENDRX) { pr_debug("Receive has ended\n"); @@ -719,42 +715,38 @@ static irqreturn_t at91_mci_irq(int irq, void *devid, struct pt_regs *regs) if (int_status & AT91_MCI_NOTBUSY) { pr_debug("Card is ready\n"); - at91_mci_write(AT91_MCI_IER, AT91_MCI_CMDRDY); + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY); } - if (int_status & AT91_MCI_DTIP) { + if (int_status & AT91_MCI_DTIP) pr_debug("Data transfer in progress\n"); - } - if (int_status & AT91_MCI_BLKE) { + if (int_status & AT91_MCI_BLKE) pr_debug("Block transfer has ended\n"); - } - if (int_status & AT91_MCI_TXRDY) { + if (int_status & AT91_MCI_TXRDY) pr_debug("Ready to transmit\n"); - } - if (int_status & AT91_MCI_RXRDY) { + if (int_status & AT91_MCI_RXRDY) pr_debug("Ready to receive\n"); - } if (int_status & AT91_MCI_CMDRDY) { pr_debug("Command ready\n"); completed = 1; } } - at91_mci_write(AT91_MCI_IDR, int_status); if (completed) { pr_debug("Completed command\n"); - at91_mci_write(AT91_MCI_IDR, 0xffffffff); + at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); at91mci_completed_command(host); - } + } else + at91_mci_write(host, AT91_MCI_IDR, int_status); return IRQ_HANDLED; } -static irqreturn_t at91_mmc_det_irq(int irq, void *_host, struct pt_regs *regs) +static irqreturn_t at91_mmc_det_irq(int irq, void *_host) { struct at91mci_host *host = _host; int present = !at91_get_gpio_value(irq); @@ -769,7 +761,7 @@ static irqreturn_t at91_mmc_det_irq(int irq, void *_host, struct pt_regs *regs) present ? "insert" : "remove"); if (!present) { pr_debug("****** Resetting SD-card bus width ******\n"); - at91_mci_write(AT91_MCI_SDCR, 0); + at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); } mmc_detect_change(host->mmc, msecs_to_jiffies(100)); } @@ -793,7 +785,7 @@ int at91_mci_get_ro(struct mmc_host *mmc) return read_only; } -static struct mmc_host_ops at91_mci_ops = { +static const struct mmc_host_ops at91_mci_ops = { .request = at91_mci_request, .set_ios = at91_mci_set_ios, .get_ro = at91_mci_get_ro, @@ -806,15 +798,22 @@ static int at91_mci_probe(struct platform_device *pdev) { struct mmc_host *mmc; struct at91mci_host *host; + struct resource *res; int ret; pr_debug("Probe MCI devices\n"); - at91_mci_disable(); - at91_mci_enable(); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + if (!request_mem_region(res->start, res->end - res->start + 1, DRIVER_NAME)) + return -EBUSY; mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev); if (!mmc) { pr_debug("Failed to allocate mmc host\n"); + release_mem_region(res->start, res->end - res->start + 1); return -ENOMEM; } @@ -822,6 +821,7 @@ static int at91_mci_probe(struct platform_device *pdev) mmc->f_min = 375000; mmc->f_max = 25000000; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->caps = MMC_CAP_BYTEBLOCK; host = mmc_priv(mmc); host->mmc = mmc; @@ -832,30 +832,51 @@ static int at91_mci_probe(struct platform_device *pdev) #ifdef SUPPORT_4WIRE mmc->caps |= MMC_CAP_4_BIT_DATA; #else - printk("MMC: 4 wire bus mode not supported by this driver - using 1 wire\n"); + printk("AT91 MMC: 4 wire bus mode not supported by this driver - using 1 wire\n"); #endif } /* * Get Clock */ - mci_clk = clk_get(&pdev->dev, "mci_clk"); - if (IS_ERR(mci_clk)) { + host->mci_clk = clk_get(&pdev->dev, "mci_clk"); + if (IS_ERR(host->mci_clk)) { printk(KERN_ERR "AT91 MMC: no clock defined.\n"); mmc_free_host(mmc); + release_mem_region(res->start, res->end - res->start + 1); return -ENODEV; } - clk_enable(mci_clk); /* Enable the peripheral clock */ + + /* + * Map I/O region + */ + host->baseaddr = ioremap(res->start, res->end - res->start + 1); + if (!host->baseaddr) { + clk_put(host->mci_clk); + mmc_free_host(mmc); + release_mem_region(res->start, res->end - res->start + 1); + return -ENOMEM; + } + + /* + * Reset hardware + */ + clk_enable(host->mci_clk); /* Enable the peripheral clock */ + at91_mci_disable(host); + at91_mci_enable(host); /* * Allocate the MCI interrupt */ - ret = request_irq(AT91_ID_MCI, at91_mci_irq, IRQF_SHARED, DRIVER_NAME, host); + host->irq = platform_get_irq(pdev, 0); + ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED, DRIVER_NAME, host); if (ret) { - printk(KERN_ERR "Failed to request MCI interrupt\n"); - clk_disable(mci_clk); - clk_put(mci_clk); + printk(KERN_ERR "AT91 MMC: Failed to request MCI interrupt\n"); + clk_disable(host->mci_clk); + clk_put(host->mci_clk); mmc_free_host(mmc); + iounmap(host->baseaddr); + release_mem_region(res->start, res->end - res->start + 1); return ret; } @@ -878,10 +899,10 @@ static int at91_mci_probe(struct platform_device *pdev) ret = request_irq(host->board->det_pin, at91_mmc_det_irq, 0, DRIVER_NAME, host); if (ret) - printk(KERN_ERR "couldn't allocate MMC detect irq\n"); + printk(KERN_ERR "AT91 MMC: Couldn't allocate MMC detect irq\n"); } - pr_debug(KERN_INFO "Added MCI driver\n"); + pr_debug("Added MCI driver\n"); return 0; } @@ -893,6 +914,7 @@ static int at91_mci_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); struct at91mci_host *host; + struct resource *res; if (!mmc) return -1; @@ -904,16 +926,19 @@ static int at91_mci_remove(struct platform_device *pdev) cancel_delayed_work(&host->mmc->detect); } + at91_mci_disable(host); mmc_remove_host(mmc); - at91_mci_disable(); - free_irq(AT91_ID_MCI, host); - mmc_free_host(mmc); + free_irq(host->irq, host); - clk_disable(mci_clk); /* Disable the peripheral clock */ - clk_put(mci_clk); + clk_disable(host->mci_clk); /* Disable the peripheral clock */ + clk_put(host->mci_clk); - platform_set_drvdata(pdev, NULL); + iounmap(host->baseaddr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res->end - res->start + 1); + mmc_free_host(mmc); + platform_set_drvdata(pdev, NULL); pr_debug("MCI Removed\n"); return 0; diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c index 61268da13957..800527cf40d5 100644 --- a/drivers/mmc/au1xmmc.c +++ b/drivers/mmc/au1xmmc.c @@ -750,7 +750,7 @@ static void au1xmmc_dma_callback(int irq, void *dev_id) #define STATUS_DATA_IN (SD_STATUS_NE) #define STATUS_DATA_OUT (SD_STATUS_TH) -static irqreturn_t au1xmmc_irq(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t au1xmmc_irq(int irq, void *dev_id) { u32 status; @@ -875,7 +875,7 @@ static void au1xmmc_init_dma(struct au1xmmc_host *host) host->rx_chan = rxchan; } -struct mmc_host_ops au1xmmc_ops = { +static const struct mmc_host_ops au1xmmc_ops = { .request = au1xmmc_request, .set_ios = au1xmmc_set_ios, }; diff --git a/drivers/mmc/imxmmc.c b/drivers/mmc/imxmmc.c index fb6565b98f32..06e7fcd19221 100644 --- a/drivers/mmc/imxmmc.c +++ b/drivers/mmc/imxmmc.c @@ -635,7 +635,7 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat) return trans_done; } -static void imxmci_dma_irq(int dma, void *devid, struct pt_regs *regs) +static void imxmci_dma_irq(int dma, void *devid) { struct imxmci_host *host = devid; uint32_t stat = MMC_STATUS; @@ -646,7 +646,7 @@ static void imxmci_dma_irq(int dma, void *devid, struct pt_regs *regs) tasklet_schedule(&host->tasklet); } -static irqreturn_t imxmci_irq(int irq, void *devid, struct pt_regs *regs) +static irqreturn_t imxmci_irq(int irq, void *devid) { struct imxmci_host *host = devid; uint32_t stat = MMC_STATUS; @@ -877,7 +877,7 @@ static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } } -static struct mmc_host_ops imxmci_ops = { +static const struct mmc_host_ops imxmci_ops = { .request = imxmci_request, .set_ios = imxmci_set_ios, }; @@ -956,7 +956,7 @@ static int imxmci_probe(struct platform_device *pdev) mmc->f_min = 150000; mmc->f_max = CLK_RATE/2; mmc->ocr_avail = MMC_VDD_32_33; - mmc->caps |= MMC_CAP_4_BIT_DATA; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_BYTEBLOCK; /* MMC core transfer sizes tunable parameters */ mmc->max_hw_segs = 64; diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 74eaaee66de0..6f2a282e2b97 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -4,6 +4,7 @@ * Copyright (C) 2003-2004 Russell King, All Rights Reserved. * SD support Copyright (C) 2004 Ian Molton, All Rights Reserved. * SD support Copyright (C) 2005 Pierre Ossman, All Rights Reserved. + * MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved. * * 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 @@ -396,23 +397,23 @@ static int mmc_select_card(struct mmc_host *host, struct mmc_card *card) return err; /* - * Default bus width is 1 bit. - */ - host->ios.bus_width = MMC_BUS_WIDTH_1; - - /* - * We can only change the bus width of the selected - * card so therefore we have to put the handling + * We can only change the bus width of SD cards when + * they are selected so we have to put the handling * here. + * + * The card is in 1 bit mode by default so + * we only need to change if it supports the + * wider version. */ - if (host->caps & MMC_CAP_4_BIT_DATA) { + if (mmc_card_sd(card) && + (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { + /* - * The card is in 1 bit mode by default so - * we only need to change if it supports the - * wider version. - */ - if (mmc_card_sd(card) && - (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { + * Default bus width is 1 bit. + */ + host->ios.bus_width = MMC_BUS_WIDTH_1; + + if (host->caps & MMC_CAP_4_BIT_DATA) { struct mmc_command cmd; cmd.opcode = SD_APP_SET_BUS_WIDTH; cmd.arg = SD_BUS_WIDTH_4; @@ -453,11 +454,11 @@ static void mmc_deselect_cards(struct mmc_host *host) static inline void mmc_delay(unsigned int ms) { - if (ms < HZ / 1000) { - yield(); + if (ms < 1000 / HZ) { + cond_resched(); mdelay(ms); } else { - msleep_interruptible (ms); + msleep(ms); } } @@ -475,7 +476,7 @@ static u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) if (bit) { bit -= 1; - ocr = 3 << bit; + ocr &= 3 << bit; host->ios.vdd = bit; mmc_set_ios(host); @@ -953,6 +954,137 @@ static void mmc_read_csds(struct mmc_host *host) } } +static void mmc_process_ext_csds(struct mmc_host *host) +{ + int err; + struct mmc_card *card; + + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + + struct scatterlist sg; + + /* + * As the ext_csd is so large and mostly unused, we don't store the + * raw block in mmc_card. + */ + u8 *ext_csd; + ext_csd = kmalloc(512, GFP_KERNEL); + if (!ext_csd) { + printk("%s: could not allocate a buffer to receive the ext_csd." + "mmc v4 cards will be treated as v3.\n", + mmc_hostname(host)); + return; + } + + list_for_each_entry(card, &host->cards, node) { + if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT)) + continue; + if (mmc_card_sd(card)) + continue; + if (card->csd.mmca_vsn < CSD_SPEC_VER_4) + continue; + + err = mmc_select_card(host, card); + if (err != MMC_ERR_NONE) { + mmc_card_set_dead(card); + continue; + } + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_SEND_EXT_CSD; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + memset(&data, 0, sizeof(struct mmc_data)); + + mmc_set_data_timeout(&data, card, 0); + + data.blksz = 512; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + memset(&mrq, 0, sizeof(struct mmc_request)); + + mrq.cmd = &cmd; + mrq.data = &data; + + sg_init_one(&sg, ext_csd, 512); + + mmc_wait_for_req(host, &mrq); + + if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { + mmc_card_set_dead(card); + continue; + } + + switch (ext_csd[EXT_CSD_CARD_TYPE]) { + case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26: + card->ext_csd.hs_max_dtr = 52000000; + break; + case EXT_CSD_CARD_TYPE_26: + card->ext_csd.hs_max_dtr = 26000000; + break; + default: + /* MMC v4 spec says this cannot happen */ + printk("%s: card is mmc v4 but doesn't support " + "any high-speed modes.\n", + mmc_hostname(card->host)); + mmc_card_set_bad(card); + continue; + } + + /* Activate highspeed support. */ + cmd.opcode = MMC_SWITCH; + cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_HS_TIMING << 16) | + (1 << 8) | + EXT_CSD_CMD_SET_NORMAL; + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) { + printk("%s: failed to switch card to mmc v4 " + "high-speed mode.\n", + mmc_hostname(card->host)); + continue; + } + + mmc_card_set_highspeed(card); + + /* Check for host support for wide-bus modes. */ + if (!(host->caps & MMC_CAP_4_BIT_DATA)) { + continue; + } + + /* Activate 4-bit support. */ + cmd.opcode = MMC_SWITCH; + cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_BUS_WIDTH << 16) | + (EXT_CSD_BUS_WIDTH_4 << 8) | + EXT_CSD_CMD_SET_NORMAL; + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) { + printk("%s: failed to switch card to " + "mmc v4 4-bit bus mode.\n", + mmc_hostname(card->host)); + continue; + } + + host->ios.bus_width = MMC_BUS_WIDTH_4; + } + + kfree(ext_csd); + + mmc_deselect_cards(host); +} + static void mmc_read_scrs(struct mmc_host *host) { int err; @@ -996,7 +1128,6 @@ static void mmc_read_scrs(struct mmc_host *host) mmc_set_data_timeout(&data, card, 0); - data.blksz_bits = 3; data.blksz = 1 << 3; data.blocks = 1; data.flags = MMC_DATA_READ; @@ -1026,14 +1157,133 @@ static void mmc_read_scrs(struct mmc_host *host) mmc_deselect_cards(host); } +static void mmc_read_switch_caps(struct mmc_host *host) +{ + int err; + struct mmc_card *card; + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + unsigned char *status; + struct scatterlist sg; + + status = kmalloc(64, GFP_KERNEL); + if (!status) { + printk(KERN_WARNING "%s: Unable to allocate buffer for " + "reading switch capabilities.\n", + mmc_hostname(host)); + return; + } + + list_for_each_entry(card, &host->cards, node) { + if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT)) + continue; + if (!mmc_card_sd(card)) + continue; + if (card->scr.sda_vsn < SCR_SPEC_VER_1) + continue; + + err = mmc_select_card(host, card); + if (err != MMC_ERR_NONE) { + mmc_card_set_dead(card); + continue; + } + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_SWITCH; + cmd.arg = 0x00FFFFF1; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + memset(&data, 0, sizeof(struct mmc_data)); + + mmc_set_data_timeout(&data, card, 0); + + data.blksz = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + memset(&mrq, 0, sizeof(struct mmc_request)); + + mrq.cmd = &cmd; + mrq.data = &data; + + sg_init_one(&sg, status, 64); + + mmc_wait_for_req(host, &mrq); + + if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { + mmc_card_set_dead(card); + continue; + } + + if (status[13] & 0x02) + card->sw_caps.hs_max_dtr = 50000000; + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_SWITCH; + cmd.arg = 0x80FFFFF1; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + memset(&data, 0, sizeof(struct mmc_data)); + + mmc_set_data_timeout(&data, card, 0); + + data.blksz = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + memset(&mrq, 0, sizeof(struct mmc_request)); + + mrq.cmd = &cmd; + mrq.data = &data; + + sg_init_one(&sg, status, 64); + + mmc_wait_for_req(host, &mrq); + + if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { + mmc_card_set_dead(card); + continue; + } + + if ((status[16] & 0xF) != 1) { + printk(KERN_WARNING "%s: Problem switching card " + "into high-speed mode!\n", + mmc_hostname(host)); + continue; + } + + mmc_card_set_highspeed(card); + } + + kfree(status); + + mmc_deselect_cards(host); +} + static unsigned int mmc_calculate_clock(struct mmc_host *host) { struct mmc_card *card; unsigned int max_dtr = host->f_max; list_for_each_entry(card, &host->cards, node) - if (!mmc_card_dead(card) && max_dtr > card->csd.max_dtr) - max_dtr = card->csd.max_dtr; + if (!mmc_card_dead(card)) { + if (mmc_card_highspeed(card) && mmc_card_sd(card)) { + if (max_dtr > card->sw_caps.hs_max_dtr) + max_dtr = card->sw_caps.hs_max_dtr; + } else if (mmc_card_highspeed(card) && !mmc_card_sd(card)) { + if (max_dtr > card->ext_csd.hs_max_dtr) + max_dtr = card->ext_csd.hs_max_dtr; + } else if (max_dtr > card->csd.max_dtr) { + max_dtr = card->csd.max_dtr; + } + } pr_debug("%s: selected %d.%03dMHz transfer rate\n", mmc_hostname(host), @@ -1151,8 +1401,11 @@ static void mmc_setup(struct mmc_host *host) mmc_read_csds(host); - if (host->mode == MMC_MODE_SD) + if (host->mode == MMC_MODE_SD) { mmc_read_scrs(host); + mmc_read_switch_caps(host); + } else + mmc_process_ext_csds(host); } @@ -1166,27 +1419,40 @@ static void mmc_setup(struct mmc_host *host) */ void mmc_detect_change(struct mmc_host *host, unsigned long delay) { - if (delay) - schedule_delayed_work(&host->detect, delay); - else - schedule_work(&host->detect); + mmc_schedule_delayed_work(&host->detect, delay); } EXPORT_SYMBOL(mmc_detect_change); -static void mmc_rescan(void *data) +static void mmc_rescan(struct work_struct *work) { - struct mmc_host *host = data; + struct mmc_host *host = + container_of(work, struct mmc_host, detect.work); struct list_head *l, *n; + unsigned char power_mode; mmc_claim_host(host); - if (host->ios.power_mode == MMC_POWER_ON) + /* + * Check for removed cards and newly inserted ones. We check for + * removed cards first so we can intelligently re-select the VDD. + */ + power_mode = host->ios.power_mode; + if (power_mode == MMC_POWER_ON) mmc_check_cards(host); mmc_setup(host); + /* + * Some broken cards process CMD1 even in stand-by state. There is + * no reply, but an ILLEGAL_COMMAND error is cached and returned + * after next command. We poll for card status here to clear any + * possibly pending error. + */ + if (power_mode == MMC_POWER_ON) + mmc_check_cards(host); + if (!list_empty(&host->cards)) { /* * (Re-)calculate the fastest clock rate which the @@ -1245,7 +1511,7 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); INIT_LIST_HEAD(&host->cards); - INIT_WORK(&host->detect, mmc_rescan, host); + INIT_DELAYED_WORK(&host->detect, mmc_rescan); /* * By default, hosts do not support SGIO or large requests. @@ -1312,7 +1578,7 @@ EXPORT_SYMBOL(mmc_remove_host); */ void mmc_free_host(struct mmc_host *host) { - flush_scheduled_work(); + mmc_flush_scheduled_work(); mmc_free_host_sysfs(host); } @@ -1343,7 +1609,7 @@ EXPORT_SYMBOL(mmc_suspend_host); */ int mmc_resume_host(struct mmc_host *host) { - mmc_rescan(host); + mmc_rescan(&host->detect.work); return 0; } diff --git a/drivers/mmc/mmc.h b/drivers/mmc/mmc.h index 97bae00292fa..149affe0b686 100644 --- a/drivers/mmc/mmc.h +++ b/drivers/mmc/mmc.h @@ -18,4 +18,8 @@ struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev); int mmc_add_host_sysfs(struct mmc_host *host); void mmc_remove_host_sysfs(struct mmc_host *host); void mmc_free_host_sysfs(struct mmc_host *host); + +int mmc_schedule_work(struct work_struct *work); +int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay); +void mmc_flush_scheduled_work(void); #endif diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c index a0e0dad1b419..87713572293f 100644 --- a/drivers/mmc/mmc_block.c +++ b/drivers/mmc/mmc_block.c @@ -28,10 +28,12 @@ #include <linux/kdev_t.h> #include <linux/blkdev.h> #include <linux/mutex.h> +#include <linux/scatterlist.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h> #include <linux/mmc/protocol.h> +#include <linux/mmc/host.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -81,7 +83,6 @@ static void mmc_blk_put(struct mmc_blk_data *md) md->usage--; if (md->usage == 0) { put_disk(md->disk); - mmc_cleanup_queue(&md->queue); kfree(md); } mutex_unlock(&open_lock); @@ -153,18 +154,84 @@ static int mmc_blk_prep_rq(struct mmc_queue *mq, struct request *req) return stat; } +static u32 mmc_sd_num_wr_blocks(struct mmc_card *card) +{ + int err; + u32 blocks; + + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + unsigned int timeout_us; + + struct scatterlist sg; + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_APP_CMD; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, 0); + if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) + return (u32)-1; + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_APP_SEND_NUM_WR_BLKS; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + memset(&data, 0, sizeof(struct mmc_data)); + + data.timeout_ns = card->csd.tacc_ns * 100; + data.timeout_clks = card->csd.tacc_clks * 100; + + timeout_us = data.timeout_ns / 1000; + timeout_us += data.timeout_clks * 1000 / + (card->host->ios.clock / 1000); + + if (timeout_us > 100000) { + data.timeout_ns = 100000000; + data.timeout_clks = 0; + } + + data.blksz = 4; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + memset(&mrq, 0, sizeof(struct mmc_request)); + + mrq.cmd = &cmd; + mrq.data = &data; + + sg_init_one(&sg, &blocks, 4); + + mmc_wait_for_req(card->host, &mrq); + + if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) + return (u32)-1; + + blocks = ntohl(blocks); + + return blocks; +} + static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) { struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; - int ret; + struct mmc_blk_request brq; + int ret = 1; if (mmc_card_claim_host(card)) - goto cmd_err; + goto flush_queue; do { - struct mmc_blk_request brq; struct mmc_command cmd; + u32 readcmd, writecmd; memset(&brq, 0, sizeof(struct mmc_blk_request)); brq.mrq.cmd = &brq.cmd; @@ -172,7 +239,6 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) brq.cmd.arg = req->sector << 9; brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - brq.data.blksz_bits = md->block_bits; brq.data.blksz = 1 << md->block_bits; brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); brq.stop.opcode = MMC_STOP_TRANSMISSION; @@ -181,20 +247,34 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) mmc_set_data_timeout(&brq.data, card, rq_data_dir(req) != READ); - if (rq_data_dir(req) == READ) { - brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK; - brq.data.flags |= MMC_DATA_READ; - } else { - brq.cmd.opcode = MMC_WRITE_BLOCK; - brq.data.flags |= MMC_DATA_WRITE; + /* + * If the host doesn't support multiple block writes, force + * block writes to single block. SD cards are excepted from + * this rule as they support querying the number of + * successfully written sectors. + */ + if (rq_data_dir(req) != READ && + !(card->host->caps & MMC_CAP_MULTIWRITE) && + !mmc_card_sd(card)) brq.data.blocks = 1; - } if (brq.data.blocks > 1) { brq.data.flags |= MMC_DATA_MULTI; brq.mrq.stop = &brq.stop; + readcmd = MMC_READ_MULTIPLE_BLOCK; + writecmd = MMC_WRITE_MULTIPLE_BLOCK; } else { brq.mrq.stop = NULL; + readcmd = MMC_READ_SINGLE_BLOCK; + writecmd = MMC_WRITE_BLOCK; + } + + if (rq_data_dir(req) == READ) { + brq.cmd.opcode = readcmd; + brq.data.flags |= MMC_DATA_READ; + } else { + brq.cmd.opcode = writecmd; + brq.data.flags |= MMC_DATA_WRITE; } brq.data.sg = mq->sg; @@ -219,27 +299,29 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) goto cmd_err; } - do { - int err; - - cmd.opcode = MMC_SEND_STATUS; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - err = mmc_wait_for_cmd(card->host, &cmd, 5); - if (err) { - printk(KERN_ERR "%s: error %d requesting status\n", - req->rq_disk->disk_name, err); - goto cmd_err; - } - } while (!(cmd.resp[0] & R1_READY_FOR_DATA)); + if (rq_data_dir(req) != READ) { + do { + int err; + + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + err = mmc_wait_for_cmd(card->host, &cmd, 5); + if (err) { + printk(KERN_ERR "%s: error %d requesting status\n", + req->rq_disk->disk_name, err); + goto cmd_err; + } + } while (!(cmd.resp[0] & R1_READY_FOR_DATA)); #if 0 - if (cmd.resp[0] & ~0x00000900) - printk(KERN_ERR "%s: status = %08x\n", - req->rq_disk->disk_name, cmd.resp[0]); - if (mmc_decode_status(cmd.resp)) - goto cmd_err; + if (cmd.resp[0] & ~0x00000900) + printk(KERN_ERR "%s: status = %08x\n", + req->rq_disk->disk_name, cmd.resp[0]); + if (mmc_decode_status(cmd.resp)) + goto cmd_err; #endif + } /* * A block was successfully transferred. @@ -262,19 +344,45 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) return 1; cmd_err: + /* + * If this is an SD card and we're writing, we can first + * mark the known good sectors as ok. + * + * If the card is not SD, we can still ok written sectors + * if the controller can do proper error reporting. + * + * For reads we just fail the entire chunk as that should + * be safe in all cases. + */ + if (rq_data_dir(req) != READ && mmc_card_sd(card)) { + u32 blocks; + unsigned int bytes; + + blocks = mmc_sd_num_wr_blocks(card); + if (blocks != (u32)-1) { + if (card->csd.write_partial) + bytes = blocks << md->block_bits; + else + bytes = blocks << 9; + spin_lock_irq(&md->lock); + ret = end_that_request_chunk(req, 1, bytes); + spin_unlock_irq(&md->lock); + } + } else if (rq_data_dir(req) != READ && + (card->host->caps & MMC_CAP_MULTIWRITE)) { + spin_lock_irq(&md->lock); + ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered); + spin_unlock_irq(&md->lock); + } + mmc_card_release_host(card); - /* - * This is a little draconian, but until we get proper - * error handling sorted out here, its the best we can - * do - especially as some hosts have no idea how much - * data was transferred before the error occurred. - */ +flush_queue: spin_lock_irq(&md->lock); - do { + while (ret) { ret = end_that_request_chunk(req, 0, req->current_nr_sectors << 9); - } while (ret); + } add_disk_randomness(req->rq_disk); blkdev_dequeue_request(req); @@ -443,12 +551,11 @@ static void mmc_blk_remove(struct mmc_card *card) if (md) { int devidx; + /* Stop new requests from getting into the queue */ del_gendisk(md->disk); - /* - * I think this is needed. - */ - md->disk->queue = NULL; + /* Then flush out any already in there */ + mmc_cleanup_queue(&md->queue); devidx = md->disk->first_minor >> MMC_SHIFT; __clear_bit(devidx, dev_use); diff --git a/drivers/mmc/mmc_queue.c b/drivers/mmc/mmc_queue.c index 74f8cdeeff0f..3e35a43819fb 100644 --- a/drivers/mmc/mmc_queue.c +++ b/drivers/mmc/mmc_queue.c @@ -10,13 +10,13 @@ */ #include <linux/module.h> #include <linux/blkdev.h> +#include <linux/kthread.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h> #include "mmc_queue.h" -#define MMC_QUEUE_EXIT (1 << 0) -#define MMC_QUEUE_SUSPENDED (1 << 1) +#define MMC_QUEUE_SUSPENDED (1 << 0) /* * Prepare a MMC request. Essentially, this means passing the @@ -28,7 +28,7 @@ static int mmc_prep_request(struct request_queue *q, struct request *req) struct mmc_queue *mq = q->queuedata; int ret = BLKPREP_KILL; - if (req->flags & REQ_SPECIAL) { + if (blk_special_request(req)) { /* * Special commands already have the command * blocks already setup in req->special. @@ -36,7 +36,7 @@ static int mmc_prep_request(struct request_queue *q, struct request *req) BUG_ON(!req->special); ret = BLKPREP_OK; - } else if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) { + } else if (blk_fs_request(req) || blk_pc_request(req)) { /* * Block I/O requests need translating according * to the protocol. @@ -50,7 +50,7 @@ static int mmc_prep_request(struct request_queue *q, struct request *req) } if (ret == BLKPREP_OK) - req->flags |= REQ_DONTPREP; + req->cmd_flags |= REQ_DONTPREP; return ret; } @@ -59,7 +59,6 @@ static int mmc_queue_thread(void *d) { struct mmc_queue *mq = d; struct request_queue *q = mq->queue; - DECLARE_WAITQUEUE(wait, current); /* * Set iothread to ensure that we aren't put to sleep by @@ -67,12 +66,7 @@ static int mmc_queue_thread(void *d) */ current->flags |= PF_MEMALLOC|PF_NOFREEZE; - daemonize("mmcqd"); - - complete(&mq->thread_complete); - down(&mq->thread_sem); - add_wait_queue(&mq->thread_wq, &wait); do { struct request *req = NULL; @@ -84,8 +78,10 @@ static int mmc_queue_thread(void *d) spin_unlock_irq(q->queue_lock); if (!req) { - if (mq->flags & MMC_QUEUE_EXIT) + if (kthread_should_stop()) { + set_current_state(TASK_RUNNING); break; + } up(&mq->thread_sem); schedule(); down(&mq->thread_sem); @@ -95,10 +91,8 @@ static int mmc_queue_thread(void *d) mq->issue_fn(mq, req); } while (1); - remove_wait_queue(&mq->thread_wq, &wait); up(&mq->thread_sem); - complete_and_exit(&mq->thread_complete, 0); return 0; } @@ -111,9 +105,22 @@ static int mmc_queue_thread(void *d) static void mmc_request(request_queue_t *q) { struct mmc_queue *mq = q->queuedata; + struct request *req; + int ret; + + if (!mq) { + printk(KERN_ERR "MMC: killing requests for dead queue\n"); + while ((req = elv_next_request(q)) != NULL) { + do { + ret = end_that_request_chunk(req, 0, + req->current_nr_sectors << 9); + } while (ret); + } + return; + } if (!mq->req) - wake_up(&mq->thread_wq); + wake_up_process(mq->thread); } /** @@ -130,8 +137,8 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock u64 limit = BLK_BOUNCE_HIGH; int ret; - if (host->dev->dma_mask && *host->dev->dma_mask) - limit = *host->dev->dma_mask; + if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask) + limit = *mmc_dev(host)->dma_mask; mq->card = card; mq->queue = blk_init_queue(mmc_request, lock); @@ -152,36 +159,40 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock GFP_KERNEL); if (!mq->sg) { ret = -ENOMEM; - goto cleanup; + goto cleanup_queue; } - init_completion(&mq->thread_complete); - init_waitqueue_head(&mq->thread_wq); init_MUTEX(&mq->thread_sem); - ret = kernel_thread(mmc_queue_thread, mq, CLONE_KERNEL); - if (ret >= 0) { - wait_for_completion(&mq->thread_complete); - init_completion(&mq->thread_complete); - ret = 0; - goto out; + mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd"); + if (IS_ERR(mq->thread)) { + ret = PTR_ERR(mq->thread); + goto free_sg; } - cleanup: + return 0; + + free_sg: kfree(mq->sg); mq->sg = NULL; - + cleanup_queue: blk_cleanup_queue(mq->queue); - out: return ret; } EXPORT_SYMBOL(mmc_init_queue); void mmc_cleanup_queue(struct mmc_queue *mq) { - mq->flags |= MMC_QUEUE_EXIT; - wake_up(&mq->thread_wq); - wait_for_completion(&mq->thread_complete); + request_queue_t *q = mq->queue; + unsigned long flags; + + /* Mark that we should start throwing out stragglers */ + spin_lock_irqsave(q->queue_lock, flags); + q->queuedata = NULL; + spin_unlock_irqrestore(q->queue_lock, flags); + + /* Then terminate our worker thread */ + kthread_stop(mq->thread); kfree(mq->sg); mq->sg = NULL; diff --git a/drivers/mmc/mmc_queue.h b/drivers/mmc/mmc_queue.h index 7182d2f69b4e..c9f139e764f6 100644 --- a/drivers/mmc/mmc_queue.h +++ b/drivers/mmc/mmc_queue.h @@ -6,8 +6,7 @@ struct task_struct; struct mmc_queue { struct mmc_card *card; - struct completion thread_complete; - wait_queue_head_t thread_wq; + struct task_struct *thread; struct semaphore thread_sem; unsigned int flags; struct request *req; diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c index a2a35fd946ee..e334acd045bc 100644 --- a/drivers/mmc/mmc_sysfs.c +++ b/drivers/mmc/mmc_sysfs.c @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/device.h> #include <linux/idr.h> +#include <linux/workqueue.h> #include <linux/mmc/card.h> #include <linux/mmc/host.h> @@ -198,7 +199,7 @@ void mmc_init_card(struct mmc_card *card, struct mmc_host *host) memset(card, 0, sizeof(struct mmc_card)); card->host = host; device_initialize(&card->dev); - card->dev.parent = card->host->dev; + card->dev.parent = mmc_dev(host); card->dev.bus = &mmc_bus_type; card->dev.release = mmc_release_card; } @@ -241,7 +242,7 @@ void mmc_remove_card(struct mmc_card *card) } -static void mmc_host_classdev_release(struct class_device *dev) +static void mmc_host_classdev_release(struct device *dev) { struct mmc_host *host = cls_dev_to_mmc_host(dev); kfree(host); @@ -249,7 +250,7 @@ static void mmc_host_classdev_release(struct class_device *dev) static struct class mmc_host_class = { .name = "mmc_host", - .release = mmc_host_classdev_release, + .dev_release = mmc_host_classdev_release, }; static DEFINE_IDR(mmc_host_idr); @@ -266,10 +267,10 @@ struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev) if (host) { memset(host, 0, sizeof(struct mmc_host) + extra); - host->dev = dev; - host->class_dev.dev = host->dev; + host->parent = dev; + host->class_dev.parent = dev; host->class_dev.class = &mmc_host_class; - class_device_initialize(&host->class_dev); + device_initialize(&host->class_dev); } return host; @@ -291,10 +292,10 @@ int mmc_add_host_sysfs(struct mmc_host *host) if (err) return err; - snprintf(host->class_dev.class_id, BUS_ID_SIZE, + snprintf(host->class_dev.bus_id, BUS_ID_SIZE, "mmc%d", host->index); - return class_device_add(&host->class_dev); + return device_add(&host->class_dev); } /* @@ -302,7 +303,7 @@ int mmc_add_host_sysfs(struct mmc_host *host) */ void mmc_remove_host_sysfs(struct mmc_host *host) { - class_device_del(&host->class_dev); + device_del(&host->class_dev); spin_lock(&mmc_host_lock); idr_remove(&mmc_host_idr, host->index); @@ -314,13 +315,36 @@ void mmc_remove_host_sysfs(struct mmc_host *host) */ void mmc_free_host_sysfs(struct mmc_host *host) { - class_device_put(&host->class_dev); + put_device(&host->class_dev); } +static struct workqueue_struct *workqueue; + +/* + * Internal function. Schedule delayed work in the MMC work queue. + */ +int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay) +{ + return queue_delayed_work(workqueue, work, delay); +} + +/* + * Internal function. Flush all scheduled work from the MMC work queue. + */ +void mmc_flush_scheduled_work(void) +{ + flush_workqueue(workqueue); +} static int __init mmc_init(void) { - int ret = bus_register(&mmc_bus_type); + int ret; + + workqueue = create_singlethread_workqueue("kmmcd"); + if (!workqueue) + return -ENOMEM; + + ret = bus_register(&mmc_bus_type); if (ret == 0) { ret = class_register(&mmc_host_class); if (ret) @@ -333,6 +357,7 @@ static void __exit mmc_exit(void) { class_unregister(&mmc_host_class); bus_unregister(&mmc_bus_type); + destroy_workqueue(workqueue); } module_init(mmc_init); diff --git a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c index 1886562abdd4..e9b80e920266 100644 --- a/drivers/mmc/mmci.c +++ b/drivers/mmc/mmci.c @@ -69,12 +69,13 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) unsigned int datactrl, timeout, irqmask; unsigned long long clks; void __iomem *base; + int blksz_bits; DBG(host, "blksz %04x blks %04x flags %08x\n", - 1 << data->blksz_bits, data->blocks, data->flags); + data->blksz, data->blocks, data->flags); host->data = data; - host->size = data->blocks << data->blksz_bits; + host->size = data->blksz; host->data_xfered = 0; mmci_init_sg(host, data); @@ -88,7 +89,10 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) writel(timeout, base + MMCIDATATIMER); writel(host->size, base + MMCIDATALENGTH); - datactrl = MCI_DPSM_ENABLE | data->blksz_bits << 4; + blksz_bits = ffs(data->blksz) - 1; + BUG_ON(1 << blksz_bits != data->blksz); + + datactrl = MCI_DPSM_ENABLE | blksz_bits << 4; if (data->flags & MMC_DATA_READ) { datactrl |= MCI_DPSM_DIRECTION; irqmask = MCI_RXFIFOHALFFULLMASK; @@ -145,7 +149,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data, unsigned int status) { if (status & MCI_DATABLOCKEND) { - host->data_xfered += 1 << data->blksz_bits; + host->data_xfered += data->blksz; } if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) { if (status & MCI_DATACRCFAIL) @@ -257,7 +261,7 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int rem /* * PIO data transfer IRQ handler. */ -static irqreturn_t mmci_pio_irq(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t mmci_pio_irq(int irq, void *dev_id) { struct mmci_host *host = dev_id; void __iomem *base = host->base; @@ -343,7 +347,7 @@ static irqreturn_t mmci_pio_irq(int irq, void *dev_id, struct pt_regs *regs) /* * Handle completion of command and data transfers. */ -static irqreturn_t mmci_irq(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t mmci_irq(int irq, void *dev_id) { struct mmci_host *host = dev_id; u32 status; @@ -439,7 +443,7 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) } } -static struct mmc_host_ops mmci_ops = { +static const struct mmc_host_ops mmci_ops = { .request = mmci_request, .set_ios = mmci_set_ios, }; @@ -505,6 +509,7 @@ static int mmci_probe(struct amba_device *dev, void *id) mmc->f_min = (host->mclk + 511) / 512; mmc->f_max = min(host->mclk, fmax); mmc->ocr_avail = plat->ocr_mask; + mmc->caps = MMC_CAP_MULTIWRITE; /* * We can do SGIO diff --git a/drivers/mmc/omap.c b/drivers/mmc/omap.c index ddf06b32c159..435d331e772a 100644 --- a/drivers/mmc/omap.c +++ b/drivers/mmc/omap.c @@ -38,7 +38,57 @@ #include <asm/arch/fpga.h> #include <asm/arch/tps65010.h> -#include "omap.h" +#define OMAP_MMC_REG_CMD 0x00 +#define OMAP_MMC_REG_ARGL 0x04 +#define OMAP_MMC_REG_ARGH 0x08 +#define OMAP_MMC_REG_CON 0x0c +#define OMAP_MMC_REG_STAT 0x10 +#define OMAP_MMC_REG_IE 0x14 +#define OMAP_MMC_REG_CTO 0x18 +#define OMAP_MMC_REG_DTO 0x1c +#define OMAP_MMC_REG_DATA 0x20 +#define OMAP_MMC_REG_BLEN 0x24 +#define OMAP_MMC_REG_NBLK 0x28 +#define OMAP_MMC_REG_BUF 0x2c +#define OMAP_MMC_REG_SDIO 0x34 +#define OMAP_MMC_REG_REV 0x3c +#define OMAP_MMC_REG_RSP0 0x40 +#define OMAP_MMC_REG_RSP1 0x44 +#define OMAP_MMC_REG_RSP2 0x48 +#define OMAP_MMC_REG_RSP3 0x4c +#define OMAP_MMC_REG_RSP4 0x50 +#define OMAP_MMC_REG_RSP5 0x54 +#define OMAP_MMC_REG_RSP6 0x58 +#define OMAP_MMC_REG_RSP7 0x5c +#define OMAP_MMC_REG_IOSR 0x60 +#define OMAP_MMC_REG_SYSC 0x64 +#define OMAP_MMC_REG_SYSS 0x68 + +#define OMAP_MMC_STAT_CARD_ERR (1 << 14) +#define OMAP_MMC_STAT_CARD_IRQ (1 << 13) +#define OMAP_MMC_STAT_OCR_BUSY (1 << 12) +#define OMAP_MMC_STAT_A_EMPTY (1 << 11) +#define OMAP_MMC_STAT_A_FULL (1 << 10) +#define OMAP_MMC_STAT_CMD_CRC (1 << 8) +#define OMAP_MMC_STAT_CMD_TOUT (1 << 7) +#define OMAP_MMC_STAT_DATA_CRC (1 << 6) +#define OMAP_MMC_STAT_DATA_TOUT (1 << 5) +#define OMAP_MMC_STAT_END_BUSY (1 << 4) +#define OMAP_MMC_STAT_END_OF_DATA (1 << 3) +#define OMAP_MMC_STAT_CARD_BUSY (1 << 2) +#define OMAP_MMC_STAT_END_OF_CMD (1 << 0) + +#define OMAP_MMC_READ(host, reg) __raw_readw((host)->virt_base + OMAP_MMC_REG_##reg) +#define OMAP_MMC_WRITE(host, reg, val) __raw_writew((val), (host)->virt_base + OMAP_MMC_REG_##reg) + +/* + * Command types + */ +#define OMAP_MMC_CMDTYPE_BC 0 +#define OMAP_MMC_CMDTYPE_BCR 1 +#define OMAP_MMC_CMDTYPE_AC 2 +#define OMAP_MMC_CMDTYPE_ADTC 3 + #define DRIVER_NAME "mmci-omap" #define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE)) @@ -60,8 +110,9 @@ struct mmc_omap_host { unsigned char id; /* 16xx chips have 2 MMC blocks */ struct clk * iclk; struct clk * fclk; - struct resource *res; - void __iomem *base; + struct resource *mem_res; + void __iomem *virt_base; + unsigned int phys_base; int irq; unsigned char bus_mode; unsigned char hw_bus_mode; @@ -191,16 +242,16 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) clk_enable(host->fclk); - OMAP_MMC_WRITE(host->base, CTO, 200); - OMAP_MMC_WRITE(host->base, ARGL, cmd->arg & 0xffff); - OMAP_MMC_WRITE(host->base, ARGH, cmd->arg >> 16); - OMAP_MMC_WRITE(host->base, IE, + OMAP_MMC_WRITE(host, CTO, 200); + OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff); + OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16); + OMAP_MMC_WRITE(host, IE, OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL | OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT | OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT | OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR | OMAP_MMC_STAT_END_OF_DATA); - OMAP_MMC_WRITE(host->base, CMD, cmdreg); + OMAP_MMC_WRITE(host, CMD, cmdreg); } static void @@ -296,22 +347,22 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) if (cmd->flags & MMC_RSP_136) { /* response type 2 */ cmd->resp[3] = - OMAP_MMC_READ(host->base, RSP0) | - (OMAP_MMC_READ(host->base, RSP1) << 16); + OMAP_MMC_READ(host, RSP0) | + (OMAP_MMC_READ(host, RSP1) << 16); cmd->resp[2] = - OMAP_MMC_READ(host->base, RSP2) | - (OMAP_MMC_READ(host->base, RSP3) << 16); + OMAP_MMC_READ(host, RSP2) | + (OMAP_MMC_READ(host, RSP3) << 16); cmd->resp[1] = - OMAP_MMC_READ(host->base, RSP4) | - (OMAP_MMC_READ(host->base, RSP5) << 16); + OMAP_MMC_READ(host, RSP4) | + (OMAP_MMC_READ(host, RSP5) << 16); cmd->resp[0] = - OMAP_MMC_READ(host->base, RSP6) | - (OMAP_MMC_READ(host->base, RSP7) << 16); + OMAP_MMC_READ(host, RSP6) | + (OMAP_MMC_READ(host, RSP7) << 16); } else { /* response types 1, 1b, 3, 4, 5, 6 */ cmd->resp[0] = - OMAP_MMC_READ(host->base, RSP6) | - (OMAP_MMC_READ(host->base, RSP7) << 16); + OMAP_MMC_READ(host, RSP6) | + (OMAP_MMC_READ(host, RSP7) << 16); } } @@ -354,9 +405,9 @@ mmc_omap_xfer_data(struct mmc_omap_host *host, int write) host->data->bytes_xfered += n; if (write) { - __raw_writesw(host->base + OMAP_MMC_REG_DATA, host->buffer, n); + __raw_writesw(host->virt_base + OMAP_MMC_REG_DATA, host->buffer, n); } else { - __raw_readsw(host->base + OMAP_MMC_REG_DATA, host->buffer, n); + __raw_readsw(host->virt_base + OMAP_MMC_REG_DATA, host->buffer, n); } } @@ -377,7 +428,7 @@ static inline void mmc_omap_report_irq(u16 status) } } -static irqreturn_t mmc_omap_irq(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t mmc_omap_irq(int irq, void *dev_id) { struct mmc_omap_host * host = (struct mmc_omap_host *)dev_id; u16 status; @@ -386,11 +437,11 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id, struct pt_regs *regs) int transfer_error; if (host->cmd == NULL && host->data == NULL) { - status = OMAP_MMC_READ(host->base, STAT); + status = OMAP_MMC_READ(host, STAT); dev_info(mmc_dev(host->mmc),"spurious irq 0x%04x\n", status); if (status != 0) { - OMAP_MMC_WRITE(host->base, STAT, status); - OMAP_MMC_WRITE(host->base, IE, 0); + OMAP_MMC_WRITE(host, STAT, status); + OMAP_MMC_WRITE(host, IE, 0); } return IRQ_HANDLED; } @@ -399,8 +450,8 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id, struct pt_regs *regs) end_transfer = 0; transfer_error = 0; - while ((status = OMAP_MMC_READ(host->base, STAT)) != 0) { - OMAP_MMC_WRITE(host->base, STAT, status); + while ((status = OMAP_MMC_READ(host, STAT)) != 0) { + OMAP_MMC_WRITE(host, STAT, status); #ifdef CONFIG_MMC_DEBUG dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ", status, host->cmd != NULL ? host->cmd->opcode : -1); @@ -470,8 +521,8 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id, struct pt_regs *regs) if (status & OMAP_MMC_STAT_CARD_ERR) { if (host->cmd && host->cmd->opcode == MMC_STOP_TRANSMISSION) { - u32 response = OMAP_MMC_READ(host->base, RSP6) - | (OMAP_MMC_READ(host->base, RSP7) << 16); + u32 response = OMAP_MMC_READ(host, RSP6) + | (OMAP_MMC_READ(host, RSP7) << 16); /* STOP sometimes sets must-ignore bits */ if (!(response & (R1_CC_ERROR | R1_ILLEGAL_COMMAND @@ -514,7 +565,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id, struct pt_regs *regs) return IRQ_HANDLED; } -static irqreturn_t mmc_omap_switch_irq(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t mmc_omap_switch_irq(int irq, void *dev_id) { struct mmc_omap_host *host = (struct mmc_omap_host *) dev_id; @@ -530,12 +581,6 @@ static void mmc_omap_switch_timer(unsigned long arg) schedule_work(&host->switch_work); } -/* FIXME: Handle card insertion and removal properly. Maybe use a mask - * for MMC state? */ -static void mmc_omap_switch_callback(unsigned long data, u8 mmc_mask) -{ -} - static void mmc_omap_switch_handler(void *data) { struct mmc_omap_host *host = (struct mmc_omap_host *) data; @@ -581,7 +626,7 @@ mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data) int dst_port = 0; int sync_dev = 0; - data_addr = io_v2p((u32) host->base) + OMAP_MMC_REG_DATA; + data_addr = host->phys_base + OMAP_MMC_REG_DATA; frame = data->blksz; count = sg_dma_len(sg); @@ -640,10 +685,9 @@ mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data) } /* Max limit for DMA frame count is 0xffff */ - if (unlikely(count > 0xffff)) - BUG(); + BUG_ON(count > 0xffff); - OMAP_MMC_WRITE(host->base, BUF, buf); + OMAP_MMC_WRITE(host, BUF, buf); omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S16, frame, count, OMAP_DMA_SYNC_FRAME, sync_dev, 0); @@ -728,11 +772,11 @@ static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_reques { u16 reg; - reg = OMAP_MMC_READ(host->base, SDIO); + reg = OMAP_MMC_READ(host, SDIO); reg &= ~(1 << 5); - OMAP_MMC_WRITE(host->base, SDIO, reg); + OMAP_MMC_WRITE(host, SDIO, reg); /* Set maximum timeout */ - OMAP_MMC_WRITE(host->base, CTO, 0xff); + OMAP_MMC_WRITE(host, CTO, 0xff); } static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req) @@ -746,14 +790,14 @@ static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_reque timeout = req->data->timeout_clks + req->data->timeout_ns / 500; /* Check if we need to use timeout multiplier register */ - reg = OMAP_MMC_READ(host->base, SDIO); + reg = OMAP_MMC_READ(host, SDIO); if (timeout > 0xffff) { reg |= (1 << 5); timeout /= 1024; } else reg &= ~(1 << 5); - OMAP_MMC_WRITE(host->base, SDIO, reg); - OMAP_MMC_WRITE(host->base, DTO, timeout); + OMAP_MMC_WRITE(host, SDIO, reg); + OMAP_MMC_WRITE(host, DTO, timeout); } static void @@ -765,19 +809,18 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) host->data = data; if (data == NULL) { - OMAP_MMC_WRITE(host->base, BLEN, 0); - OMAP_MMC_WRITE(host->base, NBLK, 0); - OMAP_MMC_WRITE(host->base, BUF, 0); + OMAP_MMC_WRITE(host, BLEN, 0); + OMAP_MMC_WRITE(host, NBLK, 0); + OMAP_MMC_WRITE(host, BUF, 0); host->dma_in_use = 0; set_cmd_timeout(host, req); return; } - block_size = data->blksz; - OMAP_MMC_WRITE(host->base, NBLK, data->blocks - 1); - OMAP_MMC_WRITE(host->base, BLEN, block_size - 1); + OMAP_MMC_WRITE(host, NBLK, data->blocks - 1); + OMAP_MMC_WRITE(host, BLEN, block_size - 1); set_data_timeout(host, req); /* cope with calling layer confusion; it issues "single @@ -819,7 +862,7 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) /* Revert to PIO? */ if (!use_dma) { - OMAP_MMC_WRITE(host->base, BUF, 0x1f1f); + OMAP_MMC_WRITE(host, BUF, 0x1f1f); host->total_bytes_left = data->blocks * block_size; host->sg_len = sg_len; mmc_omap_sg_to_buf(host); @@ -845,7 +888,6 @@ static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) static void innovator_fpga_socket_power(int on) { #if defined(CONFIG_MACH_OMAP_INNOVATOR) && defined(CONFIG_ARCH_OMAP15XX) - if (on) { fpga_write(fpga_read(OMAP1510_FPGA_POWER) | (1 << 3), OMAP1510_FPGA_POWER); @@ -871,8 +913,8 @@ static void mmc_omap_power(struct mmc_omap_host *host, int on) /* GPIO 4 of TPS65010 sends SD_EN signal */ tps65010_set_gpio_out_value(GPIO4, HIGH); else if (cpu_is_omap24xx()) { - u16 reg = OMAP_MMC_READ(host->base, CON); - OMAP_MMC_WRITE(host->base, CON, reg | (1 << 11)); + u16 reg = OMAP_MMC_READ(host, CON); + OMAP_MMC_WRITE(host, CON, reg | (1 << 11)); } else if (host->power_pin >= 0) omap_set_gpio_dataout(host->power_pin, 1); @@ -884,8 +926,8 @@ static void mmc_omap_power(struct mmc_omap_host *host, int on) else if (machine_is_omap_h3()) tps65010_set_gpio_out_value(GPIO4, LOW); else if (cpu_is_omap24xx()) { - u16 reg = OMAP_MMC_READ(host->base, CON); - OMAP_MMC_WRITE(host->base, CON, reg & ~(1 << 11)); + u16 reg = OMAP_MMC_READ(host, CON); + OMAP_MMC_WRITE(host, CON, reg & ~(1 << 11)); } else if (host->power_pin >= 0) omap_set_gpio_dataout(host->power_pin, 0); @@ -927,7 +969,7 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) case MMC_POWER_UP: case MMC_POWER_ON: mmc_omap_power(host, 1); - dsor |= 1<<11; + dsor |= 1 << 11; break; } @@ -941,14 +983,14 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) * which results in the while loop below getting stuck. * Writing to the CON register twice seems to do the trick. */ for (i = 0; i < 2; i++) - OMAP_MMC_WRITE(host->base, CON, dsor); + OMAP_MMC_WRITE(host, CON, dsor); if (ios->power_mode == MMC_POWER_UP) { /* Send clock cycles, poll completion */ - OMAP_MMC_WRITE(host->base, IE, 0); - OMAP_MMC_WRITE(host->base, STAT, 0xffff); - OMAP_MMC_WRITE(host->base, CMD, 1<<7); - while (0 == (OMAP_MMC_READ(host->base, STAT) & 1)); - OMAP_MMC_WRITE(host->base, STAT, 1); + OMAP_MMC_WRITE(host, IE, 0); + OMAP_MMC_WRITE(host, STAT, 0xffff); + OMAP_MMC_WRITE(host, CMD, 1 << 7); + while ((OMAP_MMC_READ(host, STAT) & 1) == 0); + OMAP_MMC_WRITE(host, STAT, 1); } clk_disable(host->fclk); } @@ -960,7 +1002,7 @@ static int mmc_omap_get_ro(struct mmc_host *mmc) return host->wp_pin && omap_get_gpio_datain(host->wp_pin); } -static struct mmc_host_ops mmc_omap_ops = { +static const struct mmc_host_ops mmc_omap_ops = { .request = mmc_omap_request, .set_ios = mmc_omap_set_ios, .get_ro = mmc_omap_get_ro, @@ -971,25 +1013,29 @@ static int __init mmc_omap_probe(struct platform_device *pdev) struct omap_mmc_conf *minfo = pdev->dev.platform_data; struct mmc_host *mmc; struct mmc_omap_host *host = NULL; - struct resource *r; + struct resource *res; int ret = 0; int irq; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (minfo == NULL) { + dev_err(&pdev->dev, "platform data missing\n"); + return -ENXIO; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); - if (!r || irq < 0) + if (res == NULL || irq < 0) return -ENXIO; - r = request_mem_region(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1, - pdev->name); - if (!r) + res = request_mem_region(res->start, res->end - res->start + 1, + pdev->name); + if (res == NULL) return -EBUSY; mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev); - if (!mmc) { + if (mmc == NULL) { ret = -ENOMEM; - goto out; + goto err_free_mem_region; } host = mmc_priv(mmc); @@ -1001,13 +1047,13 @@ static int __init mmc_omap_probe(struct platform_device *pdev) host->dma_timer.data = (unsigned long) host; host->id = pdev->id; - host->res = r; + host->mem_res = res; host->irq = irq; if (cpu_is_omap24xx()) { host->iclk = clk_get(&pdev->dev, "mmc_ick"); if (IS_ERR(host->iclk)) - goto out; + goto err_free_mmc_host; clk_enable(host->iclk); } @@ -1018,7 +1064,7 @@ static int __init mmc_omap_probe(struct platform_device *pdev) if (IS_ERR(host->fclk)) { ret = PTR_ERR(host->fclk); - goto out; + goto err_free_iclk; } /* REVISIT: @@ -1031,16 +1077,18 @@ static int __init mmc_omap_probe(struct platform_device *pdev) host->use_dma = 1; host->dma_ch = -1; - host->irq = pdev->resource[1].start; - host->base = (void __iomem*)IO_ADDRESS(r->start); - - if (minfo->wire4) - mmc->caps |= MMC_CAP_4_BIT_DATA; + host->irq = irq; + host->phys_base = host->mem_res->start; + host->virt_base = (void __iomem *) IO_ADDRESS(host->phys_base); mmc->ops = &mmc_omap_ops; mmc->f_min = 400000; mmc->f_max = 24000000; - mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->caps = MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; + + if (minfo->wire4) + mmc->caps |= MMC_CAP_4_BIT_DATA; /* Use scatterlist DMA to reduce per-transfer costs. * NOTE max_seg_size assumption that small blocks aren't @@ -1055,20 +1103,18 @@ static int __init mmc_omap_probe(struct platform_device *pdev) if ((ret = omap_request_gpio(host->power_pin)) != 0) { dev_err(mmc_dev(host->mmc), "Unable to get GPIO pin for MMC power\n"); - goto out; + goto err_free_fclk; } omap_set_gpio_direction(host->power_pin, 0); } ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host); if (ret) - goto out; + goto err_free_power_gpio; host->dev = &pdev->dev; platform_set_drvdata(pdev, host); - mmc_add_host(mmc); - if (host->switch_pin >= 0) { INIT_WORK(&host->switch_work, mmc_omap_switch_handler, host); init_timer(&host->switch_timer); @@ -1106,10 +1152,11 @@ static int __init mmc_omap_probe(struct platform_device *pdev) schedule_work(&host->switch_work); } -no_switch: + mmc_add_host(mmc); + return 0; -out: +no_switch: /* FIXME: Free other resources too. */ if (host) { if (host->iclk && !IS_ERR(host->iclk)) @@ -1118,6 +1165,20 @@ out: clk_put(host->fclk); mmc_free_host(host->mmc); } +err_free_power_gpio: + if (host->power_pin >= 0) + omap_free_gpio(host->power_pin); +err_free_fclk: + clk_put(host->fclk); +err_free_iclk: + if (host->iclk != NULL) { + clk_disable(host->iclk); + clk_put(host->iclk); + } +err_free_mmc_host: + mmc_free_host(host->mmc); +err_free_mem_region: + release_mem_region(res->start, res->end - res->start + 1); return ret; } @@ -1127,30 +1188,31 @@ static int mmc_omap_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); - if (host) { - mmc_remove_host(host->mmc); - free_irq(host->irq, host); - - if (host->power_pin >= 0) - omap_free_gpio(host->power_pin); - if (host->switch_pin >= 0) { - device_remove_file(&pdev->dev, &dev_attr_enable_poll); - device_remove_file(&pdev->dev, &dev_attr_cover_switch); - free_irq(OMAP_GPIO_IRQ(host->switch_pin), host); - omap_free_gpio(host->switch_pin); - host->switch_pin = -1; - del_timer_sync(&host->switch_timer); - flush_scheduled_work(); - } - if (host->iclk && !IS_ERR(host->iclk)) - clk_put(host->iclk); - if (host->fclk && !IS_ERR(host->fclk)) - clk_put(host->fclk); - mmc_free_host(host->mmc); + BUG_ON(host == NULL); + + mmc_remove_host(host->mmc); + free_irq(host->irq, host); + + if (host->power_pin >= 0) + omap_free_gpio(host->power_pin); + if (host->switch_pin >= 0) { + device_remove_file(&pdev->dev, &dev_attr_enable_poll); + device_remove_file(&pdev->dev, &dev_attr_cover_switch); + free_irq(OMAP_GPIO_IRQ(host->switch_pin), host); + omap_free_gpio(host->switch_pin); + host->switch_pin = -1; + del_timer_sync(&host->switch_timer); + flush_scheduled_work(); } + if (host->iclk && !IS_ERR(host->iclk)) + clk_put(host->iclk); + if (host->fclk && !IS_ERR(host->fclk)) + clk_put(host->fclk); release_mem_region(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1); + pdev->resource[0].end - pdev->resource[0].start + 1); + + mmc_free_host(host->mmc); return 0; } diff --git a/drivers/mmc/omap.h b/drivers/mmc/omap.h deleted file mode 100644 index c954d355a5e3..000000000000 --- a/drivers/mmc/omap.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef DRIVERS_MEDIA_MMC_OMAP_H -#define DRIVERS_MEDIA_MMC_OMAP_H - -#define OMAP_MMC_REG_CMD 0x00 -#define OMAP_MMC_REG_ARGL 0x04 -#define OMAP_MMC_REG_ARGH 0x08 -#define OMAP_MMC_REG_CON 0x0c -#define OMAP_MMC_REG_STAT 0x10 -#define OMAP_MMC_REG_IE 0x14 -#define OMAP_MMC_REG_CTO 0x18 -#define OMAP_MMC_REG_DTO 0x1c -#define OMAP_MMC_REG_DATA 0x20 -#define OMAP_MMC_REG_BLEN 0x24 -#define OMAP_MMC_REG_NBLK 0x28 -#define OMAP_MMC_REG_BUF 0x2c -#define OMAP_MMC_REG_SDIO 0x34 -#define OMAP_MMC_REG_REV 0x3c -#define OMAP_MMC_REG_RSP0 0x40 -#define OMAP_MMC_REG_RSP1 0x44 -#define OMAP_MMC_REG_RSP2 0x48 -#define OMAP_MMC_REG_RSP3 0x4c -#define OMAP_MMC_REG_RSP4 0x50 -#define OMAP_MMC_REG_RSP5 0x54 -#define OMAP_MMC_REG_RSP6 0x58 -#define OMAP_MMC_REG_RSP7 0x5c -#define OMAP_MMC_REG_IOSR 0x60 -#define OMAP_MMC_REG_SYSC 0x64 -#define OMAP_MMC_REG_SYSS 0x68 - -#define OMAP_MMC_STAT_CARD_ERR (1 << 14) -#define OMAP_MMC_STAT_CARD_IRQ (1 << 13) -#define OMAP_MMC_STAT_OCR_BUSY (1 << 12) -#define OMAP_MMC_STAT_A_EMPTY (1 << 11) -#define OMAP_MMC_STAT_A_FULL (1 << 10) -#define OMAP_MMC_STAT_CMD_CRC (1 << 8) -#define OMAP_MMC_STAT_CMD_TOUT (1 << 7) -#define OMAP_MMC_STAT_DATA_CRC (1 << 6) -#define OMAP_MMC_STAT_DATA_TOUT (1 << 5) -#define OMAP_MMC_STAT_END_BUSY (1 << 4) -#define OMAP_MMC_STAT_END_OF_DATA (1 << 3) -#define OMAP_MMC_STAT_CARD_BUSY (1 << 2) -#define OMAP_MMC_STAT_END_OF_CMD (1 << 0) - -#define OMAP_MMC_READ(base, reg) __raw_readw((base) + OMAP_MMC_REG_##reg) -#define OMAP_MMC_WRITE(base, reg, val) __raw_writew((val), (base) + OMAP_MMC_REG_##reg) - -/* - * Command types - */ -#define OMAP_MMC_CMDTYPE_BC 0 -#define OMAP_MMC_CMDTYPE_BCR 1 -#define OMAP_MMC_CMDTYPE_AC 2 -#define OMAP_MMC_CMDTYPE_ADTC 3 - -#endif diff --git a/drivers/mmc/pxamci.c b/drivers/mmc/pxamci.c index ef350908478c..45a9283ce498 100644 --- a/drivers/mmc/pxamci.c +++ b/drivers/mmc/pxamci.c @@ -299,7 +299,7 @@ static int pxamci_data_done(struct pxamci_host *host, unsigned int stat) return 1; } -static irqreturn_t pxamci_irq(int irq, void *devid, struct pt_regs *regs) +static irqreturn_t pxamci_irq(int irq, void *devid) { struct pxamci_host *host = devid; unsigned int ireg; @@ -355,7 +355,7 @@ static int pxamci_get_ro(struct mmc_host *mmc) struct pxamci_host *host = mmc_priv(mmc); if (host->pdata && host->pdata->get_ro) - return host->pdata->get_ro(mmc->dev); + return host->pdata->get_ro(mmc_dev(mmc)); /* Host doesn't support read only detection so assume writeable */ return 0; } @@ -383,7 +383,7 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->power_mode = ios->power_mode; if (host->pdata && host->pdata->setpower) - host->pdata->setpower(mmc->dev, ios->vdd); + host->pdata->setpower(mmc_dev(mmc), ios->vdd); if (ios->power_mode == MMC_POWER_ON) host->cmdat |= CMDAT_INIT; @@ -393,19 +393,19 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) host->clkrt, host->cmdat); } -static struct mmc_host_ops pxamci_ops = { +static const struct mmc_host_ops pxamci_ops = { .request = pxamci_request, .get_ro = pxamci_get_ro, .set_ios = pxamci_set_ios, }; -static void pxamci_dma_irq(int dma, void *devid, struct pt_regs *regs) +static void pxamci_dma_irq(int dma, void *devid) { printk(KERN_ERR "DMA%d: IRQ???\n", dma); DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; } -static irqreturn_t pxamci_detect_irq(int irq, void *devid, struct pt_regs *regs) +static irqreturn_t pxamci_detect_irq(int irq, void *devid) { struct pxamci_host *host = mmc_priv(devid); diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 4e21b3b9d330..c2d13d7e9911 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -4,8 +4,9 @@ * Copyright (C) 2005-2006 Pierre Ossman, All Rights Reserved. * * 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. + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. */ #include <linux/delay.h> @@ -34,6 +35,8 @@ static unsigned int debug_quirks = 0; #define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0) #define SDHCI_QUIRK_FORCE_DMA (1<<1) +/* Controller doesn't like some resets when there is no card inserted. */ +#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2) static const struct pci_device_id pci_ids[] __devinitdata = { { @@ -50,7 +53,8 @@ static const struct pci_device_id pci_ids[] __devinitdata = { .device = PCI_DEVICE_ID_RICOH_R5C822, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .driver_data = SDHCI_QUIRK_FORCE_DMA, + .driver_data = SDHCI_QUIRK_FORCE_DMA | + SDHCI_QUIRK_NO_CARD_NO_RESET, }, { @@ -124,6 +128,12 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask) { unsigned long timeout; + if (host->chip->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { + if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & + SDHCI_CARD_PRESENT)) + return; + } + writeb(mask, host->ioaddr + SDHCI_SOFTWARE_RESET); if (mask & SDHCI_RESET_ALL) @@ -606,6 +616,7 @@ static void sdhci_finish_command(struct sdhci_host *host) static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { int div; + u8 ctrl; u16 clk; unsigned long timeout; @@ -614,6 +625,13 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL); + ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); + if (clock > 25000000) + ctrl |= SDHCI_CTRL_HISPD; + else + ctrl &= ~SDHCI_CTRL_HISPD; + writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); + if (clock == 0) goto out; @@ -716,6 +734,7 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) } else sdhci_send_command(host, mrq->cmd); + mmiowb(); spin_unlock_irqrestore(&host->lock, flags); } @@ -752,6 +771,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ctrl &= ~SDHCI_CTRL_4BITBUS; writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); + mmiowb(); spin_unlock_irqrestore(&host->lock, flags); } @@ -772,7 +792,7 @@ static int sdhci_get_ro(struct mmc_host *mmc) return !(present & SDHCI_WRITE_PROTECT); } -static struct mmc_host_ops sdhci_ops = { +static const struct mmc_host_ops sdhci_ops = { .request = sdhci_request, .set_ios = sdhci_set_ios, .get_ro = sdhci_get_ro, @@ -859,6 +879,7 @@ static void sdhci_tasklet_finish(unsigned long param) sdhci_deactivate_led(host); + mmiowb(); spin_unlock_irqrestore(&host->lock, flags); mmc_request_done(host->mmc, mrq); @@ -892,6 +913,7 @@ static void sdhci_timeout_timer(unsigned long data) } } + mmiowb(); spin_unlock_irqrestore(&host->lock, flags); } @@ -971,7 +993,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) } } -static irqreturn_t sdhci_irq(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t sdhci_irq(int irq, void *dev_id) { irqreturn_t result; struct sdhci_host* host = dev_id; @@ -1029,6 +1051,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id, struct pt_regs *regs) result = IRQ_HANDLED; + mmiowb(); out: spin_unlock(&host->lock); @@ -1094,6 +1117,7 @@ static int sdhci_resume (struct pci_dev *pdev) if (chip->hosts[i]->flags & SDHCI_USE_DMA) pci_set_master(pdev); sdhci_init(chip->hosts[i]); + mmiowb(); ret = mmc_resume_host(chip->hosts[i]->mmc); if (ret) return ret; @@ -1146,8 +1170,8 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) } if (pci_resource_len(pdev, first_bar + slot) != 0x100) { - printk(KERN_ERR DRIVER_NAME ": Invalid iomem size. Aborting.\n"); - return -ENODEV; + printk(KERN_ERR DRIVER_NAME ": Invalid iomem size. " + "You may experience problems.\n"); } if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) { @@ -1167,6 +1191,9 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) host = mmc_priv(mmc); host->mmc = mmc; + host->chip = chip; + chip->hosts[slot] = host; + host->bar = first_bar + slot; host->addr = pci_resource_start(pdev, host->bar); @@ -1262,7 +1289,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) mmc->ops = &sdhci_ops; mmc->f_min = host->max_clk / 256; mmc->f_max = host->max_clk; - mmc->caps = MMC_CAP_4_BIT_DATA; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; mmc->ocr_avail = 0; if (caps & SDHCI_CAN_VDD_330) @@ -1272,6 +1299,13 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) else if (caps & SDHCI_CAN_VDD_180) mmc->ocr_avail |= MMC_VDD_17_18|MMC_VDD_18_19; + if ((host->max_clk > 25000000) && !(caps & SDHCI_CAN_DO_HISPD)) { + printk(KERN_ERR "%s: Controller reports > 25 MHz base clock," + " but no high speed support.\n", + host->slot_descr); + mmc->f_max = 25000000; + } + if (mmc->ocr_avail == 0) { printk(KERN_ERR "%s: Hardware doesn't report any " "support voltages.\n", host->slot_descr); @@ -1310,7 +1344,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) tasklet_init(&host->finish_tasklet, sdhci_tasklet_finish, (unsigned long)host); - setup_timer(&host->timer, sdhci_timeout_timer, (long)host); + setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host); ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, host->slot_descr, host); @@ -1323,8 +1357,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) sdhci_dumpregs(host); #endif - host->chip = chip; - chip->hosts[slot] = host; + mmiowb(); mmc_add_host(mmc); diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h index f2453343f783..f9d1a0a6f03a 100644 --- a/drivers/mmc/sdhci.h +++ b/drivers/mmc/sdhci.h @@ -4,8 +4,9 @@ * Copyright (C) 2005 Pierre Ossman, All Rights Reserved. * * 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. + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. */ /* @@ -70,6 +71,7 @@ #define SDHCI_HOST_CONTROL 0x28 #define SDHCI_CTRL_LED 0x01 #define SDHCI_CTRL_4BITBUS 0x02 +#define SDHCI_CTRL_HISPD 0x04 #define SDHCI_POWER_CONTROL 0x29 #define SDHCI_POWER_ON 0x01 @@ -137,6 +139,7 @@ #define SDHCI_CLOCK_BASE_SHIFT 8 #define SDHCI_MAX_BLOCK_MASK 0x00030000 #define SDHCI_MAX_BLOCK_SHIFT 16 +#define SDHCI_CAN_DO_HISPD 0x00200000 #define SDHCI_CAN_DO_DMA 0x00400000 #define SDHCI_CAN_VDD_330 0x01000000 #define SDHCI_CAN_VDD_300 0x02000000 diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c new file mode 100644 index 000000000000..f18ad998b3cb --- /dev/null +++ b/drivers/mmc/tifm_sd.c @@ -0,0 +1,936 @@ +/* + * tifm_sd.c - TI FlashMedia driver + * + * Copyright (C) 2006 Alex Dubov <oakad@yahoo.com> + * + * 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. + * + */ + + +#include <linux/tifm.h> +#include <linux/mmc/protocol.h> +#include <linux/mmc/host.h> +#include <linux/highmem.h> +#include <asm/io.h> + +#define DRIVER_NAME "tifm_sd" +#define DRIVER_VERSION "0.6" + +static int no_dma = 0; +static int fixed_timeout = 0; +module_param(no_dma, bool, 0644); +module_param(fixed_timeout, bool, 0644); + +/* Constants here are mostly from OMAP5912 datasheet */ +#define TIFM_MMCSD_RESET 0x0002 +#define TIFM_MMCSD_CLKMASK 0x03ff +#define TIFM_MMCSD_POWER 0x0800 +#define TIFM_MMCSD_4BBUS 0x8000 +#define TIFM_MMCSD_RXDE 0x8000 /* rx dma enable */ +#define TIFM_MMCSD_TXDE 0x0080 /* tx dma enable */ +#define TIFM_MMCSD_BUFINT 0x0c00 /* set bits: AE, AF */ +#define TIFM_MMCSD_DPE 0x0020 /* data timeout counted in kilocycles */ +#define TIFM_MMCSD_INAB 0x0080 /* abort / initialize command */ +#define TIFM_MMCSD_READ 0x8000 + +#define TIFM_MMCSD_DATAMASK 0x001d /* set bits: EOFB, BRS, CB, EOC */ +#define TIFM_MMCSD_ERRMASK 0x41e0 /* set bits: CERR, CCRC, CTO, DCRC, DTO */ +#define TIFM_MMCSD_EOC 0x0001 /* end of command phase */ +#define TIFM_MMCSD_CB 0x0004 /* card enter busy state */ +#define TIFM_MMCSD_BRS 0x0008 /* block received/sent */ +#define TIFM_MMCSD_EOFB 0x0010 /* card exit busy state */ +#define TIFM_MMCSD_DTO 0x0020 /* data time-out */ +#define TIFM_MMCSD_DCRC 0x0040 /* data crc error */ +#define TIFM_MMCSD_CTO 0x0080 /* command time-out */ +#define TIFM_MMCSD_CCRC 0x0100 /* command crc error */ +#define TIFM_MMCSD_AF 0x0400 /* fifo almost full */ +#define TIFM_MMCSD_AE 0x0800 /* fifo almost empty */ +#define TIFM_MMCSD_CERR 0x4000 /* card status error */ + +#define TIFM_MMCSD_FIFO_SIZE 0x0020 + +#define TIFM_MMCSD_RSP_R0 0x0000 +#define TIFM_MMCSD_RSP_R1 0x0100 +#define TIFM_MMCSD_RSP_R2 0x0200 +#define TIFM_MMCSD_RSP_R3 0x0300 +#define TIFM_MMCSD_RSP_R4 0x0400 +#define TIFM_MMCSD_RSP_R5 0x0500 +#define TIFM_MMCSD_RSP_R6 0x0600 + +#define TIFM_MMCSD_RSP_BUSY 0x0800 + +#define TIFM_MMCSD_CMD_BC 0x0000 +#define TIFM_MMCSD_CMD_BCR 0x1000 +#define TIFM_MMCSD_CMD_AC 0x2000 +#define TIFM_MMCSD_CMD_ADTC 0x3000 + +typedef enum { + IDLE = 0, + CMD, /* main command ended */ + BRS, /* block transfer finished */ + SCMD, /* stop command ended */ + CARD, /* card left busy state */ + FIFO, /* FIFO operation completed (uncertain) */ + READY +} card_state_t; + +enum { + FIFO_RDY = 0x0001, /* hardware dependent value */ + HOST_REG = 0x0002, + EJECT = 0x0004, + EJECT_DONE = 0x0008, + CARD_BUSY = 0x0010, + OPENDRAIN = 0x0040, /* hardware dependent value */ + CARD_EVENT = 0x0100, /* hardware dependent value */ + CARD_RO = 0x0200, /* hardware dependent value */ + FIFO_EVENT = 0x10000 }; /* hardware dependent value */ + +struct tifm_sd { + struct tifm_dev *dev; + + unsigned int flags; + card_state_t state; + unsigned int clk_freq; + unsigned int clk_div; + unsigned long timeout_jiffies; // software timeout - 2 sec + + struct mmc_request *req; + struct work_struct cmd_handler; + struct delayed_work abort_handler; + wait_queue_head_t can_eject; + + size_t written_blocks; + char *buffer; + size_t buffer_size; + size_t buffer_pos; + +}; + +static int tifm_sd_transfer_data(struct tifm_dev *sock, struct tifm_sd *host, + unsigned int host_status) +{ + struct mmc_command *cmd = host->req->cmd; + unsigned int t_val = 0, cnt = 0; + + if (host_status & TIFM_MMCSD_BRS) { + /* in non-dma rx mode BRS fires when fifo is still not empty */ + if (host->buffer && (cmd->data->flags & MMC_DATA_READ)) { + while (host->buffer_size > host->buffer_pos) { + t_val = readl(sock->addr + SOCK_MMCSD_DATA); + host->buffer[host->buffer_pos++] = t_val & 0xff; + host->buffer[host->buffer_pos++] = + (t_val >> 8) & 0xff; + } + } + return 1; + } else if (host->buffer) { + if ((cmd->data->flags & MMC_DATA_READ) && + (host_status & TIFM_MMCSD_AF)) { + for (cnt = 0; cnt < TIFM_MMCSD_FIFO_SIZE; cnt++) { + t_val = readl(sock->addr + SOCK_MMCSD_DATA); + if (host->buffer_size > host->buffer_pos) { + host->buffer[host->buffer_pos++] = + t_val & 0xff; + host->buffer[host->buffer_pos++] = + (t_val >> 8) & 0xff; + } + } + } else if ((cmd->data->flags & MMC_DATA_WRITE) + && (host_status & TIFM_MMCSD_AE)) { + for (cnt = 0; cnt < TIFM_MMCSD_FIFO_SIZE; cnt++) { + if (host->buffer_size > host->buffer_pos) { + t_val = host->buffer[host->buffer_pos++] & 0x00ff; + t_val |= ((host->buffer[host->buffer_pos++]) << 8) + & 0xff00; + writel(t_val, + sock->addr + SOCK_MMCSD_DATA); + } + } + } + } + return 0; +} + +static unsigned int tifm_sd_op_flags(struct mmc_command *cmd) +{ + unsigned int rc = 0; + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + rc |= TIFM_MMCSD_RSP_R0; + break; + case MMC_RSP_R1B: + rc |= TIFM_MMCSD_RSP_BUSY; // deliberate fall-through + case MMC_RSP_R1: + rc |= TIFM_MMCSD_RSP_R1; + break; + case MMC_RSP_R2: + rc |= TIFM_MMCSD_RSP_R2; + break; + case MMC_RSP_R3: + rc |= TIFM_MMCSD_RSP_R3; + break; + case MMC_RSP_R6: + rc |= TIFM_MMCSD_RSP_R6; + break; + default: + BUG(); + } + + switch (mmc_cmd_type(cmd)) { + case MMC_CMD_BC: + rc |= TIFM_MMCSD_CMD_BC; + break; + case MMC_CMD_BCR: + rc |= TIFM_MMCSD_CMD_BCR; + break; + case MMC_CMD_AC: + rc |= TIFM_MMCSD_CMD_AC; + break; + case MMC_CMD_ADTC: + rc |= TIFM_MMCSD_CMD_ADTC; + break; + default: + BUG(); + } + return rc; +} + +static void tifm_sd_exec(struct tifm_sd *host, struct mmc_command *cmd) +{ + struct tifm_dev *sock = host->dev; + unsigned int cmd_mask = tifm_sd_op_flags(cmd) | + (host->flags & OPENDRAIN); + + if (cmd->data && (cmd->data->flags & MMC_DATA_READ)) + cmd_mask |= TIFM_MMCSD_READ; + + dev_dbg(&sock->dev, "executing opcode 0x%x, arg: 0x%x, mask: 0x%x\n", + cmd->opcode, cmd->arg, cmd_mask); + + writel((cmd->arg >> 16) & 0xffff, sock->addr + SOCK_MMCSD_ARG_HIGH); + writel(cmd->arg & 0xffff, sock->addr + SOCK_MMCSD_ARG_LOW); + writel(cmd->opcode | cmd_mask, sock->addr + SOCK_MMCSD_COMMAND); +} + +static void tifm_sd_fetch_resp(struct mmc_command *cmd, struct tifm_dev *sock) +{ + cmd->resp[0] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x1c) << 16) + | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x18); + cmd->resp[1] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x14) << 16) + | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x10); + cmd->resp[2] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x0c) << 16) + | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x08); + cmd->resp[3] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x04) << 16) + | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x00); +} + +static void tifm_sd_process_cmd(struct tifm_dev *sock, struct tifm_sd *host, + unsigned int host_status) +{ + struct mmc_command *cmd = host->req->cmd; + +change_state: + switch (host->state) { + case IDLE: + return; + case CMD: + if (host_status & TIFM_MMCSD_EOC) { + tifm_sd_fetch_resp(cmd, sock); + if (cmd->data) { + host->state = BRS; + } else + host->state = READY; + goto change_state; + } + break; + case BRS: + if (tifm_sd_transfer_data(sock, host, host_status)) { + if (!host->req->stop) { + if (cmd->data->flags & MMC_DATA_WRITE) { + host->state = CARD; + } else { + host->state = + host->buffer ? READY : FIFO; + } + goto change_state; + } + tifm_sd_exec(host, host->req->stop); + host->state = SCMD; + } + break; + case SCMD: + if (host_status & TIFM_MMCSD_EOC) { + tifm_sd_fetch_resp(host->req->stop, sock); + if (cmd->error) { + host->state = READY; + } else if (cmd->data->flags & MMC_DATA_WRITE) { + host->state = CARD; + } else { + host->state = host->buffer ? READY : FIFO; + } + goto change_state; + } + break; + case CARD: + if (!(host->flags & CARD_BUSY) + && (host->written_blocks == cmd->data->blocks)) { + host->state = host->buffer ? READY : FIFO; + goto change_state; + } + break; + case FIFO: + if (host->flags & FIFO_RDY) { + host->state = READY; + host->flags &= ~FIFO_RDY; + goto change_state; + } + break; + case READY: + queue_work(sock->wq, &host->cmd_handler); + return; + } + + queue_delayed_work(sock->wq, &host->abort_handler, + host->timeout_jiffies); +} + +/* Called from interrupt handler */ +static unsigned int tifm_sd_signal_irq(struct tifm_dev *sock, + unsigned int sock_irq_status) +{ + struct tifm_sd *host; + unsigned int host_status = 0, fifo_status = 0; + int error_code = 0; + + spin_lock(&sock->lock); + host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); + cancel_delayed_work(&host->abort_handler); + + if (sock_irq_status & FIFO_EVENT) { + fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS); + writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS); + + host->flags |= fifo_status & FIFO_RDY; + } + + if (sock_irq_status & CARD_EVENT) { + host_status = readl(sock->addr + SOCK_MMCSD_STATUS); + writel(host_status, sock->addr + SOCK_MMCSD_STATUS); + + if (!(host->flags & HOST_REG)) + queue_work(sock->wq, &host->cmd_handler); + if (!host->req) + goto done; + + if (host_status & TIFM_MMCSD_ERRMASK) { + if (host_status & TIFM_MMCSD_CERR) + error_code = MMC_ERR_FAILED; + else if (host_status & + (TIFM_MMCSD_CTO | TIFM_MMCSD_DTO)) + error_code = MMC_ERR_TIMEOUT; + else if (host_status & + (TIFM_MMCSD_CCRC | TIFM_MMCSD_DCRC)) + error_code = MMC_ERR_BADCRC; + + writel(TIFM_FIFO_INT_SETALL, + sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); + writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL); + + if (host->req->stop) { + if (host->state == SCMD) { + host->req->stop->error = error_code; + } else if(host->state == BRS) { + host->req->cmd->error = error_code; + tifm_sd_exec(host, host->req->stop); + queue_delayed_work(sock->wq, + &host->abort_handler, + host->timeout_jiffies); + host->state = SCMD; + goto done; + } else { + host->req->cmd->error = error_code; + } + } else { + host->req->cmd->error = error_code; + } + host->state = READY; + } + + if (host_status & TIFM_MMCSD_CB) + host->flags |= CARD_BUSY; + if ((host_status & TIFM_MMCSD_EOFB) && + (host->flags & CARD_BUSY)) { + host->written_blocks++; + host->flags &= ~CARD_BUSY; + } + } + + if (host->req) + tifm_sd_process_cmd(sock, host, host_status); +done: + dev_dbg(&sock->dev, "host_status %x, fifo_status %x\n", + host_status, fifo_status); + spin_unlock(&sock->lock); + return sock_irq_status; +} + +static void tifm_sd_prepare_data(struct tifm_sd *card, struct mmc_command *cmd) +{ + struct tifm_dev *sock = card->dev; + unsigned int dest_cnt; + + /* DMA style IO */ + + writel(TIFM_FIFO_INT_SETALL, + sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); + writel(ilog2(cmd->data->blksz) - 2, + sock->addr + SOCK_FIFO_PAGE_SIZE); + writel(TIFM_FIFO_ENABLE, sock->addr + SOCK_FIFO_CONTROL); + writel(TIFM_FIFO_INTMASK, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); + + dest_cnt = (cmd->data->blocks) << 8; + + writel(sg_dma_address(cmd->data->sg), sock->addr + SOCK_DMA_ADDRESS); + + writel(cmd->data->blocks - 1, sock->addr + SOCK_MMCSD_NUM_BLOCKS); + writel(cmd->data->blksz - 1, sock->addr + SOCK_MMCSD_BLOCK_LEN); + + if (cmd->data->flags & MMC_DATA_WRITE) { + writel(TIFM_MMCSD_TXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); + writel(dest_cnt | TIFM_DMA_TX | TIFM_DMA_EN, + sock->addr + SOCK_DMA_CONTROL); + } else { + writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); + writel(dest_cnt | TIFM_DMA_EN, sock->addr + SOCK_DMA_CONTROL); + } +} + +static void tifm_sd_set_data_timeout(struct tifm_sd *host, + struct mmc_data *data) +{ + struct tifm_dev *sock = host->dev; + unsigned int data_timeout = data->timeout_clks; + + if (fixed_timeout) + return; + + data_timeout += data->timeout_ns / + ((1000000000 / host->clk_freq) * host->clk_div); + data_timeout *= 10; // call it fudge factor for now + + if (data_timeout < 0xffff) { + writel((~TIFM_MMCSD_DPE) & + readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), + sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); + writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); + } else { + writel(TIFM_MMCSD_DPE | + readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), + sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); + data_timeout = (data_timeout >> 10) + 1; + if(data_timeout > 0xffff) + data_timeout = 0; /* set to unlimited */ + writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); + } +} + +static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct tifm_sd *host = mmc_priv(mmc); + struct tifm_dev *sock = host->dev; + unsigned long flags; + int sg_count = 0; + struct mmc_data *r_data = mrq->cmd->data; + + spin_lock_irqsave(&sock->lock, flags); + if (host->flags & EJECT) { + spin_unlock_irqrestore(&sock->lock, flags); + goto err_out; + } + + if (host->req) { + printk(KERN_ERR DRIVER_NAME ": unfinished request detected\n"); + spin_unlock_irqrestore(&sock->lock, flags); + goto err_out; + } + + if (r_data) { + tifm_sd_set_data_timeout(host, r_data); + + sg_count = tifm_map_sg(sock, r_data->sg, r_data->sg_len, + mrq->cmd->flags & MMC_DATA_WRITE + ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + if (sg_count != 1) { + printk(KERN_ERR DRIVER_NAME + ": scatterlist map failed\n"); + spin_unlock_irqrestore(&sock->lock, flags); + goto err_out; + } + + host->written_blocks = 0; + host->flags &= ~CARD_BUSY; + tifm_sd_prepare_data(host, mrq->cmd); + } + + host->req = mrq; + host->state = CMD; + queue_delayed_work(sock->wq, &host->abort_handler, + host->timeout_jiffies); + writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + tifm_sd_exec(host, mrq->cmd); + spin_unlock_irqrestore(&sock->lock, flags); + return; + +err_out: + if (sg_count > 0) + tifm_unmap_sg(sock, r_data->sg, r_data->sg_len, + (r_data->flags & MMC_DATA_WRITE) + ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + + mrq->cmd->error = MMC_ERR_TIMEOUT; + mmc_request_done(mmc, mrq); +} + +static void tifm_sd_end_cmd(struct work_struct *work) +{ + struct tifm_sd *host = container_of(work, struct tifm_sd, cmd_handler); + struct tifm_dev *sock = host->dev; + struct mmc_host *mmc = tifm_get_drvdata(sock); + struct mmc_request *mrq; + struct mmc_data *r_data = NULL; + unsigned long flags; + + spin_lock_irqsave(&sock->lock, flags); + + mrq = host->req; + host->req = NULL; + host->state = IDLE; + + if (!mrq) { + printk(KERN_ERR DRIVER_NAME ": no request to complete?\n"); + spin_unlock_irqrestore(&sock->lock, flags); + return; + } + + r_data = mrq->cmd->data; + if (r_data) { + if (r_data->flags & MMC_DATA_WRITE) { + r_data->bytes_xfered = host->written_blocks * + r_data->blksz; + } else { + r_data->bytes_xfered = r_data->blocks - + readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; + r_data->bytes_xfered *= r_data->blksz; + r_data->bytes_xfered += r_data->blksz - + readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1; + } + tifm_unmap_sg(sock, r_data->sg, r_data->sg_len, + (r_data->flags & MMC_DATA_WRITE) + ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + } + + writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + + spin_unlock_irqrestore(&sock->lock, flags); + mmc_request_done(mmc, mrq); +} + +static void tifm_sd_request_nodma(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct tifm_sd *host = mmc_priv(mmc); + struct tifm_dev *sock = host->dev; + unsigned long flags; + struct mmc_data *r_data = mrq->cmd->data; + char *t_buffer = NULL; + + if (r_data) { + t_buffer = kmap(r_data->sg->page); + if (!t_buffer) { + printk(KERN_ERR DRIVER_NAME ": kmap failed\n"); + goto err_out; + } + } + + spin_lock_irqsave(&sock->lock, flags); + if (host->flags & EJECT) { + spin_unlock_irqrestore(&sock->lock, flags); + goto err_out; + } + + if (host->req) { + printk(KERN_ERR DRIVER_NAME ": unfinished request detected\n"); + spin_unlock_irqrestore(&sock->lock, flags); + goto err_out; + } + + if (r_data) { + tifm_sd_set_data_timeout(host, r_data); + + host->buffer = t_buffer + r_data->sg->offset; + host->buffer_size = mrq->cmd->data->blocks * + mrq->cmd->data->blksz; + + writel(TIFM_MMCSD_BUFINT | + readl(sock->addr + SOCK_MMCSD_INT_ENABLE), + sock->addr + SOCK_MMCSD_INT_ENABLE); + writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8) | + (TIFM_MMCSD_FIFO_SIZE - 1), + sock->addr + SOCK_MMCSD_BUFFER_CONFIG); + + host->written_blocks = 0; + host->flags &= ~CARD_BUSY; + host->buffer_pos = 0; + writel(r_data->blocks - 1, sock->addr + SOCK_MMCSD_NUM_BLOCKS); + writel(r_data->blksz - 1, sock->addr + SOCK_MMCSD_BLOCK_LEN); + } + + host->req = mrq; + host->state = CMD; + queue_delayed_work(sock->wq, &host->abort_handler, + host->timeout_jiffies); + writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + tifm_sd_exec(host, mrq->cmd); + spin_unlock_irqrestore(&sock->lock, flags); + return; + +err_out: + if (t_buffer) + kunmap(r_data->sg->page); + + mrq->cmd->error = MMC_ERR_TIMEOUT; + mmc_request_done(mmc, mrq); +} + +static void tifm_sd_end_cmd_nodma(struct work_struct *work) +{ + struct tifm_sd *host = container_of(work, struct tifm_sd, cmd_handler); + struct tifm_dev *sock = host->dev; + struct mmc_host *mmc = tifm_get_drvdata(sock); + struct mmc_request *mrq; + struct mmc_data *r_data = NULL; + unsigned long flags; + + spin_lock_irqsave(&sock->lock, flags); + + mrq = host->req; + host->req = NULL; + host->state = IDLE; + + if (!mrq) { + printk(KERN_ERR DRIVER_NAME ": no request to complete?\n"); + spin_unlock_irqrestore(&sock->lock, flags); + return; + } + + r_data = mrq->cmd->data; + if (r_data) { + writel((~TIFM_MMCSD_BUFINT) & + readl(sock->addr + SOCK_MMCSD_INT_ENABLE), + sock->addr + SOCK_MMCSD_INT_ENABLE); + + if (r_data->flags & MMC_DATA_WRITE) { + r_data->bytes_xfered = host->written_blocks * + r_data->blksz; + } else { + r_data->bytes_xfered = r_data->blocks - + readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; + r_data->bytes_xfered *= r_data->blksz; + r_data->bytes_xfered += r_data->blksz - + readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1; + } + host->buffer = NULL; + host->buffer_pos = 0; + host->buffer_size = 0; + } + + writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + + spin_unlock_irqrestore(&sock->lock, flags); + + if (r_data) + kunmap(r_data->sg->page); + + mmc_request_done(mmc, mrq); +} + +static void tifm_sd_abort(struct work_struct *work) +{ + struct tifm_sd *host = + container_of(work, struct tifm_sd, abort_handler.work); + + printk(KERN_ERR DRIVER_NAME + ": card failed to respond for a long period of time"); + tifm_eject(host->dev); +} + +static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct tifm_sd *host = mmc_priv(mmc); + struct tifm_dev *sock = host->dev; + unsigned int clk_div1, clk_div2; + unsigned long flags; + + spin_lock_irqsave(&sock->lock, flags); + + dev_dbg(&sock->dev, "Setting bus width %d, power %d\n", ios->bus_width, + ios->power_mode); + if (ios->bus_width == MMC_BUS_WIDTH_4) { + writel(TIFM_MMCSD_4BBUS | readl(sock->addr + SOCK_MMCSD_CONFIG), + sock->addr + SOCK_MMCSD_CONFIG); + } else { + writel((~TIFM_MMCSD_4BBUS) & + readl(sock->addr + SOCK_MMCSD_CONFIG), + sock->addr + SOCK_MMCSD_CONFIG); + } + + if (ios->clock) { + clk_div1 = 20000000 / ios->clock; + if (!clk_div1) + clk_div1 = 1; + + clk_div2 = 24000000 / ios->clock; + if (!clk_div2) + clk_div2 = 1; + + if ((20000000 / clk_div1) > ios->clock) + clk_div1++; + if ((24000000 / clk_div2) > ios->clock) + clk_div2++; + if ((20000000 / clk_div1) > (24000000 / clk_div2)) { + host->clk_freq = 20000000; + host->clk_div = clk_div1; + writel((~TIFM_CTRL_FAST_CLK) & + readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + } else { + host->clk_freq = 24000000; + host->clk_div = clk_div2; + writel(TIFM_CTRL_FAST_CLK | + readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + } + } else { + host->clk_div = 0; + } + host->clk_div &= TIFM_MMCSD_CLKMASK; + writel(host->clk_div | ((~TIFM_MMCSD_CLKMASK) & + readl(sock->addr + SOCK_MMCSD_CONFIG)), + sock->addr + SOCK_MMCSD_CONFIG); + + if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) + host->flags |= OPENDRAIN; + else + host->flags &= ~OPENDRAIN; + + /* chip_select : maybe later */ + //vdd + //power is set before probe / after remove + //I believe, power_off when already marked for eject is sufficient to + // allow removal. + if ((host->flags & EJECT) && ios->power_mode == MMC_POWER_OFF) { + host->flags |= EJECT_DONE; + wake_up_all(&host->can_eject); + } + + spin_unlock_irqrestore(&sock->lock, flags); +} + +static int tifm_sd_ro(struct mmc_host *mmc) +{ + int rc; + struct tifm_sd *host = mmc_priv(mmc); + struct tifm_dev *sock = host->dev; + unsigned long flags; + + spin_lock_irqsave(&sock->lock, flags); + + host->flags |= (CARD_RO & readl(sock->addr + SOCK_PRESENT_STATE)); + rc = (host->flags & CARD_RO) ? 1 : 0; + + spin_unlock_irqrestore(&sock->lock, flags); + return rc; +} + +static struct mmc_host_ops tifm_sd_ops = { + .request = tifm_sd_request, + .set_ios = tifm_sd_ios, + .get_ro = tifm_sd_ro +}; + +static void tifm_sd_register_host(struct work_struct *work) +{ + struct tifm_sd *host = container_of(work, struct tifm_sd, cmd_handler); + struct tifm_dev *sock = host->dev; + struct mmc_host *mmc = tifm_get_drvdata(sock); + unsigned long flags; + + spin_lock_irqsave(&sock->lock, flags); + host->flags |= HOST_REG; + PREPARE_WORK(&host->cmd_handler, + no_dma ? tifm_sd_end_cmd_nodma : tifm_sd_end_cmd); + spin_unlock_irqrestore(&sock->lock, flags); + dev_dbg(&sock->dev, "adding host\n"); + mmc_add_host(mmc); +} + +static int tifm_sd_probe(struct tifm_dev *sock) +{ + struct mmc_host *mmc; + struct tifm_sd *host; + int rc = -EIO; + + if (!(TIFM_SOCK_STATE_OCCUPIED & + readl(sock->addr + SOCK_PRESENT_STATE))) { + printk(KERN_WARNING DRIVER_NAME ": card gone, unexpectedly\n"); + return rc; + } + + mmc = mmc_alloc_host(sizeof(struct tifm_sd), &sock->dev); + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); + host->dev = sock; + host->clk_div = 61; + init_waitqueue_head(&host->can_eject); + INIT_WORK(&host->cmd_handler, tifm_sd_register_host); + INIT_DELAYED_WORK(&host->abort_handler, tifm_sd_abort); + + tifm_set_drvdata(sock, mmc); + sock->signal_irq = tifm_sd_signal_irq; + + host->clk_freq = 20000000; + host->timeout_jiffies = msecs_to_jiffies(1000); + + tifm_sd_ops.request = no_dma ? tifm_sd_request_nodma : tifm_sd_request; + mmc->ops = &tifm_sd_ops; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->caps = MMC_CAP_4_BIT_DATA; + mmc->f_min = 20000000 / 60; + mmc->f_max = 24000000; + mmc->max_hw_segs = 1; + mmc->max_phys_segs = 1; + mmc->max_sectors = 127; + mmc->max_seg_size = mmc->max_sectors << 11; //2k maximum hw block length + + writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); + writel(TIFM_MMCSD_RESET, sock->addr + SOCK_MMCSD_SYSTEM_CONTROL); + writel(host->clk_div | TIFM_MMCSD_POWER, + sock->addr + SOCK_MMCSD_CONFIG); + + for (rc = 0; rc < 50; rc++) { + /* Wait for reset ack */ + if (1 & readl(sock->addr + SOCK_MMCSD_SYSTEM_STATUS)) { + rc = 0; + break; + } + msleep(10); + } + + if (rc) { + printk(KERN_ERR DRIVER_NAME + ": card not ready - probe failed\n"); + mmc_free_host(mmc); + return -ENODEV; + } + + writel(0, sock->addr + SOCK_MMCSD_NUM_BLOCKS); + writel(host->clk_div | TIFM_MMCSD_POWER, + sock->addr + SOCK_MMCSD_CONFIG); + writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); + writel(TIFM_MMCSD_DATAMASK | TIFM_MMCSD_ERRMASK, + sock->addr + SOCK_MMCSD_INT_ENABLE); + + writel(64, sock->addr + SOCK_MMCSD_COMMAND_TO); // command timeout 64 clocks for now + writel(TIFM_MMCSD_INAB, sock->addr + SOCK_MMCSD_COMMAND); + writel(host->clk_div | TIFM_MMCSD_POWER, + sock->addr + SOCK_MMCSD_CONFIG); + + queue_delayed_work(sock->wq, &host->abort_handler, + host->timeout_jiffies); + + return 0; +} + +static int tifm_sd_host_is_down(struct tifm_dev *sock) +{ + struct mmc_host *mmc = tifm_get_drvdata(sock); + struct tifm_sd *host = mmc_priv(mmc); + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&sock->lock, flags); + rc = (host->flags & EJECT_DONE); + spin_unlock_irqrestore(&sock->lock, flags); + return rc; +} + +static void tifm_sd_remove(struct tifm_dev *sock) +{ + struct mmc_host *mmc = tifm_get_drvdata(sock); + struct tifm_sd *host = mmc_priv(mmc); + unsigned long flags; + + spin_lock_irqsave(&sock->lock, flags); + host->flags |= EJECT; + if (host->req) + queue_work(sock->wq, &host->cmd_handler); + spin_unlock_irqrestore(&sock->lock, flags); + wait_event_timeout(host->can_eject, tifm_sd_host_is_down(sock), + host->timeout_jiffies); + + if (host->flags & HOST_REG) + mmc_remove_host(mmc); + + /* The meaning of the bit majority in this constant is unknown. */ + writel(0xfff8 & readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); + writel(TIFM_FIFO_INT_SETALL, + sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); + writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); + + tifm_set_drvdata(sock, NULL); + mmc_free_host(mmc); +} + +static tifm_media_id tifm_sd_id_tbl[] = { + FM_SD, 0 +}; + +static struct tifm_driver tifm_sd_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE + }, + .id_table = tifm_sd_id_tbl, + .probe = tifm_sd_probe, + .remove = tifm_sd_remove +}; + +static int __init tifm_sd_init(void) +{ + return tifm_register_driver(&tifm_sd_driver); +} + +static void __exit tifm_sd_exit(void) +{ + tifm_unregister_driver(&tifm_sd_driver); +} + +MODULE_AUTHOR("Alex Dubov"); +MODULE_DESCRIPTION("TI FlashMedia SD driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(tifm, tifm_sd_id_tbl); +MODULE_VERSION(DRIVER_VERSION); + +module_init(tifm_sd_init); +module_exit(tifm_sd_exit); diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c index c351c6d1a18a..7a282672f8e9 100644 --- a/drivers/mmc/wbsd.c +++ b/drivers/mmc/wbsd.c @@ -4,8 +4,9 @@ * Copyright (C) 2004-2005 Pierre Ossman, All Rights Reserved. * * 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. + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. * * * Warning! @@ -1020,7 +1021,7 @@ static int wbsd_get_ro(struct mmc_host *mmc) return csr & WBSD_WRPT; } -static struct mmc_host_ops wbsd_ops = { +static const struct mmc_host_ops wbsd_ops = { .request = wbsd_request, .set_ios = wbsd_set_ios, .get_ro = wbsd_get_ro, @@ -1255,7 +1256,7 @@ end: * Interrupt handling */ -static irqreturn_t wbsd_irq(int irq, void *dev_id, struct pt_regs *regs) +static irqreturn_t wbsd_irq(int irq, void *dev_id) { struct wbsd_host *host = dev_id; int isr; @@ -1323,7 +1324,7 @@ static int __devinit wbsd_alloc_mmc(struct device *dev) mmc->f_min = 375000; mmc->f_max = 24000000; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = MMC_CAP_4_BIT_DATA; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; spin_lock_init(&host->lock); @@ -1487,7 +1488,7 @@ static void __devinit wbsd_request_dma(struct wbsd_host *host, int dma) /* * Translate the address to a physical address. */ - host->dma_addr = dma_map_single(host->mmc->dev, host->dma_buffer, + host->dma_addr = dma_map_single(mmc_dev(host->mmc), host->dma_buffer, WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); /* @@ -1511,7 +1512,7 @@ kfree: */ BUG_ON(1); - dma_unmap_single(host->mmc->dev, host->dma_addr, + dma_unmap_single(mmc_dev(host->mmc), host->dma_addr, WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); host->dma_addr = (dma_addr_t)NULL; @@ -1529,7 +1530,7 @@ err: static void __devexit wbsd_release_dma(struct wbsd_host *host) { if (host->dma_addr) { - dma_unmap_single(host->mmc->dev, host->dma_addr, + dma_unmap_single(mmc_dev(host->mmc), host->dma_addr, WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); } kfree(host->dma_buffer); diff --git a/drivers/mmc/wbsd.h b/drivers/mmc/wbsd.h index 249baa701cb0..6072993f01e3 100644 --- a/drivers/mmc/wbsd.h +++ b/drivers/mmc/wbsd.h @@ -4,8 +4,9 @@ * Copyright (C) 2004-2005 Pierre Ossman, All Rights Reserved. * * 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. + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. */ #define LOCK_CODE 0xAA |