161bfbdb8SLars-Peter Clausen /* 261bfbdb8SLars-Peter Clausen * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> 361bfbdb8SLars-Peter Clausen * JZ4740 SD/MMC controller driver 461bfbdb8SLars-Peter Clausen * 561bfbdb8SLars-Peter Clausen * This program is free software; you can redistribute it and/or modify it 661bfbdb8SLars-Peter Clausen * under the terms of the GNU General Public License as published by the 761bfbdb8SLars-Peter Clausen * Free Software Foundation; either version 2 of the License, or (at your 861bfbdb8SLars-Peter Clausen * option) any later version. 961bfbdb8SLars-Peter Clausen * 1061bfbdb8SLars-Peter Clausen * You should have received a copy of the GNU General Public License along 1161bfbdb8SLars-Peter Clausen * with this program; if not, write to the Free Software Foundation, Inc., 1261bfbdb8SLars-Peter Clausen * 675 Mass Ave, Cambridge, MA 02139, USA. 1361bfbdb8SLars-Peter Clausen * 1461bfbdb8SLars-Peter Clausen */ 1561bfbdb8SLars-Peter Clausen 1661bfbdb8SLars-Peter Clausen #include <linux/mmc/host.h> 1758e300afSLars-Peter Clausen #include <linux/mmc/slot-gpio.h> 183119cbdaSJamie Iles #include <linux/err.h> 1961bfbdb8SLars-Peter Clausen #include <linux/io.h> 2061bfbdb8SLars-Peter Clausen #include <linux/irq.h> 2161bfbdb8SLars-Peter Clausen #include <linux/interrupt.h> 2261bfbdb8SLars-Peter Clausen #include <linux/module.h> 23fa5ed6bcSPaul Cercueil #include <linux/pinctrl/consumer.h> 2461bfbdb8SLars-Peter Clausen #include <linux/platform_device.h> 2561bfbdb8SLars-Peter Clausen #include <linux/delay.h> 2661bfbdb8SLars-Peter Clausen #include <linux/scatterlist.h> 2761bfbdb8SLars-Peter Clausen #include <linux/clk.h> 2861bfbdb8SLars-Peter Clausen 2961bfbdb8SLars-Peter Clausen #include <linux/bitops.h> 3061bfbdb8SLars-Peter Clausen #include <linux/gpio.h> 3161bfbdb8SLars-Peter Clausen #include <asm/cacheflush.h> 3261bfbdb8SLars-Peter Clausen #include <linux/dma-mapping.h> 337ca27a6fSApelete Seketeli #include <linux/dmaengine.h> 3461bfbdb8SLars-Peter Clausen 357ca27a6fSApelete Seketeli #include <asm/mach-jz4740/dma.h> 3661bfbdb8SLars-Peter Clausen #include <asm/mach-jz4740/jz4740_mmc.h> 3761bfbdb8SLars-Peter Clausen 3861bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_STRPCL 0x00 3961bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_STATUS 0x04 4061bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_CLKRT 0x08 4161bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_CMDAT 0x0C 4261bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RESTO 0x10 4361bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RDTO 0x14 4461bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_BLKLEN 0x18 4561bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_NOB 0x1C 4661bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_SNOB 0x20 4761bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_IMASK 0x24 4861bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_IREG 0x28 4961bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_CMD 0x2C 5061bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_ARG 0x30 5161bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RESP_FIFO 0x34 5261bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RXFIFO 0x38 5361bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_TXFIFO 0x3C 5461bfbdb8SLars-Peter Clausen 5561bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7) 5661bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_EXIT_TRANSFER BIT(6) 5761bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_START_READWAIT BIT(5) 5861bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_STOP_READWAIT BIT(4) 5961bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_RESET BIT(3) 6061bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_START_OP BIT(2) 6161bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_CLOCK_CONTROL (BIT(1) | BIT(0)) 6261bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_CLOCK_STOP BIT(0) 6361bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_CLOCK_START BIT(1) 6461bfbdb8SLars-Peter Clausen 6561bfbdb8SLars-Peter Clausen 6661bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_IS_RESETTING BIT(15) 6761bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_SDIO_INT_ACTIVE BIT(14) 6861bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_PRG_DONE BIT(13) 6961bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_TRAN_DONE BIT(12) 7061bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_END_CMD_RES BIT(11) 7161bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_FIFO_AFULL BIT(10) 7261bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_IS_READWAIT BIT(9) 7361bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CLK_EN BIT(8) 7461bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_FIFO_FULL BIT(7) 7561bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_FIFO_EMPTY BIT(6) 7661bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CRC_RES_ERR BIT(5) 7761bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CRC_READ_ERROR BIT(4) 7861bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_TIMEOUT_WRITE BIT(3) 7961bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CRC_WRITE_ERROR BIT(2) 8061bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_TIMEOUT_RES BIT(1) 8161bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_TIMEOUT_READ BIT(0) 8261bfbdb8SLars-Peter Clausen 8361bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_READ_ERROR_MASK (BIT(4) | BIT(0)) 8461bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_WRITE_ERROR_MASK (BIT(3) | BIT(2)) 8561bfbdb8SLars-Peter Clausen 8661bfbdb8SLars-Peter Clausen 8761bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_IO_ABORT BIT(11) 8861bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_BUS_WIDTH_4BIT BIT(10) 8961bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_DMA_EN BIT(8) 9061bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_INIT BIT(7) 9161bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_BUSY BIT(6) 9261bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_STREAM BIT(5) 9361bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_WRITE BIT(4) 9461bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_DATA_EN BIT(3) 9561bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RESPONSE_FORMAT (BIT(2) | BIT(1) | BIT(0)) 9661bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RSP_R1 1 9761bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RSP_R2 2 9861bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RSP_R3 3 9961bfbdb8SLars-Peter Clausen 10061bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_SDIO BIT(7) 10161bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_TXFIFO_WR_REQ BIT(6) 10261bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_RXFIFO_RD_REQ BIT(5) 10361bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_END_CMD_RES BIT(2) 10461bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_PRG_DONE BIT(1) 10561bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_DATA_TRAN_DONE BIT(0) 10661bfbdb8SLars-Peter Clausen 10761bfbdb8SLars-Peter Clausen 10861bfbdb8SLars-Peter Clausen #define JZ_MMC_CLK_RATE 24000000 10961bfbdb8SLars-Peter Clausen 11061bfbdb8SLars-Peter Clausen enum jz4740_mmc_state { 11161bfbdb8SLars-Peter Clausen JZ4740_MMC_STATE_READ_RESPONSE, 11261bfbdb8SLars-Peter Clausen JZ4740_MMC_STATE_TRANSFER_DATA, 11361bfbdb8SLars-Peter Clausen JZ4740_MMC_STATE_SEND_STOP, 11461bfbdb8SLars-Peter Clausen JZ4740_MMC_STATE_DONE, 11561bfbdb8SLars-Peter Clausen }; 11661bfbdb8SLars-Peter Clausen 117bb2f4592SApelete Seketeli struct jz4740_mmc_host_next { 118bb2f4592SApelete Seketeli int sg_len; 119bb2f4592SApelete Seketeli s32 cookie; 120bb2f4592SApelete Seketeli }; 121bb2f4592SApelete Seketeli 12261bfbdb8SLars-Peter Clausen struct jz4740_mmc_host { 12361bfbdb8SLars-Peter Clausen struct mmc_host *mmc; 12461bfbdb8SLars-Peter Clausen struct platform_device *pdev; 12561bfbdb8SLars-Peter Clausen struct jz4740_mmc_platform_data *pdata; 12661bfbdb8SLars-Peter Clausen struct clk *clk; 12761bfbdb8SLars-Peter Clausen 12861bfbdb8SLars-Peter Clausen int irq; 12961bfbdb8SLars-Peter Clausen int card_detect_irq; 13061bfbdb8SLars-Peter Clausen 13161bfbdb8SLars-Peter Clausen void __iomem *base; 1327ca27a6fSApelete Seketeli struct resource *mem_res; 13361bfbdb8SLars-Peter Clausen struct mmc_request *req; 13461bfbdb8SLars-Peter Clausen struct mmc_command *cmd; 13561bfbdb8SLars-Peter Clausen 13661bfbdb8SLars-Peter Clausen unsigned long waiting; 13761bfbdb8SLars-Peter Clausen 13861bfbdb8SLars-Peter Clausen uint32_t cmdat; 13961bfbdb8SLars-Peter Clausen 14061bfbdb8SLars-Peter Clausen uint16_t irq_mask; 14161bfbdb8SLars-Peter Clausen 14261bfbdb8SLars-Peter Clausen spinlock_t lock; 14361bfbdb8SLars-Peter Clausen 14461bfbdb8SLars-Peter Clausen struct timer_list timeout_timer; 14561bfbdb8SLars-Peter Clausen struct sg_mapping_iter miter; 14661bfbdb8SLars-Peter Clausen enum jz4740_mmc_state state; 1477ca27a6fSApelete Seketeli 1487ca27a6fSApelete Seketeli /* DMA support */ 1497ca27a6fSApelete Seketeli struct dma_chan *dma_rx; 1507ca27a6fSApelete Seketeli struct dma_chan *dma_tx; 151bb2f4592SApelete Seketeli struct jz4740_mmc_host_next next_data; 1527ca27a6fSApelete Seketeli bool use_dma; 1537ca27a6fSApelete Seketeli int sg_len; 1547ca27a6fSApelete Seketeli 1557ca27a6fSApelete Seketeli /* The DMA trigger level is 8 words, that is to say, the DMA read 1567ca27a6fSApelete Seketeli * trigger is when data words in MSC_RXFIFO is >= 8 and the DMA write 1577ca27a6fSApelete Seketeli * trigger is when data words in MSC_TXFIFO is < 8. 1587ca27a6fSApelete Seketeli */ 1597ca27a6fSApelete Seketeli #define JZ4740_MMC_FIFO_HALF_SIZE 8 16061bfbdb8SLars-Peter Clausen }; 16161bfbdb8SLars-Peter Clausen 1627ca27a6fSApelete Seketeli /*----------------------------------------------------------------------------*/ 1637ca27a6fSApelete Seketeli /* DMA infrastructure */ 1647ca27a6fSApelete Seketeli 1657ca27a6fSApelete Seketeli static void jz4740_mmc_release_dma_channels(struct jz4740_mmc_host *host) 1667ca27a6fSApelete Seketeli { 1677ca27a6fSApelete Seketeli if (!host->use_dma) 1687ca27a6fSApelete Seketeli return; 1697ca27a6fSApelete Seketeli 1707ca27a6fSApelete Seketeli dma_release_channel(host->dma_tx); 1717ca27a6fSApelete Seketeli dma_release_channel(host->dma_rx); 1727ca27a6fSApelete Seketeli } 1737ca27a6fSApelete Seketeli 1747ca27a6fSApelete Seketeli static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host) 1757ca27a6fSApelete Seketeli { 1767ca27a6fSApelete Seketeli dma_cap_mask_t mask; 1777ca27a6fSApelete Seketeli 1787ca27a6fSApelete Seketeli dma_cap_zero(mask); 1797ca27a6fSApelete Seketeli dma_cap_set(DMA_SLAVE, mask); 1807ca27a6fSApelete Seketeli 1817ca27a6fSApelete Seketeli host->dma_tx = dma_request_channel(mask, NULL, host); 1827ca27a6fSApelete Seketeli if (!host->dma_tx) { 1837ca27a6fSApelete Seketeli dev_err(mmc_dev(host->mmc), "Failed to get dma_tx channel\n"); 1847ca27a6fSApelete Seketeli return -ENODEV; 1857ca27a6fSApelete Seketeli } 1867ca27a6fSApelete Seketeli 1877ca27a6fSApelete Seketeli host->dma_rx = dma_request_channel(mask, NULL, host); 1887ca27a6fSApelete Seketeli if (!host->dma_rx) { 1897ca27a6fSApelete Seketeli dev_err(mmc_dev(host->mmc), "Failed to get dma_rx channel\n"); 1907ca27a6fSApelete Seketeli goto free_master_write; 1917ca27a6fSApelete Seketeli } 1927ca27a6fSApelete Seketeli 193bb2f4592SApelete Seketeli /* Initialize DMA pre request cookie */ 194bb2f4592SApelete Seketeli host->next_data.cookie = 1; 195bb2f4592SApelete Seketeli 1967ca27a6fSApelete Seketeli return 0; 1977ca27a6fSApelete Seketeli 1987ca27a6fSApelete Seketeli free_master_write: 1997ca27a6fSApelete Seketeli dma_release_channel(host->dma_tx); 2007ca27a6fSApelete Seketeli return -ENODEV; 2017ca27a6fSApelete Seketeli } 2027ca27a6fSApelete Seketeli 203bb2f4592SApelete Seketeli static inline struct dma_chan *jz4740_mmc_get_dma_chan(struct jz4740_mmc_host *host, 204bb2f4592SApelete Seketeli struct mmc_data *data) 205bb2f4592SApelete Seketeli { 206bb2f4592SApelete Seketeli return (data->flags & MMC_DATA_READ) ? host->dma_rx : host->dma_tx; 207bb2f4592SApelete Seketeli } 208bb2f4592SApelete Seketeli 2097ca27a6fSApelete Seketeli static void jz4740_mmc_dma_unmap(struct jz4740_mmc_host *host, 2107ca27a6fSApelete Seketeli struct mmc_data *data) 2117ca27a6fSApelete Seketeli { 212bb2f4592SApelete Seketeli struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 213feeef096SHeiner Kallweit enum dma_data_direction dir = mmc_get_dma_dir(data); 2147ca27a6fSApelete Seketeli 2157ca27a6fSApelete Seketeli dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir); 2167ca27a6fSApelete Seketeli } 2177ca27a6fSApelete Seketeli 218bb2f4592SApelete Seketeli /* Prepares DMA data for current/next transfer, returns non-zero on failure */ 219bb2f4592SApelete Seketeli static int jz4740_mmc_prepare_dma_data(struct jz4740_mmc_host *host, 220bb2f4592SApelete Seketeli struct mmc_data *data, 221bb2f4592SApelete Seketeli struct jz4740_mmc_host_next *next, 222bb2f4592SApelete Seketeli struct dma_chan *chan) 223bb2f4592SApelete Seketeli { 224bb2f4592SApelete Seketeli struct jz4740_mmc_host_next *next_data = &host->next_data; 225feeef096SHeiner Kallweit enum dma_data_direction dir = mmc_get_dma_dir(data); 226bb2f4592SApelete Seketeli int sg_len; 227bb2f4592SApelete Seketeli 228bb2f4592SApelete Seketeli if (!next && data->host_cookie && 229bb2f4592SApelete Seketeli data->host_cookie != host->next_data.cookie) { 230bb2f4592SApelete Seketeli dev_warn(mmc_dev(host->mmc), 231bb2f4592SApelete Seketeli "[%s] invalid cookie: data->host_cookie %d host->next_data.cookie %d\n", 232bb2f4592SApelete Seketeli __func__, 233bb2f4592SApelete Seketeli data->host_cookie, 234bb2f4592SApelete Seketeli host->next_data.cookie); 235bb2f4592SApelete Seketeli data->host_cookie = 0; 236bb2f4592SApelete Seketeli } 237bb2f4592SApelete Seketeli 238bb2f4592SApelete Seketeli /* Check if next job is already prepared */ 239bb2f4592SApelete Seketeli if (next || data->host_cookie != host->next_data.cookie) { 240bb2f4592SApelete Seketeli sg_len = dma_map_sg(chan->device->dev, 241bb2f4592SApelete Seketeli data->sg, 242bb2f4592SApelete Seketeli data->sg_len, 243bb2f4592SApelete Seketeli dir); 244bb2f4592SApelete Seketeli 245bb2f4592SApelete Seketeli } else { 246bb2f4592SApelete Seketeli sg_len = next_data->sg_len; 247bb2f4592SApelete Seketeli next_data->sg_len = 0; 248bb2f4592SApelete Seketeli } 249bb2f4592SApelete Seketeli 250bb2f4592SApelete Seketeli if (sg_len <= 0) { 251bb2f4592SApelete Seketeli dev_err(mmc_dev(host->mmc), 252bb2f4592SApelete Seketeli "Failed to map scatterlist for DMA operation\n"); 253bb2f4592SApelete Seketeli return -EINVAL; 254bb2f4592SApelete Seketeli } 255bb2f4592SApelete Seketeli 256bb2f4592SApelete Seketeli if (next) { 257bb2f4592SApelete Seketeli next->sg_len = sg_len; 258bb2f4592SApelete Seketeli data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie; 259bb2f4592SApelete Seketeli } else 260bb2f4592SApelete Seketeli host->sg_len = sg_len; 261bb2f4592SApelete Seketeli 262bb2f4592SApelete Seketeli return 0; 263bb2f4592SApelete Seketeli } 264bb2f4592SApelete Seketeli 2657ca27a6fSApelete Seketeli static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host, 2667ca27a6fSApelete Seketeli struct mmc_data *data) 2677ca27a6fSApelete Seketeli { 268bb2f4592SApelete Seketeli int ret; 2697ca27a6fSApelete Seketeli struct dma_chan *chan; 2707ca27a6fSApelete Seketeli struct dma_async_tx_descriptor *desc; 2717ca27a6fSApelete Seketeli struct dma_slave_config conf = { 2727ca27a6fSApelete Seketeli .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 2737ca27a6fSApelete Seketeli .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 2747ca27a6fSApelete Seketeli .src_maxburst = JZ4740_MMC_FIFO_HALF_SIZE, 2757ca27a6fSApelete Seketeli .dst_maxburst = JZ4740_MMC_FIFO_HALF_SIZE, 2767ca27a6fSApelete Seketeli }; 2777ca27a6fSApelete Seketeli 278bb2f4592SApelete Seketeli if (data->flags & MMC_DATA_WRITE) { 2797ca27a6fSApelete Seketeli conf.direction = DMA_MEM_TO_DEV; 2807ca27a6fSApelete Seketeli conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO; 2817ca27a6fSApelete Seketeli conf.slave_id = JZ4740_DMA_TYPE_MMC_TRANSMIT; 2827ca27a6fSApelete Seketeli chan = host->dma_tx; 2837ca27a6fSApelete Seketeli } else { 2847ca27a6fSApelete Seketeli conf.direction = DMA_DEV_TO_MEM; 2857ca27a6fSApelete Seketeli conf.src_addr = host->mem_res->start + JZ_REG_MMC_RXFIFO; 2867ca27a6fSApelete Seketeli conf.slave_id = JZ4740_DMA_TYPE_MMC_RECEIVE; 2877ca27a6fSApelete Seketeli chan = host->dma_rx; 2887ca27a6fSApelete Seketeli } 2897ca27a6fSApelete Seketeli 290bb2f4592SApelete Seketeli ret = jz4740_mmc_prepare_dma_data(host, data, NULL, chan); 291bb2f4592SApelete Seketeli if (ret) 292bb2f4592SApelete Seketeli return ret; 2937ca27a6fSApelete Seketeli 2947ca27a6fSApelete Seketeli dmaengine_slave_config(chan, &conf); 2957ca27a6fSApelete Seketeli desc = dmaengine_prep_slave_sg(chan, 2967ca27a6fSApelete Seketeli data->sg, 2977ca27a6fSApelete Seketeli host->sg_len, 2987ca27a6fSApelete Seketeli conf.direction, 2997ca27a6fSApelete Seketeli DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 3007ca27a6fSApelete Seketeli if (!desc) { 3017ca27a6fSApelete Seketeli dev_err(mmc_dev(host->mmc), 3027ca27a6fSApelete Seketeli "Failed to allocate DMA %s descriptor", 3037ca27a6fSApelete Seketeli conf.direction == DMA_MEM_TO_DEV ? "TX" : "RX"); 3047ca27a6fSApelete Seketeli goto dma_unmap; 3057ca27a6fSApelete Seketeli } 3067ca27a6fSApelete Seketeli 3077ca27a6fSApelete Seketeli dmaengine_submit(desc); 3087ca27a6fSApelete Seketeli dma_async_issue_pending(chan); 3097ca27a6fSApelete Seketeli 3107ca27a6fSApelete Seketeli return 0; 3117ca27a6fSApelete Seketeli 3127ca27a6fSApelete Seketeli dma_unmap: 3137ca27a6fSApelete Seketeli jz4740_mmc_dma_unmap(host, data); 3147ca27a6fSApelete Seketeli return -ENOMEM; 3157ca27a6fSApelete Seketeli } 3167ca27a6fSApelete Seketeli 317bb2f4592SApelete Seketeli static void jz4740_mmc_pre_request(struct mmc_host *mmc, 318d3c6aac3SLinus Walleij struct mmc_request *mrq) 319bb2f4592SApelete Seketeli { 320bb2f4592SApelete Seketeli struct jz4740_mmc_host *host = mmc_priv(mmc); 321bb2f4592SApelete Seketeli struct mmc_data *data = mrq->data; 322bb2f4592SApelete Seketeli struct jz4740_mmc_host_next *next_data = &host->next_data; 323bb2f4592SApelete Seketeli 324bb2f4592SApelete Seketeli BUG_ON(data->host_cookie); 325bb2f4592SApelete Seketeli 326bb2f4592SApelete Seketeli if (host->use_dma) { 327bb2f4592SApelete Seketeli struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 328bb2f4592SApelete Seketeli 329bb2f4592SApelete Seketeli if (jz4740_mmc_prepare_dma_data(host, data, next_data, chan)) 330bb2f4592SApelete Seketeli data->host_cookie = 0; 331bb2f4592SApelete Seketeli } 332bb2f4592SApelete Seketeli } 333bb2f4592SApelete Seketeli 334bb2f4592SApelete Seketeli static void jz4740_mmc_post_request(struct mmc_host *mmc, 335bb2f4592SApelete Seketeli struct mmc_request *mrq, 336bb2f4592SApelete Seketeli int err) 337bb2f4592SApelete Seketeli { 338bb2f4592SApelete Seketeli struct jz4740_mmc_host *host = mmc_priv(mmc); 339bb2f4592SApelete Seketeli struct mmc_data *data = mrq->data; 340bb2f4592SApelete Seketeli 341bb2f4592SApelete Seketeli if (host->use_dma && data->host_cookie) { 342bb2f4592SApelete Seketeli jz4740_mmc_dma_unmap(host, data); 343bb2f4592SApelete Seketeli data->host_cookie = 0; 344bb2f4592SApelete Seketeli } 345bb2f4592SApelete Seketeli 346bb2f4592SApelete Seketeli if (err) { 347bb2f4592SApelete Seketeli struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 348bb2f4592SApelete Seketeli 349bb2f4592SApelete Seketeli dmaengine_terminate_all(chan); 350bb2f4592SApelete Seketeli } 351bb2f4592SApelete Seketeli } 352bb2f4592SApelete Seketeli 3537ca27a6fSApelete Seketeli /*----------------------------------------------------------------------------*/ 3547ca27a6fSApelete Seketeli 35561bfbdb8SLars-Peter Clausen static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host, 35661bfbdb8SLars-Peter Clausen unsigned int irq, bool enabled) 35761bfbdb8SLars-Peter Clausen { 35861bfbdb8SLars-Peter Clausen unsigned long flags; 35961bfbdb8SLars-Peter Clausen 36061bfbdb8SLars-Peter Clausen spin_lock_irqsave(&host->lock, flags); 36161bfbdb8SLars-Peter Clausen if (enabled) 36261bfbdb8SLars-Peter Clausen host->irq_mask &= ~irq; 36361bfbdb8SLars-Peter Clausen else 36461bfbdb8SLars-Peter Clausen host->irq_mask |= irq; 36561bfbdb8SLars-Peter Clausen 36661bfbdb8SLars-Peter Clausen writew(host->irq_mask, host->base + JZ_REG_MMC_IMASK); 367a04f0017SAlex Smith spin_unlock_irqrestore(&host->lock, flags); 36861bfbdb8SLars-Peter Clausen } 36961bfbdb8SLars-Peter Clausen 37061bfbdb8SLars-Peter Clausen static void jz4740_mmc_clock_enable(struct jz4740_mmc_host *host, 37161bfbdb8SLars-Peter Clausen bool start_transfer) 37261bfbdb8SLars-Peter Clausen { 37361bfbdb8SLars-Peter Clausen uint16_t val = JZ_MMC_STRPCL_CLOCK_START; 37461bfbdb8SLars-Peter Clausen 37561bfbdb8SLars-Peter Clausen if (start_transfer) 37661bfbdb8SLars-Peter Clausen val |= JZ_MMC_STRPCL_START_OP; 37761bfbdb8SLars-Peter Clausen 37861bfbdb8SLars-Peter Clausen writew(val, host->base + JZ_REG_MMC_STRPCL); 37961bfbdb8SLars-Peter Clausen } 38061bfbdb8SLars-Peter Clausen 38161bfbdb8SLars-Peter Clausen static void jz4740_mmc_clock_disable(struct jz4740_mmc_host *host) 38261bfbdb8SLars-Peter Clausen { 38361bfbdb8SLars-Peter Clausen uint32_t status; 38461bfbdb8SLars-Peter Clausen unsigned int timeout = 1000; 38561bfbdb8SLars-Peter Clausen 38661bfbdb8SLars-Peter Clausen writew(JZ_MMC_STRPCL_CLOCK_STOP, host->base + JZ_REG_MMC_STRPCL); 38761bfbdb8SLars-Peter Clausen do { 38861bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 38961bfbdb8SLars-Peter Clausen } while (status & JZ_MMC_STATUS_CLK_EN && --timeout); 39061bfbdb8SLars-Peter Clausen } 39161bfbdb8SLars-Peter Clausen 39261bfbdb8SLars-Peter Clausen static void jz4740_mmc_reset(struct jz4740_mmc_host *host) 39361bfbdb8SLars-Peter Clausen { 39461bfbdb8SLars-Peter Clausen uint32_t status; 39561bfbdb8SLars-Peter Clausen unsigned int timeout = 1000; 39661bfbdb8SLars-Peter Clausen 39761bfbdb8SLars-Peter Clausen writew(JZ_MMC_STRPCL_RESET, host->base + JZ_REG_MMC_STRPCL); 39861bfbdb8SLars-Peter Clausen udelay(10); 39961bfbdb8SLars-Peter Clausen do { 40061bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 40161bfbdb8SLars-Peter Clausen } while (status & JZ_MMC_STATUS_IS_RESETTING && --timeout); 40261bfbdb8SLars-Peter Clausen } 40361bfbdb8SLars-Peter Clausen 40461bfbdb8SLars-Peter Clausen static void jz4740_mmc_request_done(struct jz4740_mmc_host *host) 40561bfbdb8SLars-Peter Clausen { 40661bfbdb8SLars-Peter Clausen struct mmc_request *req; 40761bfbdb8SLars-Peter Clausen 40861bfbdb8SLars-Peter Clausen req = host->req; 40961bfbdb8SLars-Peter Clausen host->req = NULL; 41061bfbdb8SLars-Peter Clausen 41161bfbdb8SLars-Peter Clausen mmc_request_done(host->mmc, req); 41261bfbdb8SLars-Peter Clausen } 41361bfbdb8SLars-Peter Clausen 41461bfbdb8SLars-Peter Clausen static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host, 41561bfbdb8SLars-Peter Clausen unsigned int irq) 41661bfbdb8SLars-Peter Clausen { 41761bfbdb8SLars-Peter Clausen unsigned int timeout = 0x800; 41861bfbdb8SLars-Peter Clausen uint16_t status; 41961bfbdb8SLars-Peter Clausen 42061bfbdb8SLars-Peter Clausen do { 42161bfbdb8SLars-Peter Clausen status = readw(host->base + JZ_REG_MMC_IREG); 42261bfbdb8SLars-Peter Clausen } while (!(status & irq) && --timeout); 42361bfbdb8SLars-Peter Clausen 42461bfbdb8SLars-Peter Clausen if (timeout == 0) { 42561bfbdb8SLars-Peter Clausen set_bit(0, &host->waiting); 42661bfbdb8SLars-Peter Clausen mod_timer(&host->timeout_timer, jiffies + 5*HZ); 42761bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, irq, true); 42861bfbdb8SLars-Peter Clausen return true; 42961bfbdb8SLars-Peter Clausen } 43061bfbdb8SLars-Peter Clausen 43161bfbdb8SLars-Peter Clausen return false; 43261bfbdb8SLars-Peter Clausen } 43361bfbdb8SLars-Peter Clausen 43461bfbdb8SLars-Peter Clausen static void jz4740_mmc_transfer_check_state(struct jz4740_mmc_host *host, 43561bfbdb8SLars-Peter Clausen struct mmc_data *data) 43661bfbdb8SLars-Peter Clausen { 43761bfbdb8SLars-Peter Clausen int status; 43861bfbdb8SLars-Peter Clausen 43961bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 44061bfbdb8SLars-Peter Clausen if (status & JZ_MMC_STATUS_WRITE_ERROR_MASK) { 44161bfbdb8SLars-Peter Clausen if (status & (JZ_MMC_STATUS_TIMEOUT_WRITE)) { 44261bfbdb8SLars-Peter Clausen host->req->cmd->error = -ETIMEDOUT; 44361bfbdb8SLars-Peter Clausen data->error = -ETIMEDOUT; 44461bfbdb8SLars-Peter Clausen } else { 44561bfbdb8SLars-Peter Clausen host->req->cmd->error = -EIO; 44661bfbdb8SLars-Peter Clausen data->error = -EIO; 44761bfbdb8SLars-Peter Clausen } 4488a489aa1SPaul Cercueil } else if (status & JZ_MMC_STATUS_READ_ERROR_MASK) { 4498a489aa1SPaul Cercueil if (status & (JZ_MMC_STATUS_TIMEOUT_READ)) { 4508a489aa1SPaul Cercueil host->req->cmd->error = -ETIMEDOUT; 4518a489aa1SPaul Cercueil data->error = -ETIMEDOUT; 4528a489aa1SPaul Cercueil } else { 4538a489aa1SPaul Cercueil host->req->cmd->error = -EIO; 4548a489aa1SPaul Cercueil data->error = -EIO; 4558a489aa1SPaul Cercueil } 45661bfbdb8SLars-Peter Clausen } 45761bfbdb8SLars-Peter Clausen } 45861bfbdb8SLars-Peter Clausen 45961bfbdb8SLars-Peter Clausen static bool jz4740_mmc_write_data(struct jz4740_mmc_host *host, 46061bfbdb8SLars-Peter Clausen struct mmc_data *data) 46161bfbdb8SLars-Peter Clausen { 46261bfbdb8SLars-Peter Clausen struct sg_mapping_iter *miter = &host->miter; 46361bfbdb8SLars-Peter Clausen void __iomem *fifo_addr = host->base + JZ_REG_MMC_TXFIFO; 46461bfbdb8SLars-Peter Clausen uint32_t *buf; 46561bfbdb8SLars-Peter Clausen bool timeout; 46661bfbdb8SLars-Peter Clausen size_t i, j; 46761bfbdb8SLars-Peter Clausen 46861bfbdb8SLars-Peter Clausen while (sg_miter_next(miter)) { 46961bfbdb8SLars-Peter Clausen buf = miter->addr; 47061bfbdb8SLars-Peter Clausen i = miter->length / 4; 47161bfbdb8SLars-Peter Clausen j = i / 8; 47261bfbdb8SLars-Peter Clausen i = i & 0x7; 47361bfbdb8SLars-Peter Clausen while (j) { 47461bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); 47561bfbdb8SLars-Peter Clausen if (unlikely(timeout)) 47661bfbdb8SLars-Peter Clausen goto poll_timeout; 47761bfbdb8SLars-Peter Clausen 47861bfbdb8SLars-Peter Clausen writel(buf[0], fifo_addr); 47961bfbdb8SLars-Peter Clausen writel(buf[1], fifo_addr); 48061bfbdb8SLars-Peter Clausen writel(buf[2], fifo_addr); 48161bfbdb8SLars-Peter Clausen writel(buf[3], fifo_addr); 48261bfbdb8SLars-Peter Clausen writel(buf[4], fifo_addr); 48361bfbdb8SLars-Peter Clausen writel(buf[5], fifo_addr); 48461bfbdb8SLars-Peter Clausen writel(buf[6], fifo_addr); 48561bfbdb8SLars-Peter Clausen writel(buf[7], fifo_addr); 48661bfbdb8SLars-Peter Clausen buf += 8; 48761bfbdb8SLars-Peter Clausen --j; 48861bfbdb8SLars-Peter Clausen } 48961bfbdb8SLars-Peter Clausen if (unlikely(i)) { 49061bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); 49161bfbdb8SLars-Peter Clausen if (unlikely(timeout)) 49261bfbdb8SLars-Peter Clausen goto poll_timeout; 49361bfbdb8SLars-Peter Clausen 49461bfbdb8SLars-Peter Clausen while (i) { 49561bfbdb8SLars-Peter Clausen writel(*buf, fifo_addr); 49661bfbdb8SLars-Peter Clausen ++buf; 49761bfbdb8SLars-Peter Clausen --i; 49861bfbdb8SLars-Peter Clausen } 49961bfbdb8SLars-Peter Clausen } 50061bfbdb8SLars-Peter Clausen data->bytes_xfered += miter->length; 50161bfbdb8SLars-Peter Clausen } 50261bfbdb8SLars-Peter Clausen sg_miter_stop(miter); 50361bfbdb8SLars-Peter Clausen 50461bfbdb8SLars-Peter Clausen return false; 50561bfbdb8SLars-Peter Clausen 50661bfbdb8SLars-Peter Clausen poll_timeout: 50761bfbdb8SLars-Peter Clausen miter->consumed = (void *)buf - miter->addr; 50861bfbdb8SLars-Peter Clausen data->bytes_xfered += miter->consumed; 50961bfbdb8SLars-Peter Clausen sg_miter_stop(miter); 51061bfbdb8SLars-Peter Clausen 51161bfbdb8SLars-Peter Clausen return true; 51261bfbdb8SLars-Peter Clausen } 51361bfbdb8SLars-Peter Clausen 51461bfbdb8SLars-Peter Clausen static bool jz4740_mmc_read_data(struct jz4740_mmc_host *host, 51561bfbdb8SLars-Peter Clausen struct mmc_data *data) 51661bfbdb8SLars-Peter Clausen { 51761bfbdb8SLars-Peter Clausen struct sg_mapping_iter *miter = &host->miter; 51861bfbdb8SLars-Peter Clausen void __iomem *fifo_addr = host->base + JZ_REG_MMC_RXFIFO; 51961bfbdb8SLars-Peter Clausen uint32_t *buf; 52061bfbdb8SLars-Peter Clausen uint32_t d; 52161bfbdb8SLars-Peter Clausen uint16_t status; 52261bfbdb8SLars-Peter Clausen size_t i, j; 52361bfbdb8SLars-Peter Clausen unsigned int timeout; 52461bfbdb8SLars-Peter Clausen 52561bfbdb8SLars-Peter Clausen while (sg_miter_next(miter)) { 52661bfbdb8SLars-Peter Clausen buf = miter->addr; 52761bfbdb8SLars-Peter Clausen i = miter->length; 52861bfbdb8SLars-Peter Clausen j = i / 32; 52961bfbdb8SLars-Peter Clausen i = i & 0x1f; 53061bfbdb8SLars-Peter Clausen while (j) { 53161bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); 53261bfbdb8SLars-Peter Clausen if (unlikely(timeout)) 53361bfbdb8SLars-Peter Clausen goto poll_timeout; 53461bfbdb8SLars-Peter Clausen 53561bfbdb8SLars-Peter Clausen buf[0] = readl(fifo_addr); 53661bfbdb8SLars-Peter Clausen buf[1] = readl(fifo_addr); 53761bfbdb8SLars-Peter Clausen buf[2] = readl(fifo_addr); 53861bfbdb8SLars-Peter Clausen buf[3] = readl(fifo_addr); 53961bfbdb8SLars-Peter Clausen buf[4] = readl(fifo_addr); 54061bfbdb8SLars-Peter Clausen buf[5] = readl(fifo_addr); 54161bfbdb8SLars-Peter Clausen buf[6] = readl(fifo_addr); 54261bfbdb8SLars-Peter Clausen buf[7] = readl(fifo_addr); 54361bfbdb8SLars-Peter Clausen 54461bfbdb8SLars-Peter Clausen buf += 8; 54561bfbdb8SLars-Peter Clausen --j; 54661bfbdb8SLars-Peter Clausen } 54761bfbdb8SLars-Peter Clausen 54861bfbdb8SLars-Peter Clausen if (unlikely(i)) { 54961bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); 55061bfbdb8SLars-Peter Clausen if (unlikely(timeout)) 55161bfbdb8SLars-Peter Clausen goto poll_timeout; 55261bfbdb8SLars-Peter Clausen 55361bfbdb8SLars-Peter Clausen while (i >= 4) { 55461bfbdb8SLars-Peter Clausen *buf++ = readl(fifo_addr); 55561bfbdb8SLars-Peter Clausen i -= 4; 55661bfbdb8SLars-Peter Clausen } 55761bfbdb8SLars-Peter Clausen if (unlikely(i > 0)) { 55861bfbdb8SLars-Peter Clausen d = readl(fifo_addr); 55961bfbdb8SLars-Peter Clausen memcpy(buf, &d, i); 56061bfbdb8SLars-Peter Clausen } 56161bfbdb8SLars-Peter Clausen } 56261bfbdb8SLars-Peter Clausen data->bytes_xfered += miter->length; 56361bfbdb8SLars-Peter Clausen 56461bfbdb8SLars-Peter Clausen /* This can go away once MIPS implements 56561bfbdb8SLars-Peter Clausen * flush_kernel_dcache_page */ 56661bfbdb8SLars-Peter Clausen flush_dcache_page(miter->page); 56761bfbdb8SLars-Peter Clausen } 56861bfbdb8SLars-Peter Clausen sg_miter_stop(miter); 56961bfbdb8SLars-Peter Clausen 57061bfbdb8SLars-Peter Clausen /* For whatever reason there is sometime one word more in the fifo then 57161bfbdb8SLars-Peter Clausen * requested */ 57261bfbdb8SLars-Peter Clausen timeout = 1000; 57361bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 57461bfbdb8SLars-Peter Clausen while (!(status & JZ_MMC_STATUS_DATA_FIFO_EMPTY) && --timeout) { 57561bfbdb8SLars-Peter Clausen d = readl(fifo_addr); 57661bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 57761bfbdb8SLars-Peter Clausen } 57861bfbdb8SLars-Peter Clausen 57961bfbdb8SLars-Peter Clausen return false; 58061bfbdb8SLars-Peter Clausen 58161bfbdb8SLars-Peter Clausen poll_timeout: 58261bfbdb8SLars-Peter Clausen miter->consumed = (void *)buf - miter->addr; 58361bfbdb8SLars-Peter Clausen data->bytes_xfered += miter->consumed; 58461bfbdb8SLars-Peter Clausen sg_miter_stop(miter); 58561bfbdb8SLars-Peter Clausen 58661bfbdb8SLars-Peter Clausen return true; 58761bfbdb8SLars-Peter Clausen } 58861bfbdb8SLars-Peter Clausen 5892ee4f620SKees Cook static void jz4740_mmc_timeout(struct timer_list *t) 59061bfbdb8SLars-Peter Clausen { 5912ee4f620SKees Cook struct jz4740_mmc_host *host = from_timer(host, t, timeout_timer); 59261bfbdb8SLars-Peter Clausen 59361bfbdb8SLars-Peter Clausen if (!test_and_clear_bit(0, &host->waiting)) 59461bfbdb8SLars-Peter Clausen return; 59561bfbdb8SLars-Peter Clausen 59661bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, false); 59761bfbdb8SLars-Peter Clausen 59861bfbdb8SLars-Peter Clausen host->req->cmd->error = -ETIMEDOUT; 59961bfbdb8SLars-Peter Clausen jz4740_mmc_request_done(host); 60061bfbdb8SLars-Peter Clausen } 60161bfbdb8SLars-Peter Clausen 60261bfbdb8SLars-Peter Clausen static void jz4740_mmc_read_response(struct jz4740_mmc_host *host, 60361bfbdb8SLars-Peter Clausen struct mmc_command *cmd) 60461bfbdb8SLars-Peter Clausen { 60561bfbdb8SLars-Peter Clausen int i; 60661bfbdb8SLars-Peter Clausen uint16_t tmp; 60761bfbdb8SLars-Peter Clausen void __iomem *fifo_addr = host->base + JZ_REG_MMC_RESP_FIFO; 60861bfbdb8SLars-Peter Clausen 60961bfbdb8SLars-Peter Clausen if (cmd->flags & MMC_RSP_136) { 61061bfbdb8SLars-Peter Clausen tmp = readw(fifo_addr); 61161bfbdb8SLars-Peter Clausen for (i = 0; i < 4; ++i) { 61261bfbdb8SLars-Peter Clausen cmd->resp[i] = tmp << 24; 61361bfbdb8SLars-Peter Clausen tmp = readw(fifo_addr); 61461bfbdb8SLars-Peter Clausen cmd->resp[i] |= tmp << 8; 61561bfbdb8SLars-Peter Clausen tmp = readw(fifo_addr); 61661bfbdb8SLars-Peter Clausen cmd->resp[i] |= tmp >> 8; 61761bfbdb8SLars-Peter Clausen } 61861bfbdb8SLars-Peter Clausen } else { 61961bfbdb8SLars-Peter Clausen cmd->resp[0] = readw(fifo_addr) << 24; 62061bfbdb8SLars-Peter Clausen cmd->resp[0] |= readw(fifo_addr) << 8; 62161bfbdb8SLars-Peter Clausen cmd->resp[0] |= readw(fifo_addr) & 0xff; 62261bfbdb8SLars-Peter Clausen } 62361bfbdb8SLars-Peter Clausen } 62461bfbdb8SLars-Peter Clausen 62561bfbdb8SLars-Peter Clausen static void jz4740_mmc_send_command(struct jz4740_mmc_host *host, 62661bfbdb8SLars-Peter Clausen struct mmc_command *cmd) 62761bfbdb8SLars-Peter Clausen { 62861bfbdb8SLars-Peter Clausen uint32_t cmdat = host->cmdat; 62961bfbdb8SLars-Peter Clausen 63061bfbdb8SLars-Peter Clausen host->cmdat &= ~JZ_MMC_CMDAT_INIT; 63161bfbdb8SLars-Peter Clausen jz4740_mmc_clock_disable(host); 63261bfbdb8SLars-Peter Clausen 63361bfbdb8SLars-Peter Clausen host->cmd = cmd; 63461bfbdb8SLars-Peter Clausen 63561bfbdb8SLars-Peter Clausen if (cmd->flags & MMC_RSP_BUSY) 63661bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_BUSY; 63761bfbdb8SLars-Peter Clausen 63861bfbdb8SLars-Peter Clausen switch (mmc_resp_type(cmd)) { 63961bfbdb8SLars-Peter Clausen case MMC_RSP_R1B: 64061bfbdb8SLars-Peter Clausen case MMC_RSP_R1: 64161bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_RSP_R1; 64261bfbdb8SLars-Peter Clausen break; 64361bfbdb8SLars-Peter Clausen case MMC_RSP_R2: 64461bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_RSP_R2; 64561bfbdb8SLars-Peter Clausen break; 64661bfbdb8SLars-Peter Clausen case MMC_RSP_R3: 64761bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_RSP_R3; 64861bfbdb8SLars-Peter Clausen break; 64961bfbdb8SLars-Peter Clausen default: 65061bfbdb8SLars-Peter Clausen break; 65161bfbdb8SLars-Peter Clausen } 65261bfbdb8SLars-Peter Clausen 65361bfbdb8SLars-Peter Clausen if (cmd->data) { 65461bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_DATA_EN; 65561bfbdb8SLars-Peter Clausen if (cmd->data->flags & MMC_DATA_WRITE) 65661bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_WRITE; 6577ca27a6fSApelete Seketeli if (host->use_dma) 6587ca27a6fSApelete Seketeli cmdat |= JZ_MMC_CMDAT_DMA_EN; 65961bfbdb8SLars-Peter Clausen 66061bfbdb8SLars-Peter Clausen writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN); 66161bfbdb8SLars-Peter Clausen writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB); 66261bfbdb8SLars-Peter Clausen } 66361bfbdb8SLars-Peter Clausen 66461bfbdb8SLars-Peter Clausen writeb(cmd->opcode, host->base + JZ_REG_MMC_CMD); 66561bfbdb8SLars-Peter Clausen writel(cmd->arg, host->base + JZ_REG_MMC_ARG); 66661bfbdb8SLars-Peter Clausen writel(cmdat, host->base + JZ_REG_MMC_CMDAT); 66761bfbdb8SLars-Peter Clausen 66861bfbdb8SLars-Peter Clausen jz4740_mmc_clock_enable(host, 1); 66961bfbdb8SLars-Peter Clausen } 67061bfbdb8SLars-Peter Clausen 67161bfbdb8SLars-Peter Clausen static void jz_mmc_prepare_data_transfer(struct jz4740_mmc_host *host) 67261bfbdb8SLars-Peter Clausen { 67361bfbdb8SLars-Peter Clausen struct mmc_command *cmd = host->req->cmd; 67461bfbdb8SLars-Peter Clausen struct mmc_data *data = cmd->data; 67561bfbdb8SLars-Peter Clausen int direction; 67661bfbdb8SLars-Peter Clausen 67761bfbdb8SLars-Peter Clausen if (data->flags & MMC_DATA_READ) 67861bfbdb8SLars-Peter Clausen direction = SG_MITER_TO_SG; 67961bfbdb8SLars-Peter Clausen else 68061bfbdb8SLars-Peter Clausen direction = SG_MITER_FROM_SG; 68161bfbdb8SLars-Peter Clausen 68261bfbdb8SLars-Peter Clausen sg_miter_start(&host->miter, data->sg, data->sg_len, direction); 68361bfbdb8SLars-Peter Clausen } 68461bfbdb8SLars-Peter Clausen 68561bfbdb8SLars-Peter Clausen 68661bfbdb8SLars-Peter Clausen static irqreturn_t jz_mmc_irq_worker(int irq, void *devid) 68761bfbdb8SLars-Peter Clausen { 68861bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)devid; 68961bfbdb8SLars-Peter Clausen struct mmc_command *cmd = host->req->cmd; 69061bfbdb8SLars-Peter Clausen struct mmc_request *req = host->req; 6917ca27a6fSApelete Seketeli struct mmc_data *data = cmd->data; 69261bfbdb8SLars-Peter Clausen bool timeout = false; 69361bfbdb8SLars-Peter Clausen 69461bfbdb8SLars-Peter Clausen if (cmd->error) 69561bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_DONE; 69661bfbdb8SLars-Peter Clausen 69761bfbdb8SLars-Peter Clausen switch (host->state) { 69861bfbdb8SLars-Peter Clausen case JZ4740_MMC_STATE_READ_RESPONSE: 69961bfbdb8SLars-Peter Clausen if (cmd->flags & MMC_RSP_PRESENT) 70061bfbdb8SLars-Peter Clausen jz4740_mmc_read_response(host, cmd); 70161bfbdb8SLars-Peter Clausen 7027ca27a6fSApelete Seketeli if (!data) 70361bfbdb8SLars-Peter Clausen break; 70461bfbdb8SLars-Peter Clausen 70561bfbdb8SLars-Peter Clausen jz_mmc_prepare_data_transfer(host); 70661bfbdb8SLars-Peter Clausen 70761bfbdb8SLars-Peter Clausen case JZ4740_MMC_STATE_TRANSFER_DATA: 7087ca27a6fSApelete Seketeli if (host->use_dma) { 709bb2f4592SApelete Seketeli /* Use DMA if enabled. 710bb2f4592SApelete Seketeli * Data transfer direction is defined later by 711bb2f4592SApelete Seketeli * relying on data flags in 712bb2f4592SApelete Seketeli * jz4740_mmc_prepare_dma_data() and 713bb2f4592SApelete Seketeli * jz4740_mmc_start_dma_transfer(). 7147ca27a6fSApelete Seketeli */ 7157ca27a6fSApelete Seketeli timeout = jz4740_mmc_start_dma_transfer(host, data); 7167ca27a6fSApelete Seketeli data->bytes_xfered = data->blocks * data->blksz; 7177ca27a6fSApelete Seketeli } else if (data->flags & MMC_DATA_READ) 718bb2f4592SApelete Seketeli /* Use PIO if DMA is not enabled. 719bb2f4592SApelete Seketeli * Data transfer direction was defined before 720bb2f4592SApelete Seketeli * by relying on data flags in 721bb2f4592SApelete Seketeli * jz_mmc_prepare_data_transfer(). 7227ca27a6fSApelete Seketeli */ 7237ca27a6fSApelete Seketeli timeout = jz4740_mmc_read_data(host, data); 72461bfbdb8SLars-Peter Clausen else 7257ca27a6fSApelete Seketeli timeout = jz4740_mmc_write_data(host, data); 72661bfbdb8SLars-Peter Clausen 72761bfbdb8SLars-Peter Clausen if (unlikely(timeout)) { 72861bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_TRANSFER_DATA; 72961bfbdb8SLars-Peter Clausen break; 73061bfbdb8SLars-Peter Clausen } 73161bfbdb8SLars-Peter Clausen 7327ca27a6fSApelete Seketeli jz4740_mmc_transfer_check_state(host, data); 73361bfbdb8SLars-Peter Clausen 73461bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_DATA_TRAN_DONE); 73561bfbdb8SLars-Peter Clausen if (unlikely(timeout)) { 73661bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_SEND_STOP; 73761bfbdb8SLars-Peter Clausen break; 73861bfbdb8SLars-Peter Clausen } 73961bfbdb8SLars-Peter Clausen writew(JZ_MMC_IRQ_DATA_TRAN_DONE, host->base + JZ_REG_MMC_IREG); 74061bfbdb8SLars-Peter Clausen 74161bfbdb8SLars-Peter Clausen case JZ4740_MMC_STATE_SEND_STOP: 74261bfbdb8SLars-Peter Clausen if (!req->stop) 74361bfbdb8SLars-Peter Clausen break; 74461bfbdb8SLars-Peter Clausen 74561bfbdb8SLars-Peter Clausen jz4740_mmc_send_command(host, req->stop); 74661bfbdb8SLars-Peter Clausen 7471acee84bSAlex Smith if (mmc_resp_type(req->stop) & MMC_RSP_BUSY) { 7481acee84bSAlex Smith timeout = jz4740_mmc_poll_irq(host, 7491acee84bSAlex Smith JZ_MMC_IRQ_PRG_DONE); 75061bfbdb8SLars-Peter Clausen if (timeout) { 75161bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_DONE; 75261bfbdb8SLars-Peter Clausen break; 75361bfbdb8SLars-Peter Clausen } 7541acee84bSAlex Smith } 75561bfbdb8SLars-Peter Clausen case JZ4740_MMC_STATE_DONE: 75661bfbdb8SLars-Peter Clausen break; 75761bfbdb8SLars-Peter Clausen } 75861bfbdb8SLars-Peter Clausen 75961bfbdb8SLars-Peter Clausen if (!timeout) 76061bfbdb8SLars-Peter Clausen jz4740_mmc_request_done(host); 76161bfbdb8SLars-Peter Clausen 76261bfbdb8SLars-Peter Clausen return IRQ_HANDLED; 76361bfbdb8SLars-Peter Clausen } 76461bfbdb8SLars-Peter Clausen 76561bfbdb8SLars-Peter Clausen static irqreturn_t jz_mmc_irq(int irq, void *devid) 76661bfbdb8SLars-Peter Clausen { 76761bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = devid; 76861bfbdb8SLars-Peter Clausen struct mmc_command *cmd = host->cmd; 76961bfbdb8SLars-Peter Clausen uint16_t irq_reg, status, tmp; 77061bfbdb8SLars-Peter Clausen 77161bfbdb8SLars-Peter Clausen irq_reg = readw(host->base + JZ_REG_MMC_IREG); 77261bfbdb8SLars-Peter Clausen 77361bfbdb8SLars-Peter Clausen tmp = irq_reg; 77461bfbdb8SLars-Peter Clausen irq_reg &= ~host->irq_mask; 77561bfbdb8SLars-Peter Clausen 77661bfbdb8SLars-Peter Clausen tmp &= ~(JZ_MMC_IRQ_TXFIFO_WR_REQ | JZ_MMC_IRQ_RXFIFO_RD_REQ | 77761bfbdb8SLars-Peter Clausen JZ_MMC_IRQ_PRG_DONE | JZ_MMC_IRQ_DATA_TRAN_DONE); 77861bfbdb8SLars-Peter Clausen 77961bfbdb8SLars-Peter Clausen if (tmp != irq_reg) 78061bfbdb8SLars-Peter Clausen writew(tmp & ~irq_reg, host->base + JZ_REG_MMC_IREG); 78161bfbdb8SLars-Peter Clausen 78261bfbdb8SLars-Peter Clausen if (irq_reg & JZ_MMC_IRQ_SDIO) { 78361bfbdb8SLars-Peter Clausen writew(JZ_MMC_IRQ_SDIO, host->base + JZ_REG_MMC_IREG); 78461bfbdb8SLars-Peter Clausen mmc_signal_sdio_irq(host->mmc); 78561bfbdb8SLars-Peter Clausen irq_reg &= ~JZ_MMC_IRQ_SDIO; 78661bfbdb8SLars-Peter Clausen } 78761bfbdb8SLars-Peter Clausen 78861bfbdb8SLars-Peter Clausen if (host->req && cmd && irq_reg) { 78961bfbdb8SLars-Peter Clausen if (test_and_clear_bit(0, &host->waiting)) { 79061bfbdb8SLars-Peter Clausen del_timer(&host->timeout_timer); 79161bfbdb8SLars-Peter Clausen 79261bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 79361bfbdb8SLars-Peter Clausen 79461bfbdb8SLars-Peter Clausen if (status & JZ_MMC_STATUS_TIMEOUT_RES) { 79561bfbdb8SLars-Peter Clausen cmd->error = -ETIMEDOUT; 79661bfbdb8SLars-Peter Clausen } else if (status & JZ_MMC_STATUS_CRC_RES_ERR) { 79761bfbdb8SLars-Peter Clausen cmd->error = -EIO; 79861bfbdb8SLars-Peter Clausen } else if (status & (JZ_MMC_STATUS_CRC_READ_ERROR | 79961bfbdb8SLars-Peter Clausen JZ_MMC_STATUS_CRC_WRITE_ERROR)) { 80061bfbdb8SLars-Peter Clausen if (cmd->data) 80161bfbdb8SLars-Peter Clausen cmd->data->error = -EIO; 80261bfbdb8SLars-Peter Clausen cmd->error = -EIO; 80361bfbdb8SLars-Peter Clausen } 80461bfbdb8SLars-Peter Clausen 80561bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, irq_reg, false); 80661bfbdb8SLars-Peter Clausen writew(irq_reg, host->base + JZ_REG_MMC_IREG); 80761bfbdb8SLars-Peter Clausen 80861bfbdb8SLars-Peter Clausen return IRQ_WAKE_THREAD; 80961bfbdb8SLars-Peter Clausen } 81061bfbdb8SLars-Peter Clausen } 81161bfbdb8SLars-Peter Clausen 81261bfbdb8SLars-Peter Clausen return IRQ_HANDLED; 81361bfbdb8SLars-Peter Clausen } 81461bfbdb8SLars-Peter Clausen 81561bfbdb8SLars-Peter Clausen static int jz4740_mmc_set_clock_rate(struct jz4740_mmc_host *host, int rate) 81661bfbdb8SLars-Peter Clausen { 81761bfbdb8SLars-Peter Clausen int div = 0; 81861bfbdb8SLars-Peter Clausen int real_rate; 81961bfbdb8SLars-Peter Clausen 82061bfbdb8SLars-Peter Clausen jz4740_mmc_clock_disable(host); 82161bfbdb8SLars-Peter Clausen clk_set_rate(host->clk, JZ_MMC_CLK_RATE); 82261bfbdb8SLars-Peter Clausen 82361bfbdb8SLars-Peter Clausen real_rate = clk_get_rate(host->clk); 82461bfbdb8SLars-Peter Clausen 82561bfbdb8SLars-Peter Clausen while (real_rate > rate && div < 7) { 82661bfbdb8SLars-Peter Clausen ++div; 82761bfbdb8SLars-Peter Clausen real_rate >>= 1; 82861bfbdb8SLars-Peter Clausen } 82961bfbdb8SLars-Peter Clausen 83061bfbdb8SLars-Peter Clausen writew(div, host->base + JZ_REG_MMC_CLKRT); 83161bfbdb8SLars-Peter Clausen return real_rate; 83261bfbdb8SLars-Peter Clausen } 83361bfbdb8SLars-Peter Clausen 83461bfbdb8SLars-Peter Clausen static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req) 83561bfbdb8SLars-Peter Clausen { 83661bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = mmc_priv(mmc); 83761bfbdb8SLars-Peter Clausen 83861bfbdb8SLars-Peter Clausen host->req = req; 83961bfbdb8SLars-Peter Clausen 84061bfbdb8SLars-Peter Clausen writew(0xffff, host->base + JZ_REG_MMC_IREG); 84161bfbdb8SLars-Peter Clausen 84261bfbdb8SLars-Peter Clausen writew(JZ_MMC_IRQ_END_CMD_RES, host->base + JZ_REG_MMC_IREG); 84361bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, true); 84461bfbdb8SLars-Peter Clausen 84561bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_READ_RESPONSE; 84661bfbdb8SLars-Peter Clausen set_bit(0, &host->waiting); 84761bfbdb8SLars-Peter Clausen mod_timer(&host->timeout_timer, jiffies + 5*HZ); 84861bfbdb8SLars-Peter Clausen jz4740_mmc_send_command(host, req->cmd); 84961bfbdb8SLars-Peter Clausen } 85061bfbdb8SLars-Peter Clausen 85161bfbdb8SLars-Peter Clausen static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 85261bfbdb8SLars-Peter Clausen { 85361bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = mmc_priv(mmc); 85461bfbdb8SLars-Peter Clausen if (ios->clock) 85561bfbdb8SLars-Peter Clausen jz4740_mmc_set_clock_rate(host, ios->clock); 85661bfbdb8SLars-Peter Clausen 85761bfbdb8SLars-Peter Clausen switch (ios->power_mode) { 85861bfbdb8SLars-Peter Clausen case MMC_POWER_UP: 85961bfbdb8SLars-Peter Clausen jz4740_mmc_reset(host); 86061bfbdb8SLars-Peter Clausen if (gpio_is_valid(host->pdata->gpio_power)) 86161bfbdb8SLars-Peter Clausen gpio_set_value(host->pdata->gpio_power, 86261bfbdb8SLars-Peter Clausen !host->pdata->power_active_low); 86361bfbdb8SLars-Peter Clausen host->cmdat |= JZ_MMC_CMDAT_INIT; 864fca9661cSLars-Peter Clausen clk_prepare_enable(host->clk); 86561bfbdb8SLars-Peter Clausen break; 86661bfbdb8SLars-Peter Clausen case MMC_POWER_ON: 86761bfbdb8SLars-Peter Clausen break; 86861bfbdb8SLars-Peter Clausen default: 86961bfbdb8SLars-Peter Clausen if (gpio_is_valid(host->pdata->gpio_power)) 87061bfbdb8SLars-Peter Clausen gpio_set_value(host->pdata->gpio_power, 87161bfbdb8SLars-Peter Clausen host->pdata->power_active_low); 872fca9661cSLars-Peter Clausen clk_disable_unprepare(host->clk); 87361bfbdb8SLars-Peter Clausen break; 87461bfbdb8SLars-Peter Clausen } 87561bfbdb8SLars-Peter Clausen 87661bfbdb8SLars-Peter Clausen switch (ios->bus_width) { 87761bfbdb8SLars-Peter Clausen case MMC_BUS_WIDTH_1: 87861bfbdb8SLars-Peter Clausen host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_4BIT; 87961bfbdb8SLars-Peter Clausen break; 88061bfbdb8SLars-Peter Clausen case MMC_BUS_WIDTH_4: 88161bfbdb8SLars-Peter Clausen host->cmdat |= JZ_MMC_CMDAT_BUS_WIDTH_4BIT; 88261bfbdb8SLars-Peter Clausen break; 88361bfbdb8SLars-Peter Clausen default: 88461bfbdb8SLars-Peter Clausen break; 88561bfbdb8SLars-Peter Clausen } 88661bfbdb8SLars-Peter Clausen } 88761bfbdb8SLars-Peter Clausen 88861bfbdb8SLars-Peter Clausen static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) 88961bfbdb8SLars-Peter Clausen { 89061bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = mmc_priv(mmc); 89161bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_SDIO, enable); 89261bfbdb8SLars-Peter Clausen } 89361bfbdb8SLars-Peter Clausen 89461bfbdb8SLars-Peter Clausen static const struct mmc_host_ops jz4740_mmc_ops = { 89561bfbdb8SLars-Peter Clausen .request = jz4740_mmc_request, 896bb2f4592SApelete Seketeli .pre_req = jz4740_mmc_pre_request, 897bb2f4592SApelete Seketeli .post_req = jz4740_mmc_post_request, 89861bfbdb8SLars-Peter Clausen .set_ios = jz4740_mmc_set_ios, 89958e300afSLars-Peter Clausen .get_ro = mmc_gpio_get_ro, 90058e300afSLars-Peter Clausen .get_cd = mmc_gpio_get_cd, 90161bfbdb8SLars-Peter Clausen .enable_sdio_irq = jz4740_mmc_enable_sdio_irq, 90261bfbdb8SLars-Peter Clausen }; 90361bfbdb8SLars-Peter Clausen 904c3be1efdSBill Pemberton static int jz4740_mmc_request_gpio(struct device *dev, int gpio, 90561bfbdb8SLars-Peter Clausen const char *name, bool output, int value) 90661bfbdb8SLars-Peter Clausen { 90761bfbdb8SLars-Peter Clausen int ret; 90861bfbdb8SLars-Peter Clausen 90961bfbdb8SLars-Peter Clausen if (!gpio_is_valid(gpio)) 91061bfbdb8SLars-Peter Clausen return 0; 91161bfbdb8SLars-Peter Clausen 91261bfbdb8SLars-Peter Clausen ret = gpio_request(gpio, name); 91361bfbdb8SLars-Peter Clausen if (ret) { 91461bfbdb8SLars-Peter Clausen dev_err(dev, "Failed to request %s gpio: %d\n", name, ret); 91561bfbdb8SLars-Peter Clausen return ret; 91661bfbdb8SLars-Peter Clausen } 91761bfbdb8SLars-Peter Clausen 91861bfbdb8SLars-Peter Clausen if (output) 91961bfbdb8SLars-Peter Clausen gpio_direction_output(gpio, value); 92061bfbdb8SLars-Peter Clausen else 92161bfbdb8SLars-Peter Clausen gpio_direction_input(gpio); 92261bfbdb8SLars-Peter Clausen 92361bfbdb8SLars-Peter Clausen return 0; 92461bfbdb8SLars-Peter Clausen } 92561bfbdb8SLars-Peter Clausen 92658e300afSLars-Peter Clausen static int jz4740_mmc_request_gpios(struct mmc_host *mmc, 92758e300afSLars-Peter Clausen struct platform_device *pdev) 92861bfbdb8SLars-Peter Clausen { 92961bfbdb8SLars-Peter Clausen struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data; 93058e300afSLars-Peter Clausen int ret = 0; 93161bfbdb8SLars-Peter Clausen 93261bfbdb8SLars-Peter Clausen if (!pdata) 93361bfbdb8SLars-Peter Clausen return 0; 93461bfbdb8SLars-Peter Clausen 93558e300afSLars-Peter Clausen if (!pdata->card_detect_active_low) 93658e300afSLars-Peter Clausen mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; 93758e300afSLars-Peter Clausen if (!pdata->read_only_active_low) 93858e300afSLars-Peter Clausen mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; 93958e300afSLars-Peter Clausen 94058e300afSLars-Peter Clausen if (gpio_is_valid(pdata->gpio_card_detect)) { 941214fc309SLaurent Pinchart ret = mmc_gpio_request_cd(mmc, pdata->gpio_card_detect, 0); 94261bfbdb8SLars-Peter Clausen if (ret) 94361bfbdb8SLars-Peter Clausen return ret; 94461bfbdb8SLars-Peter Clausen } 94561bfbdb8SLars-Peter Clausen 94658e300afSLars-Peter Clausen if (gpio_is_valid(pdata->gpio_read_only)) { 94758e300afSLars-Peter Clausen ret = mmc_gpio_request_ro(mmc, pdata->gpio_read_only); 94858e300afSLars-Peter Clausen if (ret) 94958e300afSLars-Peter Clausen return ret; 95061bfbdb8SLars-Peter Clausen } 95161bfbdb8SLars-Peter Clausen 95258e300afSLars-Peter Clausen return jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_power, 95358e300afSLars-Peter Clausen "MMC read only", true, pdata->power_active_low); 95461bfbdb8SLars-Peter Clausen } 95561bfbdb8SLars-Peter Clausen 95661bfbdb8SLars-Peter Clausen static void jz4740_mmc_free_gpios(struct platform_device *pdev) 95761bfbdb8SLars-Peter Clausen { 95861bfbdb8SLars-Peter Clausen struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data; 95961bfbdb8SLars-Peter Clausen 96061bfbdb8SLars-Peter Clausen if (!pdata) 96161bfbdb8SLars-Peter Clausen return; 96261bfbdb8SLars-Peter Clausen 96361bfbdb8SLars-Peter Clausen if (gpio_is_valid(pdata->gpio_power)) 96461bfbdb8SLars-Peter Clausen gpio_free(pdata->gpio_power); 96561bfbdb8SLars-Peter Clausen } 96661bfbdb8SLars-Peter Clausen 967c3be1efdSBill Pemberton static int jz4740_mmc_probe(struct platform_device* pdev) 96861bfbdb8SLars-Peter Clausen { 96961bfbdb8SLars-Peter Clausen int ret; 97061bfbdb8SLars-Peter Clausen struct mmc_host *mmc; 97161bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host; 97261bfbdb8SLars-Peter Clausen struct jz4740_mmc_platform_data *pdata; 97361bfbdb8SLars-Peter Clausen 97461bfbdb8SLars-Peter Clausen pdata = pdev->dev.platform_data; 97561bfbdb8SLars-Peter Clausen 97661bfbdb8SLars-Peter Clausen mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev); 97761bfbdb8SLars-Peter Clausen if (!mmc) { 97861bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to alloc mmc host structure\n"); 97961bfbdb8SLars-Peter Clausen return -ENOMEM; 98061bfbdb8SLars-Peter Clausen } 98161bfbdb8SLars-Peter Clausen 98261bfbdb8SLars-Peter Clausen host = mmc_priv(mmc); 98361bfbdb8SLars-Peter Clausen host->pdata = pdata; 98461bfbdb8SLars-Peter Clausen 98561bfbdb8SLars-Peter Clausen host->irq = platform_get_irq(pdev, 0); 98661bfbdb8SLars-Peter Clausen if (host->irq < 0) { 98761bfbdb8SLars-Peter Clausen ret = host->irq; 98861bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); 98961bfbdb8SLars-Peter Clausen goto err_free_host; 99061bfbdb8SLars-Peter Clausen } 99161bfbdb8SLars-Peter Clausen 992017d84bdSLars-Peter Clausen host->clk = devm_clk_get(&pdev->dev, "mmc"); 9933119cbdaSJamie Iles if (IS_ERR(host->clk)) { 9943119cbdaSJamie Iles ret = PTR_ERR(host->clk); 99561bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to get mmc clock\n"); 99661bfbdb8SLars-Peter Clausen goto err_free_host; 99761bfbdb8SLars-Peter Clausen } 99861bfbdb8SLars-Peter Clausen 9997ca27a6fSApelete Seketeli host->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 10007ca27a6fSApelete Seketeli host->base = devm_ioremap_resource(&pdev->dev, host->mem_res); 10013e7e8c18SWei Yongjun if (IS_ERR(host->base)) { 10023e7e8c18SWei Yongjun ret = PTR_ERR(host->base); 10037ca27a6fSApelete Seketeli dev_err(&pdev->dev, "Failed to ioremap base memory\n"); 1004017d84bdSLars-Peter Clausen goto err_free_host; 100561bfbdb8SLars-Peter Clausen } 100661bfbdb8SLars-Peter Clausen 100758e300afSLars-Peter Clausen ret = jz4740_mmc_request_gpios(mmc, pdev); 100861bfbdb8SLars-Peter Clausen if (ret) 10097e8466e2SPaul Cercueil goto err_free_host; 101061bfbdb8SLars-Peter Clausen 101161bfbdb8SLars-Peter Clausen mmc->ops = &jz4740_mmc_ops; 101261bfbdb8SLars-Peter Clausen mmc->f_min = JZ_MMC_CLK_RATE / 128; 101361bfbdb8SLars-Peter Clausen mmc->f_max = JZ_MMC_CLK_RATE; 101461bfbdb8SLars-Peter Clausen mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; 101561bfbdb8SLars-Peter Clausen mmc->caps = (pdata && pdata->data_1bit) ? 0 : MMC_CAP_4_BIT_DATA; 101661bfbdb8SLars-Peter Clausen mmc->caps |= MMC_CAP_SDIO_IRQ; 101761bfbdb8SLars-Peter Clausen 101861bfbdb8SLars-Peter Clausen mmc->max_blk_size = (1 << 10) - 1; 101961bfbdb8SLars-Peter Clausen mmc->max_blk_count = (1 << 15) - 1; 102061bfbdb8SLars-Peter Clausen mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; 102161bfbdb8SLars-Peter Clausen 1022a36274e0SMartin K. Petersen mmc->max_segs = 128; 102361bfbdb8SLars-Peter Clausen mmc->max_seg_size = mmc->max_req_size; 102461bfbdb8SLars-Peter Clausen 102561bfbdb8SLars-Peter Clausen host->mmc = mmc; 102661bfbdb8SLars-Peter Clausen host->pdev = pdev; 102761bfbdb8SLars-Peter Clausen spin_lock_init(&host->lock); 102861bfbdb8SLars-Peter Clausen host->irq_mask = 0xffff; 102961bfbdb8SLars-Peter Clausen 103061bfbdb8SLars-Peter Clausen ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0, 103161bfbdb8SLars-Peter Clausen dev_name(&pdev->dev), host); 103261bfbdb8SLars-Peter Clausen if (ret) { 103361bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); 103458e300afSLars-Peter Clausen goto err_free_gpios; 103561bfbdb8SLars-Peter Clausen } 103661bfbdb8SLars-Peter Clausen 103761bfbdb8SLars-Peter Clausen jz4740_mmc_reset(host); 103861bfbdb8SLars-Peter Clausen jz4740_mmc_clock_disable(host); 10392ee4f620SKees Cook timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0); 104061bfbdb8SLars-Peter Clausen 10417e8466e2SPaul Cercueil ret = jz4740_mmc_acquire_dma_channels(host); 10427e8466e2SPaul Cercueil if (ret == -EPROBE_DEFER) 10437e8466e2SPaul Cercueil goto err_free_irq; 10447e8466e2SPaul Cercueil host->use_dma = !ret; 10457ca27a6fSApelete Seketeli 104661bfbdb8SLars-Peter Clausen platform_set_drvdata(pdev, host); 104761bfbdb8SLars-Peter Clausen ret = mmc_add_host(mmc); 104861bfbdb8SLars-Peter Clausen 104961bfbdb8SLars-Peter Clausen if (ret) { 105061bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret); 10517e8466e2SPaul Cercueil goto err_release_dma; 105261bfbdb8SLars-Peter Clausen } 105361bfbdb8SLars-Peter Clausen dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n"); 105461bfbdb8SLars-Peter Clausen 10557ca27a6fSApelete Seketeli dev_info(&pdev->dev, "Using %s, %d-bit mode\n", 10567ca27a6fSApelete Seketeli host->use_dma ? "DMA" : "PIO", 10577ca27a6fSApelete Seketeli (mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1); 10587ca27a6fSApelete Seketeli 105961bfbdb8SLars-Peter Clausen return 0; 106061bfbdb8SLars-Peter Clausen 10617e8466e2SPaul Cercueil err_release_dma: 10627e8466e2SPaul Cercueil if (host->use_dma) 10637e8466e2SPaul Cercueil jz4740_mmc_release_dma_channels(host); 106461bfbdb8SLars-Peter Clausen err_free_irq: 106561bfbdb8SLars-Peter Clausen free_irq(host->irq, host); 106661bfbdb8SLars-Peter Clausen err_free_gpios: 106761bfbdb8SLars-Peter Clausen jz4740_mmc_free_gpios(pdev); 106861bfbdb8SLars-Peter Clausen err_free_host: 106961bfbdb8SLars-Peter Clausen mmc_free_host(mmc); 107061bfbdb8SLars-Peter Clausen 107161bfbdb8SLars-Peter Clausen return ret; 107261bfbdb8SLars-Peter Clausen } 107361bfbdb8SLars-Peter Clausen 10746e0ee714SBill Pemberton static int jz4740_mmc_remove(struct platform_device *pdev) 107561bfbdb8SLars-Peter Clausen { 107661bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = platform_get_drvdata(pdev); 107761bfbdb8SLars-Peter Clausen 107861bfbdb8SLars-Peter Clausen del_timer_sync(&host->timeout_timer); 107961bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, 0xff, false); 108061bfbdb8SLars-Peter Clausen jz4740_mmc_reset(host); 108161bfbdb8SLars-Peter Clausen 108261bfbdb8SLars-Peter Clausen mmc_remove_host(host->mmc); 108361bfbdb8SLars-Peter Clausen 108461bfbdb8SLars-Peter Clausen free_irq(host->irq, host); 108561bfbdb8SLars-Peter Clausen 108661bfbdb8SLars-Peter Clausen jz4740_mmc_free_gpios(pdev); 108761bfbdb8SLars-Peter Clausen 10887ca27a6fSApelete Seketeli if (host->use_dma) 10897ca27a6fSApelete Seketeli jz4740_mmc_release_dma_channels(host); 10907ca27a6fSApelete Seketeli 109161bfbdb8SLars-Peter Clausen mmc_free_host(host->mmc); 109261bfbdb8SLars-Peter Clausen 109361bfbdb8SLars-Peter Clausen return 0; 109461bfbdb8SLars-Peter Clausen } 109561bfbdb8SLars-Peter Clausen 10965d5c0350SLars-Peter Clausen #ifdef CONFIG_PM_SLEEP 109761bfbdb8SLars-Peter Clausen 109861bfbdb8SLars-Peter Clausen static int jz4740_mmc_suspend(struct device *dev) 109961bfbdb8SLars-Peter Clausen { 1100fa5ed6bcSPaul Cercueil return pinctrl_pm_select_sleep_state(dev); 110161bfbdb8SLars-Peter Clausen } 110261bfbdb8SLars-Peter Clausen 110361bfbdb8SLars-Peter Clausen static int jz4740_mmc_resume(struct device *dev) 110461bfbdb8SLars-Peter Clausen { 1105fa5ed6bcSPaul Cercueil return pinctrl_pm_select_default_state(dev); 110661bfbdb8SLars-Peter Clausen } 110761bfbdb8SLars-Peter Clausen 11085d5c0350SLars-Peter Clausen static SIMPLE_DEV_PM_OPS(jz4740_mmc_pm_ops, jz4740_mmc_suspend, 11095d5c0350SLars-Peter Clausen jz4740_mmc_resume); 111061bfbdb8SLars-Peter Clausen #define JZ4740_MMC_PM_OPS (&jz4740_mmc_pm_ops) 111161bfbdb8SLars-Peter Clausen #else 111261bfbdb8SLars-Peter Clausen #define JZ4740_MMC_PM_OPS NULL 111361bfbdb8SLars-Peter Clausen #endif 111461bfbdb8SLars-Peter Clausen 111561bfbdb8SLars-Peter Clausen static struct platform_driver jz4740_mmc_driver = { 111661bfbdb8SLars-Peter Clausen .probe = jz4740_mmc_probe, 11170433c143SBill Pemberton .remove = jz4740_mmc_remove, 111861bfbdb8SLars-Peter Clausen .driver = { 111961bfbdb8SLars-Peter Clausen .name = "jz4740-mmc", 112061bfbdb8SLars-Peter Clausen .pm = JZ4740_MMC_PM_OPS, 112161bfbdb8SLars-Peter Clausen }, 112261bfbdb8SLars-Peter Clausen }; 112361bfbdb8SLars-Peter Clausen 1124d1f81a64SAxel Lin module_platform_driver(jz4740_mmc_driver); 112561bfbdb8SLars-Peter Clausen 112661bfbdb8SLars-Peter Clausen MODULE_DESCRIPTION("JZ4740 SD/MMC controller driver"); 112761bfbdb8SLars-Peter Clausen MODULE_LICENSE("GPL"); 112861bfbdb8SLars-Peter Clausen MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 1129