diff options
Diffstat (limited to 'drivers/mmc/host')
46 files changed, 3387 insertions, 1793 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 9bf10e7bbfa..737e4edc241 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -81,6 +81,18 @@ config MMC_RICOH_MMC If unsure, say Y. +config MMC_SDHCI_ACPI + tristate "SDHCI support for ACPI enumerated SDHCI controllers" + depends on MMC_SDHCI && ACPI + help + This selects support for ACPI enumerated SDHCI controllers, + identified by ACPI Compatibility ID PNP0D40 or specific + ACPI Hardware IDs. + + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + config MMC_SDHCI_PLTFM tristate "SDHCI platform and OF driver helper" depends on MMC_SDHCI @@ -270,26 +282,8 @@ config MMC_AU1X If unsure, say N. -choice - prompt "Atmel SD/MMC Driver" - depends on AVR32 || ARCH_AT91 - default MMC_ATMELMCI if AVR32 - help - Choose which driver to use for the Atmel MCI Silicon - -config MMC_AT91 - tristate "AT91 SD/MMC Card Interface support (DEPRECATED)" - depends on ARCH_AT91 - help - This selects the AT91 MCI controller. This driver will - be removed soon (for more information have a look to - Documentation/feature-removal-schedule.txt). Please use - MMC_ATMEL_MCI. - - If unsure, say N. - config MMC_ATMELMCI - tristate "Atmel Multimedia Card Interface support" + tristate "Atmel SD/MMC Driver (Multimedia Card Interface)" depends on AVR32 || ARCH_AT91 help This selects the Atmel Multimedia Card Interface driver. If @@ -298,8 +292,6 @@ config MMC_ATMELMCI If unsure, say N. -endchoice - config MMC_ATMELMCI_DMA bool "Atmel MCI DMA support" depends on MMC_ATMELMCI && (AVR32 || ARCH_AT91SAM9G45) && DMA_ENGINE @@ -621,3 +613,21 @@ config MMC_USHC Note: These controllers only support SDIO cards and do not support MMC or SD memory cards. + +config MMC_WMT + tristate "Wondermedia SD/MMC Host Controller support" + depends on ARCH_VT8500 + default y + help + This selects support for the SD/MMC Host Controller on + Wondermedia WM8505/WM8650 based SoCs. + + To compile this driver as a module, choose M here: the + module will be called wmt-sdmmc. + +config MMC_REALTEK_PCI + tristate "Realtek PCI-E SD/MMC Card Interface Driver" + depends on MFD_RTSX_PCI + help + Say Y here to include driver code to support SD/MMC card interface + of Realtek PCI-E card reader diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 17ad0a7ba40..b648058d718 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_MMC_MXS) += mxs-mmc.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o obj-$(subst m,y,$(CONFIG_MMC_SDHCI_PCI)) += sdhci-pci-data.o +obj-$(CONFIG_MMC_SDHCI_ACPI) += sdhci-acpi.o obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-pxav3.o obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o @@ -17,7 +18,6 @@ obj-$(CONFIG_MMC_WBSD) += wbsd.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o obj-$(CONFIG_MMC_OMAP) += omap.o obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o -obj-$(CONFIG_MMC_AT91) += at91_mci.o obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o obj-$(CONFIG_MMC_MSM) += msm_sdcc.o @@ -45,6 +45,9 @@ obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o obj-$(CONFIG_MMC_VUB300) += vub300.o obj-$(CONFIG_MMC_USHC) += ushc.o +obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o + +obj-$(CONFIG_MMC_REALTEK_PCI) += rtsx_pci_sdmmc.o obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o obj-$(CONFIG_MMC_SDHCI_CNS3XXX) += sdhci-cns3xxx.o diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c deleted file mode 100644 index 74bed0fc23e..00000000000 --- a/drivers/mmc/host/at91_mci.c +++ /dev/null @@ -1,1219 +0,0 @@ -/* - * linux/drivers/mmc/host/at91_mci.c - ATMEL AT91 MCI Driver - * - * Copyright (C) 2005 Cougar Creek Computing Devices Ltd, All Rights Reserved - * - * Copyright (C) 2006 Malcolm Noyes - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* - This 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. - - The three entry points are at91_mci_request, at91_mci_set_ios - and at91_mci_get_ro. - - SET IOS - This configures the device to put it into the correct mode and clock speed - required. - - MCI REQUEST - MCI request processes the commands sent in the mmc_request structure. This - can consist of a processing command and a stop command in the case of - multiple block transfers. - - There are three main types of request, commands, reads and writes. - - Commands are straight forward. The command is submitted to the controller and - the request function returns. When the controller generates an interrupt to indicate - the command is finished, the response to the command are read and the mmc_request_done - function called to end the request. - - Reads and writes work in a similar manner to normal commands but involve the PDC (DMA) - 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 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 - - A write is slightly different in that the bytes to write are read from the scatterlist - into a dma memory buffer (this is in case the source buffer should be read only). The - entire write buffer is then done from this single dma memory buffer. - - The sequence of write interrupts is: ENDTX, TXBUFE, NOTBUSY, CMDRDY - - GET RO - Gets the status of the write protect pin, if available. -*/ - -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/init.h> -#include <linux/ioport.h> -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/blkdev.h> -#include <linux/delay.h> -#include <linux/err.h> -#include <linux/dma-mapping.h> -#include <linux/clk.h> -#include <linux/atmel_pdc.h> -#include <linux/gfp.h> -#include <linux/highmem.h> - -#include <linux/mmc/host.h> -#include <linux/mmc/sdio.h> - -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/gpio.h> - -#include <mach/board.h> -#include <mach/cpu.h> - -#include "at91_mci.h" - -#define DRIVER_NAME "at91_mci" - -static inline int at91mci_is_mci1rev2xx(void) -{ - return ( cpu_is_at91sam9260() - || cpu_is_at91sam9263() - || cpu_is_at91sam9rl() - || cpu_is_at91sam9g10() - || cpu_is_at91sam9g20() - ); -} - -#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)) - -#define MCI_BLKSIZE 512 -#define MCI_MAXBLKSIZE 4095 -#define MCI_BLKATONCE 256 -#define MCI_BUFSIZE (MCI_BLKSIZE * MCI_BLKATONCE) - -/* - * Low level type for this driver - */ -struct at91mci_host -{ - struct mmc_host *mmc; - 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 - */ - unsigned int flags; - /* flag for current bus settings */ - u32 bus_mode; - - /* DMA buffer used for transmitting */ - unsigned int* buffer; - dma_addr_t physical_address; - unsigned int total_length; - - /* Latest in the scatterlist that has been enabled for transfer, but not freed */ - int in_use_index; - - /* Latest in the scatterlist that has been enabled for transfer */ - int transfer_index; - - /* Timer for timeouts */ - struct timer_list timer; -}; - -/* - * Reset the controller and restore most of the state - */ -static void at91_reset_host(struct at91mci_host *host) -{ - unsigned long flags; - u32 mr; - u32 sdcr; - u32 dtor; - u32 imr; - - local_irq_save(flags); - imr = at91_mci_read(host, AT91_MCI_IMR); - - at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); - - /* save current state */ - mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; - sdcr = at91_mci_read(host, AT91_MCI_SDCR); - dtor = at91_mci_read(host, AT91_MCI_DTOR); - - /* reset the controller */ - at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST); - - /* restore state */ - at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); - at91_mci_write(host, AT91_MCI_MR, mr); - at91_mci_write(host, AT91_MCI_SDCR, sdcr); - at91_mci_write(host, AT91_MCI_DTOR, dtor); - at91_mci_write(host, AT91_MCI_IER, imr); - - /* make sure sdio interrupts will fire */ - at91_mci_read(host, AT91_MCI_SR); - - local_irq_restore(flags); -} - -static void at91_timeout_timer(unsigned long data) -{ - struct at91mci_host *host; - - host = (struct at91mci_host *)data; - - if (host->request) { - dev_err(host->mmc->parent, "Timeout waiting end of packet\n"); - - if (host->cmd && host->cmd->data) { - host->cmd->data->error = -ETIMEDOUT; - } else { - if (host->cmd) - host->cmd->error = -ETIMEDOUT; - else - host->request->cmd->error = -ETIMEDOUT; - } - - at91_reset_host(host); - mmc_request_done(host->mmc, host->request); - } -} - -/* - * Copy from sg to a dma block - used for transfers - */ -static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data) -{ - unsigned int len, i, size; - unsigned *dmabuf = host->buffer; - - size = data->blksz * data->blocks; - len = data->sg_len; - - /* MCI1 rev2xx Data Write Operation and number of bytes erratum */ - if (at91mci_is_mci1rev2xx()) - if (host->total_length == 12) - memset(dmabuf, 0, 12); - - /* - * Just loop through all entries. Size might not - * be the entire list though so make sure that - * we do not transfer too much. - */ - for (i = 0; i < len; i++) { - struct scatterlist *sg; - int amount; - unsigned int *sgbuffer; - - sg = &data->sg[i]; - - sgbuffer = kmap_atomic(sg_page(sg)) + sg->offset; - amount = min(size, sg->length); - size -= amount; - - if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ - int index; - - for (index = 0; index < (amount / 4); index++) - *dmabuf++ = swab32(sgbuffer[index]); - } else { - char *tmpv = (char *)dmabuf; - memcpy(tmpv, sgbuffer, amount); - tmpv += amount; - dmabuf = (unsigned *)tmpv; - } - - kunmap_atomic(sgbuffer); - - if (size == 0) - break; - } - - /* - * Check that we didn't get a request to transfer - * more data than can fit into the SG list. - */ - BUG_ON(size != 0); -} - -/* - * Handle after a dma read - */ -static void at91_mci_post_dma_read(struct at91mci_host *host) -{ - struct mmc_command *cmd; - struct mmc_data *data; - unsigned int len, i, size; - unsigned *dmabuf = host->buffer; - - pr_debug("post dma read\n"); - - cmd = host->cmd; - if (!cmd) { - pr_debug("no command\n"); - return; - } - - data = cmd->data; - if (!data) { - pr_debug("no data\n"); - return; - } - - size = data->blksz * data->blocks; - len = data->sg_len; - - at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX); - at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); - - for (i = 0; i < len; i++) { - struct scatterlist *sg; - int amount; - unsigned int *sgbuffer; - - sg = &data->sg[i]; - - sgbuffer = kmap_atomic(sg_page(sg)) + sg->offset; - amount = min(size, sg->length); - size -= amount; - - if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ - int index; - for (index = 0; index < (amount / 4); index++) - sgbuffer[index] = swab32(*dmabuf++); - } else { - char *tmpv = (char *)dmabuf; - memcpy(sgbuffer, tmpv, amount); - tmpv += amount; - dmabuf = (unsigned *)tmpv; - } - - flush_kernel_dcache_page(sg_page(sg)); - kunmap_atomic(sgbuffer); - data->bytes_xfered += amount; - if (size == 0) - break; - } - - pr_debug("post dma read done\n"); -} - -/* - * Handle transmitted data - */ -static void at91_mci_handle_transmitted(struct at91mci_host *host) -{ - struct mmc_command *cmd; - struct mmc_data *data; - - pr_debug("Handling the transmit\n"); - - /* Disable the transfer */ - at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); - - /* Now wait for cmd ready */ - at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE); - - cmd = host->cmd; - if (!cmd) return; - - data = cmd->data; - if (!data) return; - - if (cmd->data->blocks > 1) { - pr_debug("multiple write : wait for BLKE...\n"); - at91_mci_write(host, AT91_MCI_IER, AT91_MCI_BLKE); - } else - at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); -} - -/* - * Update bytes transfered count during a write operation - */ -static void at91_mci_update_bytes_xfered(struct at91mci_host *host) -{ - struct mmc_data *data; - - /* always deal with the effective request (and not the current cmd) */ - - if (host->request->cmd && host->request->cmd->error != 0) - return; - - if (host->request->data) { - data = host->request->data; - if (data->flags & MMC_DATA_WRITE) { - /* card is in IDLE mode now */ - pr_debug("-> bytes_xfered %d, total_length = %d\n", - data->bytes_xfered, host->total_length); - data->bytes_xfered = data->blksz * data->blocks; - } - } -} - - -/*Handle after command sent ready*/ -static int at91_mci_handle_cmdrdy(struct at91mci_host *host) -{ - if (!host->cmd) - return 1; - else if (!host->cmd->data) { - if (host->flags & FL_SENT_STOP) { - /*After multi block write, we must wait for NOTBUSY*/ - at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); - } else return 1; - } else if (host->cmd->data->flags & MMC_DATA_WRITE) { - /*After sendding multi-block-write command, start DMA transfer*/ - at91_mci_write(host, AT91_MCI_IER, AT91_MCI_TXBUFE | AT91_MCI_BLKE); - at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN); - } - - /* command not completed, have to wait */ - return 0; -} - - -/* - * Enable the controller - */ -static void at91_mci_enable(struct at91mci_host *host) -{ - unsigned int mr; - - 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); - mr = AT91_MCI_PDCMODE | 0x34a; - - if (at91mci_is_mci1rev2xx()) - mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF; - - at91_mci_write(host, AT91_MCI_MR, mr); - - /* 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(struct at91mci_host *host) -{ - at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST); -} - -/* - * Send a command - */ -static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd) -{ - unsigned int cmdr, mr; - unsigned int block_length; - struct mmc_data *data = cmd->data; - - unsigned int blocks; - unsigned int ier = 0; - - host->cmd = cmd; - - /* Needed for leaving busy state before CMD1 */ - if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) { - pr_debug("Clearing timeout\n"); - 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(host, AT91_MCI_SR)); - } - } - - cmdr = cmd->opcode; - - if (mmc_resp_type(cmd) == MMC_RSP_NONE) - cmdr |= AT91_MCI_RSPTYP_NONE; - else { - /* if a response is expected then allow maximum response latancy */ - cmdr |= AT91_MCI_MAXLAT; - /* set 136 bit response for R2, 48 bit response otherwise */ - if (mmc_resp_type(cmd) == MMC_RSP_R2) - cmdr |= AT91_MCI_RSPTYP_136; - else - cmdr |= AT91_MCI_RSPTYP_48; - } - - if (data) { - - if (cpu_is_at91rm9200() || cpu_is_at91sam9261()) { - if (data->blksz & 0x3) { - pr_debug("Unsupported block size\n"); - cmd->error = -EINVAL; - mmc_request_done(host->mmc, host->request); - return; - } - if (data->flags & MMC_DATA_STREAM) { - pr_debug("Stream commands not supported\n"); - cmd->error = -EINVAL; - mmc_request_done(host->mmc, host->request); - return; - } - } - - block_length = data->blksz; - blocks = data->blocks; - - /* always set data start - also set direction flag for read */ - if (data->flags & MMC_DATA_READ) - cmdr |= (AT91_MCI_TRDIR | AT91_MCI_TRCMD_START); - else if (data->flags & MMC_DATA_WRITE) - cmdr |= AT91_MCI_TRCMD_START; - - if (cmd->opcode == SD_IO_RW_EXTENDED) { - cmdr |= AT91_MCI_TRTYP_SDIO_BLOCK; - } else { - if (data->flags & MMC_DATA_STREAM) - cmdr |= AT91_MCI_TRTYP_STREAM; - if (data->blocks > 1) - cmdr |= AT91_MCI_TRTYP_MULTIPLE; - } - } - else { - block_length = 0; - blocks = 0; - } - - if (host->flags & FL_SENT_STOP) - cmdr |= AT91_MCI_TRCMD_STOP; - - if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) - cmdr |= AT91_MCI_OPDCMD; - - /* - * Set the arguments and send the command - */ - 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(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS | ATMEL_PDC_RXTDIS); - at91_mci_write(host, ATMEL_PDC_RPR, 0); - at91_mci_write(host, ATMEL_PDC_RCR, 0); - at91_mci_write(host, ATMEL_PDC_RNPR, 0); - at91_mci_write(host, ATMEL_PDC_RNCR, 0); - at91_mci_write(host, ATMEL_PDC_TPR, 0); - at91_mci_write(host, ATMEL_PDC_TCR, 0); - at91_mci_write(host, ATMEL_PDC_TNPR, 0); - at91_mci_write(host, ATMEL_PDC_TNCR, 0); - ier = AT91_MCI_CMDRDY; - } else { - /* zero block length and PDC mode */ - mr = at91_mci_read(host, AT91_MCI_MR) & 0x5fff; - mr |= (data->blksz & 0x3) ? AT91_MCI_PDCFBYTE : 0; - mr |= (block_length << 16); - mr |= AT91_MCI_PDCMODE; - at91_mci_write(host, AT91_MCI_MR, mr); - - if (!(cpu_is_at91rm9200() || cpu_is_at91sam9261())) - at91_mci_write(host, AT91_MCI_BLKR, - AT91_MCI_BLKR_BCNT(blocks) | - AT91_MCI_BLKR_BLKLEN(block_length)); - - /* - * Disable the PDC controller - */ - at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); - - if (cmdr & AT91_MCI_TRCMD_START) { - data->bytes_xfered = 0; - host->transfer_index = 0; - host->in_use_index = 0; - if (cmdr & AT91_MCI_TRDIR) { - /* - * Handle a read - */ - host->total_length = 0; - - at91_mci_write(host, ATMEL_PDC_RPR, host->physical_address); - at91_mci_write(host, ATMEL_PDC_RCR, (data->blksz & 0x3) ? - (blocks * block_length) : (blocks * block_length) / 4); - at91_mci_write(host, ATMEL_PDC_RNPR, 0); - at91_mci_write(host, ATMEL_PDC_RNCR, 0); - - ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */; - } - else { - /* - * Handle a write - */ - host->total_length = block_length * blocks; - /* - * MCI1 rev2xx Data Write Operation and - * number of bytes erratum - */ - if (at91mci_is_mci1rev2xx()) - if (host->total_length < 12) - host->total_length = 12; - - at91_mci_sg_to_dma(host, data); - - pr_debug("Transmitting %d bytes\n", host->total_length); - - at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); - at91_mci_write(host, ATMEL_PDC_TCR, (data->blksz & 0x3) ? - host->total_length : host->total_length / 4); - - ier = AT91_MCI_CMDRDY; - } - } - } - - /* - * Send the command and then enable the PDC - not the other way round as - * the data sheet says - */ - - 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(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN); - } - - /* Enable selected interrupts */ - at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier); -} - -/* - * Process the next step in the request - */ -static void at91_mci_process_next(struct at91mci_host *host) -{ - if (!(host->flags & FL_SENT_COMMAND)) { - host->flags |= FL_SENT_COMMAND; - at91_mci_send_command(host, host->request->cmd); - } - else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) { - host->flags |= FL_SENT_STOP; - at91_mci_send_command(host, host->request->stop); - } else { - del_timer(&host->timer); - /* the at91rm9200 mci controller hangs after some transfers, - * and the workaround is to reset it after each transfer. - */ - if (cpu_is_at91rm9200()) - at91_reset_host(host); - mmc_request_done(host->mmc, host->request); - } -} - -/* - * Handle a command that has been completed - */ -static void at91_mci_completed_command(struct at91mci_host *host, unsigned int status) -{ - struct mmc_command *cmd = host->cmd; - struct mmc_data *data = cmd->data; - - at91_mci_write(host, AT91_MCI_IDR, 0xffffffff & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB)); - - 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)); - - pr_debug("Status = %08X/%08x [%08X %08X %08X %08X]\n", - status, at91_mci_read(host, AT91_MCI_SR), - cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); - - if (status & AT91_MCI_ERRORS) { - if ((status & AT91_MCI_RCRCE) && !(mmc_resp_type(cmd) & MMC_RSP_CRC)) { - cmd->error = 0; - } - else { - if (status & (AT91_MCI_DTOE | AT91_MCI_DCRCE)) { - if (data) { - if (status & AT91_MCI_DTOE) - data->error = -ETIMEDOUT; - else if (status & AT91_MCI_DCRCE) - data->error = -EILSEQ; - } - } else { - if (status & AT91_MCI_RTOE) - cmd->error = -ETIMEDOUT; - else if (status & AT91_MCI_RCRCE) - cmd->error = -EILSEQ; - else - cmd->error = -EIO; - } - - pr_debug("Error detected and set to %d/%d (cmd = %d, retries = %d)\n", - cmd->error, data ? data->error : 0, - cmd->opcode, cmd->retries); - } - } - else - cmd->error = 0; - - at91_mci_process_next(host); -} - -/* - * Handle an MMC request - */ -static void at91_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) -{ - struct at91mci_host *host = mmc_priv(mmc); - host->request = mrq; - host->flags = 0; - - /* more than 1s timeout needed with slow SD cards */ - mod_timer(&host->timer, jiffies + msecs_to_jiffies(2000)); - - at91_mci_process_next(host); -} - -/* - * Set the IOS - */ -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(host->mci_clk); - - host->bus_mode = ios->bus_mode; - - if (ios->clock == 0) { - /* Disable the MCI controller */ - at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS); - clkdiv = 0; - } - else { - /* Enable the MCI controller */ - 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; - else - clkdiv = (at91_master_clock / ios->clock) / 2; - - pr_debug("clkdiv = %d. mcck = %ld\n", clkdiv, - at91_master_clock / (2 * (clkdiv + 1))); - } - if (ios->bus_width == MMC_BUS_WIDTH_4 && host->board->wire4) { - pr_debug("MMC: Setting controller bus width to 4\n"); - 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(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); - } - - /* Set the clock divider */ - 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 (gpio_is_valid(host->board->vcc_pin)) { - switch (ios->power_mode) { - case MMC_POWER_OFF: - gpio_set_value(host->board->vcc_pin, 0); - break; - case MMC_POWER_UP: - gpio_set_value(host->board->vcc_pin, 1); - break; - case MMC_POWER_ON: - break; - default: - WARN_ON(1); - } - } -} - -/* - * Handle an interrupt - */ -static irqreturn_t at91_mci_irq(int irq, void *devid) -{ - struct at91mci_host *host = devid; - int completed = 0; - unsigned int int_status, int_mask; - - 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; - - 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 */ - - if (int_status & AT91_MCI_TXBUFE) { - pr_debug("TX buffer empty\n"); - at91_mci_handle_transmitted(host); - } - - if (int_status & AT91_MCI_ENDRX) { - pr_debug("ENDRX\n"); - at91_mci_post_dma_read(host); - } - - if (int_status & AT91_MCI_RXBUFF) { - pr_debug("RX buffer full\n"); - at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); - at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_RXBUFF | AT91_MCI_ENDRX); - completed = 1; - } - - if (int_status & AT91_MCI_ENDTX) - pr_debug("Transmit has ended\n"); - - if (int_status & AT91_MCI_NOTBUSY) { - pr_debug("Card is ready\n"); - at91_mci_update_bytes_xfered(host); - completed = 1; - } - - if (int_status & AT91_MCI_DTIP) - pr_debug("Data transfer in progress\n"); - - if (int_status & AT91_MCI_BLKE) { - pr_debug("Block transfer has ended\n"); - if (host->request->data && host->request->data->blocks > 1) { - /* multi block write : complete multi write - * command and send stop */ - completed = 1; - } else { - at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); - } - } - - if (int_status & AT91_MCI_SDIOIRQA) - mmc_signal_sdio_irq(host->mmc); - - if (int_status & AT91_MCI_SDIOIRQB) - mmc_signal_sdio_irq(host->mmc); - - if (int_status & AT91_MCI_TXRDY) - pr_debug("Ready to transmit\n"); - - if (int_status & AT91_MCI_RXRDY) - pr_debug("Ready to receive\n"); - - if (int_status & AT91_MCI_CMDRDY) { - pr_debug("Command ready\n"); - completed = at91_mci_handle_cmdrdy(host); - } - } - - if (completed) { - pr_debug("Completed command\n"); - at91_mci_write(host, AT91_MCI_IDR, 0xffffffff & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB)); - at91_mci_completed_command(host, int_status); - } else - at91_mci_write(host, AT91_MCI_IDR, int_status & ~(AT91_MCI_SDIOIRQA | AT91_MCI_SDIOIRQB)); - - return IRQ_HANDLED; -} - -static irqreturn_t at91_mmc_det_irq(int irq, void *_host) -{ - struct at91mci_host *host = _host; - int present; - - /* entering this ISR means that we have configured det_pin: - * we can use its value in board structure */ - present = !gpio_get_value(host->board->det_pin); - - /* - * we expect this irq on both insert and remove, - * and use a short delay to debounce. - */ - if (present != host->present) { - host->present = present; - pr_debug("%s: card %s\n", mmc_hostname(host->mmc), - present ? "insert" : "remove"); - if (!present) { - pr_debug("****** Resetting SD-card bus width ******\n"); - at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); - } - /* 0.5s needed because of early card detect switch firing */ - mmc_detect_change(host->mmc, msecs_to_jiffies(500)); - } - return IRQ_HANDLED; -} - -static int at91_mci_get_ro(struct mmc_host *mmc) -{ - struct at91mci_host *host = mmc_priv(mmc); - - if (gpio_is_valid(host->board->wp_pin)) - return !!gpio_get_value(host->board->wp_pin); - /* - * Board doesn't support read only detection; let the mmc core - * decide what to do. - */ - return -ENOSYS; -} - -static void at91_mci_enable_sdio_irq(struct mmc_host *mmc, int enable) -{ - struct at91mci_host *host = mmc_priv(mmc); - - pr_debug("%s: sdio_irq %c : %s\n", mmc_hostname(host->mmc), - host->board->slot_b ? 'B':'A', enable ? "enable" : "disable"); - at91_mci_write(host, enable ? AT91_MCI_IER : AT91_MCI_IDR, - host->board->slot_b ? AT91_MCI_SDIOIRQB : AT91_MCI_SDIOIRQA); - -} - -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, - .enable_sdio_irq = at91_mci_enable_sdio_irq, -}; - -/* - * Probe for the device - */ -static int __init at91_mci_probe(struct platform_device *pdev) -{ - struct mmc_host *mmc; - struct at91mci_host *host; - struct resource *res; - int ret; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENXIO; - - if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) - return -EBUSY; - - mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev); - if (!mmc) { - ret = -ENOMEM; - dev_dbg(&pdev->dev, "couldn't allocate mmc host\n"); - goto fail6; - } - - mmc->ops = &at91_mci_ops; - mmc->f_min = 375000; - mmc->f_max = 25000000; - mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = 0; - - mmc->max_blk_size = MCI_MAXBLKSIZE; - mmc->max_blk_count = MCI_BLKATONCE; - mmc->max_req_size = MCI_BUFSIZE; - mmc->max_segs = MCI_BLKATONCE; - mmc->max_seg_size = MCI_BUFSIZE; - - host = mmc_priv(mmc); - host->mmc = mmc; - host->bus_mode = 0; - host->board = pdev->dev.platform_data; - if (host->board->wire4) { - if (at91mci_is_mci1rev2xx()) - mmc->caps |= MMC_CAP_4_BIT_DATA; - else - dev_warn(&pdev->dev, "4 wire bus mode not supported" - " - using 1 wire\n"); - } - - host->buffer = dma_alloc_coherent(&pdev->dev, MCI_BUFSIZE, - &host->physical_address, GFP_KERNEL); - if (!host->buffer) { - ret = -ENOMEM; - dev_err(&pdev->dev, "Can't allocate transmit buffer\n"); - goto fail5; - } - - /* Add SDIO capability when available */ - if (at91mci_is_mci1rev2xx()) { - /* at91mci MCI1 rev2xx sdio interrupt erratum */ - if (host->board->wire4 || !host->board->slot_b) - mmc->caps |= MMC_CAP_SDIO_IRQ; - } - - /* - * Reserve GPIOs ... board init code makes sure these pins are set - * up as GPIOs with the right direction (input, except for vcc) - */ - if (gpio_is_valid(host->board->det_pin)) { - ret = gpio_request(host->board->det_pin, "mmc_detect"); - if (ret < 0) { - dev_dbg(&pdev->dev, "couldn't claim card detect pin\n"); - goto fail4b; - } - } - if (gpio_is_valid(host->board->wp_pin)) { - ret = gpio_request(host->board->wp_pin, "mmc_wp"); - if (ret < 0) { - dev_dbg(&pdev->dev, "couldn't claim wp sense pin\n"); - goto fail4; - } - } - if (gpio_is_valid(host->board->vcc_pin)) { - ret = gpio_request(host->board->vcc_pin, "mmc_vcc"); - if (ret < 0) { - dev_dbg(&pdev->dev, "couldn't claim vcc switch pin\n"); - goto fail3; - } - } - - /* - * Get Clock - */ - host->mci_clk = clk_get(&pdev->dev, "mci_clk"); - if (IS_ERR(host->mci_clk)) { - ret = -ENODEV; - dev_dbg(&pdev->dev, "no mci_clk?\n"); - goto fail2; - } - - /* - * Map I/O region - */ - host->baseaddr = ioremap(res->start, resource_size(res)); - if (!host->baseaddr) { - ret = -ENOMEM; - goto fail1; - } - - /* - * Reset hardware - */ - clk_enable(host->mci_clk); /* Enable the peripheral clock */ - at91_mci_disable(host); - at91_mci_enable(host); - - /* - * Allocate the MCI interrupt - */ - host->irq = platform_get_irq(pdev, 0); - ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED, - mmc_hostname(mmc), host); - if (ret) { - dev_dbg(&pdev->dev, "request MCI interrupt failed\n"); - goto fail0; - } - - setup_timer(&host->timer, at91_timeout_timer, (unsigned long)host); - - platform_set_drvdata(pdev, mmc); - - /* - * Add host to MMC layer - */ - if (gpio_is_valid(host->board->det_pin)) { - host->present = !gpio_get_value(host->board->det_pin); - } - else - host->present = -1; - - mmc_add_host(mmc); - - /* - * monitor card insertion/removal if we can - */ - if (gpio_is_valid(host->board->det_pin)) { - ret = request_irq(gpio_to_irq(host->board->det_pin), - at91_mmc_det_irq, 0, mmc_hostname(mmc), host); - if (ret) - dev_warn(&pdev->dev, "request MMC detect irq failed\n"); - else - device_init_wakeup(&pdev->dev, 1); - } - - pr_debug("Added MCI driver\n"); - - return 0; - -fail0: - clk_disable(host->mci_clk); - iounmap(host->baseaddr); -fail1: - clk_put(host->mci_clk); -fail2: - if (gpio_is_valid(host->board->vcc_pin)) - gpio_free(host->board->vcc_pin); -fail3: - if (gpio_is_valid(host->board->wp_pin)) - gpio_free(host->board->wp_pin); -fail4: - if (gpio_is_valid(host->board->det_pin)) - gpio_free(host->board->det_pin); -fail4b: - if (host->buffer) - dma_free_coherent(&pdev->dev, MCI_BUFSIZE, - host->buffer, host->physical_address); -fail5: - mmc_free_host(mmc); -fail6: - release_mem_region(res->start, resource_size(res)); - dev_err(&pdev->dev, "probe failed, err %d\n", ret); - return ret; -} - -/* - * Remove a device - */ -static int __exit 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; - - host = mmc_priv(mmc); - - if (host->buffer) - dma_free_coherent(&pdev->dev, MCI_BUFSIZE, - host->buffer, host->physical_address); - - if (gpio_is_valid(host->board->det_pin)) { - if (device_can_wakeup(&pdev->dev)) - free_irq(gpio_to_irq(host->board->det_pin), host); - device_init_wakeup(&pdev->dev, 0); - gpio_free(host->board->det_pin); - } - - at91_mci_disable(host); - del_timer_sync(&host->timer); - mmc_remove_host(mmc); - free_irq(host->irq, host); - - clk_disable(host->mci_clk); /* Disable the peripheral clock */ - clk_put(host->mci_clk); - - if (gpio_is_valid(host->board->vcc_pin)) - gpio_free(host->board->vcc_pin); - if (gpio_is_valid(host->board->wp_pin)) - gpio_free(host->board->wp_pin); - - iounmap(host->baseaddr); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - mmc_free_host(mmc); - platform_set_drvdata(pdev, NULL); - pr_debug("MCI Removed\n"); - - return 0; -} - -#ifdef CONFIG_PM -static int at91_mci_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct mmc_host *mmc = platform_get_drvdata(pdev); - struct at91mci_host *host = mmc_priv(mmc); - int ret = 0; - - if (gpio_is_valid(host->board->det_pin) && device_may_wakeup(&pdev->dev)) - enable_irq_wake(host->board->det_pin); - - if (mmc) - ret = mmc_suspend_host(mmc); - - return ret; -} - -static int at91_mci_resume(struct platform_device *pdev) -{ - struct mmc_host *mmc = platform_get_drvdata(pdev); - struct at91mci_host *host = mmc_priv(mmc); - int ret = 0; - - if (gpio_is_valid(host->board->det_pin) && device_may_wakeup(&pdev->dev)) - disable_irq_wake(host->board->det_pin); - - if (mmc) - ret = mmc_resume_host(mmc); - - return ret; -} -#else -#define at91_mci_suspend NULL -#define at91_mci_resume NULL -#endif - -static struct platform_driver at91_mci_driver = { - .remove = __exit_p(at91_mci_remove), - .suspend = at91_mci_suspend, - .resume = at91_mci_resume, - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - }, -}; - -static int __init at91_mci_init(void) -{ - return platform_driver_probe(&at91_mci_driver, at91_mci_probe); -} - -static void __exit at91_mci_exit(void) -{ - platform_driver_unregister(&at91_mci_driver); -} - -module_init(at91_mci_init); -module_exit(at91_mci_exit); - -MODULE_DESCRIPTION("AT91 Multimedia Card Interface driver"); -MODULE_AUTHOR("Nick Randell"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:at91_mci"); diff --git a/drivers/mmc/host/at91_mci.h b/drivers/mmc/host/at91_mci.h deleted file mode 100644 index eec3a6b1c2b..00000000000 --- a/drivers/mmc/host/at91_mci.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * drivers/mmc/host/at91_mci.h - * - * Copyright (C) 2005 Ivan Kokshaysky - * Copyright (C) SAN People - * - * MultiMedia Card Interface (MCI) registers. - * Based on AT91RM9200 datasheet revision F. - * - * This program is free software; you can redistribute it and/or modify - * 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. - */ - -#ifndef AT91_MCI_H -#define AT91_MCI_H - -#define AT91_MCI_CR 0x00 /* Control Register */ -#define AT91_MCI_MCIEN (1 << 0) /* Multi-Media Interface Enable */ -#define AT91_MCI_MCIDIS (1 << 1) /* Multi-Media Interface Disable */ -#define AT91_MCI_PWSEN (1 << 2) /* Power Save Mode Enable */ -#define AT91_MCI_PWSDIS (1 << 3) /* Power Save Mode Disable */ -#define AT91_MCI_SWRST (1 << 7) /* Software Reset */ - -#define AT91_MCI_MR 0x04 /* Mode Register */ -#define AT91_MCI_CLKDIV (0xff << 0) /* Clock Divider */ -#define AT91_MCI_PWSDIV (7 << 8) /* Power Saving Divider */ -#define AT91_MCI_RDPROOF (1 << 11) /* Read Proof Enable [SAM926[03] only] */ -#define AT91_MCI_WRPROOF (1 << 12) /* Write Proof Enable [SAM926[03] only] */ -#define AT91_MCI_PDCFBYTE (1 << 13) /* PDC Force Byte Transfer [SAM926[03] only] */ -#define AT91_MCI_PDCPADV (1 << 14) /* PDC Padding Value */ -#define AT91_MCI_PDCMODE (1 << 15) /* PDC-orientated Mode */ -#define AT91_MCI_BLKLEN (0xfff << 18) /* Data Block Length */ - -#define AT91_MCI_DTOR 0x08 /* Data Timeout Register */ -#define AT91_MCI_DTOCYC (0xf << 0) /* Data Timeout Cycle Number */ -#define AT91_MCI_DTOMUL (7 << 4) /* Data Timeout Multiplier */ -#define AT91_MCI_DTOMUL_1 (0 << 4) -#define AT91_MCI_DTOMUL_16 (1 << 4) -#define AT91_MCI_DTOMUL_128 (2 << 4) -#define AT91_MCI_DTOMUL_256 (3 << 4) -#define AT91_MCI_DTOMUL_1K (4 << 4) -#define AT91_MCI_DTOMUL_4K (5 << 4) -#define AT91_MCI_DTOMUL_64K (6 << 4) -#define AT91_MCI_DTOMUL_1M (7 << 4) - -#define AT91_MCI_SDCR 0x0c /* SD Card Register */ -#define AT91_MCI_SDCSEL (3 << 0) /* SD Card Selector */ -#define AT91_MCI_SDCBUS (1 << 7) /* 1-bit or 4-bit bus */ - -#define AT91_MCI_ARGR 0x10 /* Argument Register */ - -#define AT91_MCI_CMDR 0x14 /* Command Register */ -#define AT91_MCI_CMDNB (0x3f << 0) /* Command Number */ -#define AT91_MCI_RSPTYP (3 << 6) /* Response Type */ -#define AT91_MCI_RSPTYP_NONE (0 << 6) -#define AT91_MCI_RSPTYP_48 (1 << 6) -#define AT91_MCI_RSPTYP_136 (2 << 6) -#define AT91_MCI_SPCMD (7 << 8) /* Special Command */ -#define AT91_MCI_SPCMD_NONE (0 << 8) -#define AT91_MCI_SPCMD_INIT (1 << 8) -#define AT91_MCI_SPCMD_SYNC (2 << 8) -#define AT91_MCI_SPCMD_ICMD (4 << 8) -#define AT91_MCI_SPCMD_IRESP (5 << 8) -#define AT91_MCI_OPDCMD (1 << 11) /* Open Drain Command */ -#define AT91_MCI_MAXLAT (1 << 12) /* Max Latency for Command to Response */ -#define AT91_MCI_TRCMD (3 << 16) /* Transfer Command */ -#define AT91_MCI_TRCMD_NONE (0 << 16) -#define AT91_MCI_TRCMD_START (1 << 16) -#define AT91_MCI_TRCMD_STOP (2 << 16) -#define AT91_MCI_TRDIR (1 << 18) /* Transfer Direction */ -#define AT91_MCI_TRTYP (3 << 19) /* Transfer Type */ -#define AT91_MCI_TRTYP_BLOCK (0 << 19) -#define AT91_MCI_TRTYP_MULTIPLE (1 << 19) -#define AT91_MCI_TRTYP_STREAM (2 << 19) -#define AT91_MCI_TRTYP_SDIO_BYTE (4 << 19) -#define AT91_MCI_TRTYP_SDIO_BLOCK (5 << 19) - -#define AT91_MCI_BLKR 0x18 /* Block Register */ -#define AT91_MCI_BLKR_BCNT(n) ((0xffff & (n)) << 0) /* Block count */ -#define AT91_MCI_BLKR_BLKLEN(n) ((0xffff & (n)) << 16) /* Block length */ - -#define AT91_MCI_RSPR(n) (0x20 + ((n) * 4)) /* Response Registers 0-3 */ -#define AT91_MCR_RDR 0x30 /* Receive Data Register */ -#define AT91_MCR_TDR 0x34 /* Transmit Data Register */ - -#define AT91_MCI_SR 0x40 /* Status Register */ -#define AT91_MCI_CMDRDY (1 << 0) /* Command Ready */ -#define AT91_MCI_RXRDY (1 << 1) /* Receiver Ready */ -#define AT91_MCI_TXRDY (1 << 2) /* Transmit Ready */ -#define AT91_MCI_BLKE (1 << 3) /* Data Block Ended */ -#define AT91_MCI_DTIP (1 << 4) /* Data Transfer in Progress */ -#define AT91_MCI_NOTBUSY (1 << 5) /* Data Not Busy */ -#define AT91_MCI_ENDRX (1 << 6) /* End of RX Buffer */ -#define AT91_MCI_ENDTX (1 << 7) /* End fo TX Buffer */ -#define AT91_MCI_SDIOIRQA (1 << 8) /* SDIO Interrupt for Slot A */ -#define AT91_MCI_SDIOIRQB (1 << 9) /* SDIO Interrupt for Slot B */ -#define AT91_MCI_RXBUFF (1 << 14) /* RX Buffer Full */ -#define AT91_MCI_TXBUFE (1 << 15) /* TX Buffer Empty */ -#define AT91_MCI_RINDE (1 << 16) /* Response Index Error */ -#define AT91_MCI_RDIRE (1 << 17) /* Response Direction Error */ -#define AT91_MCI_RCRCE (1 << 18) /* Response CRC Error */ -#define AT91_MCI_RENDE (1 << 19) /* Response End Bit Error */ -#define AT91_MCI_RTOE (1 << 20) /* Response Time-out Error */ -#define AT91_MCI_DCRCE (1 << 21) /* Data CRC Error */ -#define AT91_MCI_DTOE (1 << 22) /* Data Time-out Error */ -#define AT91_MCI_OVRE (1 << 30) /* Overrun */ -#define AT91_MCI_UNRE (1 << 31) /* Underrun */ - -#define AT91_MCI_IER 0x44 /* Interrupt Enable Register */ -#define AT91_MCI_IDR 0x48 /* Interrupt Disable Register */ -#define AT91_MCI_IMR 0x4c /* Interrupt Mask Register */ - -#endif diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index ddf096e3803..722af1de796 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -28,6 +28,7 @@ #include <linux/slab.h> #include <linux/stat.h> #include <linux/types.h> +#include <linux/platform_data/atmel.h> #include <linux/mmc/host.h> #include <linux/mmc/sdio.h> @@ -40,7 +41,6 @@ #include <asm/unaligned.h> #include <mach/cpu.h> -#include <mach/board.h> #include "atmel-mci-regs.h" @@ -511,7 +511,7 @@ static const struct of_device_id atmci_dt_ids[] = { MODULE_DEVICE_TABLE(of, atmci_dt_ids); -static struct mci_platform_data __devinit* +static struct mci_platform_data* atmci_of_init(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c index dbd0c8a4e98..127a8fade4d 100644 --- a/drivers/mmc/host/au1xmmc.c +++ b/drivers/mmc/host/au1xmmc.c @@ -943,7 +943,7 @@ static const struct mmc_host_ops au1xmmc_ops = { .enable_sdio_irq = au1xmmc_enable_sdio_irq, }; -static int __devinit au1xmmc_probe(struct platform_device *pdev) +static int au1xmmc_probe(struct platform_device *pdev) { struct mmc_host *mmc; struct au1xmmc_host *host; @@ -1114,7 +1114,7 @@ out0: return ret; } -static int __devexit au1xmmc_remove(struct platform_device *pdev) +static int au1xmmc_remove(struct platform_device *pdev) { struct au1xmmc_host *host = platform_get_drvdata(pdev); diff --git a/drivers/mmc/host/bfin_sdh.c b/drivers/mmc/host/bfin_sdh.c index b9b463eca1e..fb4348c5b6a 100644 --- a/drivers/mmc/host/bfin_sdh.c +++ b/drivers/mmc/host/bfin_sdh.c @@ -522,7 +522,7 @@ static void sdh_reset(void) SSYNC(); } -static int __devinit sdh_probe(struct platform_device *pdev) +static int sdh_probe(struct platform_device *pdev) { struct mmc_host *mmc; struct sdh_host *host; @@ -617,7 +617,7 @@ out1: return ret; } -static int __devexit sdh_remove(struct platform_device *pdev) +static int sdh_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); @@ -680,7 +680,7 @@ static int sdh_resume(struct platform_device *dev) static struct platform_driver sdh_driver = { .probe = sdh_probe, - .remove = __devexit_p(sdh_remove), + .remove = sdh_remove, .suspend = sdh_suspend, .resume = sdh_resume, .driver = { diff --git a/drivers/mmc/host/cb710-mmc.c b/drivers/mmc/host/cb710-mmc.c index 83693fd7c6b..777ca2046b2 100644 --- a/drivers/mmc/host/cb710-mmc.c +++ b/drivers/mmc/host/cb710-mmc.c @@ -690,7 +690,7 @@ static int cb710_mmc_resume(struct platform_device *pdev) #endif /* CONFIG_PM */ -static int __devinit cb710_mmc_init(struct platform_device *pdev) +static int cb710_mmc_init(struct platform_device *pdev) { struct cb710_slot *slot = cb710_pdev_to_slot(pdev); struct cb710_chip *chip = cb710_slot_to_chip(slot); @@ -746,7 +746,7 @@ err_free_mmc: return err; } -static int __devexit cb710_mmc_exit(struct platform_device *pdev) +static int cb710_mmc_exit(struct platform_device *pdev) { struct cb710_slot *slot = cb710_pdev_to_slot(pdev); struct mmc_host *mmc = cb710_slot_to_mmc(slot); @@ -773,7 +773,7 @@ static int __devexit cb710_mmc_exit(struct platform_device *pdev) static struct platform_driver cb710_mmc_driver = { .driver.name = "cb710-mmc", .probe = cb710_mmc_init, - .remove = __devexit_p(cb710_mmc_exit), + .remove = cb710_mmc_exit, #ifdef CONFIG_PM .suspend = cb710_mmc_suspend, .resume = cb710_mmc_resume, diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c index edb37e9135a..8ee0f74f937 100644 --- a/drivers/mmc/host/dw_mmc-pci.c +++ b/drivers/mmc/host/dw_mmc-pci.c @@ -37,7 +37,7 @@ static struct dw_mci_board pci_board_data = { .fifo_depth = 32, }; -static int __devinit dw_mci_pci_probe(struct pci_dev *pdev, +static int dw_mci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *entries) { struct dw_mci *host; @@ -85,7 +85,7 @@ err_disable_dev: return ret; } -static void __devexit dw_mci_pci_remove(struct pci_dev *pdev) +static void dw_mci_pci_remove(struct pci_dev *pdev) { struct dw_mci *host = pci_get_drvdata(pdev); @@ -134,7 +134,7 @@ static struct pci_driver dw_mci_pci_driver = { .name = "dw_mmc_pci", .id_table = dw_mci_pci_id, .probe = dw_mci_pci_probe, - .remove = dw_mci_pci_remove, + .remove = __devexit_p(dw_mci_pci_remove), .driver = { .pm = &dw_mci_pci_pmops }, diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c index 917936bee5d..222036c9e05 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.c +++ b/drivers/mmc/host/dw_mmc-pltfm.c @@ -62,12 +62,12 @@ int dw_mci_pltfm_register(struct platform_device *pdev, } EXPORT_SYMBOL_GPL(dw_mci_pltfm_register); -static int __devinit dw_mci_pltfm_probe(struct platform_device *pdev) +static int dw_mci_pltfm_probe(struct platform_device *pdev) { return dw_mci_pltfm_register(pdev, NULL); } -static int __devexit dw_mci_pltfm_remove(struct platform_device *pdev) +static int dw_mci_pltfm_remove(struct platform_device *pdev) { struct dw_mci *host = platform_get_drvdata(pdev); @@ -119,7 +119,8 @@ static const struct of_device_id dw_mci_pltfm_match[] = { MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match); static struct platform_driver dw_mci_pltfm_driver = { - .remove = __exit_p(dw_mci_pltfm_remove), + .probe = dw_mci_pltfm_probe, + .remove = __devexit_p(dw_mci_pltfm_remove), .driver = { .name = "dw_mmc", .of_match_table = of_match_ptr(dw_mci_pltfm_match), @@ -127,18 +128,7 @@ static struct platform_driver dw_mci_pltfm_driver = { }, }; -static int __init dw_mci_init(void) -{ - return platform_driver_probe(&dw_mci_pltfm_driver, dw_mci_pltfm_probe); -} - -static void __exit dw_mci_exit(void) -{ - platform_driver_unregister(&dw_mci_pltfm_driver); -} - -module_init(dw_mci_init); -module_exit(dw_mci_exit); +module_platform_driver(dw_mci_pltfm_driver); MODULE_DESCRIPTION("DW Multimedia Card Interface driver"); MODULE_AUTHOR("NXP Semiconductor VietNam"); diff --git a/drivers/mmc/host/dw_mmc-pltfm.h b/drivers/mmc/host/dw_mmc-pltfm.h index 2ac37b81de4..68e7fd2f614 100644 --- a/drivers/mmc/host/dw_mmc-pltfm.h +++ b/drivers/mmc/host/dw_mmc-pltfm.h @@ -14,7 +14,7 @@ extern int dw_mci_pltfm_register(struct platform_device *pdev, const struct dw_mci_drv_data *drv_data); -extern int __devexit dw_mci_pltfm_remove(struct platform_device *pdev); +extern int dw_mci_pltfm_remove(struct platform_device *pdev); extern const struct dev_pm_ops dw_mci_pltfm_pmops; #endif /* _DW_MMC_PLTFM_H_ */ diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index c0667c8af2b..323c5022c2c 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -232,7 +232,7 @@ static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) { struct mmc_data *data; struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci_drv_data *drv_data = slot->host->drv_data; + const struct dw_mci_drv_data *drv_data = slot->host->drv_data; u32 cmdr; cmd->error = -EINPROGRESS; @@ -617,13 +617,13 @@ static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) cmd, arg, cmd_status); } -static void dw_mci_setup_bus(struct dw_mci_slot *slot) +static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) { struct dw_mci *host = slot->host; u32 div; u32 clk_en_a; - if (slot->clock != host->current_speed) { + if (slot->clock != host->current_speed || force_clkinit) { div = host->bus_hz / slot->clock; if (host->bus_hz % slot->clock && host->bus_hz > slot->clock) /* @@ -683,9 +683,6 @@ static void __dw_mci_start_request(struct dw_mci *host, if (host->pdata->select_slot) host->pdata->select_slot(slot->id); - /* Slot specific timing and width adjustment */ - dw_mci_setup_bus(slot); - host->cur_slot = slot; host->mrq = mrq; @@ -773,22 +770,19 @@ static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct dw_mci_slot *slot = mmc_priv(mmc); - struct dw_mci_drv_data *drv_data = slot->host->drv_data; + const struct dw_mci_drv_data *drv_data = slot->host->drv_data; u32 regs; - /* set default 1 bit mode */ - slot->ctype = SDMMC_CTYPE_1BIT; - switch (ios->bus_width) { - case MMC_BUS_WIDTH_1: - slot->ctype = SDMMC_CTYPE_1BIT; - break; case MMC_BUS_WIDTH_4: slot->ctype = SDMMC_CTYPE_4BIT; break; case MMC_BUS_WIDTH_8: slot->ctype = SDMMC_CTYPE_8BIT; break; + default: + /* set default 1 bit mode */ + slot->ctype = SDMMC_CTYPE_1BIT; } regs = mci_readl(slot->host, UHS_REG); @@ -812,6 +806,9 @@ static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (drv_data && drv_data->set_ios) drv_data->set_ios(slot->host, ios); + /* Slot specific timing and width adjustment */ + dw_mci_setup_bus(slot, false); + switch (ios->power_mode) { case MMC_POWER_UP: set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); @@ -1817,7 +1814,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) { struct mmc_host *mmc; struct dw_mci_slot *slot; - struct dw_mci_drv_data *drv_data = host->drv_data; + const struct dw_mci_drv_data *drv_data = host->drv_data; int ctrl_id, ret; u8 bus_width; @@ -1850,6 +1847,9 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) if (host->pdata->caps) mmc->caps = host->pdata->caps; + if (host->pdata->pm_caps) + mmc->pm_caps = host->pdata->pm_caps; + if (host->dev->of_node) { ctrl_id = of_alias_get_id(host->dev->of_node, "mshc"); if (ctrl_id < 0) @@ -1911,7 +1911,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) #endif /* CONFIG_MMC_DW_IDMAC */ } - host->vmmc = regulator_get(mmc_dev(mmc), "vmmc"); + host->vmmc = devm_regulator_get(mmc_dev(mmc), "vmmc"); if (IS_ERR(host->vmmc)) { pr_info("%s: no vmmc regulator found\n", mmc_hostname(mmc)); host->vmmc = NULL; @@ -1960,7 +1960,7 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id) static void dw_mci_init_dma(struct dw_mci *host) { /* Alloc memory for sg translation */ - host->sg_cpu = dma_alloc_coherent(host->dev, PAGE_SIZE, + host->sg_cpu = dmam_alloc_coherent(host->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); if (!host->sg_cpu) { dev_err(host->dev, "%s: could not alloc DMA memory\n", @@ -2038,7 +2038,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) struct dw_mci_board *pdata; struct device *dev = host->dev; struct device_node *np = dev->of_node; - struct dw_mci_drv_data *drv_data = host->drv_data; + const struct dw_mci_drv_data *drv_data = host->drv_data; int idx, ret; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); @@ -2072,6 +2072,12 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) return ERR_PTR(ret); } + if (of_find_property(np, "keep-power-in-suspend", NULL)) + pdata->pm_caps |= MMC_PM_KEEP_POWER; + + if (of_find_property(np, "enable-sdio-wakeup", NULL)) + pdata->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; + return pdata; } @@ -2084,7 +2090,7 @@ static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) int dw_mci_probe(struct dw_mci *host) { - struct dw_mci_drv_data *drv_data = host->drv_data; + const struct dw_mci_drv_data *drv_data = host->drv_data; int width, i, ret = 0; u32 fifo_size; int init_slots = 0; @@ -2103,26 +2109,24 @@ int dw_mci_probe(struct dw_mci *host) return -ENODEV; } - host->biu_clk = clk_get(host->dev, "biu"); + host->biu_clk = devm_clk_get(host->dev, "biu"); if (IS_ERR(host->biu_clk)) { dev_dbg(host->dev, "biu clock not available\n"); } else { ret = clk_prepare_enable(host->biu_clk); if (ret) { dev_err(host->dev, "failed to enable biu clock\n"); - clk_put(host->biu_clk); return ret; } } - host->ciu_clk = clk_get(host->dev, "ciu"); + host->ciu_clk = devm_clk_get(host->dev, "ciu"); if (IS_ERR(host->ciu_clk)) { dev_dbg(host->dev, "ciu clock not available\n"); } else { ret = clk_prepare_enable(host->ciu_clk); if (ret) { dev_err(host->dev, "failed to enable ciu clock\n"); - clk_put(host->ciu_clk); goto err_clk_biu; } } @@ -2224,7 +2228,8 @@ int dw_mci_probe(struct dw_mci *host) if (!host->card_workqueue) goto err_dmaunmap; INIT_WORK(&host->card_work, dw_mci_work_routine_card); - ret = request_irq(host->irq, dw_mci_interrupt, host->irq_flags, "dw-mci", host); + ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, + host->irq_flags, "dw-mci", host); if (ret) goto err_workqueue; @@ -2262,7 +2267,7 @@ int dw_mci_probe(struct dw_mci *host) } else { dev_dbg(host->dev, "attempted to initialize %d slots, " "but failed on all\n", host->num_slots); - goto err_init_slot; + goto err_workqueue; } /* @@ -2282,33 +2287,24 @@ int dw_mci_probe(struct dw_mci *host) return 0; -err_init_slot: - free_irq(host->irq, host); - err_workqueue: destroy_workqueue(host->card_workqueue); err_dmaunmap: if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); - dma_free_coherent(host->dev, PAGE_SIZE, - host->sg_cpu, host->sg_dma); - if (host->vmmc) { + if (host->vmmc) regulator_disable(host->vmmc); - regulator_put(host->vmmc); - } err_clk_ciu: - if (!IS_ERR(host->ciu_clk)) { + if (!IS_ERR(host->ciu_clk)) clk_disable_unprepare(host->ciu_clk); - clk_put(host->ciu_clk); - } + err_clk_biu: - if (!IS_ERR(host->biu_clk)) { + if (!IS_ERR(host->biu_clk)) clk_disable_unprepare(host->biu_clk); - clk_put(host->biu_clk); - } + return ret; } EXPORT_SYMBOL(dw_mci_probe); @@ -2330,24 +2326,19 @@ void dw_mci_remove(struct dw_mci *host) mci_writel(host, CLKENA, 0); mci_writel(host, CLKSRC, 0); - free_irq(host->irq, host); destroy_workqueue(host->card_workqueue); - dma_free_coherent(host->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); if (host->use_dma && host->dma_ops->exit) host->dma_ops->exit(host); - if (host->vmmc) { + if (host->vmmc) regulator_disable(host->vmmc); - regulator_put(host->vmmc); - } if (!IS_ERR(host->ciu_clk)) clk_disable_unprepare(host->ciu_clk); + if (!IS_ERR(host->biu_clk)) clk_disable_unprepare(host->biu_clk); - clk_put(host->ciu_clk); - clk_put(host->biu_clk); } EXPORT_SYMBOL(dw_mci_remove); @@ -2411,6 +2402,11 @@ int dw_mci_resume(struct dw_mci *host) struct dw_mci_slot *slot = host->slot[i]; if (!slot) continue; + if (slot->mmc->pm_flags & MMC_PM_KEEP_POWER) { + dw_mci_set_ios(slot->mmc, &slot->mmc->ios); + dw_mci_setup_bus(slot, true); + } + ret = mmc_resume_host(host->slot[i]->mmc); if (ret < 0) return ret; diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c index c8852a8128a..2391c6b7a4b 100644 --- a/drivers/mmc/host/jz4740_mmc.c +++ b/drivers/mmc/host/jz4740_mmc.c @@ -702,7 +702,7 @@ static const struct jz_gpio_bulk_request jz4740_mmc_pins[] = { JZ_GPIO_BULK_PIN(MSC_DATA3), }; -static int __devinit jz4740_mmc_request_gpio(struct device *dev, int gpio, +static int jz4740_mmc_request_gpio(struct device *dev, int gpio, const char *name, bool output, int value) { int ret; @@ -724,7 +724,7 @@ static int __devinit jz4740_mmc_request_gpio(struct device *dev, int gpio, return 0; } -static int __devinit jz4740_mmc_request_gpios(struct platform_device *pdev) +static int jz4740_mmc_request_gpios(struct platform_device *pdev) { int ret; struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data; @@ -759,7 +759,7 @@ err: return ret; } -static int __devinit jz4740_mmc_request_cd_irq(struct platform_device *pdev, +static int jz4740_mmc_request_cd_irq(struct platform_device *pdev, struct jz4740_mmc_host *host) { struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data; @@ -802,7 +802,7 @@ static inline size_t jz4740_mmc_num_pins(struct jz4740_mmc_host *host) return num_pins; } -static int __devinit jz4740_mmc_probe(struct platform_device* pdev) +static int jz4740_mmc_probe(struct platform_device* pdev) { int ret; struct mmc_host *mmc; @@ -938,7 +938,7 @@ err_free_host: return ret; } -static int __devexit jz4740_mmc_remove(struct platform_device *pdev) +static int jz4740_mmc_remove(struct platform_device *pdev) { struct jz4740_mmc_host *host = platform_get_drvdata(pdev); @@ -1004,7 +1004,7 @@ const struct dev_pm_ops jz4740_mmc_pm_ops = { static struct platform_driver jz4740_mmc_driver = { .probe = jz4740_mmc_probe, - .remove = __devexit_p(jz4740_mmc_remove), + .remove = jz4740_mmc_remove, .driver = { .name = "jz4740-mmc", .owner = THIS_MODULE, diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index a600eabbd6c..74145d1d51f 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -1485,7 +1485,7 @@ nomem: } -static int __devexit mmc_spi_remove(struct spi_device *spi) +static int mmc_spi_remove(struct spi_device *spi) { struct mmc_host *mmc = dev_get_drvdata(&spi->dev); struct mmc_spi_host *host; @@ -1517,7 +1517,7 @@ static int __devexit mmc_spi_remove(struct spi_device *spi) return 0; } -static struct of_device_id mmc_spi_of_match_table[] __devinitdata = { +static struct of_device_id mmc_spi_of_match_table[] = { { .compatible = "mmc-spi-slot", }, {}, }; @@ -1529,7 +1529,7 @@ static struct spi_driver mmc_spi_driver = { .of_match_table = mmc_spi_of_match_table, }, .probe = mmc_spi_probe, - .remove = __devexit_p(mmc_spi_remove), + .remove = mmc_spi_remove, }; module_spi_driver(mmc_spi_driver); diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index edc3e9baf0e..150772395cc 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -33,6 +33,7 @@ #include <linux/amba/mmci.h> #include <linux/pm_runtime.h> #include <linux/types.h> +#include <linux/pinctrl/consumer.h> #include <asm/div64.h> #include <asm/io.h> @@ -261,7 +262,7 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) * no custom DMA interfaces are supported. */ #ifdef CONFIG_DMA_ENGINE -static void __devinit mmci_dma_setup(struct mmci_host *host) +static void mmci_dma_setup(struct mmci_host *host) { struct mmci_platform_data *plat = host->plat; const char *rxname, *txname; @@ -337,7 +338,7 @@ static void __devinit mmci_dma_setup(struct mmci_host *host) } /* - * This is used in __devinit or __devexit so inline it + * This is used in or so inline it * so it can be discarded. */ static inline void mmci_dma_release(struct mmci_host *host) @@ -654,9 +655,31 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) /* The ST Micro variants has a special bit to enable SDIO */ if (variant->sdio && host->mmc->card) - if (mmc_card_sdio(host->mmc->card)) + if (mmc_card_sdio(host->mmc->card)) { + /* + * The ST Micro variants has a special bit + * to enable SDIO. + */ + u32 clk; + datactrl |= MCI_ST_DPSM_SDIOEN; + /* + * The ST Micro variant for SDIO small write transfers + * needs to have clock H/W flow control disabled, + * otherwise the transfer will not start. The threshold + * depends on the rate of MCLK. + */ + if (data->flags & MMC_DATA_WRITE && + (host->size < 8 || + (host->size <= 8 && host->mclk > 50000000))) + clk = host->clk_reg & ~variant->clkreg_enable; + else + clk = host->clk_reg | variant->clkreg_enable; + + mmci_write_clkreg(host, clk); + } + /* * Attempt to use DMA operation mode, if this * should fail, fall back to PIO mode @@ -840,14 +863,14 @@ static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int rema if (unlikely(count & 0x3)) { if (count < 4) { unsigned char buf[4]; - readsl(base + MMCIFIFO, buf, 1); + ioread32_rep(base + MMCIFIFO, buf, 1); memcpy(ptr, buf, count); } else { - readsl(base + MMCIFIFO, ptr, count >> 2); + ioread32_rep(base + MMCIFIFO, ptr, count >> 2); count &= ~0x3; } } else { - readsl(base + MMCIFIFO, ptr, count >> 2); + ioread32_rep(base + MMCIFIFO, ptr, count >> 2); } ptr += count; @@ -877,22 +900,6 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int rem count = min(remain, maxcnt); /* - * The ST Micro variant for SDIO transfer sizes - * less then 8 bytes should have clock H/W flow - * control disabled. - */ - if (variant->sdio && - mmc_card_sdio(host->mmc->card)) { - u32 clk; - if (count < 8) - clk = host->clk_reg & ~variant->clkreg_enable; - else - clk = host->clk_reg | variant->clkreg_enable; - - mmci_write_clkreg(host, clk); - } - - /* * SDIO especially may want to send something that is * not divisible by 4 (as opposed to card sectors * etc), and the FIFO only accept full 32-bit writes. @@ -900,7 +907,7 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int rem * byte become a 32bit write, 7 bytes will be two * 32bit writes etc. */ - writesl(base + MMCIFIFO, ptr, (count + 3) >> 2); + iowrite32_rep(base + MMCIFIFO, ptr, (count + 3) >> 2); ptr += count; remain -= count; @@ -1255,7 +1262,7 @@ static void mmci_dt_populate_generic_pdata(struct device_node *np, } #endif -static int __devinit mmci_probe(struct amba_device *dev, +static int mmci_probe(struct amba_device *dev, const struct amba_id *id) { struct mmci_platform_data *plat = dev->dev.platform_data; @@ -1360,6 +1367,23 @@ static int __devinit mmci_probe(struct amba_device *dev, mmc->f_max = min(host->mclk, fmax); dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max); + host->pinctrl = devm_pinctrl_get(&dev->dev); + if (IS_ERR(host->pinctrl)) { + ret = PTR_ERR(host->pinctrl); + goto clk_disable; + } + + host->pins_default = pinctrl_lookup_state(host->pinctrl, + PINCTRL_STATE_DEFAULT); + + /* enable pins to be muxed in and configured */ + if (!IS_ERR(host->pins_default)) { + ret = pinctrl_select_state(host->pinctrl, host->pins_default); + if (ret) + dev_warn(&dev->dev, "could not set default pins\n"); + } else + dev_warn(&dev->dev, "could not get default pinstate\n"); + #ifdef CONFIG_REGULATOR /* If we're using the regulator framework, try to fetch a regulator */ host->vcc = regulator_get(&dev->dev, "vmmc"); @@ -1522,7 +1546,7 @@ static int __devinit mmci_probe(struct amba_device *dev, return ret; } -static int __devexit mmci_remove(struct amba_device *dev) +static int mmci_remove(struct amba_device *dev) { struct mmc_host *mmc = amba_get_drvdata(dev); @@ -1669,7 +1693,7 @@ static struct amba_driver mmci_driver = { .pm = &mmci_dev_pm_ops, }, .probe = mmci_probe, - .remove = __devexit_p(mmci_remove), + .remove = mmci_remove, .id_table = mmci_ids, }; diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index d437ccf62d6..d34d8c0add8 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -195,6 +195,10 @@ struct mmci_host { unsigned int size; struct regulator *vcc; + /* pinctrl handles */ + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + #ifdef CONFIG_DMA_ENGINE /* DMA stuff */ struct dma_chan *dma_current; diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index 6290b7f1ccf..a72936eea6f 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -41,7 +41,6 @@ #include <linux/platform_data/mmc-mxcmmc.h> #include <linux/platform_data/dma-imx.h> -#include <mach/hardware.h> #define DRIVER_NAME "mxc-mmc" #define MXCMCI_TIMEOUT_MS 10000 @@ -113,6 +112,11 @@ #define INT_WRITE_OP_DONE_EN (1 << 1) #define INT_READ_OP_EN (1 << 0) +enum mxcmci_type { + IMX21_MMC, + IMX31_MMC, +}; + struct mxcmci_host { struct mmc_host *mmc; struct resource *res; @@ -153,7 +157,26 @@ struct mxcmci_host { struct imx_dma_data dma_data; struct timer_list watchdog; + enum mxcmci_type devtype; +}; + +static struct platform_device_id mxcmci_devtype[] = { + { + .name = "imx21-mmc", + .driver_data = IMX21_MMC, + }, { + .name = "imx31-mmc", + .driver_data = IMX31_MMC, + }, { + /* sentinel */ + } }; +MODULE_DEVICE_TABLE(platform, mxcmci_devtype); + +static inline int is_imx31_mmc(struct mxcmci_host *host) +{ + return host->devtype == IMX31_MMC; +} static void mxcmci_set_clk_rate(struct mxcmci_host *host, unsigned int clk_ios); @@ -240,7 +263,7 @@ static int mxcmci_setup_data(struct mxcmci_host *host, struct mmc_data *data) return 0; for_each_sg(data->sg, sg, data->sg_len, i) { - if (sg->offset & 3 || sg->length & 3) { + if (sg->offset & 3 || sg->length & 3 || sg->length < 512) { host->do_dma = 0; return 0; } @@ -843,6 +866,8 @@ static void mxcmci_enable_sdio_irq(struct mmc_host *mmc, int enable) static void mxcmci_init_card(struct mmc_host *host, struct mmc_card *card) { + struct mxcmci_host *mxcmci = mmc_priv(host); + /* * MX3 SoCs have a silicon bug which corrupts CRC calculation of * multi-block transfers when connected SDIO peripheral doesn't @@ -850,7 +875,7 @@ static void mxcmci_init_card(struct mmc_host *host, struct mmc_card *card) * One way to prevent this is to only allow 1-bit transfers. */ - if (cpu_is_mx3() && card->type == MMC_TYPE_SDIO) + if (is_imx31_mmc(mxcmci) && card->type == MMC_TYPE_SDIO) host->caps &= ~MMC_CAP_4_BIT_DATA; else host->caps |= MMC_CAP_4_BIT_DATA; @@ -948,6 +973,7 @@ static int mxcmci_probe(struct platform_device *pdev) host->mmc = mmc; host->pdata = pdev->dev.platform_data; + host->devtype = pdev->id_entry->driver_data; spin_lock_init(&host->lock); mxcmci_init_ocr(host); @@ -1120,6 +1146,7 @@ static const struct dev_pm_ops mxcmci_pm_ops = { static struct platform_driver mxcmci_driver = { .probe = mxcmci_probe, .remove = mxcmci_remove, + .id_table = mxcmci_devtype, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index 80d1e6d4b0a..206fe499ded 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -43,7 +43,6 @@ #include <linux/module.h> #include <linux/pinctrl/consumer.h> #include <linux/stmp_device.h> -#include <linux/mmc/mxs-mmc.h> #include <linux/spi/mxs-spi.h> #define DRIVER_NAME "mxs-mmc" @@ -593,13 +592,13 @@ static int mxs_mmc_probe(struct platform_device *pdev) struct mxs_mmc_host *host; struct mmc_host *mmc; struct resource *iores, *dmares; - struct mxs_mmc_platform_data *pdata; struct pinctrl *pinctrl; int ret = 0, irq_err, irq_dma; dma_cap_mask_t mask; struct regulator *reg_vmmc; enum of_gpio_flags flags; struct mxs_ssp *ssp; + u32 bus_width = 0; iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); dmares = platform_get_resource(pdev, IORESOURCE_DMA, 0); @@ -682,25 +681,15 @@ static int mxs_mmc_probe(struct platform_device *pdev) mmc->caps = MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SDIO_IRQ | MMC_CAP_NEEDS_POLL; - pdata = mmc_dev(host->mmc)->platform_data; - if (!pdata) { - u32 bus_width = 0; - of_property_read_u32(np, "bus-width", &bus_width); - if (bus_width == 4) - mmc->caps |= MMC_CAP_4_BIT_DATA; - else if (bus_width == 8) - mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; - host->wp_gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, - &flags); - if (flags & OF_GPIO_ACTIVE_LOW) - host->wp_inverted = 1; - } else { - if (pdata->flags & SLOTF_8_BIT_CAPABLE) - mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; - if (pdata->flags & SLOTF_4_BIT_CAPABLE) - mmc->caps |= MMC_CAP_4_BIT_DATA; - host->wp_gpio = pdata->wp_gpio; - } + of_property_read_u32(np, "bus-width", &bus_width); + if (bus_width == 4) + mmc->caps |= MMC_CAP_4_BIT_DATA; + else if (bus_width == 8) + mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA; + host->wp_gpio = of_get_named_gpio_flags(np, "wp-gpios", 0, &flags); + + if (flags & OF_GPIO_ACTIVE_LOW) + host->wp_inverted = 1; mmc->f_min = 400000; mmc->f_max = 288000000; diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 48ad361613e..4254975f931 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -28,9 +28,8 @@ #include <linux/clk.h> #include <linux/scatterlist.h> #include <linux/slab.h> +#include <linux/platform_data/mmc-omap.h> -#include <plat/mmc.h> -#include <plat/dma.h> #define OMAP_MMC_REG_CMD 0x00 #define OMAP_MMC_REG_ARGL 0x01 @@ -72,6 +71,13 @@ #define OMAP_MMC_STAT_CARD_BUSY (1 << 2) #define OMAP_MMC_STAT_END_OF_CMD (1 << 0) +#define mmc_omap7xx() (host->features & MMC_OMAP7XX) +#define mmc_omap15xx() (host->features & MMC_OMAP15XX) +#define mmc_omap16xx() (host->features & MMC_OMAP16XX) +#define MMC_OMAP1_MASK (MMC_OMAP7XX | MMC_OMAP15XX | MMC_OMAP16XX) +#define mmc_omap1() (host->features & MMC_OMAP1_MASK) +#define mmc_omap2() (!mmc_omap1()) + #define OMAP_MMC_REG(host, reg) (OMAP_MMC_REG_##reg << (host)->reg_shift) #define OMAP_MMC_READ(host, reg) __raw_readw((host)->virt_base + OMAP_MMC_REG(host, reg)) #define OMAP_MMC_WRITE(host, reg, val) __raw_writew((val), (host)->virt_base + OMAP_MMC_REG(host, reg)) @@ -84,6 +90,16 @@ #define OMAP_MMC_CMDTYPE_AC 2 #define OMAP_MMC_CMDTYPE_ADTC 3 +#define OMAP_DMA_MMC_TX 21 +#define OMAP_DMA_MMC_RX 22 +#define OMAP_DMA_MMC2_TX 54 +#define OMAP_DMA_MMC2_RX 55 + +#define OMAP24XX_DMA_MMC2_TX 47 +#define OMAP24XX_DMA_MMC2_RX 48 +#define OMAP24XX_DMA_MMC1_TX 61 +#define OMAP24XX_DMA_MMC1_RX 62 + #define DRIVER_NAME "mmci-omap" @@ -147,6 +163,7 @@ struct mmc_omap_host { u32 buffer_bytes_left; u32 total_bytes_left; + unsigned features; unsigned use_dma:1; unsigned brs_received:1, dma_done:1; unsigned dma_in_use:1; @@ -988,7 +1005,7 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) * blocksize is at least that large. Blocksize is * usually 512 bytes; but not for some SD reads. */ - burst = cpu_is_omap15xx() ? 32 : 64; + burst = mmc_omap15xx() ? 32 : 64; if (burst > data->blksz) burst = data->blksz; @@ -1104,8 +1121,7 @@ static void mmc_omap_set_power(struct mmc_omap_slot *slot, int power_on, if (slot->pdata->set_power != NULL) slot->pdata->set_power(mmc_dev(slot->mmc), slot->id, power_on, vdd); - - if (cpu_is_omap24xx()) { + if (mmc_omap2()) { u16 w; if (power_on) { @@ -1214,7 +1230,7 @@ static const struct mmc_host_ops mmc_omap_ops = { .set_ios = mmc_omap_set_ios, }; -static int __devinit mmc_omap_new_slot(struct mmc_omap_host *host, int id) +static int mmc_omap_new_slot(struct mmc_omap_host *host, int id) { struct mmc_omap_slot *slot = NULL; struct mmc_host *mmc; @@ -1239,7 +1255,7 @@ static int __devinit mmc_omap_new_slot(struct mmc_omap_host *host, int id) mmc->ops = &mmc_omap_ops; mmc->f_min = 400000; - if (cpu_class_is_omap2()) + if (mmc_omap2()) mmc->f_max = 48000000; else mmc->f_max = 24000000; @@ -1309,7 +1325,7 @@ static void mmc_omap_remove_slot(struct mmc_omap_slot *slot) mmc_free_host(mmc); } -static int __devinit mmc_omap_probe(struct platform_device *pdev) +static int mmc_omap_probe(struct platform_device *pdev) { struct omap_mmc_platform_data *pdata = pdev->dev.platform_data; struct mmc_omap_host *host = NULL; @@ -1359,6 +1375,7 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev) init_waitqueue_head(&host->slot_wq); host->pdata = pdata; + host->features = host->pdata->slots[0].features; host->dev = &pdev->dev; platform_set_drvdata(pdev, host); @@ -1391,7 +1408,7 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev) host->dma_tx_burst = -1; host->dma_rx_burst = -1; - if (cpu_is_omap24xx()) + if (mmc_omap2()) sig = host->id == 0 ? OMAP24XX_DMA_MMC1_TX : OMAP24XX_DMA_MMC2_TX; else sig = host->id == 0 ? OMAP_DMA_MMC_TX : OMAP_DMA_MMC2_TX; @@ -1407,7 +1424,7 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev) dev_warn(host->dev, "unable to obtain TX DMA engine channel %u\n", sig); #endif - if (cpu_is_omap24xx()) + if (mmc_omap2()) sig = host->id == 0 ? OMAP24XX_DMA_MMC1_RX : OMAP24XX_DMA_MMC2_RX; else sig = host->id == 0 ? OMAP_DMA_MMC_RX : OMAP_DMA_MMC2_RX; @@ -1435,7 +1452,7 @@ static int __devinit mmc_omap_probe(struct platform_device *pdev) } host->nr_slots = pdata->nr_slots; - host->reg_shift = (cpu_is_omap7xx() ? 1 : 2); + host->reg_shift = (mmc_omap7xx() ? 1 : 2); host->mmc_omap_wq = alloc_workqueue("mmc_omap", 0, 0); if (!host->mmc_omap_wq) @@ -1478,7 +1495,7 @@ err_free_mem_region: return ret; } -static int __devexit mmc_omap_remove(struct platform_device *pdev) +static int mmc_omap_remove(struct platform_device *pdev) { struct mmc_omap_host *host = platform_get_drvdata(pdev); int i; @@ -1566,7 +1583,7 @@ static int mmc_omap_resume(struct platform_device *pdev) static struct platform_driver mmc_omap_driver = { .probe = mmc_omap_probe, - .remove = __devexit_p(mmc_omap_remove), + .remove = mmc_omap_remove, .suspend = mmc_omap_suspend, .resume = mmc_omap_resume, .driver = { diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index fedd258cc4e..bc5807873b2 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -37,10 +37,9 @@ #include <linux/io.h> #include <linux/gpio.h> #include <linux/regulator/consumer.h> +#include <linux/pinctrl/consumer.h> #include <linux/pm_runtime.h> -#include <mach/hardware.h> -#include <plat/mmc.h> -#include <plat/cpu.h> +#include <linux/platform_data/mmc-omap.h> /* OMAP HSMMC Host Controller Registers */ #define OMAP_HSMMC_SYSSTATUS 0x0014 @@ -62,6 +61,7 @@ #define VS18 (1 << 26) #define VS30 (1 << 25) +#define HSS (1 << 21) #define SDVS18 (0x5 << 9) #define SDVS30 (0x6 << 9) #define SDVS33 (0x7 << 9) @@ -78,28 +78,17 @@ #define CLKD_SHIFT 6 #define DTO_MASK 0x000F0000 #define DTO_SHIFT 16 -#define INT_EN_MASK 0x307F0033 -#define BWR_ENABLE (1 << 4) -#define BRR_ENABLE (1 << 5) -#define DTO_ENABLE (1 << 20) #define INIT_STREAM (1 << 1) #define DP_SELECT (1 << 21) #define DDIR (1 << 4) -#define DMA_EN 0x1 +#define DMAE 0x1 #define MSBS (1 << 5) #define BCE (1 << 1) #define FOUR_BIT (1 << 1) +#define HSPE (1 << 2) #define DDR (1 << 19) #define DW8 (1 << 5) -#define CC 0x1 -#define TC 0x02 #define OD 0x1 -#define ERR (1 << 15) -#define CMD_TIMEOUT (1 << 16) -#define DATA_TIMEOUT (1 << 20) -#define CMD_CRC (1 << 17) -#define DATA_CRC (1 << 21) -#define CARD_ERR (1 << 28) #define STAT_CLEAR 0xFFFFFFFF #define INIT_STREAM_CMD 0x00000000 #define DUAL_VOLT_OCR_BIT 7 @@ -108,6 +97,26 @@ #define SOFTRESET (1 << 1) #define RESETDONE (1 << 0) +/* Interrupt masks for IE and ISE register */ +#define CC_EN (1 << 0) +#define TC_EN (1 << 1) +#define BWR_EN (1 << 4) +#define BRR_EN (1 << 5) +#define ERR_EN (1 << 15) +#define CTO_EN (1 << 16) +#define CCRC_EN (1 << 17) +#define CEB_EN (1 << 18) +#define CIE_EN (1 << 19) +#define DTO_EN (1 << 20) +#define DCRC_EN (1 << 21) +#define DEB_EN (1 << 22) +#define CERR_EN (1 << 28) +#define BADA_EN (1 << 29) + +#define INT_EN_MASK (BADA_EN | CERR_EN | DEB_EN | DCRC_EN |\ + DTO_EN | CIE_EN | CEB_EN | CCRC_EN | CTO_EN | \ + BRR_EN | BWR_EN | TC_EN | CC_EN) + #define MMC_AUTOSUSPEND_DELAY 100 #define MMC_TIMEOUT_MS 20 #define OMAP_MMC_MIN_CLOCK 400000 @@ -302,7 +311,7 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host) reg = regulator_get(host->dev, "vmmc"); if (IS_ERR(reg)) { - dev_dbg(host->dev, "vmmc regulator missing\n"); + dev_err(host->dev, "vmmc regulator missing\n"); return PTR_ERR(reg); } else { mmc_slot(host).set_power = omap_hsmmc_set_power; @@ -455,13 +464,13 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host, unsigned int irq_mask; if (host->use_dma) - irq_mask = INT_EN_MASK & ~(BRR_ENABLE | BWR_ENABLE); + irq_mask = INT_EN_MASK & ~(BRR_EN | BWR_EN); else irq_mask = INT_EN_MASK; /* Disable timeout for erases */ if (cmd->opcode == MMC_ERASE) - irq_mask &= ~DTO_ENABLE; + irq_mask &= ~DTO_EN; OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); OMAP_HSMMC_WRITE(host->base, ISE, irq_mask); @@ -494,6 +503,7 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host) struct mmc_ios *ios = &host->mmc->ios; unsigned long regval; unsigned long timeout; + unsigned long clkdiv; dev_vdbg(mmc_dev(host->mmc), "Set clock to %uHz\n", ios->clock); @@ -501,7 +511,8 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host) regval = OMAP_HSMMC_READ(host->base, SYSCTL); regval = regval & ~(CLKD_MASK | DTO_MASK); - regval = regval | (calc_divisor(host, ios) << 6) | (DTO << 16); + clkdiv = calc_divisor(host, ios); + regval = regval | (clkdiv << 6) | (DTO << 16); OMAP_HSMMC_WRITE(host->base, SYSCTL, regval); OMAP_HSMMC_WRITE(host->base, SYSCTL, OMAP_HSMMC_READ(host->base, SYSCTL) | ICE); @@ -512,6 +523,27 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host) && time_before(jiffies, timeout)) cpu_relax(); + /* + * Enable High-Speed Support + * Pre-Requisites + * - Controller should support High-Speed-Enable Bit + * - Controller should not be using DDR Mode + * - Controller should advertise that it supports High Speed + * in capabilities register + * - MMC/SD clock coming out of controller > 25MHz + */ + if ((mmc_slot(host).features & HSMMC_HAS_HSPE_SUPPORT) && + (ios->timing != MMC_TIMING_UHS_DDR50) && + ((OMAP_HSMMC_READ(host->base, CAPA) & HSS) == HSS)) { + regval = OMAP_HSMMC_READ(host->base, HCTL); + if (clkdiv && (clk_get_rate(host->fclk)/clkdiv) > 25000000) + regval |= HSPE; + else + regval &= ~HSPE; + + OMAP_HSMMC_WRITE(host->base, HCTL, regval); + } + omap_hsmmc_start_clock(host); } @@ -676,8 +708,8 @@ static void send_init_stream(struct omap_hsmmc_host *host) OMAP_HSMMC_WRITE(host->base, CMD, INIT_STREAM_CMD); timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS); - while ((reg != CC) && time_before(jiffies, timeout)) - reg = OMAP_HSMMC_READ(host->base, STAT) & CC; + while ((reg != CC_EN) && time_before(jiffies, timeout)) + reg = OMAP_HSMMC_READ(host->base, STAT) & CC_EN; OMAP_HSMMC_WRITE(host->base, CON, OMAP_HSMMC_READ(host->base, CON) & ~INIT_STREAM); @@ -768,7 +800,7 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd, } if (host->use_dma) - cmdreg |= DMA_EN; + cmdreg |= DMAE; host->req_in_progress = 1; @@ -968,16 +1000,20 @@ static inline void omap_hsmmc_reset_controller_fsm(struct omap_hsmmc_host *host, __func__); } -static void hsmmc_command_incomplete(struct omap_hsmmc_host *host, int err) +static void hsmmc_command_incomplete(struct omap_hsmmc_host *host, + int err, int end_cmd) { - omap_hsmmc_reset_controller_fsm(host, SRC); - host->cmd->error = err; + if (end_cmd) { + omap_hsmmc_reset_controller_fsm(host, SRC); + if (host->cmd) + host->cmd->error = err; + } if (host->data) { omap_hsmmc_reset_controller_fsm(host, SRD); omap_hsmmc_dma_cleanup(host, err); - } - + } else if (host->mrq && host->mrq->cmd) + host->mrq->cmd->error = err; } static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status) @@ -988,23 +1024,25 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status) data = host->data; dev_vdbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status); - if (status & ERR) { + if (status & ERR_EN) { omap_hsmmc_dbg_report_irq(host, status); - if (status & (CMD_TIMEOUT | DATA_TIMEOUT)) - hsmmc_command_incomplete(host, -ETIMEDOUT); - else if (status & (CMD_CRC | DATA_CRC)) - hsmmc_command_incomplete(host, -EILSEQ); - end_cmd = 1; + if (status & (CTO_EN | CCRC_EN)) + end_cmd = 1; + if (status & (CTO_EN | DTO_EN)) + hsmmc_command_incomplete(host, -ETIMEDOUT, end_cmd); + else if (status & (CCRC_EN | DCRC_EN)) + hsmmc_command_incomplete(host, -EILSEQ, end_cmd); + if (host->data || host->response_busy) { - end_trans = 1; + end_trans = !end_cmd; host->response_busy = 0; } } - if (end_cmd || ((status & CC) && host->cmd)) + if (end_cmd || ((status & CC_EN) && host->cmd)) omap_hsmmc_cmd_done(host, host->cmd); - if ((end_trans || (status & TC)) && host->mrq) + if ((end_trans || (status & TC_EN)) && host->mrq) omap_hsmmc_xfer_done(host, data); } @@ -1101,7 +1139,7 @@ static int omap_hsmmc_switch_opcond(struct omap_hsmmc_host *host, int vdd) return 0; err: - dev_dbg(mmc_dev(host->mmc), "Unable to switch operating voltage\n"); + dev_err(mmc_dev(host->mmc), "Unable to switch operating voltage\n"); return ret; } @@ -1360,7 +1398,7 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req) if (host->use_dma) { ret = omap_hsmmc_start_dma_transfer(host, req); if (ret != 0) { - dev_dbg(mmc_dev(host->mmc), "MMC start dma failure\n"); + dev_err(mmc_dev(host->mmc), "MMC start dma failure\n"); return ret; } } @@ -1678,7 +1716,7 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev) { struct omap_mmc_platform_data *pdata; struct device_node *np = dev->of_node; - u32 bus_width; + u32 bus_width, max_freq; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) @@ -1705,6 +1743,12 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev) if (of_find_property(np, "ti,needs-special-reset", NULL)) pdata->slots[0].features |= HSMMC_HAS_UPDATED_RESET; + if (!of_property_read_u32(np, "max-frequency", &max_freq)) + pdata->max_freq = max_freq; + + if (of_find_property(np, "ti,needs-special-hs-handling", NULL)) + pdata->slots[0].features |= HSMMC_HAS_HSPE_SUPPORT; + return pdata; } #else @@ -1715,7 +1759,7 @@ static inline struct omap_mmc_platform_data } #endif -static int __devinit omap_hsmmc_probe(struct platform_device *pdev) +static int omap_hsmmc_probe(struct platform_device *pdev) { struct omap_mmc_platform_data *pdata = pdev->dev.platform_data; struct mmc_host *mmc; @@ -1725,6 +1769,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) const struct of_device_id *match; dma_cap_mask_t mask; unsigned tx_req, rx_req; + struct pinctrl *pinctrl; match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev); if (match) { @@ -1821,7 +1866,6 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) * MMC can still work without debounce clock. */ if (IS_ERR(host->dbclk)) { - dev_warn(mmc_dev(host->mmc), "Failed to get debounce clk\n"); host->dbclk = NULL; } else if (clk_prepare_enable(host->dbclk) != 0) { dev_warn(mmc_dev(host->mmc), "Failed to enable debounce clk\n"); @@ -1889,13 +1933,13 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) ret = request_irq(host->irq, omap_hsmmc_irq, 0, mmc_hostname(mmc), host); if (ret) { - dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n"); + dev_err(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ\n"); goto err_irq; } if (pdata->init != NULL) { if (pdata->init(&pdev->dev) != 0) { - dev_dbg(mmc_dev(host->mmc), + dev_err(mmc_dev(host->mmc), "Unable to configure MMC IRQs\n"); goto err_irq_cd_init; } @@ -1918,7 +1962,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, mmc_hostname(mmc), host); if (ret) { - dev_dbg(mmc_dev(host->mmc), + dev_err(mmc_dev(host->mmc), "Unable to grab MMC CD IRQ\n"); goto err_irq_cd; } @@ -1928,6 +1972,11 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev) omap_hsmmc_disable_irq(host); + pinctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (IS_ERR(pinctrl)) + dev_warn(&pdev->dev, + "pins are not configured from the driver\n"); + omap_hsmmc_protect_card(host); mmc_add_host(mmc); @@ -1986,7 +2035,7 @@ err: return ret; } -static int __devexit omap_hsmmc_remove(struct platform_device *pdev) +static int omap_hsmmc_remove(struct platform_device *pdev) { struct omap_hsmmc_host *host = platform_get_drvdata(pdev); struct resource *res; @@ -2027,6 +2076,25 @@ static int __devexit omap_hsmmc_remove(struct platform_device *pdev) } #ifdef CONFIG_PM +static int omap_hsmmc_prepare(struct device *dev) +{ + struct omap_hsmmc_host *host = dev_get_drvdata(dev); + + if (host->pdata->suspend) + return host->pdata->suspend(dev, host->slot_id); + + return 0; +} + +static void omap_hsmmc_complete(struct device *dev) +{ + struct omap_hsmmc_host *host = dev_get_drvdata(dev); + + if (host->pdata->resume) + host->pdata->resume(dev, host->slot_id); + +} + static int omap_hsmmc_suspend(struct device *dev) { int ret = 0; @@ -2040,23 +2108,10 @@ static int omap_hsmmc_suspend(struct device *dev) pm_runtime_get_sync(host->dev); host->suspended = 1; - if (host->pdata->suspend) { - ret = host->pdata->suspend(dev, host->slot_id); - if (ret) { - dev_dbg(dev, "Unable to handle MMC board" - " level suspend\n"); - host->suspended = 0; - return ret; - } - } ret = mmc_suspend_host(host->mmc); if (ret) { host->suspended = 0; - if (host->pdata->resume) { - if (host->pdata->resume(dev, host->slot_id)) - dev_dbg(dev, "Unmask interrupt failed\n"); - } goto err; } @@ -2093,12 +2148,6 @@ static int omap_hsmmc_resume(struct device *dev) if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) omap_hsmmc_conf_bus_power(host); - if (host->pdata->resume) { - ret = host->pdata->resume(dev, host->slot_id); - if (ret) - dev_dbg(dev, "Unmask interrupt failed\n"); - } - omap_hsmmc_protect_card(host); /* Notify the core to resume the host */ @@ -2114,8 +2163,10 @@ static int omap_hsmmc_resume(struct device *dev) } #else +#define omap_hsmmc_prepare NULL +#define omap_hsmmc_complete NULL #define omap_hsmmc_suspend NULL -#define omap_hsmmc_resume NULL +#define omap_hsmmc_resume NULL #endif static int omap_hsmmc_runtime_suspend(struct device *dev) @@ -2143,13 +2194,15 @@ static int omap_hsmmc_runtime_resume(struct device *dev) static struct dev_pm_ops omap_hsmmc_dev_pm_ops = { .suspend = omap_hsmmc_suspend, .resume = omap_hsmmc_resume, + .prepare = omap_hsmmc_prepare, + .complete = omap_hsmmc_complete, .runtime_suspend = omap_hsmmc_runtime_suspend, .runtime_resume = omap_hsmmc_runtime_resume, }; static struct platform_driver omap_hsmmc_driver = { .probe = omap_hsmmc_probe, - .remove = __devexit_p(omap_hsmmc_remove), + .remove = omap_hsmmc_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c index 3f9d6d577a9..2b2f65ada22 100644 --- a/drivers/mmc/host/pxamci.c +++ b/drivers/mmc/host/pxamci.c @@ -584,7 +584,7 @@ static const struct of_device_id pxa_mmc_dt_ids[] = { MODULE_DEVICE_TABLE(of, pxa_mmc_dt_ids); -static int __devinit pxamci_of_init(struct platform_device *pdev) +static int pxamci_of_init(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct pxamci_platform_data *pdata; @@ -614,7 +614,7 @@ static int __devinit pxamci_of_init(struct platform_device *pdev) return 0; } #else -static int __devinit pxamci_of_init(struct platform_device *pdev) +static int pxamci_of_init(struct platform_device *pdev) { return 0; } diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c new file mode 100644 index 00000000000..12eff6f8cab --- /dev/null +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -0,0 +1,1348 @@ +/* Realtek PCI-Express SD/MMC Card Interface driver + * + * Copyright(c) 2009 Realtek Semiconductor Corp. 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 as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Author: + * Wei WANG <wei_wang@realsil.com.cn> + * No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China + */ + +#include <linux/module.h> +#include <linux/highmem.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/sd.h> +#include <linux/mmc/card.h> +#include <linux/mfd/rtsx_pci.h> +#include <asm/unaligned.h> + +/* SD Tuning Data Structure + * Record continuous timing phase path + */ +struct timing_phase_path { + int start; + int end; + int mid; + int len; +}; + +struct realtek_pci_sdmmc { + struct platform_device *pdev; + struct rtsx_pcr *pcr; + struct mmc_host *mmc; + struct mmc_request *mrq; + + struct mutex host_mutex; + + u8 ssc_depth; + unsigned int clock; + bool vpclk; + bool double_clk; + bool eject; + bool initial_mode; + bool ddr_mode; +}; + +static inline struct device *sdmmc_dev(struct realtek_pci_sdmmc *host) +{ + return &(host->pdev->dev); +} + +static inline void sd_clear_error(struct realtek_pci_sdmmc *host) +{ + rtsx_pci_write_register(host->pcr, CARD_STOP, + SD_STOP | SD_CLR_ERR, SD_STOP | SD_CLR_ERR); +} + +#ifdef DEBUG +static void sd_print_debug_regs(struct realtek_pci_sdmmc *host) +{ + struct rtsx_pcr *pcr = host->pcr; + u16 i; + u8 *ptr; + + /* Print SD host internal registers */ + rtsx_pci_init_cmd(pcr); + for (i = 0xFDA0; i <= 0xFDAE; i++) + rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0); + for (i = 0xFD52; i <= 0xFD69; i++) + rtsx_pci_add_cmd(pcr, READ_REG_CMD, i, 0, 0); + rtsx_pci_send_cmd(pcr, 100); + + ptr = rtsx_pci_get_cmd_data(pcr); + for (i = 0xFDA0; i <= 0xFDAE; i++) + dev_dbg(sdmmc_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); + for (i = 0xFD52; i <= 0xFD69; i++) + dev_dbg(sdmmc_dev(host), "0x%04X: 0x%02x\n", i, *(ptr++)); +} +#else +#define sd_print_debug_regs(host) +#endif /* DEBUG */ + +static int sd_read_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt, + u8 *buf, int buf_len, int timeout) +{ + struct rtsx_pcr *pcr = host->pcr; + int err, i; + u8 trans_mode; + + dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD%d\n", __func__, cmd[0] - 0x40); + + if (!buf) + buf_len = 0; + + if ((cmd[0] & 0x3F) == MMC_SEND_TUNING_BLOCK) + trans_mode = SD_TM_AUTO_TUNING; + else + trans_mode = SD_TM_NORMAL_READ; + + rtsx_pci_init_cmd(pcr); + + for (i = 0; i < 5; i++) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0 + i, 0xFF, cmd[i]); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, + 0xFF, (u8)(byte_cnt >> 8)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, + SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | + SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6); + if (trans_mode != SD_TM_AUTO_TUNING) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, + 0xFF, trans_mode | SD_TRANSFER_START); + rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, + SD_TRANSFER_END, SD_TRANSFER_END); + + err = rtsx_pci_send_cmd(pcr, timeout); + if (err < 0) { + sd_print_debug_regs(host); + dev_dbg(sdmmc_dev(host), + "rtsx_pci_send_cmd fail (err = %d)\n", err); + return err; + } + + if (buf && buf_len) { + err = rtsx_pci_read_ppbuf(pcr, buf, buf_len); + if (err < 0) { + dev_dbg(sdmmc_dev(host), + "rtsx_pci_read_ppbuf fail (err = %d)\n", err); + return err; + } + } + + return 0; +} + +static int sd_write_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt, + u8 *buf, int buf_len, int timeout) +{ + struct rtsx_pcr *pcr = host->pcr; + int err, i; + u8 trans_mode; + + if (!buf) + buf_len = 0; + + if (buf && buf_len) { + err = rtsx_pci_write_ppbuf(pcr, buf, buf_len); + if (err < 0) { + dev_dbg(sdmmc_dev(host), + "rtsx_pci_write_ppbuf fail (err = %d)\n", err); + return err; + } + } + + trans_mode = cmd ? SD_TM_AUTO_WRITE_2 : SD_TM_AUTO_WRITE_3; + rtsx_pci_init_cmd(pcr); + + if (cmd) { + dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d\n", __func__, + cmd[0] - 0x40); + + for (i = 0; i < 5; i++) + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD_CMD0 + i, 0xFF, cmd[i]); + } + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, + 0xFF, (u8)(byte_cnt >> 8)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, 0xFF, 1); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, 0xFF, 0); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, + SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | + SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_6); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF, + trans_mode | SD_TRANSFER_START); + rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, + SD_TRANSFER_END, SD_TRANSFER_END); + + err = rtsx_pci_send_cmd(pcr, timeout); + if (err < 0) { + sd_print_debug_regs(host); + dev_dbg(sdmmc_dev(host), + "rtsx_pci_send_cmd fail (err = %d)\n", err); + return err; + } + + return 0; +} + +static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host, + struct mmc_command *cmd) +{ + struct rtsx_pcr *pcr = host->pcr; + u8 cmd_idx = (u8)cmd->opcode; + u32 arg = cmd->arg; + int err = 0; + int timeout = 100; + int i; + u8 *ptr; + int stat_idx = 0; + u8 rsp_type; + int rsp_len = 5; + + dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n", + __func__, cmd_idx, arg); + + /* Response type: + * R0 + * R1, R5, R6, R7 + * R1b + * R2 + * R3, R4 + */ + switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + rsp_type = SD_RSP_TYPE_R0; + rsp_len = 0; + break; + case MMC_RSP_R1: + rsp_type = SD_RSP_TYPE_R1; + break; + case MMC_RSP_R1B: + rsp_type = SD_RSP_TYPE_R1b; + break; + case MMC_RSP_R2: + rsp_type = SD_RSP_TYPE_R2; + rsp_len = 16; + break; + case MMC_RSP_R3: + rsp_type = SD_RSP_TYPE_R3; + break; + default: + dev_dbg(sdmmc_dev(host), "cmd->flag is not valid\n"); + err = -EINVAL; + goto out; + } + + if (rsp_type == SD_RSP_TYPE_R1b) + timeout = 3000; + + if (cmd->opcode == SD_SWITCH_VOLTAGE) { + err = rtsx_pci_write_register(pcr, SD_BUS_STAT, + 0xFF, SD_CLK_TOGGLE_EN); + if (err < 0) + goto out; + } + + rtsx_pci_init_cmd(pcr); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD0, 0xFF, 0x40 | cmd_idx); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD1, 0xFF, (u8)(arg >> 24)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD2, 0xFF, (u8)(arg >> 16)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD3, 0xFF, (u8)(arg >> 8)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CMD4, 0xFF, (u8)arg); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG2, 0xFF, rsp_type); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, + 0x01, PINGPONG_BUFFER); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, + 0xFF, SD_TM_CMD_RSP | SD_TRANSFER_START); + rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, + SD_TRANSFER_END | SD_STAT_IDLE, + SD_TRANSFER_END | SD_STAT_IDLE); + + if (rsp_type == SD_RSP_TYPE_R2) { + /* Read data from ping-pong buffer */ + for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++) + rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0); + stat_idx = 16; + } else if (rsp_type != SD_RSP_TYPE_R0) { + /* Read data from SD_CMDx registers */ + for (i = SD_CMD0; i <= SD_CMD4; i++) + rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0); + stat_idx = 5; + } + + rtsx_pci_add_cmd(pcr, READ_REG_CMD, SD_STAT1, 0, 0); + + err = rtsx_pci_send_cmd(pcr, timeout); + if (err < 0) { + sd_print_debug_regs(host); + sd_clear_error(host); + dev_dbg(sdmmc_dev(host), + "rtsx_pci_send_cmd error (err = %d)\n", err); + goto out; + } + + if (rsp_type == SD_RSP_TYPE_R0) { + err = 0; + goto out; + } + + /* Eliminate returned value of CHECK_REG_CMD */ + ptr = rtsx_pci_get_cmd_data(pcr) + 1; + + /* Check (Start,Transmission) bit of Response */ + if ((ptr[0] & 0xC0) != 0) { + err = -EILSEQ; + dev_dbg(sdmmc_dev(host), "Invalid response bit\n"); + goto out; + } + + /* Check CRC7 */ + if (!(rsp_type & SD_NO_CHECK_CRC7)) { + if (ptr[stat_idx] & SD_CRC7_ERR) { + err = -EILSEQ; + dev_dbg(sdmmc_dev(host), "CRC7 error\n"); + goto out; + } + } + + if (rsp_type == SD_RSP_TYPE_R2) { + for (i = 0; i < 4; i++) { + cmd->resp[i] = get_unaligned_be32(ptr + 1 + i * 4); + dev_dbg(sdmmc_dev(host), "cmd->resp[%d] = 0x%08x\n", + i, cmd->resp[i]); + } + } else { + cmd->resp[0] = get_unaligned_be32(ptr + 1); + dev_dbg(sdmmc_dev(host), "cmd->resp[0] = 0x%08x\n", + cmd->resp[0]); + } + +out: + cmd->error = err; +} + +static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq) +{ + struct rtsx_pcr *pcr = host->pcr; + struct mmc_host *mmc = host->mmc; + struct mmc_card *card = mmc->card; + struct mmc_data *data = mrq->data; + int uhs = mmc_sd_card_uhs(card); + int read = (data->flags & MMC_DATA_READ) ? 1 : 0; + u8 cfg2, trans_mode; + int err; + size_t data_len = data->blksz * data->blocks; + + if (read) { + cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | + SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0; + trans_mode = SD_TM_AUTO_READ_3; + } else { + cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 | + SD_NO_WAIT_BUSY_END | SD_NO_CHECK_CRC7 | SD_RSP_LEN_0; + trans_mode = SD_TM_AUTO_WRITE_3; + } + + if (!uhs) + cfg2 |= SD_NO_CHECK_WAIT_CRC_TO; + + rtsx_pci_init_cmd(pcr); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_L, 0xFF, 0x00); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BYTE_CNT_H, 0xFF, 0x02); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_L, + 0xFF, (u8)data->blocks); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_BLOCK_CNT_H, + 0xFF, (u8)(data->blocks >> 8)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + CARD_DATA_SOURCE, 0x01, RING_BUFFER); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, IRQSTAT0, + DMA_DONE_INT, DMA_DONE_INT); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC3, + 0xFF, (u8)(data_len >> 24)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC2, + 0xFF, (u8)(data_len >> 16)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC1, + 0xFF, (u8)(data_len >> 8)); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMATC0, 0xFF, (u8)data_len); + if (read) { + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL, + 0x03 | DMA_PACK_SIZE_MASK, + DMA_DIR_FROM_CARD | DMA_EN | DMA_512); + } else { + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, DMACTL, + 0x03 | DMA_PACK_SIZE_MASK, + DMA_DIR_TO_CARD | DMA_EN | DMA_512); + } + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_DATA_SOURCE, + 0x01, RING_BUFFER); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_TRANSFER, 0xFF, + trans_mode | SD_TRANSFER_START); + rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER, + SD_TRANSFER_END, SD_TRANSFER_END); + + rtsx_pci_send_cmd_no_wait(pcr); + + err = rtsx_pci_transfer_data(pcr, data->sg, data->sg_len, read, 10000); + if (err < 0) { + sd_clear_error(host); + return err; + } + + return 0; +} + +static inline void sd_enable_initial_mode(struct realtek_pci_sdmmc *host) +{ + rtsx_pci_write_register(host->pcr, SD_CFG1, + SD_CLK_DIVIDE_MASK, SD_CLK_DIVIDE_128); +} + +static inline void sd_disable_initial_mode(struct realtek_pci_sdmmc *host) +{ + rtsx_pci_write_register(host->pcr, SD_CFG1, + SD_CLK_DIVIDE_MASK, SD_CLK_DIVIDE_0); +} + +static void sd_normal_rw(struct realtek_pci_sdmmc *host, + struct mmc_request *mrq) +{ + struct mmc_command *cmd = mrq->cmd; + struct mmc_data *data = mrq->data; + u8 _cmd[5], *buf; + + _cmd[0] = 0x40 | (u8)cmd->opcode; + put_unaligned_be32(cmd->arg, (u32 *)(&_cmd[1])); + + buf = kzalloc(data->blksz, GFP_NOIO); + if (!buf) { + cmd->error = -ENOMEM; + return; + } + + if (data->flags & MMC_DATA_READ) { + if (host->initial_mode) + sd_disable_initial_mode(host); + + cmd->error = sd_read_data(host, _cmd, (u16)data->blksz, buf, + data->blksz, 200); + + if (host->initial_mode) + sd_enable_initial_mode(host); + + sg_copy_from_buffer(data->sg, data->sg_len, buf, data->blksz); + } else { + sg_copy_to_buffer(data->sg, data->sg_len, buf, data->blksz); + + cmd->error = sd_write_data(host, _cmd, (u16)data->blksz, buf, + data->blksz, 200); + } + + kfree(buf); +} + +static int sd_change_phase(struct realtek_pci_sdmmc *host, u8 sample_point) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + + dev_dbg(sdmmc_dev(host), "%s: sample_point = %d\n", + __func__, sample_point); + + rtsx_pci_init_cmd(pcr); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CHANGE_CLK, CHANGE_CLK); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPRX_CTL, 0x1F, sample_point); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, PHASE_NOT_RESET, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_VPCLK0_CTL, + PHASE_NOT_RESET, PHASE_NOT_RESET); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CHANGE_CLK, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1, SD_ASYNC_FIFO_NOT_RST, 0); + + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + return 0; +} + +static u8 sd_search_final_phase(struct realtek_pci_sdmmc *host, u32 phase_map) +{ + struct timing_phase_path path[MAX_PHASE + 1]; + int i, j, cont_path_cnt; + int new_block, max_len, final_path_idx; + u8 final_phase = 0xFF; + + /* Parse phase_map, take it as a bit-ring */ + cont_path_cnt = 0; + new_block = 1; + j = 0; + for (i = 0; i < MAX_PHASE + 1; i++) { + if (phase_map & (1 << i)) { + if (new_block) { + new_block = 0; + j = cont_path_cnt++; + path[j].start = i; + path[j].end = i; + } else { + path[j].end = i; + } + } else { + new_block = 1; + if (cont_path_cnt) { + /* Calculate path length and middle point */ + int idx = cont_path_cnt - 1; + path[idx].len = + path[idx].end - path[idx].start + 1; + path[idx].mid = + path[idx].start + path[idx].len / 2; + } + } + } + + if (cont_path_cnt == 0) { + dev_dbg(sdmmc_dev(host), "No continuous phase path\n"); + goto finish; + } else { + /* Calculate last continuous path length and middle point */ + int idx = cont_path_cnt - 1; + path[idx].len = path[idx].end - path[idx].start + 1; + path[idx].mid = path[idx].start + path[idx].len / 2; + } + + /* Connect the first and last continuous paths if they are adjacent */ + if (!path[0].start && (path[cont_path_cnt - 1].end == MAX_PHASE)) { + /* Using negative index */ + path[0].start = path[cont_path_cnt - 1].start - MAX_PHASE - 1; + path[0].len += path[cont_path_cnt - 1].len; + path[0].mid = path[0].start + path[0].len / 2; + /* Convert negative middle point index to positive one */ + if (path[0].mid < 0) + path[0].mid += MAX_PHASE + 1; + cont_path_cnt--; + } + + /* Choose the longest continuous phase path */ + max_len = 0; + final_phase = 0; + final_path_idx = 0; + for (i = 0; i < cont_path_cnt; i++) { + if (path[i].len > max_len) { + max_len = path[i].len; + final_phase = (u8)path[i].mid; + final_path_idx = i; + } + + dev_dbg(sdmmc_dev(host), "path[%d].start = %d\n", + i, path[i].start); + dev_dbg(sdmmc_dev(host), "path[%d].end = %d\n", + i, path[i].end); + dev_dbg(sdmmc_dev(host), "path[%d].len = %d\n", + i, path[i].len); + dev_dbg(sdmmc_dev(host), "path[%d].mid = %d\n", + i, path[i].mid); + } + +finish: + dev_dbg(sdmmc_dev(host), "Final chosen phase: %d\n", final_phase); + return final_phase; +} + +static void sd_wait_data_idle(struct realtek_pci_sdmmc *host) +{ + int err, i; + u8 val = 0; + + for (i = 0; i < 100; i++) { + err = rtsx_pci_read_register(host->pcr, SD_DATA_STATE, &val); + if (val & SD_DATA_IDLE) + return; + + udelay(100); + } +} + +static int sd_tuning_rx_cmd(struct realtek_pci_sdmmc *host, + u8 opcode, u8 sample_point) +{ + int err; + u8 cmd[5] = {0}; + + err = sd_change_phase(host, sample_point); + if (err < 0) + return err; + + cmd[0] = 0x40 | opcode; + err = sd_read_data(host, cmd, 0x40, NULL, 0, 100); + if (err < 0) { + /* Wait till SD DATA IDLE */ + sd_wait_data_idle(host); + sd_clear_error(host); + return err; + } + + return 0; +} + +static int sd_tuning_phase(struct realtek_pci_sdmmc *host, + u8 opcode, u32 *phase_map) +{ + int err, i; + u32 raw_phase_map = 0; + + for (i = MAX_PHASE; i >= 0; i--) { + err = sd_tuning_rx_cmd(host, opcode, (u8)i); + if (err == 0) + raw_phase_map |= 1 << i; + } + + if (phase_map) + *phase_map = raw_phase_map; + + return 0; +} + +static int sd_tuning_rx(struct realtek_pci_sdmmc *host, u8 opcode) +{ + int err, i; + u32 raw_phase_map[RX_TUNING_CNT] = {0}, phase_map; + u8 final_phase; + + for (i = 0; i < RX_TUNING_CNT; i++) { + err = sd_tuning_phase(host, opcode, &(raw_phase_map[i])); + if (err < 0) + return err; + + if (raw_phase_map[i] == 0) + break; + } + + phase_map = 0xFFFFFFFF; + for (i = 0; i < RX_TUNING_CNT; i++) { + dev_dbg(sdmmc_dev(host), "RX raw_phase_map[%d] = 0x%08x\n", + i, raw_phase_map[i]); + phase_map &= raw_phase_map[i]; + } + dev_dbg(sdmmc_dev(host), "RX phase_map = 0x%08x\n", phase_map); + + if (phase_map) { + final_phase = sd_search_final_phase(host, phase_map); + if (final_phase == 0xFF) + return -EINVAL; + + err = sd_change_phase(host, final_phase); + if (err < 0) + return err; + } else { + return -EINVAL; + } + + return 0; +} + +static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct realtek_pci_sdmmc *host = mmc_priv(mmc); + struct rtsx_pcr *pcr = host->pcr; + struct mmc_command *cmd = mrq->cmd; + struct mmc_data *data = mrq->data; + unsigned int data_size = 0; + + if (host->eject) { + cmd->error = -ENOMEDIUM; + goto finish; + } + + mutex_lock(&pcr->pcr_mutex); + + rtsx_pci_start_run(pcr); + + rtsx_pci_switch_clock(pcr, host->clock, host->ssc_depth, + host->initial_mode, host->double_clk, host->vpclk); + rtsx_pci_write_register(pcr, CARD_SELECT, 0x07, SD_MOD_SEL); + rtsx_pci_write_register(pcr, CARD_SHARE_MODE, + CARD_SHARE_MASK, CARD_SHARE_48_SD); + + mutex_lock(&host->host_mutex); + host->mrq = mrq; + mutex_unlock(&host->host_mutex); + + if (mrq->data) + data_size = data->blocks * data->blksz; + + if (!data_size || mmc_op_multi(cmd->opcode) || + (cmd->opcode == MMC_READ_SINGLE_BLOCK) || + (cmd->opcode == MMC_WRITE_BLOCK)) { + sd_send_cmd_get_rsp(host, cmd); + + if (!cmd->error && data_size) { + sd_rw_multi(host, mrq); + + if (mmc_op_multi(cmd->opcode) && mrq->stop) + sd_send_cmd_get_rsp(host, mrq->stop); + } + } else { + sd_normal_rw(host, mrq); + } + + if (mrq->data) { + if (cmd->error || data->error) + data->bytes_xfered = 0; + else + data->bytes_xfered = data->blocks * data->blksz; + } + + mutex_unlock(&pcr->pcr_mutex); + +finish: + if (cmd->error) + dev_dbg(sdmmc_dev(host), "cmd->error = %d\n", cmd->error); + + mutex_lock(&host->host_mutex); + host->mrq = NULL; + mutex_unlock(&host->host_mutex); + + mmc_request_done(mmc, mrq); +} + +static int sd_set_bus_width(struct realtek_pci_sdmmc *host, + unsigned char bus_width) +{ + int err = 0; + u8 width[] = { + [MMC_BUS_WIDTH_1] = SD_BUS_WIDTH_1BIT, + [MMC_BUS_WIDTH_4] = SD_BUS_WIDTH_4BIT, + [MMC_BUS_WIDTH_8] = SD_BUS_WIDTH_8BIT, + }; + + if (bus_width <= MMC_BUS_WIDTH_8) + err = rtsx_pci_write_register(host->pcr, SD_CFG1, + 0x03, width[bus_width]); + + return err; +} + +static int sd_power_on(struct realtek_pci_sdmmc *host) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + + rtsx_pci_init_cmd(pcr); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_SHARE_MODE, + CARD_SHARE_MASK, CARD_SHARE_48_SD); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, + SD_CLK_EN, SD_CLK_EN); + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + err = rtsx_pci_card_pull_ctl_enable(pcr, RTSX_SD_CARD); + if (err < 0) + return err; + + err = rtsx_pci_card_power_on(pcr, RTSX_SD_CARD); + if (err < 0) + return err; + + err = rtsx_pci_write_register(pcr, CARD_OE, SD_OUTPUT_EN, SD_OUTPUT_EN); + if (err < 0) + return err; + + return 0; +} + +static int sd_power_off(struct realtek_pci_sdmmc *host) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + + rtsx_pci_init_cmd(pcr); + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_EN, SD_CLK_EN, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_OE, SD_OUTPUT_EN, 0); + + err = rtsx_pci_send_cmd(pcr, 100); + if (err < 0) + return err; + + err = rtsx_pci_card_power_off(pcr, RTSX_SD_CARD); + if (err < 0) + return err; + + return rtsx_pci_card_pull_ctl_disable(pcr, RTSX_SD_CARD); +} + +static int sd_set_power_mode(struct realtek_pci_sdmmc *host, + unsigned char power_mode) +{ + int err; + + if (power_mode == MMC_POWER_OFF) + err = sd_power_off(host); + else + err = sd_power_on(host); + + return err; +} + +static int sd_set_timing(struct realtek_pci_sdmmc *host, + unsigned char timing, bool *ddr_mode) +{ + struct rtsx_pcr *pcr = host->pcr; + int err = 0; + + *ddr_mode = false; + + rtsx_pci_init_cmd(pcr); + + switch (timing) { + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_UHS_SDR50: + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1, + 0x0C | SD_ASYNC_FIFO_NOT_RST, + SD_30_MODE | SD_ASYNC_FIFO_NOT_RST); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, + CLK_LOW_FREQ, CLK_LOW_FREQ); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF, + CRC_VAR_CLK0 | SD30_FIX_CLK | SAMPLE_VAR_CLK1); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0); + break; + + case MMC_TIMING_UHS_DDR50: + *ddr_mode = true; + + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1, + 0x0C | SD_ASYNC_FIFO_NOT_RST, + SD_DDR_MODE | SD_ASYNC_FIFO_NOT_RST); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, + CLK_LOW_FREQ, CLK_LOW_FREQ); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF, + CRC_VAR_CLK0 | SD30_FIX_CLK | SAMPLE_VAR_CLK1); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_PUSH_POINT_CTL, + DDR_VAR_TX_CMD_DAT, DDR_VAR_TX_CMD_DAT); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL, + DDR_VAR_RX_DAT | DDR_VAR_RX_CMD, + DDR_VAR_RX_DAT | DDR_VAR_RX_CMD); + break; + + case MMC_TIMING_MMC_HS: + case MMC_TIMING_SD_HS: + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_CFG1, + 0x0C, SD_20_MODE); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, + CLK_LOW_FREQ, CLK_LOW_FREQ); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF, + CRC_FIX_CLK | SD30_VAR_CLK0 | SAMPLE_VAR_CLK1); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_PUSH_POINT_CTL, + SD20_TX_SEL_MASK, SD20_TX_14_AHEAD); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL, + SD20_RX_SEL_MASK, SD20_RX_14_DELAY); + break; + + default: + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD_CFG1, 0x0C, SD_20_MODE); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, + CLK_LOW_FREQ, CLK_LOW_FREQ); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CARD_CLK_SOURCE, 0xFF, + CRC_FIX_CLK | SD30_VAR_CLK0 | SAMPLE_VAR_CLK1); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, CLK_CTL, CLK_LOW_FREQ, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, + SD_PUSH_POINT_CTL, 0xFF, 0); + rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, SD_SAMPLE_POINT_CTL, + SD20_RX_SEL_MASK, SD20_RX_POS_EDGE); + break; + } + + err = rtsx_pci_send_cmd(pcr, 100); + + return err; +} + +static void sdmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct realtek_pci_sdmmc *host = mmc_priv(mmc); + struct rtsx_pcr *pcr = host->pcr; + + if (host->eject) + return; + + mutex_lock(&pcr->pcr_mutex); + + rtsx_pci_start_run(pcr); + + sd_set_bus_width(host, ios->bus_width); + sd_set_power_mode(host, ios->power_mode); + sd_set_timing(host, ios->timing, &host->ddr_mode); + + host->vpclk = false; + host->double_clk = true; + + switch (ios->timing) { + case MMC_TIMING_UHS_SDR104: + case MMC_TIMING_UHS_SDR50: + host->ssc_depth = RTSX_SSC_DEPTH_2M; + host->vpclk = true; + host->double_clk = false; + break; + case MMC_TIMING_UHS_DDR50: + case MMC_TIMING_UHS_SDR25: + host->ssc_depth = RTSX_SSC_DEPTH_1M; + break; + default: + host->ssc_depth = RTSX_SSC_DEPTH_500K; + break; + } + + host->initial_mode = (ios->clock <= 1000000) ? true : false; + + host->clock = ios->clock; + rtsx_pci_switch_clock(pcr, ios->clock, host->ssc_depth, + host->initial_mode, host->double_clk, host->vpclk); + + mutex_unlock(&pcr->pcr_mutex); +} + +static int sdmmc_get_ro(struct mmc_host *mmc) +{ + struct realtek_pci_sdmmc *host = mmc_priv(mmc); + struct rtsx_pcr *pcr = host->pcr; + int ro = 0; + u32 val; + + if (host->eject) + return -ENOMEDIUM; + + mutex_lock(&pcr->pcr_mutex); + + rtsx_pci_start_run(pcr); + + /* Check SD mechanical write-protect switch */ + val = rtsx_pci_readl(pcr, RTSX_BIPR); + dev_dbg(sdmmc_dev(host), "%s: RTSX_BIPR = 0x%08x\n", __func__, val); + if (val & SD_WRITE_PROTECT) + ro = 1; + + mutex_unlock(&pcr->pcr_mutex); + + return ro; +} + +static int sdmmc_get_cd(struct mmc_host *mmc) +{ + struct realtek_pci_sdmmc *host = mmc_priv(mmc); + struct rtsx_pcr *pcr = host->pcr; + int cd = 0; + u32 val; + + if (host->eject) + return -ENOMEDIUM; + + mutex_lock(&pcr->pcr_mutex); + + rtsx_pci_start_run(pcr); + + /* Check SD card detect */ + val = rtsx_pci_card_exist(pcr); + dev_dbg(sdmmc_dev(host), "%s: RTSX_BIPR = 0x%08x\n", __func__, val); + if (val & SD_EXIST) + cd = 1; + + mutex_unlock(&pcr->pcr_mutex); + + return cd; +} + +static int sd_wait_voltage_stable_1(struct realtek_pci_sdmmc *host) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + u8 stat; + + /* Reference to Signal Voltage Switch Sequence in SD spec. + * Wait for a period of time so that the card can drive SD_CMD and + * SD_DAT[3:0] to low after sending back CMD11 response. + */ + mdelay(1); + + /* SD_CMD, SD_DAT[3:0] should be driven to low by card; + * If either one of SD_CMD,SD_DAT[3:0] is not low, + * abort the voltage switch sequence; + */ + err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat); + if (err < 0) + return err; + + if (stat & (SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | + SD_DAT1_STATUS | SD_DAT0_STATUS)) + return -EINVAL; + + /* Stop toggle SD clock */ + err = rtsx_pci_write_register(pcr, SD_BUS_STAT, + 0xFF, SD_CLK_FORCE_STOP); + if (err < 0) + return err; + + return 0; +} + +static int sd_wait_voltage_stable_2(struct realtek_pci_sdmmc *host) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + u8 stat, mask, val; + + /* Wait 1.8V output of voltage regulator in card stable */ + msleep(50); + + /* Toggle SD clock again */ + err = rtsx_pci_write_register(pcr, SD_BUS_STAT, 0xFF, SD_CLK_TOGGLE_EN); + if (err < 0) + return err; + + /* Wait for a period of time so that the card can drive + * SD_DAT[3:0] to high at 1.8V + */ + msleep(20); + + /* SD_CMD, SD_DAT[3:0] should be pulled high by host */ + err = rtsx_pci_read_register(pcr, SD_BUS_STAT, &stat); + if (err < 0) + return err; + + mask = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | + SD_DAT1_STATUS | SD_DAT0_STATUS; + val = SD_CMD_STATUS | SD_DAT3_STATUS | SD_DAT2_STATUS | + SD_DAT1_STATUS | SD_DAT0_STATUS; + if ((stat & mask) != val) { + dev_dbg(sdmmc_dev(host), + "%s: SD_BUS_STAT = 0x%x\n", __func__, stat); + rtsx_pci_write_register(pcr, SD_BUS_STAT, + SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); + rtsx_pci_write_register(pcr, CARD_CLK_EN, 0xFF, 0); + return -EINVAL; + } + + return 0; +} + +static int sd_change_bank_voltage(struct realtek_pci_sdmmc *host, u8 voltage) +{ + struct rtsx_pcr *pcr = host->pcr; + int err; + + if (voltage == SD_IO_3V3) { + err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4FC0 | 0x24); + if (err < 0) + return err; + } else if (voltage == SD_IO_1V8) { + err = rtsx_pci_write_phy_register(pcr, 0x08, 0x4C40 | 0x24); + if (err < 0) + return err; + } else { + return -EINVAL; + } + + return 0; +} + +static int sdmmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct realtek_pci_sdmmc *host = mmc_priv(mmc); + struct rtsx_pcr *pcr = host->pcr; + int err = 0; + u8 voltage; + + dev_dbg(sdmmc_dev(host), "%s: signal_voltage = %d\n", + __func__, ios->signal_voltage); + + if (host->eject) + return -ENOMEDIUM; + + mutex_lock(&pcr->pcr_mutex); + + rtsx_pci_start_run(pcr); + + if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) + voltage = SD_IO_3V3; + else + voltage = SD_IO_1V8; + + if (voltage == SD_IO_1V8) { + err = rtsx_pci_write_register(pcr, + SD30_DRIVE_SEL, 0x07, DRIVER_TYPE_B); + if (err < 0) + goto out; + + err = sd_wait_voltage_stable_1(host); + if (err < 0) + goto out; + } + + err = sd_change_bank_voltage(host, voltage); + if (err < 0) + goto out; + + if (voltage == SD_IO_1V8) { + err = sd_wait_voltage_stable_2(host); + if (err < 0) + goto out; + } + + /* Stop toggle SD clock in idle */ + err = rtsx_pci_write_register(pcr, SD_BUS_STAT, + SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0); + +out: + mutex_unlock(&pcr->pcr_mutex); + + return err; +} + +static int sdmmc_execute_tuning(struct mmc_host *mmc, u32 opcode) +{ + struct realtek_pci_sdmmc *host = mmc_priv(mmc); + struct rtsx_pcr *pcr = host->pcr; + int err = 0; + + if (host->eject) + return -ENOMEDIUM; + + mutex_lock(&pcr->pcr_mutex); + + rtsx_pci_start_run(pcr); + + if (!host->ddr_mode) + err = sd_tuning_rx(host, MMC_SEND_TUNING_BLOCK); + + mutex_unlock(&pcr->pcr_mutex); + + return err; +} + +static const struct mmc_host_ops realtek_pci_sdmmc_ops = { + .request = sdmmc_request, + .set_ios = sdmmc_set_ios, + .get_ro = sdmmc_get_ro, + .get_cd = sdmmc_get_cd, + .start_signal_voltage_switch = sdmmc_switch_voltage, + .execute_tuning = sdmmc_execute_tuning, +}; + +#ifdef CONFIG_PM +static int rtsx_pci_sdmmc_suspend(struct platform_device *pdev, + pm_message_t state) +{ + struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); + struct mmc_host *mmc = host->mmc; + int err; + + dev_dbg(sdmmc_dev(host), "--> %s\n", __func__); + + err = mmc_suspend_host(mmc); + if (err) + return err; + + return 0; +} + +static int rtsx_pci_sdmmc_resume(struct platform_device *pdev) +{ + struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); + struct mmc_host *mmc = host->mmc; + + dev_dbg(sdmmc_dev(host), "--> %s\n", __func__); + + return mmc_resume_host(mmc); +} +#else /* CONFIG_PM */ +#define rtsx_pci_sdmmc_suspend NULL +#define rtsx_pci_sdmmc_resume NULL +#endif /* CONFIG_PM */ + +static void init_extra_caps(struct realtek_pci_sdmmc *host) +{ + struct mmc_host *mmc = host->mmc; + struct rtsx_pcr *pcr = host->pcr; + + dev_dbg(sdmmc_dev(host), "pcr->extra_caps = 0x%x\n", pcr->extra_caps); + + if (pcr->extra_caps & EXTRA_CAPS_SD_SDR50) + mmc->caps |= MMC_CAP_UHS_SDR50; + if (pcr->extra_caps & EXTRA_CAPS_SD_SDR104) + mmc->caps |= MMC_CAP_UHS_SDR104; + if (pcr->extra_caps & EXTRA_CAPS_SD_DDR50) + mmc->caps |= MMC_CAP_UHS_DDR50; + if (pcr->extra_caps & EXTRA_CAPS_MMC_HSDDR) + mmc->caps |= MMC_CAP_1_8V_DDR; + if (pcr->extra_caps & EXTRA_CAPS_MMC_8BIT) + mmc->caps |= MMC_CAP_8_BIT_DATA; +} + +static void realtek_init_host(struct realtek_pci_sdmmc *host) +{ + struct mmc_host *mmc = host->mmc; + + mmc->f_min = 250000; + mmc->f_max = 208000000; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | + MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST | + MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25; + mmc->max_current_330 = 400; + mmc->max_current_180 = 800; + mmc->ops = &realtek_pci_sdmmc_ops; + + init_extra_caps(host); + + mmc->max_segs = 256; + mmc->max_seg_size = 65536; + mmc->max_blk_size = 512; + mmc->max_blk_count = 65535; + mmc->max_req_size = 524288; +} + +static void rtsx_pci_sdmmc_card_event(struct platform_device *pdev) +{ + struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); + + mmc_detect_change(host->mmc, 0); +} + +static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct realtek_pci_sdmmc *host; + struct rtsx_pcr *pcr; + struct pcr_handle *handle = pdev->dev.platform_data; + + if (!handle) + return -ENXIO; + + pcr = handle->pcr; + if (!pcr) + return -ENXIO; + + dev_dbg(&(pdev->dev), ": Realtek PCI-E SDMMC controller found\n"); + + mmc = mmc_alloc_host(sizeof(*host), &pdev->dev); + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); + host->pcr = pcr; + host->mmc = mmc; + host->pdev = pdev; + platform_set_drvdata(pdev, host); + pcr->slots[RTSX_SD_CARD].p_dev = pdev; + pcr->slots[RTSX_SD_CARD].card_event = rtsx_pci_sdmmc_card_event; + + mutex_init(&host->host_mutex); + + realtek_init_host(host); + + mmc_add_host(mmc); + + return 0; +} + +static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev) +{ + struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); + struct rtsx_pcr *pcr; + struct mmc_host *mmc; + + if (!host) + return 0; + + pcr = host->pcr; + pcr->slots[RTSX_SD_CARD].p_dev = NULL; + pcr->slots[RTSX_SD_CARD].card_event = NULL; + mmc = host->mmc; + host->eject = true; + + mutex_lock(&host->host_mutex); + if (host->mrq) { + dev_dbg(&(pdev->dev), + "%s: Controller removed during transfer\n", + mmc_hostname(mmc)); + + rtsx_pci_complete_unfinished_transfer(pcr); + + host->mrq->cmd->error = -ENOMEDIUM; + if (host->mrq->stop) + host->mrq->stop->error = -ENOMEDIUM; + mmc_request_done(mmc, host->mrq); + } + mutex_unlock(&host->host_mutex); + + mmc_remove_host(mmc); + mmc_free_host(mmc); + + platform_set_drvdata(pdev, NULL); + + dev_dbg(&(pdev->dev), + ": Realtek PCI-E SDMMC controller has been removed\n"); + + return 0; +} + +static struct platform_device_id rtsx_pci_sdmmc_ids[] = { + { + .name = DRV_NAME_RTSX_PCI_SDMMC, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, rtsx_pci_sdmmc_ids); + +static struct platform_driver rtsx_pci_sdmmc_driver = { + .probe = rtsx_pci_sdmmc_drv_probe, + .remove = rtsx_pci_sdmmc_drv_remove, + .id_table = rtsx_pci_sdmmc_ids, + .suspend = rtsx_pci_sdmmc_suspend, + .resume = rtsx_pci_sdmmc_resume, + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME_RTSX_PCI_SDMMC, + }, +}; +module_platform_driver(rtsx_pci_sdmmc_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Wei WANG <wei_wang@realsil.com.cn>"); +MODULE_DESCRIPTION("Realtek PCI-E SD/MMC Card Host Driver"); diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 4638ddab97b..63fb265e0da 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -1540,7 +1540,7 @@ static inline void s3cmci_debugfs_remove(struct s3cmci_host *host) { } #endif /* CONFIG_DEBUG_FS */ -static int __devinit s3cmci_probe(struct platform_device *pdev) +static int s3cmci_probe(struct platform_device *pdev) { struct s3cmci_host *host; struct mmc_host *mmc; @@ -1819,7 +1819,7 @@ static void s3cmci_shutdown(struct platform_device *pdev) clk_disable(host->clk); } -static int __devexit s3cmci_remove(struct platform_device *pdev) +static int s3cmci_remove(struct platform_device *pdev) { struct mmc_host *mmc = platform_get_drvdata(pdev); struct s3cmci_host *host = mmc_priv(mmc); @@ -1906,7 +1906,7 @@ static struct platform_driver s3cmci_driver = { }, .id_table = s3cmci_driver_ids, .probe = s3cmci_probe, - .remove = __devexit_p(s3cmci_remove), + .remove = s3cmci_remove, .shutdown = s3cmci_shutdown, }; diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c new file mode 100644 index 00000000000..12b0a78497f --- /dev/null +++ b/drivers/mmc/host/sdhci-acpi.c @@ -0,0 +1,312 @@ +/* + * Secure Digital Host Controller Interface ACPI driver. + * + * Copyright (c) 2012, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/init.h> +#include <linux/export.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/compiler.h> +#include <linux/stddef.h> +#include <linux/bitops.h> +#include <linux/types.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/acpi.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> + +#include <linux/mmc/host.h> +#include <linux/mmc/pm.h> +#include <linux/mmc/sdhci.h> + +#include "sdhci.h" + +enum { + SDHCI_ACPI_SD_CD = BIT(0), + SDHCI_ACPI_RUNTIME_PM = BIT(1), +}; + +struct sdhci_acpi_chip { + const struct sdhci_ops *ops; + unsigned int quirks; + unsigned int quirks2; + unsigned long caps; + unsigned int caps2; + mmc_pm_flag_t pm_caps; +}; + +struct sdhci_acpi_slot { + const struct sdhci_acpi_chip *chip; + unsigned int quirks; + unsigned int quirks2; + unsigned long caps; + unsigned int caps2; + mmc_pm_flag_t pm_caps; + unsigned int flags; +}; + +struct sdhci_acpi_host { + struct sdhci_host *host; + const struct sdhci_acpi_slot *slot; + struct platform_device *pdev; + bool use_runtime_pm; +}; + +static inline bool sdhci_acpi_flag(struct sdhci_acpi_host *c, unsigned int flag) +{ + return c->slot && (c->slot->flags & flag); +} + +static int sdhci_acpi_enable_dma(struct sdhci_host *host) +{ + return 0; +} + +static const struct sdhci_ops sdhci_acpi_ops_dflt = { + .enable_dma = sdhci_acpi_enable_dma, +}; + +static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { + .quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON, + .caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD, + .flags = SDHCI_ACPI_RUNTIME_PM, + .pm_caps = MMC_PM_KEEP_POWER, +}; + +static const struct acpi_device_id sdhci_acpi_ids[] = { + { "INT33C6", (kernel_ulong_t)&sdhci_acpi_slot_int_sdio }, + { "PNP0D40" }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, sdhci_acpi_ids); + +static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(const char *hid) +{ + const struct acpi_device_id *id; + + for (id = sdhci_acpi_ids; id->id[0]; id++) + if (!strcmp(id->id, hid)) + return (const struct sdhci_acpi_slot *)id->driver_data; + return NULL; +} + +static int __devinit sdhci_acpi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + acpi_handle handle = ACPI_HANDLE(dev); + struct acpi_device *device; + struct sdhci_acpi_host *c; + struct sdhci_host *host; + struct resource *iomem; + resource_size_t len; + const char *hid; + int err; + + if (acpi_bus_get_device(handle, &device)) + return -ENODEV; + + if (acpi_bus_get_status(device) || !device->status.present) + return -ENODEV; + + hid = acpi_device_hid(device); + + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!iomem) + return -ENOMEM; + + len = resource_size(iomem); + if (len < 0x100) + dev_err(dev, "Invalid iomem size!\n"); + + if (!devm_request_mem_region(dev, iomem->start, len, dev_name(dev))) + return -ENOMEM; + + host = sdhci_alloc_host(dev, sizeof(struct sdhci_acpi_host)); + if (IS_ERR(host)) + return PTR_ERR(host); + + c = sdhci_priv(host); + c->host = host; + c->slot = sdhci_acpi_get_slot(hid); + c->pdev = pdev; + c->use_runtime_pm = sdhci_acpi_flag(c, SDHCI_ACPI_RUNTIME_PM); + + platform_set_drvdata(pdev, c); + + host->hw_name = "ACPI"; + host->ops = &sdhci_acpi_ops_dflt; + host->irq = platform_get_irq(pdev, 0); + + host->ioaddr = devm_ioremap_nocache(dev, iomem->start, + resource_size(iomem)); + if (host->ioaddr == NULL) { + err = -ENOMEM; + goto err_free; + } + + if (!dev->dma_mask) { + u64 dma_mask; + + if (sdhci_readl(host, SDHCI_CAPABILITIES) & SDHCI_CAN_64BIT) { + /* 64-bit DMA is not supported at present */ + dma_mask = DMA_BIT_MASK(32); + } else { + dma_mask = DMA_BIT_MASK(32); + } + + dev->dma_mask = &dev->coherent_dma_mask; + dev->coherent_dma_mask = dma_mask; + } + + if (c->slot) { + if (c->slot->chip) { + host->ops = c->slot->chip->ops; + host->quirks |= c->slot->chip->quirks; + host->quirks2 |= c->slot->chip->quirks2; + host->mmc->caps |= c->slot->chip->caps; + host->mmc->caps2 |= c->slot->chip->caps2; + host->mmc->pm_caps |= c->slot->chip->pm_caps; + } + host->quirks |= c->slot->quirks; + host->quirks2 |= c->slot->quirks2; + host->mmc->caps |= c->slot->caps; + host->mmc->caps2 |= c->slot->caps2; + host->mmc->pm_caps |= c->slot->pm_caps; + } + + err = sdhci_add_host(host); + if (err) + goto err_free; + + if (c->use_runtime_pm) { + pm_suspend_ignore_children(dev, 1); + pm_runtime_set_autosuspend_delay(dev, 50); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + } + + return 0; + +err_free: + platform_set_drvdata(pdev, NULL); + sdhci_free_host(c->host); + return err; +} + +static int __devexit sdhci_acpi_remove(struct platform_device *pdev) +{ + struct sdhci_acpi_host *c = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int dead; + + if (c->use_runtime_pm) { + pm_runtime_get_sync(dev); + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + } + + dead = (sdhci_readl(c->host, SDHCI_INT_STATUS) == ~0); + sdhci_remove_host(c->host, dead); + platform_set_drvdata(pdev, NULL); + sdhci_free_host(c->host); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP + +static int sdhci_acpi_suspend(struct device *dev) +{ + struct sdhci_acpi_host *c = dev_get_drvdata(dev); + + return sdhci_suspend_host(c->host); +} + +static int sdhci_acpi_resume(struct device *dev) +{ + struct sdhci_acpi_host *c = dev_get_drvdata(dev); + + return sdhci_resume_host(c->host); +} + +#else + +#define sdhci_acpi_suspend NULL +#define sdhci_acpi_resume NULL + +#endif + +#ifdef CONFIG_PM_RUNTIME + +static int sdhci_acpi_runtime_suspend(struct device *dev) +{ + struct sdhci_acpi_host *c = dev_get_drvdata(dev); + + return sdhci_runtime_suspend_host(c->host); +} + +static int sdhci_acpi_runtime_resume(struct device *dev) +{ + struct sdhci_acpi_host *c = dev_get_drvdata(dev); + + return sdhci_runtime_resume_host(c->host); +} + +static int sdhci_acpi_runtime_idle(struct device *dev) +{ + return 0; +} + +#else + +#define sdhci_acpi_runtime_suspend NULL +#define sdhci_acpi_runtime_resume NULL +#define sdhci_acpi_runtime_idle NULL + +#endif + +static const struct dev_pm_ops sdhci_acpi_pm_ops = { + .suspend = sdhci_acpi_suspend, + .resume = sdhci_acpi_resume, + .runtime_suspend = sdhci_acpi_runtime_suspend, + .runtime_resume = sdhci_acpi_runtime_resume, + .runtime_idle = sdhci_acpi_runtime_idle, +}; + +static struct platform_driver sdhci_acpi_driver = { + .driver = { + .name = "sdhci-acpi", + .owner = THIS_MODULE, + .acpi_match_table = sdhci_acpi_ids, + .pm = &sdhci_acpi_pm_ops, + }, + .probe = sdhci_acpi_probe, + .remove = __devexit_p(sdhci_acpi_remove), +}; + +module_platform_driver(sdhci_acpi_driver); + +MODULE_DESCRIPTION("Secure Digital Host Controller Interface ACPI driver"); +MODULE_AUTHOR("Adrian Hunter"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmc/host/sdhci-cns3xxx.c b/drivers/mmc/host/sdhci-cns3xxx.c index 28a870804f6..30bfdc4ae52 100644 --- a/drivers/mmc/host/sdhci-cns3xxx.c +++ b/drivers/mmc/host/sdhci-cns3xxx.c @@ -95,12 +95,12 @@ static struct sdhci_pltfm_data sdhci_cns3xxx_pdata = { SDHCI_QUIRK_NONSTANDARD_CLOCK, }; -static int __devinit sdhci_cns3xxx_probe(struct platform_device *pdev) +static int sdhci_cns3xxx_probe(struct platform_device *pdev) { return sdhci_pltfm_register(pdev, &sdhci_cns3xxx_pdata); } -static int __devexit sdhci_cns3xxx_remove(struct platform_device *pdev) +static int sdhci_cns3xxx_remove(struct platform_device *pdev) { return sdhci_pltfm_unregister(pdev); } @@ -112,7 +112,7 @@ static struct platform_driver sdhci_cns3xxx_driver = { .pm = SDHCI_PLTFM_PMOPS, }, .probe = sdhci_cns3xxx_probe, - .remove = __devexit_p(sdhci_cns3xxx_remove), + .remove = sdhci_cns3xxx_remove, }; module_platform_driver(sdhci_cns3xxx_driver); diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 8fd50a21103..169fab91778 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -19,20 +19,30 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include <linux/err.h> -#include <linux/io.h> #include <linux/clk.h> #include <linux/err.h> -#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/io.h> #include <linux/mmc/host.h> +#include <linux/module.h> #include <linux/of.h> +#include <linux/of_gpio.h> #include "sdhci-pltfm.h" struct sdhci_dove_priv { struct clk *clk; + int gpio_cd; }; +static irqreturn_t sdhci_dove_carddetect_irq(int irq, void *data) +{ + struct sdhci_host *host = data; + + tasklet_schedule(&host->card_tasklet); + return IRQ_HANDLED; +} + static u16 sdhci_dove_readw(struct sdhci_host *host, int reg) { u16 ret; @@ -50,16 +60,25 @@ static u16 sdhci_dove_readw(struct sdhci_host *host, int reg) static u32 sdhci_dove_readl(struct sdhci_host *host, int reg) { + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_dove_priv *priv = pltfm_host->priv; u32 ret; + ret = readl(host->ioaddr + reg); + switch (reg) { case SDHCI_CAPABILITIES: - ret = readl(host->ioaddr + reg); /* Mask the support for 3.0V */ ret &= ~SDHCI_CAN_VDD_300; break; - default: - ret = readl(host->ioaddr + reg); + case SDHCI_PRESENT_STATE: + if (gpio_is_valid(priv->gpio_cd)) { + if (gpio_get_value(priv->gpio_cd) == 0) + ret |= SDHCI_CARD_PRESENT; + else + ret &= ~SDHCI_CARD_PRESENT; + } + break; } return ret; } @@ -78,7 +97,7 @@ static struct sdhci_pltfm_data sdhci_dove_pdata = { SDHCI_QUIRK_NO_HISPD_BIT, }; -static int __devinit sdhci_dove_probe(struct platform_device *pdev) +static int sdhci_dove_probe(struct platform_device *pdev) { struct sdhci_host *host; struct sdhci_pltfm_host *pltfm_host; @@ -92,29 +111,74 @@ static int __devinit sdhci_dove_probe(struct platform_device *pdev) return -ENOMEM; } - priv->clk = clk_get(&pdev->dev, NULL); - if (!IS_ERR(priv->clk)) - clk_prepare_enable(priv->clk); + priv->clk = devm_clk_get(&pdev->dev, NULL); - ret = sdhci_pltfm_register(pdev, &sdhci_dove_pdata); - if (ret) - goto sdhci_dove_register_fail; + if (pdev->dev.of_node) { + priv->gpio_cd = of_get_named_gpio(pdev->dev.of_node, + "cd-gpios", 0); + } else { + priv->gpio_cd = -EINVAL; + } + + if (gpio_is_valid(priv->gpio_cd)) { + ret = gpio_request(priv->gpio_cd, "sdhci-cd"); + if (ret) { + dev_err(&pdev->dev, "card detect gpio request failed: %d\n", + ret); + return ret; + } + gpio_direction_input(priv->gpio_cd); + } + + host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata); + if (IS_ERR(host)) { + ret = PTR_ERR(host); + goto err_sdhci_pltfm_init; + } - host = platform_get_drvdata(pdev); pltfm_host = sdhci_priv(host); pltfm_host->priv = priv; + if (!IS_ERR(priv->clk)) + clk_prepare_enable(priv->clk); + + sdhci_get_of_property(pdev); + + ret = sdhci_add_host(host); + if (ret) + goto err_sdhci_add; + + /* + * We must request the IRQ after sdhci_add_host(), as the tasklet only + * gets setup in sdhci_add_host() and we oops. + */ + if (gpio_is_valid(priv->gpio_cd)) { + ret = request_irq(gpio_to_irq(priv->gpio_cd), + sdhci_dove_carddetect_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + mmc_hostname(host->mmc), host); + if (ret) { + dev_err(&pdev->dev, "card detect irq request failed: %d\n", + ret); + goto err_request_irq; + } + } + return 0; -sdhci_dove_register_fail: - if (!IS_ERR(priv->clk)) { +err_request_irq: + sdhci_remove_host(host, 0); +err_sdhci_add: + if (!IS_ERR(priv->clk)) clk_disable_unprepare(priv->clk); - clk_put(priv->clk); - } + sdhci_pltfm_free(pdev); +err_sdhci_pltfm_init: + if (gpio_is_valid(priv->gpio_cd)) + gpio_free(priv->gpio_cd); return ret; } -static int __devexit sdhci_dove_remove(struct platform_device *pdev) +static int sdhci_dove_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -122,14 +186,18 @@ static int __devexit sdhci_dove_remove(struct platform_device *pdev) sdhci_pltfm_unregister(pdev); - if (!IS_ERR(priv->clk)) { - clk_disable_unprepare(priv->clk); - clk_put(priv->clk); + if (gpio_is_valid(priv->gpio_cd)) { + free_irq(gpio_to_irq(priv->gpio_cd), host); + gpio_free(priv->gpio_cd); } + + if (!IS_ERR(priv->clk)) + clk_disable_unprepare(priv->clk); + return 0; } -static const struct of_device_id sdhci_dove_of_match_table[] __devinitdata = { +static const struct of_device_id sdhci_dove_of_match_table[] = { { .compatible = "marvell,dove-sdhci", }, {} }; @@ -143,7 +211,7 @@ static struct platform_driver sdhci_dove_driver = { .of_match_table = of_match_ptr(sdhci_dove_of_match_table), }, .probe = sdhci_dove_probe, - .remove = __devexit_p(sdhci_dove_remove), + .remove = sdhci_dove_remove, }; module_platform_driver(sdhci_dove_driver); diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c index effc2acfe77..e07df812ff1 100644 --- a/drivers/mmc/host/sdhci-esdhc-imx.c +++ b/drivers/mmc/host/sdhci-esdhc-imx.c @@ -403,7 +403,7 @@ static irqreturn_t cd_irq(int irq, void *data) }; #ifdef CONFIG_OF -static int __devinit +static int sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, struct esdhc_platform_data *boarddata) { @@ -440,7 +440,7 @@ sdhci_esdhc_imx_probe_dt(struct platform_device *pdev, } #endif -static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev) +static int sdhci_esdhc_imx_probe(struct platform_device *pdev) { const struct of_device_id *of_id = of_match_device(imx_esdhc_dt_ids, &pdev->dev); @@ -456,10 +456,10 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev) pltfm_host = sdhci_priv(host); - imx_data = kzalloc(sizeof(struct pltfm_imx_data), GFP_KERNEL); + imx_data = devm_kzalloc(&pdev->dev, sizeof(*imx_data), GFP_KERNEL); if (!imx_data) { err = -ENOMEM; - goto err_imx_data; + goto free_sdhci; } if (of_id) @@ -470,19 +470,19 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev) imx_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); if (IS_ERR(imx_data->clk_ipg)) { err = PTR_ERR(imx_data->clk_ipg); - goto err_clk_get; + goto free_sdhci; } imx_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb"); if (IS_ERR(imx_data->clk_ahb)) { err = PTR_ERR(imx_data->clk_ahb); - goto err_clk_get; + goto free_sdhci; } imx_data->clk_per = devm_clk_get(&pdev->dev, "per"); if (IS_ERR(imx_data->clk_per)) { err = PTR_ERR(imx_data->clk_per); - goto err_clk_get; + goto free_sdhci; } pltfm_host->clk = imx_data->clk_per; @@ -494,7 +494,7 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev) imx_data->pinctrl = devm_pinctrl_get_select_default(&pdev->dev); if (IS_ERR(imx_data->pinctrl)) { err = PTR_ERR(imx_data->pinctrl); - goto pin_err; + goto disable_clk; } host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; @@ -519,7 +519,7 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev) if (!host->mmc->parent->platform_data) { dev_err(mmc_dev(host->mmc), "no board data!\n"); err = -EINVAL; - goto no_board_data; + goto disable_clk; } imx_data->boarddata = *((struct esdhc_platform_data *) host->mmc->parent->platform_data); @@ -527,7 +527,8 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev) /* write_protect */ if (boarddata->wp_type == ESDHC_WP_GPIO) { - err = gpio_request_one(boarddata->wp_gpio, GPIOF_IN, "ESDHC_WP"); + err = devm_gpio_request_one(&pdev->dev, boarddata->wp_gpio, + GPIOF_IN, "ESDHC_WP"); if (err) { dev_warn(mmc_dev(host->mmc), "no write-protect pin available!\n"); @@ -543,19 +544,21 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev) switch (boarddata->cd_type) { case ESDHC_CD_GPIO: - err = gpio_request_one(boarddata->cd_gpio, GPIOF_IN, "ESDHC_CD"); + err = devm_gpio_request_one(&pdev->dev, boarddata->cd_gpio, + GPIOF_IN, "ESDHC_CD"); if (err) { dev_err(mmc_dev(host->mmc), "no card-detect pin available!\n"); - goto no_card_detect_pin; + goto disable_clk; } - err = request_irq(gpio_to_irq(boarddata->cd_gpio), cd_irq, + err = devm_request_irq(&pdev->dev, + gpio_to_irq(boarddata->cd_gpio), cd_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, mmc_hostname(host->mmc), host); if (err) { dev_err(mmc_dev(host->mmc), "request irq error\n"); - goto no_card_detect_irq; + goto disable_clk; } /* fall through */ @@ -574,55 +577,32 @@ static int __devinit sdhci_esdhc_imx_probe(struct platform_device *pdev) err = sdhci_add_host(host); if (err) - goto err_add_host; + goto disable_clk; return 0; -err_add_host: - if (gpio_is_valid(boarddata->cd_gpio)) - free_irq(gpio_to_irq(boarddata->cd_gpio), host); -no_card_detect_irq: - if (gpio_is_valid(boarddata->cd_gpio)) - gpio_free(boarddata->cd_gpio); - if (gpio_is_valid(boarddata->wp_gpio)) - gpio_free(boarddata->wp_gpio); -no_card_detect_pin: -no_board_data: -pin_err: +disable_clk: clk_disable_unprepare(imx_data->clk_per); clk_disable_unprepare(imx_data->clk_ipg); clk_disable_unprepare(imx_data->clk_ahb); -err_clk_get: - kfree(imx_data); -err_imx_data: +free_sdhci: sdhci_pltfm_free(pdev); return err; } -static int __devexit sdhci_esdhc_imx_remove(struct platform_device *pdev) +static int sdhci_esdhc_imx_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct pltfm_imx_data *imx_data = pltfm_host->priv; - struct esdhc_platform_data *boarddata = &imx_data->boarddata; int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff); sdhci_remove_host(host, dead); - if (gpio_is_valid(boarddata->wp_gpio)) - gpio_free(boarddata->wp_gpio); - - if (gpio_is_valid(boarddata->cd_gpio)) { - free_irq(gpio_to_irq(boarddata->cd_gpio), host); - gpio_free(boarddata->cd_gpio); - } - clk_disable_unprepare(imx_data->clk_per); clk_disable_unprepare(imx_data->clk_ipg); clk_disable_unprepare(imx_data->clk_ahb); - kfree(imx_data); - sdhci_pltfm_free(pdev); return 0; @@ -637,7 +617,7 @@ static struct platform_driver sdhci_esdhc_imx_driver = { }, .id_table = imx_esdhc_devtype, .probe = sdhci_esdhc_imx_probe, - .remove = __devexit_p(sdhci_esdhc_imx_remove), + .remove = sdhci_esdhc_imx_remove, }; module_platform_driver(sdhci_esdhc_imx_driver); diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 63d219f57ca..f32526d2d96 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -22,6 +22,7 @@ #include "sdhci-esdhc.h" #define VENDOR_V_22 0x12 +#define VENDOR_V_23 0x13 static u32 esdhc_readl(struct sdhci_host *host, int reg) { u32 ret; @@ -85,6 +86,18 @@ static u8 esdhc_readb(struct sdhci_host *host, int reg) return ret; } +static void esdhc_writel(struct sdhci_host *host, u32 val, int reg) +{ + /* + * Enable IRQSTATEN[BGESEN] is just to set IRQSTAT[BGE] + * when SYSCTL[RSTD]) is set for some special operations. + * No any impact other operation. + */ + if (reg == SDHCI_INT_ENABLE) + val |= SDHCI_INT_BLK_GAP; + sdhci_be32bs_writel(host, val, reg); +} + static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) { if (reg == SDHCI_BLOCK_SIZE) { @@ -121,6 +134,41 @@ static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) sdhci_be32bs_writeb(host, val, reg); } +/* + * For Abort or Suspend after Stop at Block Gap, ignore the ADMA + * error(IRQSTAT[ADMAE]) if both Transfer Complete(IRQSTAT[TC]) + * and Block Gap Event(IRQSTAT[BGE]) are also set. + * For Continue, apply soft reset for data(SYSCTL[RSTD]); + * and re-issue the entire read transaction from beginning. + */ +static void esdhci_of_adma_workaround(struct sdhci_host *host, u32 intmask) +{ + u32 tmp; + bool applicable; + dma_addr_t dmastart; + dma_addr_t dmanow; + + tmp = in_be32(host->ioaddr + SDHCI_SLOT_INT_STATUS); + tmp = (tmp & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; + + applicable = (intmask & SDHCI_INT_DATA_END) && + (intmask & SDHCI_INT_BLK_GAP) && + (tmp == VENDOR_V_23); + if (!applicable) + return; + + host->data->error = 0; + dmastart = sg_dma_address(host->data->sg); + dmanow = dmastart + host->data->bytes_xfered; + /* + * Force update to the next DMA block boundary. + */ + dmanow = (dmanow & ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1)) + + SDHCI_DEFAULT_BOUNDARY_SIZE; + host->data->bytes_xfered = dmanow - dmastart; + sdhci_writel(host, dmanow, SDHCI_DMA_ADDRESS); +} + static int esdhc_of_enable_dma(struct sdhci_host *host) { setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP); @@ -177,13 +225,16 @@ static void esdhc_of_platform_init(struct sdhci_host *host) vvn = (vvn & SDHCI_VENDOR_VER_MASK) >> SDHCI_VENDOR_VER_SHIFT; if (vvn == VENDOR_V_22) host->quirks2 |= SDHCI_QUIRK2_HOST_NO_CMD23; + + if (vvn > VENDOR_V_22) + host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; } static struct sdhci_ops sdhci_esdhc_ops = { .read_l = esdhc_readl, .read_w = esdhc_readw, .read_b = esdhc_readb, - .write_l = sdhci_be32bs_writel, + .write_l = esdhc_writel, .write_w = esdhc_writew, .write_b = esdhc_writeb, .set_clock = esdhc_of_set_clock, @@ -195,6 +246,7 @@ static struct sdhci_ops sdhci_esdhc_ops = { .platform_suspend = esdhc_of_suspend, .platform_resume = esdhc_of_resume, #endif + .adma_workaround = esdhci_of_adma_workaround, }; static struct sdhci_pltfm_data sdhci_esdhc_pdata = { @@ -208,12 +260,12 @@ static struct sdhci_pltfm_data sdhci_esdhc_pdata = { .ops = &sdhci_esdhc_ops, }; -static int __devinit sdhci_esdhc_probe(struct platform_device *pdev) +static int sdhci_esdhc_probe(struct platform_device *pdev) { return sdhci_pltfm_register(pdev, &sdhci_esdhc_pdata); } -static int __devexit sdhci_esdhc_remove(struct platform_device *pdev) +static int sdhci_esdhc_remove(struct platform_device *pdev) { return sdhci_pltfm_unregister(pdev); } @@ -234,7 +286,7 @@ static struct platform_driver sdhci_esdhc_driver = { .pm = SDHCI_PLTFM_PMOPS, }, .probe = sdhci_esdhc_probe, - .remove = __devexit_p(sdhci_esdhc_remove), + .remove = sdhci_esdhc_remove, }; module_platform_driver(sdhci_esdhc_driver); diff --git a/drivers/mmc/host/sdhci-of-hlwd.c b/drivers/mmc/host/sdhci-of-hlwd.c index 0ce088ae022..c3d3715ec3d 100644 --- a/drivers/mmc/host/sdhci-of-hlwd.c +++ b/drivers/mmc/host/sdhci-of-hlwd.c @@ -66,12 +66,12 @@ static struct sdhci_pltfm_data sdhci_hlwd_pdata = { .ops = &sdhci_hlwd_ops, }; -static int __devinit sdhci_hlwd_probe(struct platform_device *pdev) +static int sdhci_hlwd_probe(struct platform_device *pdev) { return sdhci_pltfm_register(pdev, &sdhci_hlwd_pdata); } -static int __devexit sdhci_hlwd_remove(struct platform_device *pdev) +static int sdhci_hlwd_remove(struct platform_device *pdev) { return sdhci_pltfm_unregister(pdev); } @@ -90,7 +90,7 @@ static struct platform_driver sdhci_hlwd_driver = { .pm = SDHCI_PLTFM_PMOPS, }, .probe = sdhci_hlwd_probe, - .remove = __devexit_p(sdhci_hlwd_remove), + .remove = sdhci_hlwd_remove, }; module_platform_driver(sdhci_hlwd_driver); diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 04936f353ce..c7dd0cbc99d 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -114,6 +114,7 @@ static int ricoh_mmc_probe_slot(struct sdhci_pci_slot *slot) SDHCI_TIMEOUT_CLK_UNIT | SDHCI_CAN_VDD_330 | + SDHCI_CAN_DO_HISPD | SDHCI_CAN_DO_SDMA; return 0; } @@ -653,7 +654,7 @@ static const struct sdhci_pci_fixes sdhci_via = { .probe = via_probe, }; -static const struct pci_device_id pci_ids[] __devinitconst = { +static const struct pci_device_id pci_ids[] = { { .vendor = PCI_VENDOR_ID_RICOH, .device = PCI_DEVICE_ID_RICOH_R5C822, @@ -1183,7 +1184,7 @@ static const struct dev_pm_ops sdhci_pci_pm_ops = { * * \*****************************************************************************/ -static struct sdhci_pci_slot * __devinit sdhci_pci_probe_slot( +static struct sdhci_pci_slot *sdhci_pci_probe_slot( struct pci_dev *pdev, struct sdhci_pci_chip *chip, int first_bar, int slotno) { @@ -1338,7 +1339,7 @@ static void sdhci_pci_remove_slot(struct sdhci_pci_slot *slot) sdhci_free_host(slot->host); } -static void __devinit sdhci_pci_runtime_pm_allow(struct device *dev) +static void sdhci_pci_runtime_pm_allow(struct device *dev) { pm_runtime_put_noidle(dev); pm_runtime_allow(dev); @@ -1347,13 +1348,13 @@ static void __devinit sdhci_pci_runtime_pm_allow(struct device *dev) pm_suspend_ignore_children(dev, 1); } -static void __devexit sdhci_pci_runtime_pm_forbid(struct device *dev) +static void sdhci_pci_runtime_pm_forbid(struct device *dev) { pm_runtime_forbid(dev); pm_runtime_get_noresume(dev); } -static int __devinit sdhci_pci_probe(struct pci_dev *pdev, +static int sdhci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct sdhci_pci_chip *chip; @@ -1445,7 +1446,7 @@ err: return ret; } -static void __devexit sdhci_pci_remove(struct pci_dev *pdev) +static void sdhci_pci_remove(struct pci_dev *pdev) { int i; struct sdhci_pci_chip *chip; @@ -1470,7 +1471,7 @@ static struct pci_driver sdhci_driver = { .name = "sdhci-pci", .id_table = pci_ids, .probe = sdhci_pci_probe, - .remove = __devexit_p(sdhci_pci_remove), + .remove = sdhci_pci_remove, .driver = { .pm = &sdhci_pci_pm_ops }, diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 27164457f86..d4283ef5917 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -78,6 +78,9 @@ void sdhci_get_of_property(struct platform_device *pdev) if (of_get_property(np, "broken-cd", NULL)) host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; + if (of_get_property(np, "no-1-8-v", NULL)) + host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; + if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc")) host->quirks |= SDHCI_QUIRK_BROKEN_DMA; @@ -89,6 +92,12 @@ void sdhci_get_of_property(struct platform_device *pdev) clk = of_get_property(np, "clock-frequency", &size); if (clk && size == sizeof(*clk) && *clk) pltfm_host->clock = be32_to_cpup(clk); + + if (of_find_property(np, "keep-power-in-suspend", NULL)) + host->mmc->pm_caps |= MMC_PM_KEEP_POWER; + + if (of_find_property(np, "enable-sdio-wakeup", NULL)) + host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ; } } #else diff --git a/drivers/mmc/host/sdhci-pxav2.c b/drivers/mmc/host/sdhci-pxav2.c index 8e63a9c04e3..ac854aa192a 100644 --- a/drivers/mmc/host/sdhci-pxav2.c +++ b/drivers/mmc/host/sdhci-pxav2.c @@ -166,7 +166,7 @@ static inline struct sdhci_pxa_platdata *pxav2_get_mmc_pdata(struct device *dev) } #endif -static int __devinit sdhci_pxav2_probe(struct platform_device *pdev) +static int sdhci_pxav2_probe(struct platform_device *pdev) { struct sdhci_pltfm_host *pltfm_host; struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; @@ -247,7 +247,7 @@ err_clk_get: return ret; } -static int __devexit sdhci_pxav2_remove(struct platform_device *pdev) +static int sdhci_pxav2_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -275,7 +275,7 @@ static struct platform_driver sdhci_pxav2_driver = { .pm = SDHCI_PLTFM_PMOPS, }, .probe = sdhci_pxav2_probe, - .remove = __devexit_p(sdhci_pxav2_remove), + .remove = sdhci_pxav2_remove, }; module_platform_driver(sdhci_pxav2_driver); diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index e918a2bb3af..fad0966427f 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -163,10 +163,18 @@ static int pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) return 0; } +static u32 pxav3_get_max_clock(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + return clk_get_rate(pltfm_host->clk); +} + static struct sdhci_ops pxav3_sdhci_ops = { .platform_reset_exit = pxav3_set_private_registers, .set_uhs_signaling = pxav3_set_uhs_signaling, .platform_send_init_74_clocks = pxav3_gen_init_74_clocks, + .get_max_clock = pxav3_get_max_clock, }; #ifdef CONFIG_OF @@ -214,7 +222,7 @@ static inline struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev) } #endif -static int __devinit sdhci_pxav3_probe(struct platform_device *pdev) +static int sdhci_pxav3_probe(struct platform_device *pdev) { struct sdhci_pltfm_host *pltfm_host; struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; @@ -249,7 +257,8 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev) host->quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC - | SDHCI_QUIRK_32BIT_ADMA_SIZE; + | SDHCI_QUIRK_32BIT_ADMA_SIZE + | SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN; /* enable 1/8V DDR capable */ host->mmc->caps |= MMC_CAP_1_8V_DDR; @@ -271,6 +280,8 @@ static int __devinit sdhci_pxav3_probe(struct platform_device *pdev) if (pdata->quirks) host->quirks |= pdata->quirks; + if (pdata->quirks2) + host->quirks2 |= pdata->quirks2; if (pdata->host_caps) host->mmc->caps |= pdata->host_caps; if (pdata->host_caps2) @@ -313,7 +324,7 @@ err_clk_get: return ret; } -static int __devexit sdhci_pxav3_remove(struct platform_device *pdev) +static int sdhci_pxav3_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -346,7 +357,7 @@ static struct platform_driver sdhci_pxav3_driver = { .pm = SDHCI_PLTFM_PMOPS, }, .probe = sdhci_pxav3_probe, - .remove = __devexit_p(sdhci_pxav3_remove), + .remove = sdhci_pxav3_remove, }; module_platform_driver(sdhci_pxav3_driver); diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c index a54dd5d7a5f..82a8de148a8 100644 --- a/drivers/mmc/host/sdhci-s3c.c +++ b/drivers/mmc/host/sdhci-s3c.c @@ -24,6 +24,7 @@ #include <linux/of_gpio.h> #include <linux/pm.h> #include <linux/pm_runtime.h> +#include <linux/pinctrl/consumer.h> #include <linux/mmc/host.h> @@ -57,6 +58,7 @@ struct sdhci_s3c { int ext_cd_irq; int ext_cd_gpio; int *gpios; + struct pinctrl *pctrl; struct clk *clk_io; struct clk *clk_bus[MAX_BUS_CLK]; @@ -373,18 +375,27 @@ static struct sdhci_ops sdhci_s3c_ops = { static void sdhci_s3c_notify_change(struct platform_device *dev, int state) { struct sdhci_host *host = platform_get_drvdata(dev); +#ifdef CONFIG_PM_RUNTIME + struct sdhci_s3c *sc = sdhci_priv(host); +#endif unsigned long flags; if (host) { spin_lock_irqsave(&host->lock, flags); if (state) { dev_dbg(&dev->dev, "card inserted.\n"); +#ifdef CONFIG_PM_RUNTIME + clk_prepare_enable(sc->clk_io); +#endif host->flags &= ~SDHCI_DEVICE_DEAD; host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; } else { dev_dbg(&dev->dev, "card removed.\n"); host->flags |= SDHCI_DEVICE_DEAD; host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION; +#ifdef CONFIG_PM_RUNTIME + clk_disable_unprepare(sc->clk_io); +#endif } tasklet_schedule(&host->card_tasklet); spin_unlock_irqrestore(&host->lock, flags); @@ -406,7 +417,7 @@ static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc) struct s3c_sdhci_platdata *pdata = sc->pdata; struct device *dev = &sc->pdev->dev; - if (gpio_request(pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) { + if (devm_gpio_request(dev, pdata->ext_cd_gpio, "SDHCI EXT CD") == 0) { sc->ext_cd_gpio = pdata->ext_cd_gpio; sc->ext_cd_irq = gpio_to_irq(pdata->ext_cd_gpio); if (sc->ext_cd_irq && @@ -430,7 +441,7 @@ static void sdhci_s3c_setup_card_detect_gpio(struct sdhci_s3c *sc) } #ifdef CONFIG_OF -static int __devinit sdhci_s3c_parse_dt(struct device *dev, +static int sdhci_s3c_parse_dt(struct device *dev, struct sdhci_host *host, struct s3c_sdhci_platdata *pdata) { struct device_node *node = dev->of_node; @@ -449,12 +460,12 @@ static int __devinit sdhci_s3c_parse_dt(struct device *dev, return -ENOMEM; /* get the card detection method */ - if (of_get_property(node, "broken-cd", 0)) { + if (of_get_property(node, "broken-cd", NULL)) { pdata->cd_type = S3C_SDHCI_CD_NONE; goto setup_bus; } - if (of_get_property(node, "non-removable", 0)) { + if (of_get_property(node, "non-removable", NULL)) { pdata->cd_type = S3C_SDHCI_CD_PERMANENT; goto setup_bus; } @@ -477,8 +488,9 @@ static int __devinit sdhci_s3c_parse_dt(struct device *dev, return -EINVAL; } - dev_info(dev, "assuming no card detect line available\n"); - pdata->cd_type = S3C_SDHCI_CD_NONE; + /* assuming internal card detect that will be configured by pinctrl */ + pdata->cd_type = S3C_SDHCI_CD_INTERNAL; + goto setup_bus; found_cd: if (pdata->cd_type == S3C_SDHCI_CD_GPIO) { @@ -487,7 +499,7 @@ static int __devinit sdhci_s3c_parse_dt(struct device *dev, if (of_get_property(node, "cd-inverted", NULL)) pdata->ext_cd_gpio_invert = 1; } else if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) { - ret = gpio_request(gpio, "sdhci-cd"); + ret = devm_gpio_request(dev, gpio, "sdhci-cd"); if (ret) { dev_err(dev, "card detect gpio request failed\n"); return -EINVAL; @@ -496,36 +508,31 @@ static int __devinit sdhci_s3c_parse_dt(struct device *dev, } setup_bus: + if (!IS_ERR(ourhost->pctrl)) + return 0; + /* get the gpios for command, clock and data lines */ for (cnt = 0; cnt < NUM_GPIOS(pdata->max_width); cnt++) { gpio = of_get_gpio(node, cnt); if (!gpio_is_valid(gpio)) { dev_err(dev, "invalid gpio[%d]\n", cnt); - goto err_free_dt_cd_gpio; + return -EINVAL; } ourhost->gpios[cnt] = gpio; } for (cnt = 0; cnt < NUM_GPIOS(pdata->max_width); cnt++) { - ret = gpio_request(ourhost->gpios[cnt], "sdhci-gpio"); + ret = devm_gpio_request(dev, ourhost->gpios[cnt], "sdhci-gpio"); if (ret) { dev_err(dev, "gpio[%d] request failed\n", cnt); - goto err_free_dt_gpios; + return -EINVAL; } } return 0; - - err_free_dt_gpios: - while (--cnt >= 0) - gpio_free(ourhost->gpios[cnt]); - err_free_dt_cd_gpio: - if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) - gpio_free(ourhost->ext_cd_gpio); - return -EINVAL; } #else -static int __devinit sdhci_s3c_parse_dt(struct device *dev, +static int sdhci_s3c_parse_dt(struct device *dev, struct sdhci_host *host, struct s3c_sdhci_platdata *pdata) { return -EINVAL; @@ -548,7 +555,7 @@ static inline struct sdhci_s3c_drv_data *sdhci_s3c_get_driver_data( platform_get_device_id(pdev)->driver_data; } -static int __devinit sdhci_s3c_probe(struct platform_device *pdev) +static int sdhci_s3c_probe(struct platform_device *pdev) { struct s3c_sdhci_platdata *pdata; struct sdhci_s3c_drv_data *drv_data; @@ -579,13 +586,15 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) { ret = -ENOMEM; - goto err_pdata; + goto err_pdata_io_clk; } + sc->pctrl = devm_pinctrl_get_select_default(&pdev->dev); + if (pdev->dev.of_node) { ret = sdhci_s3c_parse_dt(&pdev->dev, host, pdata); if (ret) - goto err_pdata; + goto err_pdata_io_clk; } else { memcpy(pdata, pdev->dev.platform_data, sizeof(*pdata)); sc->ext_cd_gpio = -1; /* invalid gpio number */ @@ -603,7 +612,7 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) if (IS_ERR(sc->clk_io)) { dev_err(dev, "failed to get io clock\n"); ret = PTR_ERR(sc->clk_io); - goto err_io_clk; + goto err_pdata_io_clk; } /* enable the local io clock and keep it running for the moment. */ @@ -766,19 +775,13 @@ static int __devinit sdhci_s3c_probe(struct platform_device *pdev) clk_disable_unprepare(sc->clk_io); clk_put(sc->clk_io); - err_io_clk: - for (ptr = 0; ptr < NUM_GPIOS(sc->pdata->max_width); ptr++) - gpio_free(sc->gpios[ptr]); - if (pdata->cd_type == S3C_SDHCI_CD_INTERNAL) - gpio_free(sc->ext_cd_gpio); - - err_pdata: + err_pdata_io_clk: sdhci_free_host(host); return ret; } -static int __devexit sdhci_s3c_remove(struct platform_device *pdev) +static int sdhci_s3c_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_s3c *sc = sdhci_priv(host); @@ -791,9 +794,6 @@ static int __devexit sdhci_s3c_remove(struct platform_device *pdev) if (sc->ext_cd_irq) free_irq(sc->ext_cd_irq, sc); - if (gpio_is_valid(sc->ext_cd_gpio)) - gpio_free(sc->ext_cd_gpio); - #ifdef CONFIG_PM_RUNTIME if (pdata->cd_type != S3C_SDHCI_CD_INTERNAL) clk_prepare_enable(sc->clk_io); @@ -814,11 +814,6 @@ static int __devexit sdhci_s3c_remove(struct platform_device *pdev) clk_disable_unprepare(sc->clk_io); clk_put(sc->clk_io); - if (pdev->dev.of_node) { - for (ptr = 0; ptr < NUM_GPIOS(sc->pdata->max_width); ptr++) - gpio_free(sc->gpios[ptr]); - } - sdhci_free_host(host); platform_set_drvdata(pdev, NULL); @@ -916,7 +911,7 @@ MODULE_DEVICE_TABLE(of, sdhci_s3c_dt_match); static struct platform_driver sdhci_s3c_driver = { .probe = sdhci_s3c_probe, - .remove = __devexit_p(sdhci_s3c_remove), + .remove = sdhci_s3c_remove, .id_table = sdhci_s3c_driver_ids, .driver = { .owner = THIS_MODULE, diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c index 6be89c032de..c6ece0bd03b 100644 --- a/drivers/mmc/host/sdhci-spear.c +++ b/drivers/mmc/host/sdhci-spear.c @@ -71,8 +71,7 @@ static irqreturn_t sdhci_gpio_irq(int irq, void *dev_id) } #ifdef CONFIG_OF -static struct sdhci_plat_data * __devinit -sdhci_probe_config_dt(struct platform_device *pdev) +static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct sdhci_plat_data *pdata = NULL; @@ -96,14 +95,13 @@ sdhci_probe_config_dt(struct platform_device *pdev) return pdata; } #else -static struct sdhci_plat_data * __devinit -sdhci_probe_config_dt(struct platform_device *pdev) +static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev) { return ERR_PTR(-ENOSYS); } #endif -static int __devinit sdhci_probe(struct platform_device *pdev) +static int sdhci_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct sdhci_host *host; @@ -146,6 +144,11 @@ static int __devinit sdhci_probe(struct platform_device *pdev) goto put_clk; } + ret = clk_set_rate(sdhci->clk, 50000000); + if (ret) + dev_dbg(&pdev->dev, "Error setting desired clk, clk=%lu\n", + clk_get_rate(sdhci->clk)); + if (np) { sdhci->data = sdhci_probe_config_dt(pdev); if (IS_ERR(sdhci->data)) { @@ -268,7 +271,7 @@ err: return ret; } -static int __devexit sdhci_remove(struct platform_device *pdev) +static int sdhci_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev); @@ -297,7 +300,7 @@ static int sdhci_suspend(struct device *dev) ret = sdhci_suspend_host(host); if (!ret) - clk_disable_unprepare(sdhci->clk); + clk_disable(sdhci->clk); return ret; } @@ -308,7 +311,7 @@ static int sdhci_resume(struct device *dev) struct spear_sdhci *sdhci = dev_get_platdata(dev); int ret; - ret = clk_prepare_enable(sdhci->clk); + ret = clk_enable(sdhci->clk); if (ret) { dev_dbg(dev, "Resume: Error enabling clock\n"); return ret; @@ -336,7 +339,7 @@ static struct platform_driver sdhci_driver = { .of_match_table = of_match_ptr(sdhci_spear_id_table), }, .probe = sdhci_probe, - .remove = __devexit_p(sdhci_remove), + .remove = sdhci_remove, }; module_platform_driver(sdhci_driver); diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index f9eb9162370..3695b2e0cbd 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -206,7 +206,7 @@ static struct sdhci_tegra_soc_data soc_data_tegra30 = { }; #endif -static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = { +static const struct of_device_id sdhci_tegra_dt_match[] = { #ifdef CONFIG_ARCH_TEGRA_3x_SOC { .compatible = "nvidia,tegra30-sdhci", .data = &soc_data_tegra30 }, #endif @@ -217,7 +217,7 @@ static const struct of_device_id sdhci_tegra_dt_match[] __devinitdata = { }; MODULE_DEVICE_TABLE(of, sdhci_dt_ids); -static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata( +static struct tegra_sdhci_platform_data *sdhci_tegra_dt_parse_pdata( struct platform_device *pdev) { struct tegra_sdhci_platform_data *plat; @@ -244,7 +244,7 @@ static struct tegra_sdhci_platform_data * __devinit sdhci_tegra_dt_parse_pdata( return plat; } -static int __devinit sdhci_tegra_probe(struct platform_device *pdev) +static int sdhci_tegra_probe(struct platform_device *pdev) { const struct of_device_id *match; const struct sdhci_tegra_soc_data *soc_data; @@ -370,7 +370,7 @@ err_no_plat: return rc; } -static int __devexit sdhci_tegra_remove(struct platform_device *pdev) +static int sdhci_tegra_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -407,7 +407,7 @@ static struct platform_driver sdhci_tegra_driver = { .pm = SDHCI_PLTFM_PMOPS, }, .probe = sdhci_tegra_probe, - .remove = __devexit_p(sdhci_tegra_remove), + .remove = sdhci_tegra_remove, }; module_platform_driver(sdhci_tegra_driver); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index c7851c0aabc..6f0bfc0c8c9 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1618,7 +1618,7 @@ static int sdhci_do_3_3v_signal_voltage_switch(struct sdhci_host *host, sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); if (host->vqmmc) { - ret = regulator_set_voltage(host->vqmmc, 3300000, 3300000); + ret = regulator_set_voltage(host->vqmmc, 2700000, 3600000); if (ret) { pr_warning("%s: Switching to 3.3V signalling voltage " " failed\n", mmc_hostname(host->mmc)); @@ -1662,7 +1662,7 @@ static int sdhci_do_1_8v_signal_voltage_switch(struct sdhci_host *host, */ if (host->vqmmc) ret = regulator_set_voltage(host->vqmmc, - 1800000, 1800000); + 1700000, 1950000); else ret = 0; @@ -1994,30 +1994,11 @@ static void sdhci_enable_preset_value(struct mmc_host *mmc, bool enable) sdhci_runtime_pm_put(host); } -static const struct mmc_host_ops sdhci_ops = { - .request = sdhci_request, - .set_ios = sdhci_set_ios, - .get_ro = sdhci_get_ro, - .hw_reset = sdhci_hw_reset, - .enable_sdio_irq = sdhci_enable_sdio_irq, - .start_signal_voltage_switch = sdhci_start_signal_voltage_switch, - .execute_tuning = sdhci_execute_tuning, - .enable_preset_value = sdhci_enable_preset_value, -}; - -/*****************************************************************************\ - * * - * Tasklets * - * * -\*****************************************************************************/ - -static void sdhci_tasklet_card(unsigned long param) +static void sdhci_card_event(struct mmc_host *mmc) { - struct sdhci_host *host; + struct sdhci_host *host = mmc_priv(mmc); unsigned long flags; - host = (struct sdhci_host*)param; - spin_lock_irqsave(&host->lock, flags); /* Check host->mrq first in case we are runtime suspended */ @@ -2036,6 +2017,31 @@ static void sdhci_tasklet_card(unsigned long param) } spin_unlock_irqrestore(&host->lock, flags); +} + +static const struct mmc_host_ops sdhci_ops = { + .request = sdhci_request, + .set_ios = sdhci_set_ios, + .get_ro = sdhci_get_ro, + .hw_reset = sdhci_hw_reset, + .enable_sdio_irq = sdhci_enable_sdio_irq, + .start_signal_voltage_switch = sdhci_start_signal_voltage_switch, + .execute_tuning = sdhci_execute_tuning, + .enable_preset_value = sdhci_enable_preset_value, + .card_event = sdhci_card_event, +}; + +/*****************************************************************************\ + * * + * Tasklets * + * * +\*****************************************************************************/ + +static void sdhci_tasklet_card(unsigned long param) +{ + struct sdhci_host *host = (struct sdhci_host*)param; + + sdhci_card_event(host->mmc); mmc_detect_change(host->mmc, msecs_to_jiffies(200)); } @@ -2282,6 +2288,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) pr_err("%s: ADMA error\n", mmc_hostname(host->mmc)); sdhci_show_adma_error(host); host->data->error = -EIO; + if (host->ops->adma_workaround) + host->ops->adma_workaround(host, intmask); } if (host->data->error) @@ -2858,10 +2866,16 @@ int sdhci_add_host(struct sdhci_host *host) mmc_hostname(mmc)); host->vqmmc = NULL; } - } - else if (regulator_is_supported_voltage(host->vqmmc, 1800000, 1800000)) + } else { regulator_enable(host->vqmmc); - else + if (!regulator_is_supported_voltage(host->vqmmc, 1700000, + 1950000)) + caps[1] &= ~(SDHCI_SUPPORT_SDR104 | + SDHCI_SUPPORT_SDR50 | + SDHCI_SUPPORT_DDR50); + } + + if (host->quirks2 & SDHCI_QUIRK2_NO_1_8_V) caps[1] &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50); @@ -2919,21 +2933,18 @@ int sdhci_add_host(struct sdhci_host *host) mmc_hostname(mmc)); host->vmmc = NULL; } - } else - regulator_enable(host->vmmc); + } #ifdef CONFIG_REGULATOR if (host->vmmc) { - ret = regulator_is_supported_voltage(host->vmmc, 3300000, - 3300000); + ret = regulator_is_supported_voltage(host->vmmc, 2700000, + 3600000); if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_330))) caps[0] &= ~SDHCI_CAN_VDD_330; - ret = regulator_is_supported_voltage(host->vmmc, 3000000, - 3000000); if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_300))) caps[0] &= ~SDHCI_CAN_VDD_300; - ret = regulator_is_supported_voltage(host->vmmc, 1800000, - 1800000); + ret = regulator_is_supported_voltage(host->vmmc, 1700000, + 1950000); if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_180))) caps[0] &= ~SDHCI_CAN_VDD_180; } diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 71a4a7ed46c..a6d69b7bdea 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -120,6 +120,7 @@ #define SDHCI_SIGNAL_ENABLE 0x38 #define SDHCI_INT_RESPONSE 0x00000001 #define SDHCI_INT_DATA_END 0x00000002 +#define SDHCI_INT_BLK_GAP 0x00000004 #define SDHCI_INT_DMA_END 0x00000008 #define SDHCI_INT_SPACE_AVAIL 0x00000010 #define SDHCI_INT_DATA_AVAIL 0x00000020 @@ -146,7 +147,8 @@ #define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \ SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \ SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \ - SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR) + SDHCI_INT_DATA_END_BIT | SDHCI_INT_ADMA_ERROR | \ + SDHCI_INT_BLK_GAP) #define SDHCI_INT_ALL_MASK ((unsigned int)-1) #define SDHCI_ACMD12_ERR 0x3C @@ -278,6 +280,7 @@ struct sdhci_ops { void (*hw_reset)(struct sdhci_host *host); void (*platform_suspend)(struct sdhci_host *host); void (*platform_resume)(struct sdhci_host *host); + void (*adma_workaround)(struct sdhci_host *host, u32 intmask); void (*platform_init)(struct sdhci_host *host); }; diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index d25bc97dc5c..9a4c151067d 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1104,7 +1104,6 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) { struct sh_mmcif_host *host = dev_id; struct mmc_request *mrq = host->mrq; - struct mmc_data *data = mrq->data; cancel_delayed_work_sync(&host->timeout_work); @@ -1152,13 +1151,14 @@ static irqreturn_t sh_mmcif_irqt(int irq, void *dev_id) case MMCIF_WAIT_FOR_READ_END: case MMCIF_WAIT_FOR_WRITE_END: if (host->sd_error) - data->error = sh_mmcif_error_manage(host); + mrq->data->error = sh_mmcif_error_manage(host); break; default: BUG(); } if (host->wait_for != MMCIF_WAIT_FOR_STOP) { + struct mmc_data *data = mrq->data; if (!mrq->cmd->error && data && !data->error) data->bytes_xfered = data->blocks * data->blksz; @@ -1231,10 +1231,6 @@ static irqreturn_t sh_mmcif_intr(int irq, void *dev_id) host->sd_error = true; dev_dbg(&host->pd->dev, "int err state = %08x\n", state); } - if (host->state == STATE_IDLE) { - dev_info(&host->pd->dev, "Spurious IRQ status 0x%x", state); - return IRQ_HANDLED; - } if (state & ~(INT_CMD12RBE | INT_CMD12CRE)) { if (!host->dma_active) return IRQ_WAKE_THREAD; @@ -1302,7 +1298,7 @@ static void sh_mmcif_init_ocr(struct sh_mmcif_host *host) dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n"); } -static int __devinit sh_mmcif_probe(struct platform_device *pdev) +static int sh_mmcif_probe(struct platform_device *pdev) { int ret = 0, irq[2]; struct mmc_host *mmc; @@ -1310,7 +1306,6 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) struct sh_mmcif_plat_data *pd = pdev->dev.platform_data; struct resource *res; void __iomem *reg; - char clk_name[8]; irq[0] = platform_get_irq(pdev, 0); irq[1] = platform_get_irq(pdev, 1); @@ -1360,11 +1355,10 @@ static int __devinit sh_mmcif_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); host->power = false; - snprintf(clk_name, sizeof(clk_name), "mmc%d", pdev->id); - host->hclk = clk_get(&pdev->dev, clk_name); + host->hclk = clk_get(&pdev->dev, NULL); if (IS_ERR(host->hclk)) { ret = PTR_ERR(host->hclk); - dev_err(&pdev->dev, "cannot get clock \"%s\": %d\n", clk_name, ret); + dev_err(&pdev->dev, "cannot get clock: %d\n", ret); goto eclkget; } ret = sh_mmcif_clk_update(host); @@ -1430,7 +1424,7 @@ ealloch: return ret; } -static int __devexit sh_mmcif_remove(struct platform_device *pdev) +static int sh_mmcif_remove(struct platform_device *pdev) { struct sh_mmcif_host *host = platform_get_drvdata(pdev); struct sh_mmcif_plat_data *pd = pdev->dev.platform_data; diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c index 0bdc146178d..524a7f77382 100644 --- a/drivers/mmc/host/sh_mobile_sdhi.c +++ b/drivers/mmc/host/sh_mobile_sdhi.c @@ -117,13 +117,12 @@ static const struct sh_mobile_sdhi_ops sdhi_ops = { .cd_wakeup = sh_mobile_sdhi_cd_wakeup, }; -static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) +static int sh_mobile_sdhi_probe(struct platform_device *pdev) { struct sh_mobile_sdhi *priv; struct tmio_mmc_data *mmc_data; struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; struct tmio_mmc_host *host; - char clk_name[8]; int irq, ret, i = 0; bool multiplexed_isr = true; @@ -144,11 +143,10 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) } } - snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id); - priv->clk = clk_get(&pdev->dev, clk_name); + priv->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(priv->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); ret = PTR_ERR(priv->clk); + dev_err(&pdev->dev, "cannot get clock: %d\n", ret); goto eclkget; } @@ -250,7 +248,7 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) dev_info(&pdev->dev, "%s base at 0x%08lx clock rate %u MHz\n", mmc_hostname(host->mmc), (unsigned long) (platform_get_resource(pdev, IORESOURCE_MEM, 0)->start), - mmc_data->hclk / 1000000); + host->mmc->f_max / 1000000); return ret; @@ -330,7 +328,7 @@ static struct platform_driver sh_mobile_sdhi_driver = { .of_match_table = sh_mobile_sdhi_of_match, }, .probe = sh_mobile_sdhi_probe, - .remove = __devexit_p(sh_mobile_sdhi_remove), + .remove = sh_mobile_sdhi_remove, }; module_platform_driver(sh_mobile_sdhi_driver); diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 113ce6c9cf3..139212e79cd 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -57,7 +57,7 @@ static int tmio_mmc_resume(struct platform_device *dev) #define tmio_mmc_resume NULL #endif -static int __devinit tmio_mmc_probe(struct platform_device *pdev) +static int tmio_mmc_probe(struct platform_device *pdev) { const struct mfd_cell *cell = mfd_get_cell(pdev); struct tmio_mmc_data *pdata; @@ -107,7 +107,7 @@ out: return ret; } -static int __devexit tmio_mmc_remove(struct platform_device *pdev) +static int tmio_mmc_remove(struct platform_device *pdev) { const struct mfd_cell *cell = mfd_get_cell(pdev); struct mmc_host *mmc = platform_get_drvdata(pdev); @@ -133,7 +133,7 @@ static struct platform_driver tmio_mmc_driver = { .owner = THIS_MODULE, }, .probe = tmio_mmc_probe, - .remove = __devexit_p(tmio_mmc_remove), + .remove = tmio_mmc_remove, .suspend = tmio_mmc_suspend, .resume = tmio_mmc_resume, }; diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c index 0d8a9bbe30b..50bf495a988 100644 --- a/drivers/mmc/host/tmio_mmc_pio.c +++ b/drivers/mmc/host/tmio_mmc_pio.c @@ -918,7 +918,7 @@ static void tmio_mmc_init_ocr(struct tmio_mmc_host *host) dev_warn(mmc_dev(mmc), "Platform OCR mask is ignored\n"); } -int __devinit tmio_mmc_host_probe(struct tmio_mmc_host **host, +int tmio_mmc_host_probe(struct tmio_mmc_host **host, struct platform_device *pdev, struct tmio_mmc_data *pdata) { diff --git a/drivers/mmc/host/via-sdmmc.c b/drivers/mmc/host/via-sdmmc.c index f18becef156..4f84586c6e9 100644 --- a/drivers/mmc/host/via-sdmmc.c +++ b/drivers/mmc/host/via-sdmmc.c @@ -1082,7 +1082,7 @@ static void via_init_mmc_host(struct via_crdr_mmc_host *host) msleep(1); } -static int __devinit via_sd_probe(struct pci_dev *pcidev, +static int via_sd_probe(struct pci_dev *pcidev, const struct pci_device_id *id) { struct mmc_host *mmc; @@ -1176,7 +1176,7 @@ disable: return ret; } -static void __devexit via_sd_remove(struct pci_dev *pcidev) +static void via_sd_remove(struct pci_dev *pcidev) { struct via_crdr_mmc_host *sdhost = pci_get_drvdata(pcidev); unsigned long flags; @@ -1332,7 +1332,7 @@ static struct pci_driver via_sd_driver = { .name = DRV_NAME, .id_table = via_ids, .probe = via_sd_probe, - .remove = __devexit_p(via_sd_remove), + .remove = via_sd_remove, .suspend = via_sd_suspend, .resume = via_sd_resume, }; diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c index d5655a63eda..cb9f361c03a 100644 --- a/drivers/mmc/host/vub300.c +++ b/drivers/mmc/host/vub300.c @@ -2362,6 +2362,7 @@ error4: error1: usb_free_urb(command_out_urb); error0: + usb_put_dev(udev); return retval; } diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index 64acd9ce141..e954b775887 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -1196,7 +1196,7 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id) * Allocate/free MMC structure. */ -static int __devinit wbsd_alloc_mmc(struct device *dev) +static int wbsd_alloc_mmc(struct device *dev) { struct mmc_host *mmc; struct wbsd_host *host; @@ -1288,7 +1288,7 @@ static void wbsd_free_mmc(struct device *dev) * Scan for known chip id:s */ -static int __devinit wbsd_scan(struct wbsd_host *host) +static int wbsd_scan(struct wbsd_host *host) { int i, j, k; int id; @@ -1344,7 +1344,7 @@ static int __devinit wbsd_scan(struct wbsd_host *host) * Allocate/free io port ranges */ -static int __devinit wbsd_request_region(struct wbsd_host *host, int base) +static int wbsd_request_region(struct wbsd_host *host, int base) { if (base & 0x7) return -EINVAL; @@ -1374,7 +1374,7 @@ static void wbsd_release_regions(struct wbsd_host *host) * Allocate/free DMA port and buffer */ -static void __devinit wbsd_request_dma(struct wbsd_host *host, int dma) +static void wbsd_request_dma(struct wbsd_host *host, int dma) { if (dma < 0) return; @@ -1452,7 +1452,7 @@ static void wbsd_release_dma(struct wbsd_host *host) * Allocate/free IRQ. */ -static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq) +static int wbsd_request_irq(struct wbsd_host *host, int irq) { int ret; @@ -1502,7 +1502,7 @@ static void wbsd_release_irq(struct wbsd_host *host) * Allocate all resources for the host. */ -static int __devinit wbsd_request_resources(struct wbsd_host *host, +static int wbsd_request_resources(struct wbsd_host *host, int base, int irq, int dma) { int ret; @@ -1644,7 +1644,7 @@ static void wbsd_chip_poweroff(struct wbsd_host *host) * * \*****************************************************************************/ -static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma, +static int wbsd_init(struct device *dev, int base, int irq, int dma, int pnp) { struct wbsd_host *host = NULL; @@ -1735,7 +1735,7 @@ static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma, return 0; } -static void __devexit wbsd_shutdown(struct device *dev, int pnp) +static void wbsd_shutdown(struct device *dev, int pnp) { struct mmc_host *mmc = dev_get_drvdata(dev); struct wbsd_host *host; @@ -1762,13 +1762,13 @@ static void __devexit wbsd_shutdown(struct device *dev, int pnp) * Non-PnP */ -static int __devinit wbsd_probe(struct platform_device *dev) +static int wbsd_probe(struct platform_device *dev) { /* Use the module parameters for resources */ return wbsd_init(&dev->dev, param_io, param_irq, param_dma, 0); } -static int __devexit wbsd_remove(struct platform_device *dev) +static int wbsd_remove(struct platform_device *dev) { wbsd_shutdown(&dev->dev, 0); @@ -1781,7 +1781,7 @@ static int __devexit wbsd_remove(struct platform_device *dev) #ifdef CONFIG_PNP -static int __devinit +static int wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id) { int io, irq, dma; @@ -1801,7 +1801,7 @@ wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id) return wbsd_init(&pnpdev->dev, io, irq, dma, 1); } -static void __devexit wbsd_pnp_remove(struct pnp_dev *dev) +static void wbsd_pnp_remove(struct pnp_dev *dev) { wbsd_shutdown(&dev->dev, 1); } @@ -1941,7 +1941,7 @@ static struct platform_device *wbsd_device; static struct platform_driver wbsd_driver = { .probe = wbsd_probe, - .remove = __devexit_p(wbsd_remove), + .remove = wbsd_remove, .suspend = wbsd_platform_suspend, .resume = wbsd_platform_resume, @@ -1957,7 +1957,7 @@ static struct pnp_driver wbsd_pnp_driver = { .name = DRIVER_NAME, .id_table = pnp_dev_table, .probe = wbsd_pnp_probe, - .remove = __devexit_p(wbsd_pnp_remove), + .remove = wbsd_pnp_remove, .suspend = wbsd_pnp_suspend, .resume = wbsd_pnp_resume, diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c new file mode 100644 index 00000000000..5ba4605e4f8 --- /dev/null +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -0,0 +1,1029 @@ +/* + * WM8505/WM8650 SD/MMC Host Controller + * + * Copyright (C) 2010 Tony Prisk + * Copyright (C) 2008 WonderMedia Technologies, Inc. + * + * 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/init.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/ioport.h> +#include <linux/errno.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/clk.h> +#include <linux/gpio.h> + +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_device.h> + +#include <linux/mmc/host.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/sd.h> + +#include <asm/byteorder.h> + + +#define DRIVER_NAME "wmt-sdhc" + + +/* MMC/SD controller registers */ +#define SDMMC_CTLR 0x00 +#define SDMMC_CMD 0x01 +#define SDMMC_RSPTYPE 0x02 +#define SDMMC_ARG 0x04 +#define SDMMC_BUSMODE 0x08 +#define SDMMC_BLKLEN 0x0C +#define SDMMC_BLKCNT 0x0E +#define SDMMC_RSP 0x10 +#define SDMMC_CBCR 0x20 +#define SDMMC_INTMASK0 0x24 +#define SDMMC_INTMASK1 0x25 +#define SDMMC_STS0 0x28 +#define SDMMC_STS1 0x29 +#define SDMMC_STS2 0x2A +#define SDMMC_STS3 0x2B +#define SDMMC_RSPTIMEOUT 0x2C +#define SDMMC_CLK 0x30 /* VT8500 only */ +#define SDMMC_EXTCTRL 0x34 +#define SDMMC_SBLKLEN 0x38 +#define SDMMC_DMATIMEOUT 0x3C + + +/* SDMMC_CTLR bit fields */ +#define CTLR_CMD_START 0x01 +#define CTLR_CMD_WRITE 0x04 +#define CTLR_FIFO_RESET 0x08 + +/* SDMMC_BUSMODE bit fields */ +#define BM_SPI_MODE 0x01 +#define BM_FOURBIT_MODE 0x02 +#define BM_EIGHTBIT_MODE 0x04 +#define BM_SD_OFF 0x10 +#define BM_SPI_CS 0x20 +#define BM_SD_POWER 0x40 +#define BM_SOFT_RESET 0x80 +#define BM_ONEBIT_MASK 0xFD + +/* SDMMC_BLKLEN bit fields */ +#define BLKL_CRCERR_ABORT 0x0800 +#define BLKL_CD_POL_HIGH 0x1000 +#define BLKL_GPI_CD 0x2000 +#define BLKL_DATA3_CD 0x4000 +#define BLKL_INT_ENABLE 0x8000 + +/* SDMMC_INTMASK0 bit fields */ +#define INT0_MBLK_TRAN_DONE_INT_EN 0x10 +#define INT0_BLK_TRAN_DONE_INT_EN 0x20 +#define INT0_CD_INT_EN 0x40 +#define INT0_DI_INT_EN 0x80 + +/* SDMMC_INTMASK1 bit fields */ +#define INT1_CMD_RES_TRAN_DONE_INT_EN 0x02 +#define INT1_CMD_RES_TOUT_INT_EN 0x04 +#define INT1_MBLK_AUTO_STOP_INT_EN 0x08 +#define INT1_DATA_TOUT_INT_EN 0x10 +#define INT1_RESCRC_ERR_INT_EN 0x20 +#define INT1_RCRC_ERR_INT_EN 0x40 +#define INT1_WCRC_ERR_INT_EN 0x80 + +/* SDMMC_STS0 bit fields */ +#define STS0_WRITE_PROTECT 0x02 +#define STS0_CD_DATA3 0x04 +#define STS0_CD_GPI 0x08 +#define STS0_MBLK_DONE 0x10 +#define STS0_BLK_DONE 0x20 +#define STS0_CARD_DETECT 0x40 +#define STS0_DEVICE_INS 0x80 + +/* SDMMC_STS1 bit fields */ +#define STS1_SDIO_INT 0x01 +#define STS1_CMDRSP_DONE 0x02 +#define STS1_RSP_TIMEOUT 0x04 +#define STS1_AUTOSTOP_DONE 0x08 +#define STS1_DATA_TIMEOUT 0x10 +#define STS1_RSP_CRC_ERR 0x20 +#define STS1_RCRC_ERR 0x40 +#define STS1_WCRC_ERR 0x80 + +/* SDMMC_STS2 bit fields */ +#define STS2_CMD_RES_BUSY 0x10 +#define STS2_DATARSP_BUSY 0x20 +#define STS2_DIS_FORCECLK 0x80 + + +/* MMC/SD DMA Controller Registers */ +#define SDDMA_GCR 0x100 +#define SDDMA_IER 0x104 +#define SDDMA_ISR 0x108 +#define SDDMA_DESPR 0x10C +#define SDDMA_RBR 0x110 +#define SDDMA_DAR 0x114 +#define SDDMA_BAR 0x118 +#define SDDMA_CPR 0x11C +#define SDDMA_CCR 0x120 + + +/* SDDMA_GCR bit fields */ +#define DMA_GCR_DMA_EN 0x00000001 +#define DMA_GCR_SOFT_RESET 0x00000100 + +/* SDDMA_IER bit fields */ +#define DMA_IER_INT_EN 0x00000001 + +/* SDDMA_ISR bit fields */ +#define DMA_ISR_INT_STS 0x00000001 + +/* SDDMA_RBR bit fields */ +#define DMA_RBR_FORMAT 0x40000000 +#define DMA_RBR_END 0x80000000 + +/* SDDMA_CCR bit fields */ +#define DMA_CCR_RUN 0x00000080 +#define DMA_CCR_IF_TO_PERIPHERAL 0x00000000 +#define DMA_CCR_PERIPHERAL_TO_IF 0x00400000 + +/* SDDMA_CCR event status */ +#define DMA_CCR_EVT_NO_STATUS 0x00000000 +#define DMA_CCR_EVT_UNDERRUN 0x00000001 +#define DMA_CCR_EVT_OVERRUN 0x00000002 +#define DMA_CCR_EVT_DESP_READ 0x00000003 +#define DMA_CCR_EVT_DATA_RW 0x00000004 +#define DMA_CCR_EVT_EARLY_END 0x00000005 +#define DMA_CCR_EVT_SUCCESS 0x0000000F + +#define PDMA_READ 0x00 +#define PDMA_WRITE 0x01 + +#define WMT_SD_POWER_OFF 0 +#define WMT_SD_POWER_ON 1 + +struct wmt_dma_descriptor { + u32 flags; + u32 data_buffer_addr; + u32 branch_addr; + u32 reserved1; +}; + +struct wmt_mci_caps { + unsigned int f_min; + unsigned int f_max; + u32 ocr_avail; + u32 caps; + u32 max_seg_size; + u32 max_segs; + u32 max_blk_size; +}; + +struct wmt_mci_priv { + struct mmc_host *mmc; + void __iomem *sdmmc_base; + + int irq_regular; + int irq_dma; + + void *dma_desc_buffer; + dma_addr_t dma_desc_device_addr; + + struct completion cmdcomp; + struct completion datacomp; + + struct completion *comp_cmd; + struct completion *comp_dma; + + struct mmc_request *req; + struct mmc_command *cmd; + + struct clk *clk_sdmmc; + struct device *dev; + + u8 power_inverted; + u8 cd_inverted; +}; + +static void wmt_set_sd_power(struct wmt_mci_priv *priv, int enable) +{ + u32 reg_tmp; + if (enable) { + if (priv->power_inverted) { + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SD_OFF, + priv->sdmmc_base + SDMMC_BUSMODE); + } else { + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp & (~BM_SD_OFF), + priv->sdmmc_base + SDMMC_BUSMODE); + } + } else { + if (priv->power_inverted) { + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp & (~BM_SD_OFF), + priv->sdmmc_base + SDMMC_BUSMODE); + } else { + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SD_OFF, + priv->sdmmc_base + SDMMC_BUSMODE); + } + } +} + +static void wmt_mci_read_response(struct mmc_host *mmc) +{ + struct wmt_mci_priv *priv; + int idx1, idx2; + u8 tmp_resp; + u32 response; + + priv = mmc_priv(mmc); + + for (idx1 = 0; idx1 < 4; idx1++) { + response = 0; + for (idx2 = 0; idx2 < 4; idx2++) { + if ((idx1 == 3) && (idx2 == 3)) + tmp_resp = readb(priv->sdmmc_base + SDMMC_RSP); + else + tmp_resp = readb(priv->sdmmc_base + SDMMC_RSP + + (idx1*4) + idx2 + 1); + response |= (tmp_resp << (idx2 * 8)); + } + priv->cmd->resp[idx1] = cpu_to_be32(response); + } +} + +static void wmt_mci_start_command(struct wmt_mci_priv *priv) +{ + u32 reg_tmp; + + reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); + writeb(reg_tmp | CTLR_CMD_START, priv->sdmmc_base + SDMMC_CTLR); +} + +static int wmt_mci_send_command(struct mmc_host *mmc, u8 command, u8 cmdtype, + u32 arg, u8 rsptype) +{ + struct wmt_mci_priv *priv; + u32 reg_tmp; + + priv = mmc_priv(mmc); + + /* write command, arg, resptype registers */ + writeb(command, priv->sdmmc_base + SDMMC_CMD); + writel(arg, priv->sdmmc_base + SDMMC_ARG); + writeb(rsptype, priv->sdmmc_base + SDMMC_RSPTYPE); + + /* reset response FIFO */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); + writeb(reg_tmp | CTLR_FIFO_RESET, priv->sdmmc_base + SDMMC_CTLR); + + /* ensure clock enabled - VT3465 */ + wmt_set_sd_power(priv, WMT_SD_POWER_ON); + + /* clear status bits */ + writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS2); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS3); + + /* set command type */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); + writeb((reg_tmp & 0x0F) | (cmdtype << 4), + priv->sdmmc_base + SDMMC_CTLR); + + return 0; +} + +static void wmt_mci_disable_dma(struct wmt_mci_priv *priv) +{ + writel(DMA_ISR_INT_STS, priv->sdmmc_base + SDDMA_ISR); + writel(0, priv->sdmmc_base + SDDMA_IER); +} + +static void wmt_complete_data_request(struct wmt_mci_priv *priv) +{ + struct mmc_request *req; + req = priv->req; + + req->data->bytes_xfered = req->data->blksz * req->data->blocks; + + /* unmap the DMA pages used for write data */ + if (req->data->flags & MMC_DATA_WRITE) + dma_unmap_sg(mmc_dev(priv->mmc), req->data->sg, + req->data->sg_len, DMA_TO_DEVICE); + else + dma_unmap_sg(mmc_dev(priv->mmc), req->data->sg, + req->data->sg_len, DMA_FROM_DEVICE); + + /* Check if the DMA ISR returned a data error */ + if ((req->cmd->error) || (req->data->error)) + mmc_request_done(priv->mmc, req); + else { + wmt_mci_read_response(priv->mmc); + if (!req->data->stop) { + /* single-block read/write requests end here */ + mmc_request_done(priv->mmc, req); + } else { + /* + * we change the priv->cmd variable so the response is + * stored in the stop struct rather than the original + * calling command struct + */ + priv->comp_cmd = &priv->cmdcomp; + init_completion(priv->comp_cmd); + priv->cmd = req->data->stop; + wmt_mci_send_command(priv->mmc, req->data->stop->opcode, + 7, req->data->stop->arg, 9); + wmt_mci_start_command(priv); + } + } +} + +static irqreturn_t wmt_mci_dma_isr(int irq_num, void *data) +{ + struct mmc_host *mmc; + struct wmt_mci_priv *priv; + + int status; + + priv = (struct wmt_mci_priv *)data; + mmc = priv->mmc; + + status = readl(priv->sdmmc_base + SDDMA_CCR) & 0x0F; + + if (status != DMA_CCR_EVT_SUCCESS) { + dev_err(priv->dev, "DMA Error: Status = %d\n", status); + priv->req->data->error = -ETIMEDOUT; + complete(priv->comp_dma); + return IRQ_HANDLED; + } + + priv->req->data->error = 0; + + wmt_mci_disable_dma(priv); + + complete(priv->comp_dma); + + if (priv->comp_cmd) { + if (completion_done(priv->comp_cmd)) { + /* + * if the command (regular) interrupt has already + * completed, finish off the request otherwise we wait + * for the command interrupt and finish from there. + */ + wmt_complete_data_request(priv); + } + } + + return IRQ_HANDLED; +} + +static irqreturn_t wmt_mci_regular_isr(int irq_num, void *data) +{ + struct wmt_mci_priv *priv; + u32 status0; + u32 status1; + u32 status2; + u32 reg_tmp; + int cmd_done; + + priv = (struct wmt_mci_priv *)data; + cmd_done = 0; + status0 = readb(priv->sdmmc_base + SDMMC_STS0); + status1 = readb(priv->sdmmc_base + SDMMC_STS1); + status2 = readb(priv->sdmmc_base + SDMMC_STS2); + + /* Check for card insertion */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_INTMASK0); + if ((reg_tmp & INT0_DI_INT_EN) && (status0 & STS0_DEVICE_INS)) { + mmc_detect_change(priv->mmc, 0); + if (priv->cmd) + priv->cmd->error = -ETIMEDOUT; + if (priv->comp_cmd) + complete(priv->comp_cmd); + if (priv->comp_dma) { + wmt_mci_disable_dma(priv); + complete(priv->comp_dma); + } + writeb(STS0_DEVICE_INS, priv->sdmmc_base + SDMMC_STS0); + return IRQ_HANDLED; + } + + if ((!priv->req->data) || + ((priv->req->data->stop) && (priv->cmd == priv->req->data->stop))) { + /* handle non-data & stop_transmission requests */ + if (status1 & STS1_CMDRSP_DONE) { + priv->cmd->error = 0; + cmd_done = 1; + } else if ((status1 & STS1_RSP_TIMEOUT) || + (status1 & STS1_DATA_TIMEOUT)) { + priv->cmd->error = -ETIMEDOUT; + cmd_done = 1; + } + + if (cmd_done) { + priv->comp_cmd = NULL; + + if (!priv->cmd->error) + wmt_mci_read_response(priv->mmc); + + priv->cmd = NULL; + + mmc_request_done(priv->mmc, priv->req); + } + } else { + /* handle data requests */ + if (status1 & STS1_CMDRSP_DONE) { + if (priv->cmd) + priv->cmd->error = 0; + if (priv->comp_cmd) + complete(priv->comp_cmd); + } + + if ((status1 & STS1_RSP_TIMEOUT) || + (status1 & STS1_DATA_TIMEOUT)) { + if (priv->cmd) + priv->cmd->error = -ETIMEDOUT; + if (priv->comp_cmd) + complete(priv->comp_cmd); + if (priv->comp_dma) { + wmt_mci_disable_dma(priv); + complete(priv->comp_dma); + } + } + + if (priv->comp_dma) { + /* + * If the dma interrupt has already completed, finish + * off the request; otherwise we wait for the DMA + * interrupt and finish from there. + */ + if (completion_done(priv->comp_dma)) + wmt_complete_data_request(priv); + } + } + + writeb(status0, priv->sdmmc_base + SDMMC_STS0); + writeb(status1, priv->sdmmc_base + SDMMC_STS1); + writeb(status2, priv->sdmmc_base + SDMMC_STS2); + + return IRQ_HANDLED; +} + +static void wmt_reset_hardware(struct mmc_host *mmc) +{ + struct wmt_mci_priv *priv; + u32 reg_tmp; + + priv = mmc_priv(mmc); + + /* reset controller */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + SDMMC_BUSMODE); + + /* reset response FIFO */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR); + writeb(reg_tmp | CTLR_FIFO_RESET, priv->sdmmc_base + SDMMC_CTLR); + + /* enable GPI pin to detect card */ + writew(BLKL_INT_ENABLE | BLKL_GPI_CD, priv->sdmmc_base + SDMMC_BLKLEN); + + /* clear interrupt status */ + writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); + + /* setup interrupts */ + writeb(INT0_CD_INT_EN | INT0_DI_INT_EN, priv->sdmmc_base + + SDMMC_INTMASK0); + writeb(INT1_DATA_TOUT_INT_EN | INT1_CMD_RES_TRAN_DONE_INT_EN | + INT1_CMD_RES_TOUT_INT_EN, priv->sdmmc_base + SDMMC_INTMASK1); + + /* set the DMA timeout */ + writew(8191, priv->sdmmc_base + SDMMC_DMATIMEOUT); + + /* auto clock freezing enable */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_STS2); + writeb(reg_tmp | STS2_DIS_FORCECLK, priv->sdmmc_base + SDMMC_STS2); + + /* set a default clock speed of 400Khz */ + clk_set_rate(priv->clk_sdmmc, 400000); +} + +static int wmt_dma_init(struct mmc_host *mmc) +{ + struct wmt_mci_priv *priv; + + priv = mmc_priv(mmc); + + writel(DMA_GCR_SOFT_RESET, priv->sdmmc_base + SDDMA_GCR); + writel(DMA_GCR_DMA_EN, priv->sdmmc_base + SDDMA_GCR); + if ((readl(priv->sdmmc_base + SDDMA_GCR) & DMA_GCR_DMA_EN) != 0) + return 0; + else + return 1; +} + +static void wmt_dma_init_descriptor(struct wmt_dma_descriptor *desc, + u16 req_count, u32 buffer_addr, u32 branch_addr, int end) +{ + desc->flags = 0x40000000 | req_count; + if (end) + desc->flags |= 0x80000000; + desc->data_buffer_addr = buffer_addr; + desc->branch_addr = branch_addr; +} + +static void wmt_dma_config(struct mmc_host *mmc, u32 descaddr, u8 dir) +{ + struct wmt_mci_priv *priv; + u32 reg_tmp; + + priv = mmc_priv(mmc); + + /* Enable DMA Interrupts */ + writel(DMA_IER_INT_EN, priv->sdmmc_base + SDDMA_IER); + + /* Write DMA Descriptor Pointer Register */ + writel(descaddr, priv->sdmmc_base + SDDMA_DESPR); + + writel(0x00, priv->sdmmc_base + SDDMA_CCR); + + if (dir == PDMA_WRITE) { + reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR); + writel(reg_tmp & DMA_CCR_IF_TO_PERIPHERAL, priv->sdmmc_base + + SDDMA_CCR); + } else { + reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR); + writel(reg_tmp | DMA_CCR_PERIPHERAL_TO_IF, priv->sdmmc_base + + SDDMA_CCR); + } +} + +static void wmt_dma_start(struct wmt_mci_priv *priv) +{ + u32 reg_tmp; + + reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR); + writel(reg_tmp | DMA_CCR_RUN, priv->sdmmc_base + SDDMA_CCR); +} + +static void wmt_mci_request(struct mmc_host *mmc, struct mmc_request *req) +{ + struct wmt_mci_priv *priv; + struct wmt_dma_descriptor *desc; + u8 command; + u8 cmdtype; + u32 arg; + u8 rsptype; + u32 reg_tmp; + + struct scatterlist *sg; + int i; + int sg_cnt; + int offset; + u32 dma_address; + int desc_cnt; + + priv = mmc_priv(mmc); + priv->req = req; + + /* + * Use the cmd variable to pass a pointer to the resp[] structure + * This is required on multi-block requests to pass the pointer to the + * stop command + */ + priv->cmd = req->cmd; + + command = req->cmd->opcode; + arg = req->cmd->arg; + rsptype = mmc_resp_type(req->cmd); + cmdtype = 0; + + /* rsptype=7 only valid for SPI commands - should be =2 for SD */ + if (rsptype == 7) + rsptype = 2; + /* rsptype=21 is R1B, convert for controller */ + if (rsptype == 21) + rsptype = 9; + + if (!req->data) { + wmt_mci_send_command(mmc, command, cmdtype, arg, rsptype); + wmt_mci_start_command(priv); + /* completion is now handled in the regular_isr() */ + } + if (req->data) { + priv->comp_cmd = &priv->cmdcomp; + init_completion(priv->comp_cmd); + + wmt_dma_init(mmc); + + /* set controller data length */ + reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); + writew((reg_tmp & 0xF800) | (req->data->blksz - 1), + priv->sdmmc_base + SDMMC_BLKLEN); + + /* set controller block count */ + writew(req->data->blocks, priv->sdmmc_base + SDMMC_BLKCNT); + + desc = (struct wmt_dma_descriptor *)priv->dma_desc_buffer; + + if (req->data->flags & MMC_DATA_WRITE) { + sg_cnt = dma_map_sg(mmc_dev(mmc), req->data->sg, + req->data->sg_len, DMA_TO_DEVICE); + cmdtype = 1; + if (req->data->blocks > 1) + cmdtype = 3; + } else { + sg_cnt = dma_map_sg(mmc_dev(mmc), req->data->sg, + req->data->sg_len, DMA_FROM_DEVICE); + cmdtype = 2; + if (req->data->blocks > 1) + cmdtype = 4; + } + + dma_address = priv->dma_desc_device_addr + 16; + desc_cnt = 0; + + for_each_sg(req->data->sg, sg, sg_cnt, i) { + offset = 0; + while (offset < sg_dma_len(sg)) { + wmt_dma_init_descriptor(desc, req->data->blksz, + sg_dma_address(sg)+offset, + dma_address, 0); + desc++; + desc_cnt++; + offset += req->data->blksz; + dma_address += 16; + if (desc_cnt == req->data->blocks) + break; + } + } + desc--; + desc->flags |= 0x80000000; + + if (req->data->flags & MMC_DATA_WRITE) + wmt_dma_config(mmc, priv->dma_desc_device_addr, + PDMA_WRITE); + else + wmt_dma_config(mmc, priv->dma_desc_device_addr, + PDMA_READ); + + wmt_mci_send_command(mmc, command, cmdtype, arg, rsptype); + + priv->comp_dma = &priv->datacomp; + init_completion(priv->comp_dma); + + wmt_dma_start(priv); + wmt_mci_start_command(priv); + } +} + +static void wmt_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct wmt_mci_priv *priv; + u32 reg_tmp; + + priv = mmc_priv(mmc); + + if (ios->power_mode == MMC_POWER_UP) { + wmt_reset_hardware(mmc); + + wmt_set_sd_power(priv, WMT_SD_POWER_ON); + } + if (ios->power_mode == MMC_POWER_OFF) + wmt_set_sd_power(priv, WMT_SD_POWER_OFF); + + if (ios->clock != 0) + clk_set_rate(priv->clk_sdmmc, ios->clock); + + switch (ios->bus_width) { + case MMC_BUS_WIDTH_8: + reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL); + writeb(reg_tmp | 0x04, priv->sdmmc_base + SDMMC_EXTCTRL); + break; + case MMC_BUS_WIDTH_4: + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_FOURBIT_MODE, priv->sdmmc_base + + SDMMC_BUSMODE); + + reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL); + writeb(reg_tmp & 0xFB, priv->sdmmc_base + SDMMC_EXTCTRL); + break; + case MMC_BUS_WIDTH_1: + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp & BM_ONEBIT_MASK, priv->sdmmc_base + + SDMMC_BUSMODE); + + reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL); + writeb(reg_tmp & 0xFB, priv->sdmmc_base + SDMMC_EXTCTRL); + break; + } +} + +static int wmt_mci_get_ro(struct mmc_host *mmc) +{ + struct wmt_mci_priv *priv = mmc_priv(mmc); + + return !(readb(priv->sdmmc_base + SDMMC_STS0) & STS0_WRITE_PROTECT); +} + +static int wmt_mci_get_cd(struct mmc_host *mmc) +{ + struct wmt_mci_priv *priv = mmc_priv(mmc); + u32 cd = (readb(priv->sdmmc_base + SDMMC_STS0) & STS0_CD_GPI) >> 3; + + return !(cd ^ priv->cd_inverted); +} + +static struct mmc_host_ops wmt_mci_ops = { + .request = wmt_mci_request, + .set_ios = wmt_mci_set_ios, + .get_ro = wmt_mci_get_ro, + .get_cd = wmt_mci_get_cd, +}; + +/* Controller capabilities */ +static struct wmt_mci_caps wm8505_caps = { + .f_min = 390425, + .f_max = 50000000, + .ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34, + .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED | + MMC_CAP_SD_HIGHSPEED, + .max_seg_size = 65024, + .max_segs = 128, + .max_blk_size = 2048, +}; + +static struct of_device_id wmt_mci_dt_ids[] = { + { .compatible = "wm,wm8505-sdhc", .data = &wm8505_caps }, + { /* Sentinel */ }, +}; + +static int __devinit wmt_mci_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct wmt_mci_priv *priv; + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *of_id = + of_match_device(wmt_mci_dt_ids, &pdev->dev); + const struct wmt_mci_caps *wmt_caps = of_id->data; + int ret; + int regular_irq, dma_irq; + + if (!of_id || !of_id->data) { + dev_err(&pdev->dev, "Controller capabilities data missing\n"); + return -EFAULT; + } + + if (!np) { + dev_err(&pdev->dev, "Missing SDMMC description in devicetree\n"); + return -EFAULT; + } + + regular_irq = irq_of_parse_and_map(np, 0); + dma_irq = irq_of_parse_and_map(np, 1); + + if (!regular_irq || !dma_irq) { + dev_err(&pdev->dev, "Getting IRQs failed!\n"); + ret = -ENXIO; + goto fail1; + } + + mmc = mmc_alloc_host(sizeof(struct wmt_mci_priv), &pdev->dev); + if (!mmc) { + dev_err(&pdev->dev, "Failed to allocate mmc_host\n"); + ret = -ENOMEM; + goto fail1; + } + + mmc->ops = &wmt_mci_ops; + mmc->f_min = wmt_caps->f_min; + mmc->f_max = wmt_caps->f_max; + mmc->ocr_avail = wmt_caps->ocr_avail; + mmc->caps = wmt_caps->caps; + + mmc->max_seg_size = wmt_caps->max_seg_size; + mmc->max_segs = wmt_caps->max_segs; + mmc->max_blk_size = wmt_caps->max_blk_size; + + mmc->max_req_size = (16*512*mmc->max_segs); + mmc->max_blk_count = mmc->max_req_size / 512; + + priv = mmc_priv(mmc); + priv->mmc = mmc; + priv->dev = &pdev->dev; + + priv->power_inverted = 0; + priv->cd_inverted = 0; + + if (of_get_property(np, "sdon-inverted", NULL)) + priv->power_inverted = 1; + if (of_get_property(np, "cd-inverted", NULL)) + priv->cd_inverted = 1; + + priv->sdmmc_base = of_iomap(np, 0); + if (!priv->sdmmc_base) { + dev_err(&pdev->dev, "Failed to map IO space\n"); + ret = -ENOMEM; + goto fail2; + } + + priv->irq_regular = regular_irq; + priv->irq_dma = dma_irq; + + ret = request_irq(regular_irq, wmt_mci_regular_isr, 0, "sdmmc", priv); + if (ret) { + dev_err(&pdev->dev, "Register regular IRQ fail\n"); + goto fail3; + } + + ret = request_irq(dma_irq, wmt_mci_dma_isr, 32, "sdmmc", priv); + if (ret) { + dev_err(&pdev->dev, "Register DMA IRQ fail\n"); + goto fail4; + } + + /* alloc some DMA buffers for descriptors/transfers */ + priv->dma_desc_buffer = dma_alloc_coherent(&pdev->dev, + mmc->max_blk_count * 16, + &priv->dma_desc_device_addr, + 208); + if (!priv->dma_desc_buffer) { + dev_err(&pdev->dev, "DMA alloc fail\n"); + ret = -EPERM; + goto fail5; + } + + platform_set_drvdata(pdev, mmc); + + priv->clk_sdmmc = of_clk_get(np, 0); + if (IS_ERR(priv->clk_sdmmc)) { + dev_err(&pdev->dev, "Error getting clock\n"); + ret = PTR_ERR(priv->clk_sdmmc); + goto fail5; + } + + clk_prepare_enable(priv->clk_sdmmc); + + /* configure the controller to a known 'ready' state */ + wmt_reset_hardware(mmc); + + mmc_add_host(mmc); + + dev_info(&pdev->dev, "WMT SDHC Controller initialized\n"); + + return 0; +fail5: + free_irq(dma_irq, priv); +fail4: + free_irq(regular_irq, priv); +fail3: + iounmap(priv->sdmmc_base); +fail2: + mmc_free_host(mmc); +fail1: + return ret; +} + +static int __devexit wmt_mci_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct wmt_mci_priv *priv; + struct resource *res; + u32 reg_tmp; + + mmc = platform_get_drvdata(pdev); + priv = mmc_priv(mmc); + + /* reset SD controller */ + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writel(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + SDMMC_BUSMODE); + reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); + writew(reg_tmp & ~(0xA000), priv->sdmmc_base + SDMMC_BLKLEN); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); + + /* release the dma buffers */ + dma_free_coherent(&pdev->dev, priv->mmc->max_blk_count * 16, + priv->dma_desc_buffer, priv->dma_desc_device_addr); + + mmc_remove_host(mmc); + + free_irq(priv->irq_regular, priv); + free_irq(priv->irq_dma, priv); + + iounmap(priv->sdmmc_base); + + clk_disable_unprepare(priv->clk_sdmmc); + clk_put(priv->clk_sdmmc); + + 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); + + dev_info(&pdev->dev, "WMT MCI device removed\n"); + + return 0; +} + +#ifdef CONFIG_PM +static int wmt_mci_suspend(struct device *dev) +{ + u32 reg_tmp; + struct platform_device *pdev = to_platform_device(dev); + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct wmt_mci_priv *priv; + int ret; + + if (!mmc) + return 0; + + priv = mmc_priv(mmc); + ret = mmc_suspend_host(mmc); + + if (!ret) { + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + + SDMMC_BUSMODE); + + reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); + writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN); + + writeb(0xFF, priv->sdmmc_base + SDMMC_STS0); + writeb(0xFF, priv->sdmmc_base + SDMMC_STS1); + + clk_disable(priv->clk_sdmmc); + } + return ret; +} + +static int wmt_mci_resume(struct device *dev) +{ + u32 reg_tmp; + struct platform_device *pdev = to_platform_device(dev); + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct wmt_mci_priv *priv; + int ret = 0; + + if (mmc) { + priv = mmc_priv(mmc); + clk_enable(priv->clk_sdmmc); + + reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); + writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + + SDMMC_BUSMODE); + + reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN); + writew(reg_tmp | (BLKL_GPI_CD | BLKL_INT_ENABLE), + priv->sdmmc_base + SDMMC_BLKLEN); + + reg_tmp = readb(priv->sdmmc_base + SDMMC_INTMASK0); + writeb(reg_tmp | INT0_DI_INT_EN, priv->sdmmc_base + + SDMMC_INTMASK0); + + ret = mmc_resume_host(mmc); + } + + return ret; +} + +static const struct dev_pm_ops wmt_mci_pm = { + .suspend = wmt_mci_suspend, + .resume = wmt_mci_resume, +}; + +#define wmt_mci_pm_ops (&wmt_mci_pm) + +#else /* !CONFIG_PM */ + +#define wmt_mci_pm_ops NULL + +#endif + +static struct platform_driver wmt_mci_driver = { + .probe = wmt_mci_probe, + .remove = __exit_p(wmt_mci_remove), + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .pm = wmt_mci_pm_ops, + .of_match_table = wmt_mci_dt_ids, + }, +}; + +module_platform_driver(wmt_mci_driver); + +MODULE_DESCRIPTION("Wondermedia MMC/SD Driver"); +MODULE_AUTHOR("Tony Prisk"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, wmt_mci_dt_ids); |