1a912e80bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 261bfbdb8SLars-Peter Clausen /* 361bfbdb8SLars-Peter Clausen * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> 46a78768aSAlex Smith * Copyright (C) 2013, Imagination Technologies 56a78768aSAlex Smith * 661bfbdb8SLars-Peter Clausen * JZ4740 SD/MMC controller driver 761bfbdb8SLars-Peter Clausen */ 861bfbdb8SLars-Peter Clausen 97e7845f3SEzequiel Garcia #include <linux/bitops.h> 107e7845f3SEzequiel Garcia #include <linux/clk.h> 117e7845f3SEzequiel Garcia #include <linux/delay.h> 127e7845f3SEzequiel Garcia #include <linux/dmaengine.h> 137e7845f3SEzequiel Garcia #include <linux/dma-mapping.h> 143119cbdaSJamie Iles #include <linux/err.h> 157e7845f3SEzequiel Garcia #include <linux/interrupt.h> 1661bfbdb8SLars-Peter Clausen #include <linux/io.h> 1761bfbdb8SLars-Peter Clausen #include <linux/irq.h> 187e7845f3SEzequiel Garcia #include <linux/mmc/host.h> 197e7845f3SEzequiel Garcia #include <linux/mmc/slot-gpio.h> 2061bfbdb8SLars-Peter Clausen #include <linux/module.h> 2161e11ebaSEzequiel Garcia #include <linux/of_device.h> 22fa5ed6bcSPaul Cercueil #include <linux/pinctrl/consumer.h> 2361bfbdb8SLars-Peter Clausen #include <linux/platform_device.h> 2461bfbdb8SLars-Peter Clausen #include <linux/scatterlist.h> 2561bfbdb8SLars-Peter Clausen 2661bfbdb8SLars-Peter Clausen #include <asm/cacheflush.h> 2761bfbdb8SLars-Peter Clausen 2861bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_STRPCL 0x00 2961bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_STATUS 0x04 3061bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_CLKRT 0x08 3161bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_CMDAT 0x0C 3261bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RESTO 0x10 3361bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RDTO 0x14 3461bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_BLKLEN 0x18 3561bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_NOB 0x1C 3661bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_SNOB 0x20 3761bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_IMASK 0x24 3861bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_IREG 0x28 3961bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_CMD 0x2C 4061bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_ARG 0x30 4161bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RESP_FIFO 0x34 4261bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RXFIFO 0x38 4361bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_TXFIFO 0x3C 4480fe4e90SZhou Yanjie #define JZ_REG_MMC_LPM 0x40 456a78768aSAlex Smith #define JZ_REG_MMC_DMAC 0x44 4661bfbdb8SLars-Peter Clausen 4761bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7) 4861bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_EXIT_TRANSFER BIT(6) 4961bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_START_READWAIT BIT(5) 5061bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_STOP_READWAIT BIT(4) 5161bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_RESET BIT(3) 5261bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_START_OP BIT(2) 5361bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_CLOCK_CONTROL (BIT(1) | BIT(0)) 5461bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_CLOCK_STOP BIT(0) 5561bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_CLOCK_START BIT(1) 5661bfbdb8SLars-Peter Clausen 5761bfbdb8SLars-Peter Clausen 5861bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_IS_RESETTING BIT(15) 5961bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_SDIO_INT_ACTIVE BIT(14) 6061bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_PRG_DONE BIT(13) 6161bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_TRAN_DONE BIT(12) 6261bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_END_CMD_RES BIT(11) 6361bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_FIFO_AFULL BIT(10) 6461bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_IS_READWAIT BIT(9) 6561bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CLK_EN BIT(8) 6661bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_FIFO_FULL BIT(7) 6761bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_FIFO_EMPTY BIT(6) 6861bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CRC_RES_ERR BIT(5) 6961bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CRC_READ_ERROR BIT(4) 7061bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_TIMEOUT_WRITE BIT(3) 7161bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CRC_WRITE_ERROR BIT(2) 7261bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_TIMEOUT_RES BIT(1) 7361bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_TIMEOUT_READ BIT(0) 7461bfbdb8SLars-Peter Clausen 7561bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_READ_ERROR_MASK (BIT(4) | BIT(0)) 7661bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_WRITE_ERROR_MASK (BIT(3) | BIT(2)) 7761bfbdb8SLars-Peter Clausen 7861bfbdb8SLars-Peter Clausen 7961bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_IO_ABORT BIT(11) 8061bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_BUS_WIDTH_4BIT BIT(10) 81a02f8f48SZhou Yanjie #define JZ_MMC_CMDAT_BUS_WIDTH_8BIT (BIT(10) | BIT(9)) 82a02f8f48SZhou Yanjie #define JZ_MMC_CMDAT_BUS_WIDTH_MASK (BIT(10) | BIT(9)) 8361bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_DMA_EN BIT(8) 8461bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_INIT BIT(7) 8561bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_BUSY BIT(6) 8661bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_STREAM BIT(5) 8761bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_WRITE BIT(4) 8861bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_DATA_EN BIT(3) 8961bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RESPONSE_FORMAT (BIT(2) | BIT(1) | BIT(0)) 9061bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RSP_R1 1 9161bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RSP_R2 2 9261bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RSP_R3 3 9361bfbdb8SLars-Peter Clausen 9461bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_SDIO BIT(7) 9561bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_TXFIFO_WR_REQ BIT(6) 9661bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_RXFIFO_RD_REQ BIT(5) 9761bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_END_CMD_RES BIT(2) 9861bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_PRG_DONE BIT(1) 9961bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_DATA_TRAN_DONE BIT(0) 10061bfbdb8SLars-Peter Clausen 1016a78768aSAlex Smith #define JZ_MMC_DMAC_DMA_SEL BIT(1) 1026a78768aSAlex Smith #define JZ_MMC_DMAC_DMA_EN BIT(0) 10361bfbdb8SLars-Peter Clausen 10480fe4e90SZhou Yanjie #define JZ_MMC_LPM_DRV_RISING BIT(31) 10580fe4e90SZhou Yanjie #define JZ_MMC_LPM_DRV_RISING_QTR_PHASE_DLY BIT(31) 10680fe4e90SZhou Yanjie #define JZ_MMC_LPM_DRV_RISING_1NS_DLY BIT(30) 10780fe4e90SZhou Yanjie #define JZ_MMC_LPM_SMP_RISING_QTR_OR_HALF_PHASE_DLY BIT(29) 10880fe4e90SZhou Yanjie #define JZ_MMC_LPM_LOW_POWER_MODE_EN BIT(0) 10980fe4e90SZhou Yanjie 11061bfbdb8SLars-Peter Clausen #define JZ_MMC_CLK_RATE 24000000 111d422f8b9SUlf Hansson #define JZ_MMC_REQ_TIMEOUT_MS 5000 11261bfbdb8SLars-Peter Clausen 11361e11ebaSEzequiel Garcia enum jz4740_mmc_version { 11461e11ebaSEzequiel Garcia JZ_MMC_JZ4740, 115a0c938b5SPaul Cercueil JZ_MMC_JZ4725B, 1162af2af99SZhou Yanjie JZ_MMC_JZ4760, 1176a78768aSAlex Smith JZ_MMC_JZ4780, 118fea5fcc2SZhou Yanjie JZ_MMC_X1000, 11961e11ebaSEzequiel Garcia }; 12061e11ebaSEzequiel Garcia 12161bfbdb8SLars-Peter Clausen enum jz4740_mmc_state { 12261bfbdb8SLars-Peter Clausen JZ4740_MMC_STATE_READ_RESPONSE, 12361bfbdb8SLars-Peter Clausen JZ4740_MMC_STATE_TRANSFER_DATA, 12461bfbdb8SLars-Peter Clausen JZ4740_MMC_STATE_SEND_STOP, 12561bfbdb8SLars-Peter Clausen JZ4740_MMC_STATE_DONE, 12661bfbdb8SLars-Peter Clausen }; 12761bfbdb8SLars-Peter Clausen 12896e03fffSEzequiel Garcia /* 12996e03fffSEzequiel Garcia * The MMC core allows to prepare a mmc_request while another mmc_request 13096e03fffSEzequiel Garcia * is in-flight. This is used via the pre_req/post_req hooks. 13196e03fffSEzequiel Garcia * This driver uses the pre_req/post_req hooks to map/unmap the mmc_request. 13296e03fffSEzequiel Garcia * Following what other drivers do (sdhci, dw_mmc) we use the following cookie 13396e03fffSEzequiel Garcia * flags to keep track of the mmc_request mapping state. 13496e03fffSEzequiel Garcia * 13596e03fffSEzequiel Garcia * COOKIE_UNMAPPED: the request is not mapped. 13696e03fffSEzequiel Garcia * COOKIE_PREMAPPED: the request was mapped in pre_req, 13796e03fffSEzequiel Garcia * and should be unmapped in post_req. 13896e03fffSEzequiel Garcia * COOKIE_MAPPED: the request was mapped in the irq handler, 13996e03fffSEzequiel Garcia * and should be unmapped before mmc_request_done is called.. 14096e03fffSEzequiel Garcia */ 14196e03fffSEzequiel Garcia enum jz4780_cookie { 14296e03fffSEzequiel Garcia COOKIE_UNMAPPED = 0, 14396e03fffSEzequiel Garcia COOKIE_PREMAPPED, 14496e03fffSEzequiel Garcia COOKIE_MAPPED, 145bb2f4592SApelete Seketeli }; 146bb2f4592SApelete Seketeli 14761bfbdb8SLars-Peter Clausen struct jz4740_mmc_host { 14861bfbdb8SLars-Peter Clausen struct mmc_host *mmc; 14961bfbdb8SLars-Peter Clausen struct platform_device *pdev; 15061bfbdb8SLars-Peter Clausen struct clk *clk; 15161bfbdb8SLars-Peter Clausen 15261e11ebaSEzequiel Garcia enum jz4740_mmc_version version; 15361e11ebaSEzequiel Garcia 15461bfbdb8SLars-Peter Clausen int irq; 15561bfbdb8SLars-Peter Clausen int card_detect_irq; 15661bfbdb8SLars-Peter Clausen 15761bfbdb8SLars-Peter Clausen void __iomem *base; 1587ca27a6fSApelete Seketeli struct resource *mem_res; 15961bfbdb8SLars-Peter Clausen struct mmc_request *req; 16061bfbdb8SLars-Peter Clausen struct mmc_command *cmd; 16161bfbdb8SLars-Peter Clausen 16261bfbdb8SLars-Peter Clausen unsigned long waiting; 16361bfbdb8SLars-Peter Clausen 16461bfbdb8SLars-Peter Clausen uint32_t cmdat; 16561bfbdb8SLars-Peter Clausen 1666a78768aSAlex Smith uint32_t irq_mask; 16761bfbdb8SLars-Peter Clausen 16861bfbdb8SLars-Peter Clausen spinlock_t lock; 16961bfbdb8SLars-Peter Clausen 17061bfbdb8SLars-Peter Clausen struct timer_list timeout_timer; 17161bfbdb8SLars-Peter Clausen struct sg_mapping_iter miter; 17261bfbdb8SLars-Peter Clausen enum jz4740_mmc_state state; 1737ca27a6fSApelete Seketeli 1747ca27a6fSApelete Seketeli /* DMA support */ 1757ca27a6fSApelete Seketeli struct dma_chan *dma_rx; 1767ca27a6fSApelete Seketeli struct dma_chan *dma_tx; 1777ca27a6fSApelete Seketeli bool use_dma; 1787ca27a6fSApelete Seketeli 1797ca27a6fSApelete Seketeli /* The DMA trigger level is 8 words, that is to say, the DMA read 1807ca27a6fSApelete Seketeli * trigger is when data words in MSC_RXFIFO is >= 8 and the DMA write 1817ca27a6fSApelete Seketeli * trigger is when data words in MSC_TXFIFO is < 8. 1827ca27a6fSApelete Seketeli */ 1837ca27a6fSApelete Seketeli #define JZ4740_MMC_FIFO_HALF_SIZE 8 18461bfbdb8SLars-Peter Clausen }; 18561bfbdb8SLars-Peter Clausen 1866a78768aSAlex Smith static void jz4740_mmc_write_irq_mask(struct jz4740_mmc_host *host, 1876a78768aSAlex Smith uint32_t val) 1886a78768aSAlex Smith { 189a0c938b5SPaul Cercueil if (host->version >= JZ_MMC_JZ4725B) 1906a78768aSAlex Smith return writel(val, host->base + JZ_REG_MMC_IMASK); 1916a78768aSAlex Smith else 1926a78768aSAlex Smith return writew(val, host->base + JZ_REG_MMC_IMASK); 1936a78768aSAlex Smith } 1946a78768aSAlex Smith 1956a78768aSAlex Smith static void jz4740_mmc_write_irq_reg(struct jz4740_mmc_host *host, 1966a78768aSAlex Smith uint32_t val) 1976a78768aSAlex Smith { 1986a78768aSAlex Smith if (host->version >= JZ_MMC_JZ4780) 19965af9866SPaul Cercueil writel(val, host->base + JZ_REG_MMC_IREG); 2006a78768aSAlex Smith else 20165af9866SPaul Cercueil writew(val, host->base + JZ_REG_MMC_IREG); 2026a78768aSAlex Smith } 2036a78768aSAlex Smith 2046a78768aSAlex Smith static uint32_t jz4740_mmc_read_irq_reg(struct jz4740_mmc_host *host) 2056a78768aSAlex Smith { 2066a78768aSAlex Smith if (host->version >= JZ_MMC_JZ4780) 2076a78768aSAlex Smith return readl(host->base + JZ_REG_MMC_IREG); 2086a78768aSAlex Smith else 2096a78768aSAlex Smith return readw(host->base + JZ_REG_MMC_IREG); 2106a78768aSAlex Smith } 2116a78768aSAlex Smith 2127ca27a6fSApelete Seketeli /*----------------------------------------------------------------------------*/ 2137ca27a6fSApelete Seketeli /* DMA infrastructure */ 2147ca27a6fSApelete Seketeli 2157ca27a6fSApelete Seketeli static void jz4740_mmc_release_dma_channels(struct jz4740_mmc_host *host) 2167ca27a6fSApelete Seketeli { 2177ca27a6fSApelete Seketeli if (!host->use_dma) 2187ca27a6fSApelete Seketeli return; 2197ca27a6fSApelete Seketeli 2207ca27a6fSApelete Seketeli dma_release_channel(host->dma_tx); 2217ca27a6fSApelete Seketeli dma_release_channel(host->dma_rx); 2227ca27a6fSApelete Seketeli } 2237ca27a6fSApelete Seketeli 2247ca27a6fSApelete Seketeli static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host) 2257ca27a6fSApelete Seketeli { 226fb0ce9ddSEzequiel Garcia host->dma_tx = dma_request_chan(mmc_dev(host->mmc), "tx"); 227fb0ce9ddSEzequiel Garcia if (IS_ERR(host->dma_tx)) { 2287ca27a6fSApelete Seketeli dev_err(mmc_dev(host->mmc), "Failed to get dma_tx channel\n"); 229fb0ce9ddSEzequiel Garcia return PTR_ERR(host->dma_tx); 2307ca27a6fSApelete Seketeli } 2317ca27a6fSApelete Seketeli 232fb0ce9ddSEzequiel Garcia host->dma_rx = dma_request_chan(mmc_dev(host->mmc), "rx"); 233fb0ce9ddSEzequiel Garcia if (IS_ERR(host->dma_rx)) { 2347ca27a6fSApelete Seketeli dev_err(mmc_dev(host->mmc), "Failed to get dma_rx channel\n"); 235fb0ce9ddSEzequiel Garcia dma_release_channel(host->dma_tx); 236fb0ce9ddSEzequiel Garcia return PTR_ERR(host->dma_rx); 2377ca27a6fSApelete Seketeli } 2387ca27a6fSApelete Seketeli 2397ca27a6fSApelete Seketeli return 0; 2407ca27a6fSApelete Seketeli } 2417ca27a6fSApelete Seketeli 242bb2f4592SApelete Seketeli static inline struct dma_chan *jz4740_mmc_get_dma_chan(struct jz4740_mmc_host *host, 243bb2f4592SApelete Seketeli struct mmc_data *data) 244bb2f4592SApelete Seketeli { 245bb2f4592SApelete Seketeli return (data->flags & MMC_DATA_READ) ? host->dma_rx : host->dma_tx; 246bb2f4592SApelete Seketeli } 247bb2f4592SApelete Seketeli 2487ca27a6fSApelete Seketeli static void jz4740_mmc_dma_unmap(struct jz4740_mmc_host *host, 2497ca27a6fSApelete Seketeli struct mmc_data *data) 2507ca27a6fSApelete Seketeli { 251bb2f4592SApelete Seketeli struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 252feeef096SHeiner Kallweit enum dma_data_direction dir = mmc_get_dma_dir(data); 2537ca27a6fSApelete Seketeli 2547ca27a6fSApelete Seketeli dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir); 25596e03fffSEzequiel Garcia data->host_cookie = COOKIE_UNMAPPED; 2567ca27a6fSApelete Seketeli } 2577ca27a6fSApelete Seketeli 25896e03fffSEzequiel Garcia /* Prepares DMA data for current or next transfer. 25996e03fffSEzequiel Garcia * A request can be in-flight when this is called. 26096e03fffSEzequiel Garcia */ 261bb2f4592SApelete Seketeli static int jz4740_mmc_prepare_dma_data(struct jz4740_mmc_host *host, 262bb2f4592SApelete Seketeli struct mmc_data *data, 26396e03fffSEzequiel Garcia int cookie) 264bb2f4592SApelete Seketeli { 26596e03fffSEzequiel Garcia struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 266feeef096SHeiner Kallweit enum dma_data_direction dir = mmc_get_dma_dir(data); 26796e03fffSEzequiel Garcia int sg_count; 268bb2f4592SApelete Seketeli 26996e03fffSEzequiel Garcia if (data->host_cookie == COOKIE_PREMAPPED) 27096e03fffSEzequiel Garcia return data->sg_count; 271bb2f4592SApelete Seketeli 27296e03fffSEzequiel Garcia sg_count = dma_map_sg(chan->device->dev, 273bb2f4592SApelete Seketeli data->sg, 274bb2f4592SApelete Seketeli data->sg_len, 275bb2f4592SApelete Seketeli dir); 276bb2f4592SApelete Seketeli 27796e03fffSEzequiel Garcia if (sg_count <= 0) { 278bb2f4592SApelete Seketeli dev_err(mmc_dev(host->mmc), 279bb2f4592SApelete Seketeli "Failed to map scatterlist for DMA operation\n"); 280bb2f4592SApelete Seketeli return -EINVAL; 281bb2f4592SApelete Seketeli } 282bb2f4592SApelete Seketeli 28396e03fffSEzequiel Garcia data->sg_count = sg_count; 28496e03fffSEzequiel Garcia data->host_cookie = cookie; 285bb2f4592SApelete Seketeli 28696e03fffSEzequiel Garcia return data->sg_count; 287bb2f4592SApelete Seketeli } 288bb2f4592SApelete Seketeli 2897ca27a6fSApelete Seketeli static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host, 2907ca27a6fSApelete Seketeli struct mmc_data *data) 2917ca27a6fSApelete Seketeli { 29296e03fffSEzequiel Garcia struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 2937ca27a6fSApelete Seketeli struct dma_async_tx_descriptor *desc; 2947ca27a6fSApelete Seketeli struct dma_slave_config conf = { 2957ca27a6fSApelete Seketeli .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 2967ca27a6fSApelete Seketeli .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 2977ca27a6fSApelete Seketeli .src_maxburst = JZ4740_MMC_FIFO_HALF_SIZE, 2987ca27a6fSApelete Seketeli .dst_maxburst = JZ4740_MMC_FIFO_HALF_SIZE, 2997ca27a6fSApelete Seketeli }; 30096e03fffSEzequiel Garcia int sg_count; 3017ca27a6fSApelete Seketeli 302bb2f4592SApelete Seketeli if (data->flags & MMC_DATA_WRITE) { 3037ca27a6fSApelete Seketeli conf.direction = DMA_MEM_TO_DEV; 3047ca27a6fSApelete Seketeli conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO; 3057ca27a6fSApelete Seketeli } else { 3067ca27a6fSApelete Seketeli conf.direction = DMA_DEV_TO_MEM; 3077ca27a6fSApelete Seketeli conf.src_addr = host->mem_res->start + JZ_REG_MMC_RXFIFO; 3087ca27a6fSApelete Seketeli } 3097ca27a6fSApelete Seketeli 31096e03fffSEzequiel Garcia sg_count = jz4740_mmc_prepare_dma_data(host, data, COOKIE_MAPPED); 31196e03fffSEzequiel Garcia if (sg_count < 0) 31296e03fffSEzequiel Garcia return sg_count; 3137ca27a6fSApelete Seketeli 3147ca27a6fSApelete Seketeli dmaengine_slave_config(chan, &conf); 31596e03fffSEzequiel Garcia desc = dmaengine_prep_slave_sg(chan, data->sg, sg_count, 3167ca27a6fSApelete Seketeli conf.direction, 3177ca27a6fSApelete Seketeli DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 3187ca27a6fSApelete Seketeli if (!desc) { 3197ca27a6fSApelete Seketeli dev_err(mmc_dev(host->mmc), 3207ca27a6fSApelete Seketeli "Failed to allocate DMA %s descriptor", 3217ca27a6fSApelete Seketeli conf.direction == DMA_MEM_TO_DEV ? "TX" : "RX"); 3227ca27a6fSApelete Seketeli goto dma_unmap; 3237ca27a6fSApelete Seketeli } 3247ca27a6fSApelete Seketeli 3257ca27a6fSApelete Seketeli dmaengine_submit(desc); 3267ca27a6fSApelete Seketeli dma_async_issue_pending(chan); 3277ca27a6fSApelete Seketeli 3287ca27a6fSApelete Seketeli return 0; 3297ca27a6fSApelete Seketeli 3307ca27a6fSApelete Seketeli dma_unmap: 33196e03fffSEzequiel Garcia if (data->host_cookie == COOKIE_MAPPED) 3327ca27a6fSApelete Seketeli jz4740_mmc_dma_unmap(host, data); 3337ca27a6fSApelete Seketeli return -ENOMEM; 3347ca27a6fSApelete Seketeli } 3357ca27a6fSApelete Seketeli 336bb2f4592SApelete Seketeli static void jz4740_mmc_pre_request(struct mmc_host *mmc, 337d3c6aac3SLinus Walleij struct mmc_request *mrq) 338bb2f4592SApelete Seketeli { 339bb2f4592SApelete Seketeli struct jz4740_mmc_host *host = mmc_priv(mmc); 340bb2f4592SApelete Seketeli struct mmc_data *data = mrq->data; 341bb2f4592SApelete Seketeli 34296e03fffSEzequiel Garcia if (!host->use_dma) 34396e03fffSEzequiel Garcia return; 344bb2f4592SApelete Seketeli 34596e03fffSEzequiel Garcia data->host_cookie = COOKIE_UNMAPPED; 34696e03fffSEzequiel Garcia if (jz4740_mmc_prepare_dma_data(host, data, COOKIE_PREMAPPED) < 0) 34796e03fffSEzequiel Garcia data->host_cookie = COOKIE_UNMAPPED; 348bb2f4592SApelete Seketeli } 349bb2f4592SApelete Seketeli 350bb2f4592SApelete Seketeli static void jz4740_mmc_post_request(struct mmc_host *mmc, 351bb2f4592SApelete Seketeli struct mmc_request *mrq, 352bb2f4592SApelete Seketeli int err) 353bb2f4592SApelete Seketeli { 354bb2f4592SApelete Seketeli struct jz4740_mmc_host *host = mmc_priv(mmc); 355bb2f4592SApelete Seketeli struct mmc_data *data = mrq->data; 356bb2f4592SApelete Seketeli 35796e03fffSEzequiel Garcia if (data && data->host_cookie != COOKIE_UNMAPPED) 358bb2f4592SApelete Seketeli jz4740_mmc_dma_unmap(host, data); 359bb2f4592SApelete Seketeli 360bb2f4592SApelete Seketeli if (err) { 361bb2f4592SApelete Seketeli struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 362bb2f4592SApelete Seketeli 363bb2f4592SApelete Seketeli dmaengine_terminate_all(chan); 364bb2f4592SApelete Seketeli } 365bb2f4592SApelete Seketeli } 366bb2f4592SApelete Seketeli 3677ca27a6fSApelete Seketeli /*----------------------------------------------------------------------------*/ 3687ca27a6fSApelete Seketeli 36961bfbdb8SLars-Peter Clausen static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host, 37061bfbdb8SLars-Peter Clausen unsigned int irq, bool enabled) 37161bfbdb8SLars-Peter Clausen { 37261bfbdb8SLars-Peter Clausen unsigned long flags; 37361bfbdb8SLars-Peter Clausen 37461bfbdb8SLars-Peter Clausen spin_lock_irqsave(&host->lock, flags); 37561bfbdb8SLars-Peter Clausen if (enabled) 37661bfbdb8SLars-Peter Clausen host->irq_mask &= ~irq; 37761bfbdb8SLars-Peter Clausen else 37861bfbdb8SLars-Peter Clausen host->irq_mask |= irq; 37961bfbdb8SLars-Peter Clausen 3806a78768aSAlex Smith jz4740_mmc_write_irq_mask(host, host->irq_mask); 381a04f0017SAlex Smith spin_unlock_irqrestore(&host->lock, flags); 38261bfbdb8SLars-Peter Clausen } 38361bfbdb8SLars-Peter Clausen 38461bfbdb8SLars-Peter Clausen static void jz4740_mmc_clock_enable(struct jz4740_mmc_host *host, 38561bfbdb8SLars-Peter Clausen bool start_transfer) 38661bfbdb8SLars-Peter Clausen { 38761bfbdb8SLars-Peter Clausen uint16_t val = JZ_MMC_STRPCL_CLOCK_START; 38861bfbdb8SLars-Peter Clausen 38961bfbdb8SLars-Peter Clausen if (start_transfer) 39061bfbdb8SLars-Peter Clausen val |= JZ_MMC_STRPCL_START_OP; 39161bfbdb8SLars-Peter Clausen 39261bfbdb8SLars-Peter Clausen writew(val, host->base + JZ_REG_MMC_STRPCL); 39361bfbdb8SLars-Peter Clausen } 39461bfbdb8SLars-Peter Clausen 39561bfbdb8SLars-Peter Clausen static void jz4740_mmc_clock_disable(struct jz4740_mmc_host *host) 39661bfbdb8SLars-Peter Clausen { 39761bfbdb8SLars-Peter Clausen uint32_t status; 39861bfbdb8SLars-Peter Clausen unsigned int timeout = 1000; 39961bfbdb8SLars-Peter Clausen 40061bfbdb8SLars-Peter Clausen writew(JZ_MMC_STRPCL_CLOCK_STOP, host->base + JZ_REG_MMC_STRPCL); 40161bfbdb8SLars-Peter Clausen do { 40261bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 40361bfbdb8SLars-Peter Clausen } while (status & JZ_MMC_STATUS_CLK_EN && --timeout); 40461bfbdb8SLars-Peter Clausen } 40561bfbdb8SLars-Peter Clausen 40661bfbdb8SLars-Peter Clausen static void jz4740_mmc_reset(struct jz4740_mmc_host *host) 40761bfbdb8SLars-Peter Clausen { 40861bfbdb8SLars-Peter Clausen uint32_t status; 40961bfbdb8SLars-Peter Clausen unsigned int timeout = 1000; 41061bfbdb8SLars-Peter Clausen 41161bfbdb8SLars-Peter Clausen writew(JZ_MMC_STRPCL_RESET, host->base + JZ_REG_MMC_STRPCL); 41261bfbdb8SLars-Peter Clausen udelay(10); 41361bfbdb8SLars-Peter Clausen do { 41461bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 41561bfbdb8SLars-Peter Clausen } while (status & JZ_MMC_STATUS_IS_RESETTING && --timeout); 41661bfbdb8SLars-Peter Clausen } 41761bfbdb8SLars-Peter Clausen 41861bfbdb8SLars-Peter Clausen static void jz4740_mmc_request_done(struct jz4740_mmc_host *host) 41961bfbdb8SLars-Peter Clausen { 42061bfbdb8SLars-Peter Clausen struct mmc_request *req; 42196e03fffSEzequiel Garcia struct mmc_data *data; 42261bfbdb8SLars-Peter Clausen 42361bfbdb8SLars-Peter Clausen req = host->req; 42496e03fffSEzequiel Garcia data = req->data; 42561bfbdb8SLars-Peter Clausen host->req = NULL; 42661bfbdb8SLars-Peter Clausen 42796e03fffSEzequiel Garcia if (data && data->host_cookie == COOKIE_MAPPED) 42896e03fffSEzequiel Garcia jz4740_mmc_dma_unmap(host, data); 42961bfbdb8SLars-Peter Clausen mmc_request_done(host->mmc, req); 43061bfbdb8SLars-Peter Clausen } 43161bfbdb8SLars-Peter Clausen 43261bfbdb8SLars-Peter Clausen static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host, 43361bfbdb8SLars-Peter Clausen unsigned int irq) 43461bfbdb8SLars-Peter Clausen { 43561bfbdb8SLars-Peter Clausen unsigned int timeout = 0x800; 4366a78768aSAlex Smith uint32_t status; 43761bfbdb8SLars-Peter Clausen 43861bfbdb8SLars-Peter Clausen do { 4396a78768aSAlex Smith status = jz4740_mmc_read_irq_reg(host); 44061bfbdb8SLars-Peter Clausen } while (!(status & irq) && --timeout); 44161bfbdb8SLars-Peter Clausen 44261bfbdb8SLars-Peter Clausen if (timeout == 0) { 44361bfbdb8SLars-Peter Clausen set_bit(0, &host->waiting); 444d422f8b9SUlf Hansson mod_timer(&host->timeout_timer, 445d422f8b9SUlf Hansson jiffies + msecs_to_jiffies(JZ_MMC_REQ_TIMEOUT_MS)); 44661bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, irq, true); 44761bfbdb8SLars-Peter Clausen return true; 44861bfbdb8SLars-Peter Clausen } 44961bfbdb8SLars-Peter Clausen 45061bfbdb8SLars-Peter Clausen return false; 45161bfbdb8SLars-Peter Clausen } 45261bfbdb8SLars-Peter Clausen 45361bfbdb8SLars-Peter Clausen static void jz4740_mmc_transfer_check_state(struct jz4740_mmc_host *host, 45461bfbdb8SLars-Peter Clausen struct mmc_data *data) 45561bfbdb8SLars-Peter Clausen { 45661bfbdb8SLars-Peter Clausen int status; 45761bfbdb8SLars-Peter Clausen 45861bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 45961bfbdb8SLars-Peter Clausen if (status & JZ_MMC_STATUS_WRITE_ERROR_MASK) { 46061bfbdb8SLars-Peter Clausen if (status & (JZ_MMC_STATUS_TIMEOUT_WRITE)) { 46161bfbdb8SLars-Peter Clausen host->req->cmd->error = -ETIMEDOUT; 46261bfbdb8SLars-Peter Clausen data->error = -ETIMEDOUT; 46361bfbdb8SLars-Peter Clausen } else { 46461bfbdb8SLars-Peter Clausen host->req->cmd->error = -EIO; 46561bfbdb8SLars-Peter Clausen data->error = -EIO; 46661bfbdb8SLars-Peter Clausen } 4678a489aa1SPaul Cercueil } else if (status & JZ_MMC_STATUS_READ_ERROR_MASK) { 4688a489aa1SPaul Cercueil if (status & (JZ_MMC_STATUS_TIMEOUT_READ)) { 4698a489aa1SPaul Cercueil host->req->cmd->error = -ETIMEDOUT; 4708a489aa1SPaul Cercueil data->error = -ETIMEDOUT; 4718a489aa1SPaul Cercueil } else { 4728a489aa1SPaul Cercueil host->req->cmd->error = -EIO; 4738a489aa1SPaul Cercueil data->error = -EIO; 4748a489aa1SPaul Cercueil } 47561bfbdb8SLars-Peter Clausen } 47661bfbdb8SLars-Peter Clausen } 47761bfbdb8SLars-Peter Clausen 47861bfbdb8SLars-Peter Clausen static bool jz4740_mmc_write_data(struct jz4740_mmc_host *host, 47961bfbdb8SLars-Peter Clausen struct mmc_data *data) 48061bfbdb8SLars-Peter Clausen { 48161bfbdb8SLars-Peter Clausen struct sg_mapping_iter *miter = &host->miter; 48261bfbdb8SLars-Peter Clausen void __iomem *fifo_addr = host->base + JZ_REG_MMC_TXFIFO; 48361bfbdb8SLars-Peter Clausen uint32_t *buf; 48461bfbdb8SLars-Peter Clausen bool timeout; 48561bfbdb8SLars-Peter Clausen size_t i, j; 48661bfbdb8SLars-Peter Clausen 48761bfbdb8SLars-Peter Clausen while (sg_miter_next(miter)) { 48861bfbdb8SLars-Peter Clausen buf = miter->addr; 48961bfbdb8SLars-Peter Clausen i = miter->length / 4; 49061bfbdb8SLars-Peter Clausen j = i / 8; 49161bfbdb8SLars-Peter Clausen i = i & 0x7; 49261bfbdb8SLars-Peter Clausen while (j) { 49361bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); 49461bfbdb8SLars-Peter Clausen if (unlikely(timeout)) 49561bfbdb8SLars-Peter Clausen goto poll_timeout; 49661bfbdb8SLars-Peter Clausen 49761bfbdb8SLars-Peter Clausen writel(buf[0], fifo_addr); 49861bfbdb8SLars-Peter Clausen writel(buf[1], fifo_addr); 49961bfbdb8SLars-Peter Clausen writel(buf[2], fifo_addr); 50061bfbdb8SLars-Peter Clausen writel(buf[3], fifo_addr); 50161bfbdb8SLars-Peter Clausen writel(buf[4], fifo_addr); 50261bfbdb8SLars-Peter Clausen writel(buf[5], fifo_addr); 50361bfbdb8SLars-Peter Clausen writel(buf[6], fifo_addr); 50461bfbdb8SLars-Peter Clausen writel(buf[7], fifo_addr); 50561bfbdb8SLars-Peter Clausen buf += 8; 50661bfbdb8SLars-Peter Clausen --j; 50761bfbdb8SLars-Peter Clausen } 50861bfbdb8SLars-Peter Clausen if (unlikely(i)) { 50961bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); 51061bfbdb8SLars-Peter Clausen if (unlikely(timeout)) 51161bfbdb8SLars-Peter Clausen goto poll_timeout; 51261bfbdb8SLars-Peter Clausen 51361bfbdb8SLars-Peter Clausen while (i) { 51461bfbdb8SLars-Peter Clausen writel(*buf, fifo_addr); 51561bfbdb8SLars-Peter Clausen ++buf; 51661bfbdb8SLars-Peter Clausen --i; 51761bfbdb8SLars-Peter Clausen } 51861bfbdb8SLars-Peter Clausen } 51961bfbdb8SLars-Peter Clausen data->bytes_xfered += miter->length; 52061bfbdb8SLars-Peter Clausen } 52161bfbdb8SLars-Peter Clausen sg_miter_stop(miter); 52261bfbdb8SLars-Peter Clausen 52361bfbdb8SLars-Peter Clausen return false; 52461bfbdb8SLars-Peter Clausen 52561bfbdb8SLars-Peter Clausen poll_timeout: 52661bfbdb8SLars-Peter Clausen miter->consumed = (void *)buf - miter->addr; 52761bfbdb8SLars-Peter Clausen data->bytes_xfered += miter->consumed; 52861bfbdb8SLars-Peter Clausen sg_miter_stop(miter); 52961bfbdb8SLars-Peter Clausen 53061bfbdb8SLars-Peter Clausen return true; 53161bfbdb8SLars-Peter Clausen } 53261bfbdb8SLars-Peter Clausen 53361bfbdb8SLars-Peter Clausen static bool jz4740_mmc_read_data(struct jz4740_mmc_host *host, 53461bfbdb8SLars-Peter Clausen struct mmc_data *data) 53561bfbdb8SLars-Peter Clausen { 53661bfbdb8SLars-Peter Clausen struct sg_mapping_iter *miter = &host->miter; 53761bfbdb8SLars-Peter Clausen void __iomem *fifo_addr = host->base + JZ_REG_MMC_RXFIFO; 53861bfbdb8SLars-Peter Clausen uint32_t *buf; 53961bfbdb8SLars-Peter Clausen uint32_t d; 5406a78768aSAlex Smith uint32_t status; 54161bfbdb8SLars-Peter Clausen size_t i, j; 54261bfbdb8SLars-Peter Clausen unsigned int timeout; 54361bfbdb8SLars-Peter Clausen 54461bfbdb8SLars-Peter Clausen while (sg_miter_next(miter)) { 54561bfbdb8SLars-Peter Clausen buf = miter->addr; 54661bfbdb8SLars-Peter Clausen i = miter->length; 54761bfbdb8SLars-Peter Clausen j = i / 32; 54861bfbdb8SLars-Peter Clausen i = i & 0x1f; 54961bfbdb8SLars-Peter Clausen while (j) { 55061bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); 55161bfbdb8SLars-Peter Clausen if (unlikely(timeout)) 55261bfbdb8SLars-Peter Clausen goto poll_timeout; 55361bfbdb8SLars-Peter Clausen 55461bfbdb8SLars-Peter Clausen buf[0] = readl(fifo_addr); 55561bfbdb8SLars-Peter Clausen buf[1] = readl(fifo_addr); 55661bfbdb8SLars-Peter Clausen buf[2] = readl(fifo_addr); 55761bfbdb8SLars-Peter Clausen buf[3] = readl(fifo_addr); 55861bfbdb8SLars-Peter Clausen buf[4] = readl(fifo_addr); 55961bfbdb8SLars-Peter Clausen buf[5] = readl(fifo_addr); 56061bfbdb8SLars-Peter Clausen buf[6] = readl(fifo_addr); 56161bfbdb8SLars-Peter Clausen buf[7] = readl(fifo_addr); 56261bfbdb8SLars-Peter Clausen 56361bfbdb8SLars-Peter Clausen buf += 8; 56461bfbdb8SLars-Peter Clausen --j; 56561bfbdb8SLars-Peter Clausen } 56661bfbdb8SLars-Peter Clausen 56761bfbdb8SLars-Peter Clausen if (unlikely(i)) { 56861bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); 56961bfbdb8SLars-Peter Clausen if (unlikely(timeout)) 57061bfbdb8SLars-Peter Clausen goto poll_timeout; 57161bfbdb8SLars-Peter Clausen 57261bfbdb8SLars-Peter Clausen while (i >= 4) { 57361bfbdb8SLars-Peter Clausen *buf++ = readl(fifo_addr); 57461bfbdb8SLars-Peter Clausen i -= 4; 57561bfbdb8SLars-Peter Clausen } 57661bfbdb8SLars-Peter Clausen if (unlikely(i > 0)) { 57761bfbdb8SLars-Peter Clausen d = readl(fifo_addr); 57861bfbdb8SLars-Peter Clausen memcpy(buf, &d, i); 57961bfbdb8SLars-Peter Clausen } 58061bfbdb8SLars-Peter Clausen } 58161bfbdb8SLars-Peter Clausen data->bytes_xfered += miter->length; 58261bfbdb8SLars-Peter Clausen 58361bfbdb8SLars-Peter Clausen /* This can go away once MIPS implements 58461bfbdb8SLars-Peter Clausen * flush_kernel_dcache_page */ 58561bfbdb8SLars-Peter Clausen flush_dcache_page(miter->page); 58661bfbdb8SLars-Peter Clausen } 58761bfbdb8SLars-Peter Clausen sg_miter_stop(miter); 58861bfbdb8SLars-Peter Clausen 58961bfbdb8SLars-Peter Clausen /* For whatever reason there is sometime one word more in the fifo then 59061bfbdb8SLars-Peter Clausen * requested */ 59161bfbdb8SLars-Peter Clausen timeout = 1000; 59261bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 59361bfbdb8SLars-Peter Clausen while (!(status & JZ_MMC_STATUS_DATA_FIFO_EMPTY) && --timeout) { 59461bfbdb8SLars-Peter Clausen d = readl(fifo_addr); 59561bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 59661bfbdb8SLars-Peter Clausen } 59761bfbdb8SLars-Peter Clausen 59861bfbdb8SLars-Peter Clausen return false; 59961bfbdb8SLars-Peter Clausen 60061bfbdb8SLars-Peter Clausen poll_timeout: 60161bfbdb8SLars-Peter Clausen miter->consumed = (void *)buf - miter->addr; 60261bfbdb8SLars-Peter Clausen data->bytes_xfered += miter->consumed; 60361bfbdb8SLars-Peter Clausen sg_miter_stop(miter); 60461bfbdb8SLars-Peter Clausen 60561bfbdb8SLars-Peter Clausen return true; 60661bfbdb8SLars-Peter Clausen } 60761bfbdb8SLars-Peter Clausen 6082ee4f620SKees Cook static void jz4740_mmc_timeout(struct timer_list *t) 60961bfbdb8SLars-Peter Clausen { 6102ee4f620SKees Cook struct jz4740_mmc_host *host = from_timer(host, t, timeout_timer); 61161bfbdb8SLars-Peter Clausen 61261bfbdb8SLars-Peter Clausen if (!test_and_clear_bit(0, &host->waiting)) 61361bfbdb8SLars-Peter Clausen return; 61461bfbdb8SLars-Peter Clausen 61561bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, false); 61661bfbdb8SLars-Peter Clausen 61761bfbdb8SLars-Peter Clausen host->req->cmd->error = -ETIMEDOUT; 61861bfbdb8SLars-Peter Clausen jz4740_mmc_request_done(host); 61961bfbdb8SLars-Peter Clausen } 62061bfbdb8SLars-Peter Clausen 62161bfbdb8SLars-Peter Clausen static void jz4740_mmc_read_response(struct jz4740_mmc_host *host, 62261bfbdb8SLars-Peter Clausen struct mmc_command *cmd) 62361bfbdb8SLars-Peter Clausen { 62461bfbdb8SLars-Peter Clausen int i; 62561bfbdb8SLars-Peter Clausen uint16_t tmp; 62661bfbdb8SLars-Peter Clausen void __iomem *fifo_addr = host->base + JZ_REG_MMC_RESP_FIFO; 62761bfbdb8SLars-Peter Clausen 62861bfbdb8SLars-Peter Clausen if (cmd->flags & MMC_RSP_136) { 62961bfbdb8SLars-Peter Clausen tmp = readw(fifo_addr); 63061bfbdb8SLars-Peter Clausen for (i = 0; i < 4; ++i) { 63161bfbdb8SLars-Peter Clausen cmd->resp[i] = tmp << 24; 63261bfbdb8SLars-Peter Clausen tmp = readw(fifo_addr); 63361bfbdb8SLars-Peter Clausen cmd->resp[i] |= tmp << 8; 63461bfbdb8SLars-Peter Clausen tmp = readw(fifo_addr); 63561bfbdb8SLars-Peter Clausen cmd->resp[i] |= tmp >> 8; 63661bfbdb8SLars-Peter Clausen } 63761bfbdb8SLars-Peter Clausen } else { 63861bfbdb8SLars-Peter Clausen cmd->resp[0] = readw(fifo_addr) << 24; 63961bfbdb8SLars-Peter Clausen cmd->resp[0] |= readw(fifo_addr) << 8; 64061bfbdb8SLars-Peter Clausen cmd->resp[0] |= readw(fifo_addr) & 0xff; 64161bfbdb8SLars-Peter Clausen } 64261bfbdb8SLars-Peter Clausen } 64361bfbdb8SLars-Peter Clausen 64461bfbdb8SLars-Peter Clausen static void jz4740_mmc_send_command(struct jz4740_mmc_host *host, 64561bfbdb8SLars-Peter Clausen struct mmc_command *cmd) 64661bfbdb8SLars-Peter Clausen { 64761bfbdb8SLars-Peter Clausen uint32_t cmdat = host->cmdat; 64861bfbdb8SLars-Peter Clausen 64961bfbdb8SLars-Peter Clausen host->cmdat &= ~JZ_MMC_CMDAT_INIT; 65061bfbdb8SLars-Peter Clausen jz4740_mmc_clock_disable(host); 65161bfbdb8SLars-Peter Clausen 65261bfbdb8SLars-Peter Clausen host->cmd = cmd; 65361bfbdb8SLars-Peter Clausen 65461bfbdb8SLars-Peter Clausen if (cmd->flags & MMC_RSP_BUSY) 65561bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_BUSY; 65661bfbdb8SLars-Peter Clausen 65761bfbdb8SLars-Peter Clausen switch (mmc_resp_type(cmd)) { 65861bfbdb8SLars-Peter Clausen case MMC_RSP_R1B: 65961bfbdb8SLars-Peter Clausen case MMC_RSP_R1: 66061bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_RSP_R1; 66161bfbdb8SLars-Peter Clausen break; 66261bfbdb8SLars-Peter Clausen case MMC_RSP_R2: 66361bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_RSP_R2; 66461bfbdb8SLars-Peter Clausen break; 66561bfbdb8SLars-Peter Clausen case MMC_RSP_R3: 66661bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_RSP_R3; 66761bfbdb8SLars-Peter Clausen break; 66861bfbdb8SLars-Peter Clausen default: 66961bfbdb8SLars-Peter Clausen break; 67061bfbdb8SLars-Peter Clausen } 67161bfbdb8SLars-Peter Clausen 67261bfbdb8SLars-Peter Clausen if (cmd->data) { 67361bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_DATA_EN; 67461bfbdb8SLars-Peter Clausen if (cmd->data->flags & MMC_DATA_WRITE) 67561bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_WRITE; 6766a78768aSAlex Smith if (host->use_dma) { 6776a78768aSAlex Smith /* 6786a78768aSAlex Smith * The 4780's MMC controller has integrated DMA ability 6796a78768aSAlex Smith * in addition to being able to use the external DMA 6806a78768aSAlex Smith * controller. It moves DMA control bits to a separate 6816a78768aSAlex Smith * register. The DMA_SEL bit chooses the external 6826a78768aSAlex Smith * controller over the integrated one. Earlier SoCs 6836a78768aSAlex Smith * can only use the external controller, and have a 6846a78768aSAlex Smith * single DMA enable bit in CMDAT. 6856a78768aSAlex Smith */ 6866a78768aSAlex Smith if (host->version >= JZ_MMC_JZ4780) { 6876a78768aSAlex Smith writel(JZ_MMC_DMAC_DMA_EN | JZ_MMC_DMAC_DMA_SEL, 6886a78768aSAlex Smith host->base + JZ_REG_MMC_DMAC); 6896a78768aSAlex Smith } else { 6907ca27a6fSApelete Seketeli cmdat |= JZ_MMC_CMDAT_DMA_EN; 6916a78768aSAlex Smith } 6926a78768aSAlex Smith } else if (host->version >= JZ_MMC_JZ4780) { 6936a78768aSAlex Smith writel(0, host->base + JZ_REG_MMC_DMAC); 6946a78768aSAlex Smith } 69561bfbdb8SLars-Peter Clausen 69661bfbdb8SLars-Peter Clausen writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN); 69761bfbdb8SLars-Peter Clausen writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB); 69861bfbdb8SLars-Peter Clausen } 69961bfbdb8SLars-Peter Clausen 70061bfbdb8SLars-Peter Clausen writeb(cmd->opcode, host->base + JZ_REG_MMC_CMD); 70161bfbdb8SLars-Peter Clausen writel(cmd->arg, host->base + JZ_REG_MMC_ARG); 70261bfbdb8SLars-Peter Clausen writel(cmdat, host->base + JZ_REG_MMC_CMDAT); 70361bfbdb8SLars-Peter Clausen 70461bfbdb8SLars-Peter Clausen jz4740_mmc_clock_enable(host, 1); 70561bfbdb8SLars-Peter Clausen } 70661bfbdb8SLars-Peter Clausen 70761bfbdb8SLars-Peter Clausen static void jz_mmc_prepare_data_transfer(struct jz4740_mmc_host *host) 70861bfbdb8SLars-Peter Clausen { 70961bfbdb8SLars-Peter Clausen struct mmc_command *cmd = host->req->cmd; 71061bfbdb8SLars-Peter Clausen struct mmc_data *data = cmd->data; 71161bfbdb8SLars-Peter Clausen int direction; 71261bfbdb8SLars-Peter Clausen 71361bfbdb8SLars-Peter Clausen if (data->flags & MMC_DATA_READ) 71461bfbdb8SLars-Peter Clausen direction = SG_MITER_TO_SG; 71561bfbdb8SLars-Peter Clausen else 71661bfbdb8SLars-Peter Clausen direction = SG_MITER_FROM_SG; 71761bfbdb8SLars-Peter Clausen 71861bfbdb8SLars-Peter Clausen sg_miter_start(&host->miter, data->sg, data->sg_len, direction); 71961bfbdb8SLars-Peter Clausen } 72061bfbdb8SLars-Peter Clausen 72161bfbdb8SLars-Peter Clausen 72261bfbdb8SLars-Peter Clausen static irqreturn_t jz_mmc_irq_worker(int irq, void *devid) 72361bfbdb8SLars-Peter Clausen { 72461bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)devid; 72561bfbdb8SLars-Peter Clausen struct mmc_command *cmd = host->req->cmd; 72661bfbdb8SLars-Peter Clausen struct mmc_request *req = host->req; 7277ca27a6fSApelete Seketeli struct mmc_data *data = cmd->data; 72861bfbdb8SLars-Peter Clausen bool timeout = false; 72961bfbdb8SLars-Peter Clausen 73061bfbdb8SLars-Peter Clausen if (cmd->error) 73161bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_DONE; 73261bfbdb8SLars-Peter Clausen 73361bfbdb8SLars-Peter Clausen switch (host->state) { 73461bfbdb8SLars-Peter Clausen case JZ4740_MMC_STATE_READ_RESPONSE: 73561bfbdb8SLars-Peter Clausen if (cmd->flags & MMC_RSP_PRESENT) 73661bfbdb8SLars-Peter Clausen jz4740_mmc_read_response(host, cmd); 73761bfbdb8SLars-Peter Clausen 7387ca27a6fSApelete Seketeli if (!data) 73961bfbdb8SLars-Peter Clausen break; 74061bfbdb8SLars-Peter Clausen 74161bfbdb8SLars-Peter Clausen jz_mmc_prepare_data_transfer(host); 742df561f66SGustavo A. R. Silva fallthrough; 74361bfbdb8SLars-Peter Clausen 74461bfbdb8SLars-Peter Clausen case JZ4740_MMC_STATE_TRANSFER_DATA: 7457ca27a6fSApelete Seketeli if (host->use_dma) { 746bb2f4592SApelete Seketeli /* Use DMA if enabled. 747bb2f4592SApelete Seketeli * Data transfer direction is defined later by 748bb2f4592SApelete Seketeli * relying on data flags in 749bb2f4592SApelete Seketeli * jz4740_mmc_prepare_dma_data() and 750bb2f4592SApelete Seketeli * jz4740_mmc_start_dma_transfer(). 7517ca27a6fSApelete Seketeli */ 7527ca27a6fSApelete Seketeli timeout = jz4740_mmc_start_dma_transfer(host, data); 7537ca27a6fSApelete Seketeli data->bytes_xfered = data->blocks * data->blksz; 7547ca27a6fSApelete Seketeli } else if (data->flags & MMC_DATA_READ) 755bb2f4592SApelete Seketeli /* Use PIO if DMA is not enabled. 756bb2f4592SApelete Seketeli * Data transfer direction was defined before 757bb2f4592SApelete Seketeli * by relying on data flags in 758bb2f4592SApelete Seketeli * jz_mmc_prepare_data_transfer(). 7597ca27a6fSApelete Seketeli */ 7607ca27a6fSApelete Seketeli timeout = jz4740_mmc_read_data(host, data); 76161bfbdb8SLars-Peter Clausen else 7627ca27a6fSApelete Seketeli timeout = jz4740_mmc_write_data(host, data); 76361bfbdb8SLars-Peter Clausen 76461bfbdb8SLars-Peter Clausen if (unlikely(timeout)) { 76561bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_TRANSFER_DATA; 76661bfbdb8SLars-Peter Clausen break; 76761bfbdb8SLars-Peter Clausen } 76861bfbdb8SLars-Peter Clausen 7697ca27a6fSApelete Seketeli jz4740_mmc_transfer_check_state(host, data); 77061bfbdb8SLars-Peter Clausen 77161bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_DATA_TRAN_DONE); 77261bfbdb8SLars-Peter Clausen if (unlikely(timeout)) { 77361bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_SEND_STOP; 77461bfbdb8SLars-Peter Clausen break; 77561bfbdb8SLars-Peter Clausen } 7766a78768aSAlex Smith jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_DATA_TRAN_DONE); 777df561f66SGustavo A. R. Silva fallthrough; 77861bfbdb8SLars-Peter Clausen 77961bfbdb8SLars-Peter Clausen case JZ4740_MMC_STATE_SEND_STOP: 78061bfbdb8SLars-Peter Clausen if (!req->stop) 78161bfbdb8SLars-Peter Clausen break; 78261bfbdb8SLars-Peter Clausen 78361bfbdb8SLars-Peter Clausen jz4740_mmc_send_command(host, req->stop); 78461bfbdb8SLars-Peter Clausen 7851acee84bSAlex Smith if (mmc_resp_type(req->stop) & MMC_RSP_BUSY) { 7861acee84bSAlex Smith timeout = jz4740_mmc_poll_irq(host, 7871acee84bSAlex Smith JZ_MMC_IRQ_PRG_DONE); 78861bfbdb8SLars-Peter Clausen if (timeout) { 78961bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_DONE; 79061bfbdb8SLars-Peter Clausen break; 79161bfbdb8SLars-Peter Clausen } 7921acee84bSAlex Smith } 79361bfbdb8SLars-Peter Clausen case JZ4740_MMC_STATE_DONE: 79461bfbdb8SLars-Peter Clausen break; 79561bfbdb8SLars-Peter Clausen } 79661bfbdb8SLars-Peter Clausen 79761bfbdb8SLars-Peter Clausen if (!timeout) 79861bfbdb8SLars-Peter Clausen jz4740_mmc_request_done(host); 79961bfbdb8SLars-Peter Clausen 80061bfbdb8SLars-Peter Clausen return IRQ_HANDLED; 80161bfbdb8SLars-Peter Clausen } 80261bfbdb8SLars-Peter Clausen 80361bfbdb8SLars-Peter Clausen static irqreturn_t jz_mmc_irq(int irq, void *devid) 80461bfbdb8SLars-Peter Clausen { 80561bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = devid; 80661bfbdb8SLars-Peter Clausen struct mmc_command *cmd = host->cmd; 8076a78768aSAlex Smith uint32_t irq_reg, status, tmp; 80861bfbdb8SLars-Peter Clausen 8096a78768aSAlex Smith status = readl(host->base + JZ_REG_MMC_STATUS); 8106a78768aSAlex Smith irq_reg = jz4740_mmc_read_irq_reg(host); 81161bfbdb8SLars-Peter Clausen 81261bfbdb8SLars-Peter Clausen tmp = irq_reg; 81361bfbdb8SLars-Peter Clausen irq_reg &= ~host->irq_mask; 81461bfbdb8SLars-Peter Clausen 81561bfbdb8SLars-Peter Clausen tmp &= ~(JZ_MMC_IRQ_TXFIFO_WR_REQ | JZ_MMC_IRQ_RXFIFO_RD_REQ | 81661bfbdb8SLars-Peter Clausen JZ_MMC_IRQ_PRG_DONE | JZ_MMC_IRQ_DATA_TRAN_DONE); 81761bfbdb8SLars-Peter Clausen 81861bfbdb8SLars-Peter Clausen if (tmp != irq_reg) 8196a78768aSAlex Smith jz4740_mmc_write_irq_reg(host, tmp & ~irq_reg); 82061bfbdb8SLars-Peter Clausen 82161bfbdb8SLars-Peter Clausen if (irq_reg & JZ_MMC_IRQ_SDIO) { 8226a78768aSAlex Smith jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_SDIO); 82361bfbdb8SLars-Peter Clausen mmc_signal_sdio_irq(host->mmc); 82461bfbdb8SLars-Peter Clausen irq_reg &= ~JZ_MMC_IRQ_SDIO; 82561bfbdb8SLars-Peter Clausen } 82661bfbdb8SLars-Peter Clausen 82761bfbdb8SLars-Peter Clausen if (host->req && cmd && irq_reg) { 82861bfbdb8SLars-Peter Clausen if (test_and_clear_bit(0, &host->waiting)) { 82961bfbdb8SLars-Peter Clausen del_timer(&host->timeout_timer); 83061bfbdb8SLars-Peter Clausen 83161bfbdb8SLars-Peter Clausen if (status & JZ_MMC_STATUS_TIMEOUT_RES) { 83261bfbdb8SLars-Peter Clausen cmd->error = -ETIMEDOUT; 83361bfbdb8SLars-Peter Clausen } else if (status & JZ_MMC_STATUS_CRC_RES_ERR) { 83461bfbdb8SLars-Peter Clausen cmd->error = -EIO; 83561bfbdb8SLars-Peter Clausen } else if (status & (JZ_MMC_STATUS_CRC_READ_ERROR | 83661bfbdb8SLars-Peter Clausen JZ_MMC_STATUS_CRC_WRITE_ERROR)) { 83761bfbdb8SLars-Peter Clausen if (cmd->data) 83861bfbdb8SLars-Peter Clausen cmd->data->error = -EIO; 83961bfbdb8SLars-Peter Clausen cmd->error = -EIO; 84061bfbdb8SLars-Peter Clausen } 84161bfbdb8SLars-Peter Clausen 84261bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, irq_reg, false); 8436a78768aSAlex Smith jz4740_mmc_write_irq_reg(host, irq_reg); 84461bfbdb8SLars-Peter Clausen 84561bfbdb8SLars-Peter Clausen return IRQ_WAKE_THREAD; 84661bfbdb8SLars-Peter Clausen } 84761bfbdb8SLars-Peter Clausen } 84861bfbdb8SLars-Peter Clausen 84961bfbdb8SLars-Peter Clausen return IRQ_HANDLED; 85061bfbdb8SLars-Peter Clausen } 85161bfbdb8SLars-Peter Clausen 85261bfbdb8SLars-Peter Clausen static int jz4740_mmc_set_clock_rate(struct jz4740_mmc_host *host, int rate) 85361bfbdb8SLars-Peter Clausen { 85461bfbdb8SLars-Peter Clausen int div = 0; 85561bfbdb8SLars-Peter Clausen int real_rate; 85661bfbdb8SLars-Peter Clausen 85761bfbdb8SLars-Peter Clausen jz4740_mmc_clock_disable(host); 8586861fce6SAlex Smith clk_set_rate(host->clk, host->mmc->f_max); 85961bfbdb8SLars-Peter Clausen 86061bfbdb8SLars-Peter Clausen real_rate = clk_get_rate(host->clk); 86161bfbdb8SLars-Peter Clausen 86261bfbdb8SLars-Peter Clausen while (real_rate > rate && div < 7) { 86361bfbdb8SLars-Peter Clausen ++div; 86461bfbdb8SLars-Peter Clausen real_rate >>= 1; 86561bfbdb8SLars-Peter Clausen } 86661bfbdb8SLars-Peter Clausen 86761bfbdb8SLars-Peter Clausen writew(div, host->base + JZ_REG_MMC_CLKRT); 86880fe4e90SZhou Yanjie 86980fe4e90SZhou Yanjie if (real_rate > 25000000) { 87080fe4e90SZhou Yanjie if (host->version >= JZ_MMC_X1000) { 87180fe4e90SZhou Yanjie writel(JZ_MMC_LPM_DRV_RISING_QTR_PHASE_DLY | 87280fe4e90SZhou Yanjie JZ_MMC_LPM_SMP_RISING_QTR_OR_HALF_PHASE_DLY | 87380fe4e90SZhou Yanjie JZ_MMC_LPM_LOW_POWER_MODE_EN, 87480fe4e90SZhou Yanjie host->base + JZ_REG_MMC_LPM); 87580fe4e90SZhou Yanjie } else if (host->version >= JZ_MMC_JZ4760) { 87680fe4e90SZhou Yanjie writel(JZ_MMC_LPM_DRV_RISING | 87780fe4e90SZhou Yanjie JZ_MMC_LPM_LOW_POWER_MODE_EN, 87880fe4e90SZhou Yanjie host->base + JZ_REG_MMC_LPM); 87980fe4e90SZhou Yanjie } else if (host->version >= JZ_MMC_JZ4725B) 88080fe4e90SZhou Yanjie writel(JZ_MMC_LPM_LOW_POWER_MODE_EN, 88180fe4e90SZhou Yanjie host->base + JZ_REG_MMC_LPM); 88280fe4e90SZhou Yanjie } 88380fe4e90SZhou Yanjie 88461bfbdb8SLars-Peter Clausen return real_rate; 88561bfbdb8SLars-Peter Clausen } 88661bfbdb8SLars-Peter Clausen 88761bfbdb8SLars-Peter Clausen static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req) 88861bfbdb8SLars-Peter Clausen { 88961bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = mmc_priv(mmc); 89061bfbdb8SLars-Peter Clausen 89161bfbdb8SLars-Peter Clausen host->req = req; 89261bfbdb8SLars-Peter Clausen 8936a78768aSAlex Smith jz4740_mmc_write_irq_reg(host, ~0); 89461bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, true); 89561bfbdb8SLars-Peter Clausen 89661bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_READ_RESPONSE; 89761bfbdb8SLars-Peter Clausen set_bit(0, &host->waiting); 898d422f8b9SUlf Hansson mod_timer(&host->timeout_timer, 899d422f8b9SUlf Hansson jiffies + msecs_to_jiffies(JZ_MMC_REQ_TIMEOUT_MS)); 90061bfbdb8SLars-Peter Clausen jz4740_mmc_send_command(host, req->cmd); 90161bfbdb8SLars-Peter Clausen } 90261bfbdb8SLars-Peter Clausen 90361bfbdb8SLars-Peter Clausen static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 90461bfbdb8SLars-Peter Clausen { 90561bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = mmc_priv(mmc); 90661bfbdb8SLars-Peter Clausen if (ios->clock) 90761bfbdb8SLars-Peter Clausen jz4740_mmc_set_clock_rate(host, ios->clock); 90861bfbdb8SLars-Peter Clausen 90961bfbdb8SLars-Peter Clausen switch (ios->power_mode) { 91061bfbdb8SLars-Peter Clausen case MMC_POWER_UP: 91161bfbdb8SLars-Peter Clausen jz4740_mmc_reset(host); 91205395527SPaul Cercueil if (!IS_ERR(mmc->supply.vmmc)) 91305395527SPaul Cercueil mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); 91461bfbdb8SLars-Peter Clausen host->cmdat |= JZ_MMC_CMDAT_INIT; 915fca9661cSLars-Peter Clausen clk_prepare_enable(host->clk); 91661bfbdb8SLars-Peter Clausen break; 91761bfbdb8SLars-Peter Clausen case MMC_POWER_ON: 91861bfbdb8SLars-Peter Clausen break; 91961bfbdb8SLars-Peter Clausen default: 92005395527SPaul Cercueil if (!IS_ERR(mmc->supply.vmmc)) 92105395527SPaul Cercueil mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); 922fca9661cSLars-Peter Clausen clk_disable_unprepare(host->clk); 92361bfbdb8SLars-Peter Clausen break; 92461bfbdb8SLars-Peter Clausen } 92561bfbdb8SLars-Peter Clausen 92661bfbdb8SLars-Peter Clausen switch (ios->bus_width) { 92761bfbdb8SLars-Peter Clausen case MMC_BUS_WIDTH_1: 928a02f8f48SZhou Yanjie host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_MASK; 92961bfbdb8SLars-Peter Clausen break; 93061bfbdb8SLars-Peter Clausen case MMC_BUS_WIDTH_4: 931a02f8f48SZhou Yanjie host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_MASK; 93261bfbdb8SLars-Peter Clausen host->cmdat |= JZ_MMC_CMDAT_BUS_WIDTH_4BIT; 93361bfbdb8SLars-Peter Clausen break; 934a02f8f48SZhou Yanjie case MMC_BUS_WIDTH_8: 935a02f8f48SZhou Yanjie host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_MASK; 936a02f8f48SZhou Yanjie host->cmdat |= JZ_MMC_CMDAT_BUS_WIDTH_8BIT; 937a02f8f48SZhou Yanjie break; 93861bfbdb8SLars-Peter Clausen default: 93961bfbdb8SLars-Peter Clausen break; 94061bfbdb8SLars-Peter Clausen } 94161bfbdb8SLars-Peter Clausen } 94261bfbdb8SLars-Peter Clausen 94361bfbdb8SLars-Peter Clausen static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) 94461bfbdb8SLars-Peter Clausen { 94561bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = mmc_priv(mmc); 94661bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_SDIO, enable); 94761bfbdb8SLars-Peter Clausen } 94861bfbdb8SLars-Peter Clausen 94961bfbdb8SLars-Peter Clausen static const struct mmc_host_ops jz4740_mmc_ops = { 95061bfbdb8SLars-Peter Clausen .request = jz4740_mmc_request, 951bb2f4592SApelete Seketeli .pre_req = jz4740_mmc_pre_request, 952bb2f4592SApelete Seketeli .post_req = jz4740_mmc_post_request, 95361bfbdb8SLars-Peter Clausen .set_ios = jz4740_mmc_set_ios, 95458e300afSLars-Peter Clausen .get_ro = mmc_gpio_get_ro, 95558e300afSLars-Peter Clausen .get_cd = mmc_gpio_get_cd, 95661bfbdb8SLars-Peter Clausen .enable_sdio_irq = jz4740_mmc_enable_sdio_irq, 95761bfbdb8SLars-Peter Clausen }; 95861bfbdb8SLars-Peter Clausen 95961e11ebaSEzequiel Garcia static const struct of_device_id jz4740_mmc_of_match[] = { 96061e11ebaSEzequiel Garcia { .compatible = "ingenic,jz4740-mmc", .data = (void *) JZ_MMC_JZ4740 }, 961a0c938b5SPaul Cercueil { .compatible = "ingenic,jz4725b-mmc", .data = (void *)JZ_MMC_JZ4725B }, 9622af2af99SZhou Yanjie { .compatible = "ingenic,jz4760-mmc", .data = (void *) JZ_MMC_JZ4760 }, 9636a78768aSAlex Smith { .compatible = "ingenic,jz4780-mmc", .data = (void *) JZ_MMC_JZ4780 }, 964fea5fcc2SZhou Yanjie { .compatible = "ingenic,x1000-mmc", .data = (void *) JZ_MMC_X1000 }, 96561e11ebaSEzequiel Garcia {}, 96661e11ebaSEzequiel Garcia }; 96761e11ebaSEzequiel Garcia MODULE_DEVICE_TABLE(of, jz4740_mmc_of_match); 96861e11ebaSEzequiel Garcia 969c3be1efdSBill Pemberton static int jz4740_mmc_probe(struct platform_device* pdev) 97061bfbdb8SLars-Peter Clausen { 97161bfbdb8SLars-Peter Clausen int ret; 97261bfbdb8SLars-Peter Clausen struct mmc_host *mmc; 97361bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host; 97461e11ebaSEzequiel Garcia const struct of_device_id *match; 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 98461e11ebaSEzequiel Garcia match = of_match_device(jz4740_mmc_of_match, &pdev->dev); 98561e11ebaSEzequiel Garcia if (match) { 98661e11ebaSEzequiel Garcia host->version = (enum jz4740_mmc_version)match->data; 98705395527SPaul Cercueil } else { 98805395527SPaul Cercueil /* JZ4740 should be the only one using legacy probe */ 98905395527SPaul Cercueil host->version = JZ_MMC_JZ4740; 99005395527SPaul Cercueil } 99105395527SPaul Cercueil 99261e11ebaSEzequiel Garcia ret = mmc_of_parse(mmc); 99361e11ebaSEzequiel Garcia if (ret) { 99461e11ebaSEzequiel Garcia if (ret != -EPROBE_DEFER) 99561e11ebaSEzequiel Garcia dev_err(&pdev->dev, 99605395527SPaul Cercueil "could not parse device properties: %d\n", ret); 99761e11ebaSEzequiel Garcia goto err_free_host; 99861e11ebaSEzequiel Garcia } 99905395527SPaul Cercueil 100005395527SPaul Cercueil mmc_regulator_get_supply(mmc); 100161e11ebaSEzequiel Garcia 100261bfbdb8SLars-Peter Clausen host->irq = platform_get_irq(pdev, 0); 100361bfbdb8SLars-Peter Clausen if (host->irq < 0) { 100461bfbdb8SLars-Peter Clausen ret = host->irq; 100561bfbdb8SLars-Peter Clausen goto err_free_host; 100661bfbdb8SLars-Peter Clausen } 100761bfbdb8SLars-Peter Clausen 1008017d84bdSLars-Peter Clausen host->clk = devm_clk_get(&pdev->dev, "mmc"); 10093119cbdaSJamie Iles if (IS_ERR(host->clk)) { 10103119cbdaSJamie Iles ret = PTR_ERR(host->clk); 101161bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to get mmc clock\n"); 101261bfbdb8SLars-Peter Clausen goto err_free_host; 101361bfbdb8SLars-Peter Clausen } 101461bfbdb8SLars-Peter Clausen 10157ca27a6fSApelete Seketeli host->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 10167ca27a6fSApelete Seketeli host->base = devm_ioremap_resource(&pdev->dev, host->mem_res); 10173e7e8c18SWei Yongjun if (IS_ERR(host->base)) { 10183e7e8c18SWei Yongjun ret = PTR_ERR(host->base); 10197ca27a6fSApelete Seketeli dev_err(&pdev->dev, "Failed to ioremap base memory\n"); 1020017d84bdSLars-Peter Clausen goto err_free_host; 102161bfbdb8SLars-Peter Clausen } 102261bfbdb8SLars-Peter Clausen 102361bfbdb8SLars-Peter Clausen mmc->ops = &jz4740_mmc_ops; 102461e11ebaSEzequiel Garcia if (!mmc->f_max) 102561bfbdb8SLars-Peter Clausen mmc->f_max = JZ_MMC_CLK_RATE; 102661e11ebaSEzequiel Garcia mmc->f_min = mmc->f_max / 128; 102761bfbdb8SLars-Peter Clausen mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; 102861bfbdb8SLars-Peter Clausen 1029d422f8b9SUlf Hansson /* 1030d422f8b9SUlf Hansson * We use a fixed timeout of 5s, hence inform the core about it. A 1031d422f8b9SUlf Hansson * future improvement should instead respect the cmd->busy_timeout. 1032d422f8b9SUlf Hansson */ 1033d422f8b9SUlf Hansson mmc->max_busy_timeout = JZ_MMC_REQ_TIMEOUT_MS; 1034d422f8b9SUlf Hansson 103561bfbdb8SLars-Peter Clausen mmc->max_blk_size = (1 << 10) - 1; 103661bfbdb8SLars-Peter Clausen mmc->max_blk_count = (1 << 15) - 1; 103761bfbdb8SLars-Peter Clausen mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; 103861bfbdb8SLars-Peter Clausen 1039a36274e0SMartin K. Petersen mmc->max_segs = 128; 104061bfbdb8SLars-Peter Clausen mmc->max_seg_size = mmc->max_req_size; 104161bfbdb8SLars-Peter Clausen 104261bfbdb8SLars-Peter Clausen host->mmc = mmc; 104361bfbdb8SLars-Peter Clausen host->pdev = pdev; 104461bfbdb8SLars-Peter Clausen spin_lock_init(&host->lock); 10456a78768aSAlex Smith host->irq_mask = ~0; 104661bfbdb8SLars-Peter Clausen 1047436a3cfdSZubair Lutfullah Kakakhel jz4740_mmc_reset(host); 1048436a3cfdSZubair Lutfullah Kakakhel 104961bfbdb8SLars-Peter Clausen ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0, 105061bfbdb8SLars-Peter Clausen dev_name(&pdev->dev), host); 105161bfbdb8SLars-Peter Clausen if (ret) { 105261bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); 10530f6f3235SLinus Walleij goto err_free_host; 105461bfbdb8SLars-Peter Clausen } 105561bfbdb8SLars-Peter Clausen 105661bfbdb8SLars-Peter Clausen jz4740_mmc_clock_disable(host); 10572ee4f620SKees Cook timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0); 105861bfbdb8SLars-Peter Clausen 10597e8466e2SPaul Cercueil ret = jz4740_mmc_acquire_dma_channels(host); 10607e8466e2SPaul Cercueil if (ret == -EPROBE_DEFER) 10617e8466e2SPaul Cercueil goto err_free_irq; 10627e8466e2SPaul Cercueil host->use_dma = !ret; 10637ca27a6fSApelete Seketeli 106461bfbdb8SLars-Peter Clausen platform_set_drvdata(pdev, host); 106561bfbdb8SLars-Peter Clausen ret = mmc_add_host(mmc); 106661bfbdb8SLars-Peter Clausen 106761bfbdb8SLars-Peter Clausen if (ret) { 106861bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret); 10697e8466e2SPaul Cercueil goto err_release_dma; 107061bfbdb8SLars-Peter Clausen } 1071fea5fcc2SZhou Yanjie dev_info(&pdev->dev, "Ingenic SD/MMC card driver registered\n"); 107261bfbdb8SLars-Peter Clausen 10737ca27a6fSApelete Seketeli dev_info(&pdev->dev, "Using %s, %d-bit mode\n", 10747ca27a6fSApelete Seketeli host->use_dma ? "DMA" : "PIO", 1075a02f8f48SZhou Yanjie (mmc->caps & MMC_CAP_8_BIT_DATA) ? 8 : 1076a02f8f48SZhou Yanjie ((mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1)); 10777ca27a6fSApelete Seketeli 107861bfbdb8SLars-Peter Clausen return 0; 107961bfbdb8SLars-Peter Clausen 10807e8466e2SPaul Cercueil err_release_dma: 10817e8466e2SPaul Cercueil if (host->use_dma) 10827e8466e2SPaul Cercueil jz4740_mmc_release_dma_channels(host); 108361bfbdb8SLars-Peter Clausen err_free_irq: 108461bfbdb8SLars-Peter Clausen free_irq(host->irq, host); 108561bfbdb8SLars-Peter Clausen err_free_host: 108661bfbdb8SLars-Peter Clausen mmc_free_host(mmc); 108761bfbdb8SLars-Peter Clausen 108861bfbdb8SLars-Peter Clausen return ret; 108961bfbdb8SLars-Peter Clausen } 109061bfbdb8SLars-Peter Clausen 10916e0ee714SBill Pemberton static int jz4740_mmc_remove(struct platform_device *pdev) 109261bfbdb8SLars-Peter Clausen { 109361bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = platform_get_drvdata(pdev); 109461bfbdb8SLars-Peter Clausen 109561bfbdb8SLars-Peter Clausen del_timer_sync(&host->timeout_timer); 109661bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, 0xff, false); 109761bfbdb8SLars-Peter Clausen jz4740_mmc_reset(host); 109861bfbdb8SLars-Peter Clausen 109961bfbdb8SLars-Peter Clausen mmc_remove_host(host->mmc); 110061bfbdb8SLars-Peter Clausen 110161bfbdb8SLars-Peter Clausen free_irq(host->irq, host); 110261bfbdb8SLars-Peter Clausen 11037ca27a6fSApelete Seketeli if (host->use_dma) 11047ca27a6fSApelete Seketeli jz4740_mmc_release_dma_channels(host); 11057ca27a6fSApelete Seketeli 110661bfbdb8SLars-Peter Clausen mmc_free_host(host->mmc); 110761bfbdb8SLars-Peter Clausen 110861bfbdb8SLars-Peter Clausen return 0; 110961bfbdb8SLars-Peter Clausen } 111061bfbdb8SLars-Peter Clausen 111102fd86b6SPaul Cercueil static int __maybe_unused jz4740_mmc_suspend(struct device *dev) 111261bfbdb8SLars-Peter Clausen { 1113fa5ed6bcSPaul Cercueil return pinctrl_pm_select_sleep_state(dev); 111461bfbdb8SLars-Peter Clausen } 111561bfbdb8SLars-Peter Clausen 111602fd86b6SPaul Cercueil static int __maybe_unused jz4740_mmc_resume(struct device *dev) 111761bfbdb8SLars-Peter Clausen { 1118a62ff540SUlf Hansson return pinctrl_select_default_state(dev); 111961bfbdb8SLars-Peter Clausen } 112061bfbdb8SLars-Peter Clausen 11215d5c0350SLars-Peter Clausen static SIMPLE_DEV_PM_OPS(jz4740_mmc_pm_ops, jz4740_mmc_suspend, 11225d5c0350SLars-Peter Clausen jz4740_mmc_resume); 112361bfbdb8SLars-Peter Clausen 112461bfbdb8SLars-Peter Clausen static struct platform_driver jz4740_mmc_driver = { 112561bfbdb8SLars-Peter Clausen .probe = jz4740_mmc_probe, 11260433c143SBill Pemberton .remove = jz4740_mmc_remove, 112761bfbdb8SLars-Peter Clausen .driver = { 112861bfbdb8SLars-Peter Clausen .name = "jz4740-mmc", 112961e11ebaSEzequiel Garcia .of_match_table = of_match_ptr(jz4740_mmc_of_match), 113002fd86b6SPaul Cercueil .pm = pm_ptr(&jz4740_mmc_pm_ops), 113161bfbdb8SLars-Peter Clausen }, 113261bfbdb8SLars-Peter Clausen }; 113361bfbdb8SLars-Peter Clausen 1134d1f81a64SAxel Lin module_platform_driver(jz4740_mmc_driver); 113561bfbdb8SLars-Peter Clausen 113661bfbdb8SLars-Peter Clausen MODULE_DESCRIPTION("JZ4740 SD/MMC controller driver"); 113761bfbdb8SLars-Peter Clausen MODULE_LICENSE("GPL"); 113861bfbdb8SLars-Peter Clausen MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 1139