161bfbdb8SLars-Peter Clausen /* 261bfbdb8SLars-Peter Clausen * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> 36a78768aSAlex Smith * Copyright (C) 2013, Imagination Technologies 46a78768aSAlex Smith * 561bfbdb8SLars-Peter Clausen * JZ4740 SD/MMC controller driver 661bfbdb8SLars-Peter Clausen * 761bfbdb8SLars-Peter Clausen * This program is free software; you can redistribute it and/or modify it 861bfbdb8SLars-Peter Clausen * under the terms of the GNU General Public License as published by the 961bfbdb8SLars-Peter Clausen * Free Software Foundation; either version 2 of the License, or (at your 1061bfbdb8SLars-Peter Clausen * option) any later version. 1161bfbdb8SLars-Peter Clausen * 1261bfbdb8SLars-Peter Clausen * You should have received a copy of the GNU General Public License along 1361bfbdb8SLars-Peter Clausen * with this program; if not, write to the Free Software Foundation, Inc., 1461bfbdb8SLars-Peter Clausen * 675 Mass Ave, Cambridge, MA 02139, USA. 1561bfbdb8SLars-Peter Clausen * 1661bfbdb8SLars-Peter Clausen */ 1761bfbdb8SLars-Peter Clausen 187e7845f3SEzequiel Garcia #include <linux/bitops.h> 197e7845f3SEzequiel Garcia #include <linux/clk.h> 207e7845f3SEzequiel Garcia #include <linux/delay.h> 217e7845f3SEzequiel Garcia #include <linux/dmaengine.h> 227e7845f3SEzequiel Garcia #include <linux/dma-mapping.h> 233119cbdaSJamie Iles #include <linux/err.h> 247e7845f3SEzequiel Garcia #include <linux/gpio.h> 257e7845f3SEzequiel Garcia #include <linux/interrupt.h> 2661bfbdb8SLars-Peter Clausen #include <linux/io.h> 2761bfbdb8SLars-Peter Clausen #include <linux/irq.h> 287e7845f3SEzequiel Garcia #include <linux/mmc/host.h> 297e7845f3SEzequiel Garcia #include <linux/mmc/slot-gpio.h> 3061bfbdb8SLars-Peter Clausen #include <linux/module.h> 3161e11ebaSEzequiel Garcia #include <linux/of_device.h> 32fa5ed6bcSPaul Cercueil #include <linux/pinctrl/consumer.h> 3361bfbdb8SLars-Peter Clausen #include <linux/platform_device.h> 3461bfbdb8SLars-Peter Clausen #include <linux/scatterlist.h> 3561bfbdb8SLars-Peter Clausen 3661bfbdb8SLars-Peter Clausen #include <asm/cacheflush.h> 3761bfbdb8SLars-Peter Clausen 387ca27a6fSApelete Seketeli #include <asm/mach-jz4740/dma.h> 3961bfbdb8SLars-Peter Clausen #include <asm/mach-jz4740/jz4740_mmc.h> 4061bfbdb8SLars-Peter Clausen 4161bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_STRPCL 0x00 4261bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_STATUS 0x04 4361bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_CLKRT 0x08 4461bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_CMDAT 0x0C 4561bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RESTO 0x10 4661bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RDTO 0x14 4761bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_BLKLEN 0x18 4861bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_NOB 0x1C 4961bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_SNOB 0x20 5061bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_IMASK 0x24 5161bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_IREG 0x28 5261bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_CMD 0x2C 5361bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_ARG 0x30 5461bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RESP_FIFO 0x34 5561bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RXFIFO 0x38 5661bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_TXFIFO 0x3C 576a78768aSAlex Smith #define JZ_REG_MMC_DMAC 0x44 5861bfbdb8SLars-Peter Clausen 5961bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7) 6061bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_EXIT_TRANSFER BIT(6) 6161bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_START_READWAIT BIT(5) 6261bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_STOP_READWAIT BIT(4) 6361bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_RESET BIT(3) 6461bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_START_OP BIT(2) 6561bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_CLOCK_CONTROL (BIT(1) | BIT(0)) 6661bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_CLOCK_STOP BIT(0) 6761bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_CLOCK_START BIT(1) 6861bfbdb8SLars-Peter Clausen 6961bfbdb8SLars-Peter Clausen 7061bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_IS_RESETTING BIT(15) 7161bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_SDIO_INT_ACTIVE BIT(14) 7261bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_PRG_DONE BIT(13) 7361bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_TRAN_DONE BIT(12) 7461bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_END_CMD_RES BIT(11) 7561bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_FIFO_AFULL BIT(10) 7661bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_IS_READWAIT BIT(9) 7761bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CLK_EN BIT(8) 7861bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_FIFO_FULL BIT(7) 7961bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_FIFO_EMPTY BIT(6) 8061bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CRC_RES_ERR BIT(5) 8161bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CRC_READ_ERROR BIT(4) 8261bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_TIMEOUT_WRITE BIT(3) 8361bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CRC_WRITE_ERROR BIT(2) 8461bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_TIMEOUT_RES BIT(1) 8561bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_TIMEOUT_READ BIT(0) 8661bfbdb8SLars-Peter Clausen 8761bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_READ_ERROR_MASK (BIT(4) | BIT(0)) 8861bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_WRITE_ERROR_MASK (BIT(3) | BIT(2)) 8961bfbdb8SLars-Peter Clausen 9061bfbdb8SLars-Peter Clausen 9161bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_IO_ABORT BIT(11) 9261bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_BUS_WIDTH_4BIT BIT(10) 9361bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_DMA_EN BIT(8) 9461bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_INIT BIT(7) 9561bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_BUSY BIT(6) 9661bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_STREAM BIT(5) 9761bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_WRITE BIT(4) 9861bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_DATA_EN BIT(3) 9961bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RESPONSE_FORMAT (BIT(2) | BIT(1) | BIT(0)) 10061bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RSP_R1 1 10161bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RSP_R2 2 10261bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RSP_R3 3 10361bfbdb8SLars-Peter Clausen 10461bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_SDIO BIT(7) 10561bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_TXFIFO_WR_REQ BIT(6) 10661bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_RXFIFO_RD_REQ BIT(5) 10761bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_END_CMD_RES BIT(2) 10861bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_PRG_DONE BIT(1) 10961bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_DATA_TRAN_DONE BIT(0) 11061bfbdb8SLars-Peter Clausen 1116a78768aSAlex Smith #define JZ_MMC_DMAC_DMA_SEL BIT(1) 1126a78768aSAlex Smith #define JZ_MMC_DMAC_DMA_EN BIT(0) 11361bfbdb8SLars-Peter Clausen 11461bfbdb8SLars-Peter Clausen #define JZ_MMC_CLK_RATE 24000000 11561bfbdb8SLars-Peter Clausen 11661e11ebaSEzequiel Garcia enum jz4740_mmc_version { 11761e11ebaSEzequiel Garcia JZ_MMC_JZ4740, 118a0c938b5SPaul Cercueil JZ_MMC_JZ4725B, 1196a78768aSAlex Smith JZ_MMC_JZ4780, 12061e11ebaSEzequiel Garcia }; 12161e11ebaSEzequiel Garcia 12261bfbdb8SLars-Peter Clausen enum jz4740_mmc_state { 12361bfbdb8SLars-Peter Clausen JZ4740_MMC_STATE_READ_RESPONSE, 12461bfbdb8SLars-Peter Clausen JZ4740_MMC_STATE_TRANSFER_DATA, 12561bfbdb8SLars-Peter Clausen JZ4740_MMC_STATE_SEND_STOP, 12661bfbdb8SLars-Peter Clausen JZ4740_MMC_STATE_DONE, 12761bfbdb8SLars-Peter Clausen }; 12861bfbdb8SLars-Peter Clausen 129bb2f4592SApelete Seketeli struct jz4740_mmc_host_next { 130bb2f4592SApelete Seketeli int sg_len; 131bb2f4592SApelete Seketeli s32 cookie; 132bb2f4592SApelete Seketeli }; 133bb2f4592SApelete Seketeli 13461bfbdb8SLars-Peter Clausen struct jz4740_mmc_host { 13561bfbdb8SLars-Peter Clausen struct mmc_host *mmc; 13661bfbdb8SLars-Peter Clausen struct platform_device *pdev; 13761bfbdb8SLars-Peter Clausen struct jz4740_mmc_platform_data *pdata; 13861bfbdb8SLars-Peter Clausen struct clk *clk; 13961bfbdb8SLars-Peter Clausen 14061e11ebaSEzequiel Garcia enum jz4740_mmc_version version; 14161e11ebaSEzequiel Garcia 14261bfbdb8SLars-Peter Clausen int irq; 14361bfbdb8SLars-Peter Clausen int card_detect_irq; 14461bfbdb8SLars-Peter Clausen 14561bfbdb8SLars-Peter Clausen void __iomem *base; 1467ca27a6fSApelete Seketeli struct resource *mem_res; 14761bfbdb8SLars-Peter Clausen struct mmc_request *req; 14861bfbdb8SLars-Peter Clausen struct mmc_command *cmd; 14961bfbdb8SLars-Peter Clausen 15061bfbdb8SLars-Peter Clausen unsigned long waiting; 15161bfbdb8SLars-Peter Clausen 15261bfbdb8SLars-Peter Clausen uint32_t cmdat; 15361bfbdb8SLars-Peter Clausen 1546a78768aSAlex Smith uint32_t irq_mask; 15561bfbdb8SLars-Peter Clausen 15661bfbdb8SLars-Peter Clausen spinlock_t lock; 15761bfbdb8SLars-Peter Clausen 15861bfbdb8SLars-Peter Clausen struct timer_list timeout_timer; 15961bfbdb8SLars-Peter Clausen struct sg_mapping_iter miter; 16061bfbdb8SLars-Peter Clausen enum jz4740_mmc_state state; 1617ca27a6fSApelete Seketeli 1627ca27a6fSApelete Seketeli /* DMA support */ 1637ca27a6fSApelete Seketeli struct dma_chan *dma_rx; 1647ca27a6fSApelete Seketeli struct dma_chan *dma_tx; 165bb2f4592SApelete Seketeli struct jz4740_mmc_host_next next_data; 1667ca27a6fSApelete Seketeli bool use_dma; 1677ca27a6fSApelete Seketeli int sg_len; 1687ca27a6fSApelete Seketeli 1697ca27a6fSApelete Seketeli /* The DMA trigger level is 8 words, that is to say, the DMA read 1707ca27a6fSApelete Seketeli * trigger is when data words in MSC_RXFIFO is >= 8 and the DMA write 1717ca27a6fSApelete Seketeli * trigger is when data words in MSC_TXFIFO is < 8. 1727ca27a6fSApelete Seketeli */ 1737ca27a6fSApelete Seketeli #define JZ4740_MMC_FIFO_HALF_SIZE 8 17461bfbdb8SLars-Peter Clausen }; 17561bfbdb8SLars-Peter Clausen 1766a78768aSAlex Smith static void jz4740_mmc_write_irq_mask(struct jz4740_mmc_host *host, 1776a78768aSAlex Smith uint32_t val) 1786a78768aSAlex Smith { 179a0c938b5SPaul Cercueil if (host->version >= JZ_MMC_JZ4725B) 1806a78768aSAlex Smith return writel(val, host->base + JZ_REG_MMC_IMASK); 1816a78768aSAlex Smith else 1826a78768aSAlex Smith return writew(val, host->base + JZ_REG_MMC_IMASK); 1836a78768aSAlex Smith } 1846a78768aSAlex Smith 1856a78768aSAlex Smith static void jz4740_mmc_write_irq_reg(struct jz4740_mmc_host *host, 1866a78768aSAlex Smith uint32_t val) 1876a78768aSAlex Smith { 1886a78768aSAlex Smith if (host->version >= JZ_MMC_JZ4780) 1896a78768aSAlex Smith return writel(val, host->base + JZ_REG_MMC_IREG); 1906a78768aSAlex Smith else 1916a78768aSAlex Smith return writew(val, host->base + JZ_REG_MMC_IREG); 1926a78768aSAlex Smith } 1936a78768aSAlex Smith 1946a78768aSAlex Smith static uint32_t jz4740_mmc_read_irq_reg(struct jz4740_mmc_host *host) 1956a78768aSAlex Smith { 1966a78768aSAlex Smith if (host->version >= JZ_MMC_JZ4780) 1976a78768aSAlex Smith return readl(host->base + JZ_REG_MMC_IREG); 1986a78768aSAlex Smith else 1996a78768aSAlex Smith return readw(host->base + JZ_REG_MMC_IREG); 2006a78768aSAlex Smith } 2016a78768aSAlex Smith 2027ca27a6fSApelete Seketeli /*----------------------------------------------------------------------------*/ 2037ca27a6fSApelete Seketeli /* DMA infrastructure */ 2047ca27a6fSApelete Seketeli 2057ca27a6fSApelete Seketeli static void jz4740_mmc_release_dma_channels(struct jz4740_mmc_host *host) 2067ca27a6fSApelete Seketeli { 2077ca27a6fSApelete Seketeli if (!host->use_dma) 2087ca27a6fSApelete Seketeli return; 2097ca27a6fSApelete Seketeli 2107ca27a6fSApelete Seketeli dma_release_channel(host->dma_tx); 2117ca27a6fSApelete Seketeli dma_release_channel(host->dma_rx); 2127ca27a6fSApelete Seketeli } 2137ca27a6fSApelete Seketeli 2147ca27a6fSApelete Seketeli static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host) 2157ca27a6fSApelete Seketeli { 216fb0ce9ddSEzequiel Garcia host->dma_tx = dma_request_chan(mmc_dev(host->mmc), "tx"); 217fb0ce9ddSEzequiel Garcia if (IS_ERR(host->dma_tx)) { 2187ca27a6fSApelete Seketeli dev_err(mmc_dev(host->mmc), "Failed to get dma_tx channel\n"); 219fb0ce9ddSEzequiel Garcia return PTR_ERR(host->dma_tx); 2207ca27a6fSApelete Seketeli } 2217ca27a6fSApelete Seketeli 222fb0ce9ddSEzequiel Garcia host->dma_rx = dma_request_chan(mmc_dev(host->mmc), "rx"); 223fb0ce9ddSEzequiel Garcia if (IS_ERR(host->dma_rx)) { 2247ca27a6fSApelete Seketeli dev_err(mmc_dev(host->mmc), "Failed to get dma_rx channel\n"); 225fb0ce9ddSEzequiel Garcia dma_release_channel(host->dma_tx); 226fb0ce9ddSEzequiel Garcia return PTR_ERR(host->dma_rx); 2277ca27a6fSApelete Seketeli } 2287ca27a6fSApelete Seketeli 229bb2f4592SApelete Seketeli /* Initialize DMA pre request cookie */ 230bb2f4592SApelete Seketeli host->next_data.cookie = 1; 231bb2f4592SApelete Seketeli 2327ca27a6fSApelete Seketeli return 0; 2337ca27a6fSApelete Seketeli } 2347ca27a6fSApelete Seketeli 235bb2f4592SApelete Seketeli static inline struct dma_chan *jz4740_mmc_get_dma_chan(struct jz4740_mmc_host *host, 236bb2f4592SApelete Seketeli struct mmc_data *data) 237bb2f4592SApelete Seketeli { 238bb2f4592SApelete Seketeli return (data->flags & MMC_DATA_READ) ? host->dma_rx : host->dma_tx; 239bb2f4592SApelete Seketeli } 240bb2f4592SApelete Seketeli 2417ca27a6fSApelete Seketeli static void jz4740_mmc_dma_unmap(struct jz4740_mmc_host *host, 2427ca27a6fSApelete Seketeli struct mmc_data *data) 2437ca27a6fSApelete Seketeli { 244bb2f4592SApelete Seketeli struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 245feeef096SHeiner Kallweit enum dma_data_direction dir = mmc_get_dma_dir(data); 2467ca27a6fSApelete Seketeli 2477ca27a6fSApelete Seketeli dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir); 2487ca27a6fSApelete Seketeli } 2497ca27a6fSApelete Seketeli 250bb2f4592SApelete Seketeli /* Prepares DMA data for current/next transfer, returns non-zero on failure */ 251bb2f4592SApelete Seketeli static int jz4740_mmc_prepare_dma_data(struct jz4740_mmc_host *host, 252bb2f4592SApelete Seketeli struct mmc_data *data, 253bb2f4592SApelete Seketeli struct jz4740_mmc_host_next *next, 254bb2f4592SApelete Seketeli struct dma_chan *chan) 255bb2f4592SApelete Seketeli { 256bb2f4592SApelete Seketeli struct jz4740_mmc_host_next *next_data = &host->next_data; 257feeef096SHeiner Kallweit enum dma_data_direction dir = mmc_get_dma_dir(data); 258bb2f4592SApelete Seketeli int sg_len; 259bb2f4592SApelete Seketeli 260bb2f4592SApelete Seketeli if (!next && data->host_cookie && 261bb2f4592SApelete Seketeli data->host_cookie != host->next_data.cookie) { 262bb2f4592SApelete Seketeli dev_warn(mmc_dev(host->mmc), 263bb2f4592SApelete Seketeli "[%s] invalid cookie: data->host_cookie %d host->next_data.cookie %d\n", 264bb2f4592SApelete Seketeli __func__, 265bb2f4592SApelete Seketeli data->host_cookie, 266bb2f4592SApelete Seketeli host->next_data.cookie); 267bb2f4592SApelete Seketeli data->host_cookie = 0; 268bb2f4592SApelete Seketeli } 269bb2f4592SApelete Seketeli 270bb2f4592SApelete Seketeli /* Check if next job is already prepared */ 271bb2f4592SApelete Seketeli if (next || data->host_cookie != host->next_data.cookie) { 272bb2f4592SApelete Seketeli sg_len = dma_map_sg(chan->device->dev, 273bb2f4592SApelete Seketeli data->sg, 274bb2f4592SApelete Seketeli data->sg_len, 275bb2f4592SApelete Seketeli dir); 276bb2f4592SApelete Seketeli 277bb2f4592SApelete Seketeli } else { 278bb2f4592SApelete Seketeli sg_len = next_data->sg_len; 279bb2f4592SApelete Seketeli next_data->sg_len = 0; 280bb2f4592SApelete Seketeli } 281bb2f4592SApelete Seketeli 282bb2f4592SApelete Seketeli if (sg_len <= 0) { 283bb2f4592SApelete Seketeli dev_err(mmc_dev(host->mmc), 284bb2f4592SApelete Seketeli "Failed to map scatterlist for DMA operation\n"); 285bb2f4592SApelete Seketeli return -EINVAL; 286bb2f4592SApelete Seketeli } 287bb2f4592SApelete Seketeli 288bb2f4592SApelete Seketeli if (next) { 289bb2f4592SApelete Seketeli next->sg_len = sg_len; 290bb2f4592SApelete Seketeli data->host_cookie = ++next->cookie < 0 ? 1 : next->cookie; 291bb2f4592SApelete Seketeli } else 292bb2f4592SApelete Seketeli host->sg_len = sg_len; 293bb2f4592SApelete Seketeli 294bb2f4592SApelete Seketeli return 0; 295bb2f4592SApelete Seketeli } 296bb2f4592SApelete Seketeli 2977ca27a6fSApelete Seketeli static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host, 2987ca27a6fSApelete Seketeli struct mmc_data *data) 2997ca27a6fSApelete Seketeli { 300bb2f4592SApelete Seketeli int ret; 3017ca27a6fSApelete Seketeli struct dma_chan *chan; 3027ca27a6fSApelete Seketeli struct dma_async_tx_descriptor *desc; 3037ca27a6fSApelete Seketeli struct dma_slave_config conf = { 3047ca27a6fSApelete Seketeli .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 3057ca27a6fSApelete Seketeli .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 3067ca27a6fSApelete Seketeli .src_maxburst = JZ4740_MMC_FIFO_HALF_SIZE, 3077ca27a6fSApelete Seketeli .dst_maxburst = JZ4740_MMC_FIFO_HALF_SIZE, 3087ca27a6fSApelete Seketeli }; 3097ca27a6fSApelete Seketeli 310bb2f4592SApelete Seketeli if (data->flags & MMC_DATA_WRITE) { 3117ca27a6fSApelete Seketeli conf.direction = DMA_MEM_TO_DEV; 3127ca27a6fSApelete Seketeli conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO; 3137ca27a6fSApelete Seketeli conf.slave_id = JZ4740_DMA_TYPE_MMC_TRANSMIT; 3147ca27a6fSApelete Seketeli chan = host->dma_tx; 3157ca27a6fSApelete Seketeli } else { 3167ca27a6fSApelete Seketeli conf.direction = DMA_DEV_TO_MEM; 3177ca27a6fSApelete Seketeli conf.src_addr = host->mem_res->start + JZ_REG_MMC_RXFIFO; 3187ca27a6fSApelete Seketeli conf.slave_id = JZ4740_DMA_TYPE_MMC_RECEIVE; 3197ca27a6fSApelete Seketeli chan = host->dma_rx; 3207ca27a6fSApelete Seketeli } 3217ca27a6fSApelete Seketeli 322bb2f4592SApelete Seketeli ret = jz4740_mmc_prepare_dma_data(host, data, NULL, chan); 323bb2f4592SApelete Seketeli if (ret) 324bb2f4592SApelete Seketeli return ret; 3257ca27a6fSApelete Seketeli 3267ca27a6fSApelete Seketeli dmaengine_slave_config(chan, &conf); 3277ca27a6fSApelete Seketeli desc = dmaengine_prep_slave_sg(chan, 3287ca27a6fSApelete Seketeli data->sg, 3297ca27a6fSApelete Seketeli host->sg_len, 3307ca27a6fSApelete Seketeli conf.direction, 3317ca27a6fSApelete Seketeli DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 3327ca27a6fSApelete Seketeli if (!desc) { 3337ca27a6fSApelete Seketeli dev_err(mmc_dev(host->mmc), 3347ca27a6fSApelete Seketeli "Failed to allocate DMA %s descriptor", 3357ca27a6fSApelete Seketeli conf.direction == DMA_MEM_TO_DEV ? "TX" : "RX"); 3367ca27a6fSApelete Seketeli goto dma_unmap; 3377ca27a6fSApelete Seketeli } 3387ca27a6fSApelete Seketeli 3397ca27a6fSApelete Seketeli dmaengine_submit(desc); 3407ca27a6fSApelete Seketeli dma_async_issue_pending(chan); 3417ca27a6fSApelete Seketeli 3427ca27a6fSApelete Seketeli return 0; 3437ca27a6fSApelete Seketeli 3447ca27a6fSApelete Seketeli dma_unmap: 3457ca27a6fSApelete Seketeli jz4740_mmc_dma_unmap(host, data); 3467ca27a6fSApelete Seketeli return -ENOMEM; 3477ca27a6fSApelete Seketeli } 3487ca27a6fSApelete Seketeli 349bb2f4592SApelete Seketeli static void jz4740_mmc_pre_request(struct mmc_host *mmc, 350d3c6aac3SLinus Walleij struct mmc_request *mrq) 351bb2f4592SApelete Seketeli { 352bb2f4592SApelete Seketeli struct jz4740_mmc_host *host = mmc_priv(mmc); 353bb2f4592SApelete Seketeli struct mmc_data *data = mrq->data; 354bb2f4592SApelete Seketeli struct jz4740_mmc_host_next *next_data = &host->next_data; 355bb2f4592SApelete Seketeli 356bb2f4592SApelete Seketeli BUG_ON(data->host_cookie); 357bb2f4592SApelete Seketeli 358bb2f4592SApelete Seketeli if (host->use_dma) { 359bb2f4592SApelete Seketeli struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 360bb2f4592SApelete Seketeli 361bb2f4592SApelete Seketeli if (jz4740_mmc_prepare_dma_data(host, data, next_data, chan)) 362bb2f4592SApelete Seketeli data->host_cookie = 0; 363bb2f4592SApelete Seketeli } 364bb2f4592SApelete Seketeli } 365bb2f4592SApelete Seketeli 366bb2f4592SApelete Seketeli static void jz4740_mmc_post_request(struct mmc_host *mmc, 367bb2f4592SApelete Seketeli struct mmc_request *mrq, 368bb2f4592SApelete Seketeli int err) 369bb2f4592SApelete Seketeli { 370bb2f4592SApelete Seketeli struct jz4740_mmc_host *host = mmc_priv(mmc); 371bb2f4592SApelete Seketeli struct mmc_data *data = mrq->data; 372bb2f4592SApelete Seketeli 373bb2f4592SApelete Seketeli if (host->use_dma && data->host_cookie) { 374bb2f4592SApelete Seketeli jz4740_mmc_dma_unmap(host, data); 375bb2f4592SApelete Seketeli data->host_cookie = 0; 376bb2f4592SApelete Seketeli } 377bb2f4592SApelete Seketeli 378bb2f4592SApelete Seketeli if (err) { 379bb2f4592SApelete Seketeli struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 380bb2f4592SApelete Seketeli 381bb2f4592SApelete Seketeli dmaengine_terminate_all(chan); 382bb2f4592SApelete Seketeli } 383bb2f4592SApelete Seketeli } 384bb2f4592SApelete Seketeli 3857ca27a6fSApelete Seketeli /*----------------------------------------------------------------------------*/ 3867ca27a6fSApelete Seketeli 38761bfbdb8SLars-Peter Clausen static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host, 38861bfbdb8SLars-Peter Clausen unsigned int irq, bool enabled) 38961bfbdb8SLars-Peter Clausen { 39061bfbdb8SLars-Peter Clausen unsigned long flags; 39161bfbdb8SLars-Peter Clausen 39261bfbdb8SLars-Peter Clausen spin_lock_irqsave(&host->lock, flags); 39361bfbdb8SLars-Peter Clausen if (enabled) 39461bfbdb8SLars-Peter Clausen host->irq_mask &= ~irq; 39561bfbdb8SLars-Peter Clausen else 39661bfbdb8SLars-Peter Clausen host->irq_mask |= irq; 39761bfbdb8SLars-Peter Clausen 3986a78768aSAlex Smith jz4740_mmc_write_irq_mask(host, host->irq_mask); 399a04f0017SAlex Smith spin_unlock_irqrestore(&host->lock, flags); 40061bfbdb8SLars-Peter Clausen } 40161bfbdb8SLars-Peter Clausen 40261bfbdb8SLars-Peter Clausen static void jz4740_mmc_clock_enable(struct jz4740_mmc_host *host, 40361bfbdb8SLars-Peter Clausen bool start_transfer) 40461bfbdb8SLars-Peter Clausen { 40561bfbdb8SLars-Peter Clausen uint16_t val = JZ_MMC_STRPCL_CLOCK_START; 40661bfbdb8SLars-Peter Clausen 40761bfbdb8SLars-Peter Clausen if (start_transfer) 40861bfbdb8SLars-Peter Clausen val |= JZ_MMC_STRPCL_START_OP; 40961bfbdb8SLars-Peter Clausen 41061bfbdb8SLars-Peter Clausen writew(val, host->base + JZ_REG_MMC_STRPCL); 41161bfbdb8SLars-Peter Clausen } 41261bfbdb8SLars-Peter Clausen 41361bfbdb8SLars-Peter Clausen static void jz4740_mmc_clock_disable(struct jz4740_mmc_host *host) 41461bfbdb8SLars-Peter Clausen { 41561bfbdb8SLars-Peter Clausen uint32_t status; 41661bfbdb8SLars-Peter Clausen unsigned int timeout = 1000; 41761bfbdb8SLars-Peter Clausen 41861bfbdb8SLars-Peter Clausen writew(JZ_MMC_STRPCL_CLOCK_STOP, host->base + JZ_REG_MMC_STRPCL); 41961bfbdb8SLars-Peter Clausen do { 42061bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 42161bfbdb8SLars-Peter Clausen } while (status & JZ_MMC_STATUS_CLK_EN && --timeout); 42261bfbdb8SLars-Peter Clausen } 42361bfbdb8SLars-Peter Clausen 42461bfbdb8SLars-Peter Clausen static void jz4740_mmc_reset(struct jz4740_mmc_host *host) 42561bfbdb8SLars-Peter Clausen { 42661bfbdb8SLars-Peter Clausen uint32_t status; 42761bfbdb8SLars-Peter Clausen unsigned int timeout = 1000; 42861bfbdb8SLars-Peter Clausen 42961bfbdb8SLars-Peter Clausen writew(JZ_MMC_STRPCL_RESET, host->base + JZ_REG_MMC_STRPCL); 43061bfbdb8SLars-Peter Clausen udelay(10); 43161bfbdb8SLars-Peter Clausen do { 43261bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 43361bfbdb8SLars-Peter Clausen } while (status & JZ_MMC_STATUS_IS_RESETTING && --timeout); 43461bfbdb8SLars-Peter Clausen } 43561bfbdb8SLars-Peter Clausen 43661bfbdb8SLars-Peter Clausen static void jz4740_mmc_request_done(struct jz4740_mmc_host *host) 43761bfbdb8SLars-Peter Clausen { 43861bfbdb8SLars-Peter Clausen struct mmc_request *req; 43961bfbdb8SLars-Peter Clausen 44061bfbdb8SLars-Peter Clausen req = host->req; 44161bfbdb8SLars-Peter Clausen host->req = NULL; 44261bfbdb8SLars-Peter Clausen 44361bfbdb8SLars-Peter Clausen mmc_request_done(host->mmc, req); 44461bfbdb8SLars-Peter Clausen } 44561bfbdb8SLars-Peter Clausen 44661bfbdb8SLars-Peter Clausen static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host, 44761bfbdb8SLars-Peter Clausen unsigned int irq) 44861bfbdb8SLars-Peter Clausen { 44961bfbdb8SLars-Peter Clausen unsigned int timeout = 0x800; 4506a78768aSAlex Smith uint32_t status; 45161bfbdb8SLars-Peter Clausen 45261bfbdb8SLars-Peter Clausen do { 4536a78768aSAlex Smith status = jz4740_mmc_read_irq_reg(host); 45461bfbdb8SLars-Peter Clausen } while (!(status & irq) && --timeout); 45561bfbdb8SLars-Peter Clausen 45661bfbdb8SLars-Peter Clausen if (timeout == 0) { 45761bfbdb8SLars-Peter Clausen set_bit(0, &host->waiting); 45861bfbdb8SLars-Peter Clausen mod_timer(&host->timeout_timer, jiffies + 5*HZ); 45961bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, irq, true); 46061bfbdb8SLars-Peter Clausen return true; 46161bfbdb8SLars-Peter Clausen } 46261bfbdb8SLars-Peter Clausen 46361bfbdb8SLars-Peter Clausen return false; 46461bfbdb8SLars-Peter Clausen } 46561bfbdb8SLars-Peter Clausen 46661bfbdb8SLars-Peter Clausen static void jz4740_mmc_transfer_check_state(struct jz4740_mmc_host *host, 46761bfbdb8SLars-Peter Clausen struct mmc_data *data) 46861bfbdb8SLars-Peter Clausen { 46961bfbdb8SLars-Peter Clausen int status; 47061bfbdb8SLars-Peter Clausen 47161bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 47261bfbdb8SLars-Peter Clausen if (status & JZ_MMC_STATUS_WRITE_ERROR_MASK) { 47361bfbdb8SLars-Peter Clausen if (status & (JZ_MMC_STATUS_TIMEOUT_WRITE)) { 47461bfbdb8SLars-Peter Clausen host->req->cmd->error = -ETIMEDOUT; 47561bfbdb8SLars-Peter Clausen data->error = -ETIMEDOUT; 47661bfbdb8SLars-Peter Clausen } else { 47761bfbdb8SLars-Peter Clausen host->req->cmd->error = -EIO; 47861bfbdb8SLars-Peter Clausen data->error = -EIO; 47961bfbdb8SLars-Peter Clausen } 4808a489aa1SPaul Cercueil } else if (status & JZ_MMC_STATUS_READ_ERROR_MASK) { 4818a489aa1SPaul Cercueil if (status & (JZ_MMC_STATUS_TIMEOUT_READ)) { 4828a489aa1SPaul Cercueil host->req->cmd->error = -ETIMEDOUT; 4838a489aa1SPaul Cercueil data->error = -ETIMEDOUT; 4848a489aa1SPaul Cercueil } else { 4858a489aa1SPaul Cercueil host->req->cmd->error = -EIO; 4868a489aa1SPaul Cercueil data->error = -EIO; 4878a489aa1SPaul Cercueil } 48861bfbdb8SLars-Peter Clausen } 48961bfbdb8SLars-Peter Clausen } 49061bfbdb8SLars-Peter Clausen 49161bfbdb8SLars-Peter Clausen static bool jz4740_mmc_write_data(struct jz4740_mmc_host *host, 49261bfbdb8SLars-Peter Clausen struct mmc_data *data) 49361bfbdb8SLars-Peter Clausen { 49461bfbdb8SLars-Peter Clausen struct sg_mapping_iter *miter = &host->miter; 49561bfbdb8SLars-Peter Clausen void __iomem *fifo_addr = host->base + JZ_REG_MMC_TXFIFO; 49661bfbdb8SLars-Peter Clausen uint32_t *buf; 49761bfbdb8SLars-Peter Clausen bool timeout; 49861bfbdb8SLars-Peter Clausen size_t i, j; 49961bfbdb8SLars-Peter Clausen 50061bfbdb8SLars-Peter Clausen while (sg_miter_next(miter)) { 50161bfbdb8SLars-Peter Clausen buf = miter->addr; 50261bfbdb8SLars-Peter Clausen i = miter->length / 4; 50361bfbdb8SLars-Peter Clausen j = i / 8; 50461bfbdb8SLars-Peter Clausen i = i & 0x7; 50561bfbdb8SLars-Peter Clausen while (j) { 50661bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); 50761bfbdb8SLars-Peter Clausen if (unlikely(timeout)) 50861bfbdb8SLars-Peter Clausen goto poll_timeout; 50961bfbdb8SLars-Peter Clausen 51061bfbdb8SLars-Peter Clausen writel(buf[0], fifo_addr); 51161bfbdb8SLars-Peter Clausen writel(buf[1], fifo_addr); 51261bfbdb8SLars-Peter Clausen writel(buf[2], fifo_addr); 51361bfbdb8SLars-Peter Clausen writel(buf[3], fifo_addr); 51461bfbdb8SLars-Peter Clausen writel(buf[4], fifo_addr); 51561bfbdb8SLars-Peter Clausen writel(buf[5], fifo_addr); 51661bfbdb8SLars-Peter Clausen writel(buf[6], fifo_addr); 51761bfbdb8SLars-Peter Clausen writel(buf[7], fifo_addr); 51861bfbdb8SLars-Peter Clausen buf += 8; 51961bfbdb8SLars-Peter Clausen --j; 52061bfbdb8SLars-Peter Clausen } 52161bfbdb8SLars-Peter Clausen if (unlikely(i)) { 52261bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); 52361bfbdb8SLars-Peter Clausen if (unlikely(timeout)) 52461bfbdb8SLars-Peter Clausen goto poll_timeout; 52561bfbdb8SLars-Peter Clausen 52661bfbdb8SLars-Peter Clausen while (i) { 52761bfbdb8SLars-Peter Clausen writel(*buf, fifo_addr); 52861bfbdb8SLars-Peter Clausen ++buf; 52961bfbdb8SLars-Peter Clausen --i; 53061bfbdb8SLars-Peter Clausen } 53161bfbdb8SLars-Peter Clausen } 53261bfbdb8SLars-Peter Clausen data->bytes_xfered += miter->length; 53361bfbdb8SLars-Peter Clausen } 53461bfbdb8SLars-Peter Clausen sg_miter_stop(miter); 53561bfbdb8SLars-Peter Clausen 53661bfbdb8SLars-Peter Clausen return false; 53761bfbdb8SLars-Peter Clausen 53861bfbdb8SLars-Peter Clausen poll_timeout: 53961bfbdb8SLars-Peter Clausen miter->consumed = (void *)buf - miter->addr; 54061bfbdb8SLars-Peter Clausen data->bytes_xfered += miter->consumed; 54161bfbdb8SLars-Peter Clausen sg_miter_stop(miter); 54261bfbdb8SLars-Peter Clausen 54361bfbdb8SLars-Peter Clausen return true; 54461bfbdb8SLars-Peter Clausen } 54561bfbdb8SLars-Peter Clausen 54661bfbdb8SLars-Peter Clausen static bool jz4740_mmc_read_data(struct jz4740_mmc_host *host, 54761bfbdb8SLars-Peter Clausen struct mmc_data *data) 54861bfbdb8SLars-Peter Clausen { 54961bfbdb8SLars-Peter Clausen struct sg_mapping_iter *miter = &host->miter; 55061bfbdb8SLars-Peter Clausen void __iomem *fifo_addr = host->base + JZ_REG_MMC_RXFIFO; 55161bfbdb8SLars-Peter Clausen uint32_t *buf; 55261bfbdb8SLars-Peter Clausen uint32_t d; 5536a78768aSAlex Smith uint32_t status; 55461bfbdb8SLars-Peter Clausen size_t i, j; 55561bfbdb8SLars-Peter Clausen unsigned int timeout; 55661bfbdb8SLars-Peter Clausen 55761bfbdb8SLars-Peter Clausen while (sg_miter_next(miter)) { 55861bfbdb8SLars-Peter Clausen buf = miter->addr; 55961bfbdb8SLars-Peter Clausen i = miter->length; 56061bfbdb8SLars-Peter Clausen j = i / 32; 56161bfbdb8SLars-Peter Clausen i = i & 0x1f; 56261bfbdb8SLars-Peter Clausen while (j) { 56361bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); 56461bfbdb8SLars-Peter Clausen if (unlikely(timeout)) 56561bfbdb8SLars-Peter Clausen goto poll_timeout; 56661bfbdb8SLars-Peter Clausen 56761bfbdb8SLars-Peter Clausen buf[0] = readl(fifo_addr); 56861bfbdb8SLars-Peter Clausen buf[1] = readl(fifo_addr); 56961bfbdb8SLars-Peter Clausen buf[2] = readl(fifo_addr); 57061bfbdb8SLars-Peter Clausen buf[3] = readl(fifo_addr); 57161bfbdb8SLars-Peter Clausen buf[4] = readl(fifo_addr); 57261bfbdb8SLars-Peter Clausen buf[5] = readl(fifo_addr); 57361bfbdb8SLars-Peter Clausen buf[6] = readl(fifo_addr); 57461bfbdb8SLars-Peter Clausen buf[7] = readl(fifo_addr); 57561bfbdb8SLars-Peter Clausen 57661bfbdb8SLars-Peter Clausen buf += 8; 57761bfbdb8SLars-Peter Clausen --j; 57861bfbdb8SLars-Peter Clausen } 57961bfbdb8SLars-Peter Clausen 58061bfbdb8SLars-Peter Clausen if (unlikely(i)) { 58161bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); 58261bfbdb8SLars-Peter Clausen if (unlikely(timeout)) 58361bfbdb8SLars-Peter Clausen goto poll_timeout; 58461bfbdb8SLars-Peter Clausen 58561bfbdb8SLars-Peter Clausen while (i >= 4) { 58661bfbdb8SLars-Peter Clausen *buf++ = readl(fifo_addr); 58761bfbdb8SLars-Peter Clausen i -= 4; 58861bfbdb8SLars-Peter Clausen } 58961bfbdb8SLars-Peter Clausen if (unlikely(i > 0)) { 59061bfbdb8SLars-Peter Clausen d = readl(fifo_addr); 59161bfbdb8SLars-Peter Clausen memcpy(buf, &d, i); 59261bfbdb8SLars-Peter Clausen } 59361bfbdb8SLars-Peter Clausen } 59461bfbdb8SLars-Peter Clausen data->bytes_xfered += miter->length; 59561bfbdb8SLars-Peter Clausen 59661bfbdb8SLars-Peter Clausen /* This can go away once MIPS implements 59761bfbdb8SLars-Peter Clausen * flush_kernel_dcache_page */ 59861bfbdb8SLars-Peter Clausen flush_dcache_page(miter->page); 59961bfbdb8SLars-Peter Clausen } 60061bfbdb8SLars-Peter Clausen sg_miter_stop(miter); 60161bfbdb8SLars-Peter Clausen 60261bfbdb8SLars-Peter Clausen /* For whatever reason there is sometime one word more in the fifo then 60361bfbdb8SLars-Peter Clausen * requested */ 60461bfbdb8SLars-Peter Clausen timeout = 1000; 60561bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 60661bfbdb8SLars-Peter Clausen while (!(status & JZ_MMC_STATUS_DATA_FIFO_EMPTY) && --timeout) { 60761bfbdb8SLars-Peter Clausen d = readl(fifo_addr); 60861bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 60961bfbdb8SLars-Peter Clausen } 61061bfbdb8SLars-Peter Clausen 61161bfbdb8SLars-Peter Clausen return false; 61261bfbdb8SLars-Peter Clausen 61361bfbdb8SLars-Peter Clausen poll_timeout: 61461bfbdb8SLars-Peter Clausen miter->consumed = (void *)buf - miter->addr; 61561bfbdb8SLars-Peter Clausen data->bytes_xfered += miter->consumed; 61661bfbdb8SLars-Peter Clausen sg_miter_stop(miter); 61761bfbdb8SLars-Peter Clausen 61861bfbdb8SLars-Peter Clausen return true; 61961bfbdb8SLars-Peter Clausen } 62061bfbdb8SLars-Peter Clausen 6212ee4f620SKees Cook static void jz4740_mmc_timeout(struct timer_list *t) 62261bfbdb8SLars-Peter Clausen { 6232ee4f620SKees Cook struct jz4740_mmc_host *host = from_timer(host, t, timeout_timer); 62461bfbdb8SLars-Peter Clausen 62561bfbdb8SLars-Peter Clausen if (!test_and_clear_bit(0, &host->waiting)) 62661bfbdb8SLars-Peter Clausen return; 62761bfbdb8SLars-Peter Clausen 62861bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, false); 62961bfbdb8SLars-Peter Clausen 63061bfbdb8SLars-Peter Clausen host->req->cmd->error = -ETIMEDOUT; 63161bfbdb8SLars-Peter Clausen jz4740_mmc_request_done(host); 63261bfbdb8SLars-Peter Clausen } 63361bfbdb8SLars-Peter Clausen 63461bfbdb8SLars-Peter Clausen static void jz4740_mmc_read_response(struct jz4740_mmc_host *host, 63561bfbdb8SLars-Peter Clausen struct mmc_command *cmd) 63661bfbdb8SLars-Peter Clausen { 63761bfbdb8SLars-Peter Clausen int i; 63861bfbdb8SLars-Peter Clausen uint16_t tmp; 63961bfbdb8SLars-Peter Clausen void __iomem *fifo_addr = host->base + JZ_REG_MMC_RESP_FIFO; 64061bfbdb8SLars-Peter Clausen 64161bfbdb8SLars-Peter Clausen if (cmd->flags & MMC_RSP_136) { 64261bfbdb8SLars-Peter Clausen tmp = readw(fifo_addr); 64361bfbdb8SLars-Peter Clausen for (i = 0; i < 4; ++i) { 64461bfbdb8SLars-Peter Clausen cmd->resp[i] = tmp << 24; 64561bfbdb8SLars-Peter Clausen tmp = readw(fifo_addr); 64661bfbdb8SLars-Peter Clausen cmd->resp[i] |= tmp << 8; 64761bfbdb8SLars-Peter Clausen tmp = readw(fifo_addr); 64861bfbdb8SLars-Peter Clausen cmd->resp[i] |= tmp >> 8; 64961bfbdb8SLars-Peter Clausen } 65061bfbdb8SLars-Peter Clausen } else { 65161bfbdb8SLars-Peter Clausen cmd->resp[0] = readw(fifo_addr) << 24; 65261bfbdb8SLars-Peter Clausen cmd->resp[0] |= readw(fifo_addr) << 8; 65361bfbdb8SLars-Peter Clausen cmd->resp[0] |= readw(fifo_addr) & 0xff; 65461bfbdb8SLars-Peter Clausen } 65561bfbdb8SLars-Peter Clausen } 65661bfbdb8SLars-Peter Clausen 65761bfbdb8SLars-Peter Clausen static void jz4740_mmc_send_command(struct jz4740_mmc_host *host, 65861bfbdb8SLars-Peter Clausen struct mmc_command *cmd) 65961bfbdb8SLars-Peter Clausen { 66061bfbdb8SLars-Peter Clausen uint32_t cmdat = host->cmdat; 66161bfbdb8SLars-Peter Clausen 66261bfbdb8SLars-Peter Clausen host->cmdat &= ~JZ_MMC_CMDAT_INIT; 66361bfbdb8SLars-Peter Clausen jz4740_mmc_clock_disable(host); 66461bfbdb8SLars-Peter Clausen 66561bfbdb8SLars-Peter Clausen host->cmd = cmd; 66661bfbdb8SLars-Peter Clausen 66761bfbdb8SLars-Peter Clausen if (cmd->flags & MMC_RSP_BUSY) 66861bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_BUSY; 66961bfbdb8SLars-Peter Clausen 67061bfbdb8SLars-Peter Clausen switch (mmc_resp_type(cmd)) { 67161bfbdb8SLars-Peter Clausen case MMC_RSP_R1B: 67261bfbdb8SLars-Peter Clausen case MMC_RSP_R1: 67361bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_RSP_R1; 67461bfbdb8SLars-Peter Clausen break; 67561bfbdb8SLars-Peter Clausen case MMC_RSP_R2: 67661bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_RSP_R2; 67761bfbdb8SLars-Peter Clausen break; 67861bfbdb8SLars-Peter Clausen case MMC_RSP_R3: 67961bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_RSP_R3; 68061bfbdb8SLars-Peter Clausen break; 68161bfbdb8SLars-Peter Clausen default: 68261bfbdb8SLars-Peter Clausen break; 68361bfbdb8SLars-Peter Clausen } 68461bfbdb8SLars-Peter Clausen 68561bfbdb8SLars-Peter Clausen if (cmd->data) { 68661bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_DATA_EN; 68761bfbdb8SLars-Peter Clausen if (cmd->data->flags & MMC_DATA_WRITE) 68861bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_WRITE; 6896a78768aSAlex Smith if (host->use_dma) { 6906a78768aSAlex Smith /* 6916a78768aSAlex Smith * The 4780's MMC controller has integrated DMA ability 6926a78768aSAlex Smith * in addition to being able to use the external DMA 6936a78768aSAlex Smith * controller. It moves DMA control bits to a separate 6946a78768aSAlex Smith * register. The DMA_SEL bit chooses the external 6956a78768aSAlex Smith * controller over the integrated one. Earlier SoCs 6966a78768aSAlex Smith * can only use the external controller, and have a 6976a78768aSAlex Smith * single DMA enable bit in CMDAT. 6986a78768aSAlex Smith */ 6996a78768aSAlex Smith if (host->version >= JZ_MMC_JZ4780) { 7006a78768aSAlex Smith writel(JZ_MMC_DMAC_DMA_EN | JZ_MMC_DMAC_DMA_SEL, 7016a78768aSAlex Smith host->base + JZ_REG_MMC_DMAC); 7026a78768aSAlex Smith } else { 7037ca27a6fSApelete Seketeli cmdat |= JZ_MMC_CMDAT_DMA_EN; 7046a78768aSAlex Smith } 7056a78768aSAlex Smith } else if (host->version >= JZ_MMC_JZ4780) { 7066a78768aSAlex Smith writel(0, host->base + JZ_REG_MMC_DMAC); 7076a78768aSAlex Smith } 70861bfbdb8SLars-Peter Clausen 70961bfbdb8SLars-Peter Clausen writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN); 71061bfbdb8SLars-Peter Clausen writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB); 71161bfbdb8SLars-Peter Clausen } 71261bfbdb8SLars-Peter Clausen 71361bfbdb8SLars-Peter Clausen writeb(cmd->opcode, host->base + JZ_REG_MMC_CMD); 71461bfbdb8SLars-Peter Clausen writel(cmd->arg, host->base + JZ_REG_MMC_ARG); 71561bfbdb8SLars-Peter Clausen writel(cmdat, host->base + JZ_REG_MMC_CMDAT); 71661bfbdb8SLars-Peter Clausen 71761bfbdb8SLars-Peter Clausen jz4740_mmc_clock_enable(host, 1); 71861bfbdb8SLars-Peter Clausen } 71961bfbdb8SLars-Peter Clausen 72061bfbdb8SLars-Peter Clausen static void jz_mmc_prepare_data_transfer(struct jz4740_mmc_host *host) 72161bfbdb8SLars-Peter Clausen { 72261bfbdb8SLars-Peter Clausen struct mmc_command *cmd = host->req->cmd; 72361bfbdb8SLars-Peter Clausen struct mmc_data *data = cmd->data; 72461bfbdb8SLars-Peter Clausen int direction; 72561bfbdb8SLars-Peter Clausen 72661bfbdb8SLars-Peter Clausen if (data->flags & MMC_DATA_READ) 72761bfbdb8SLars-Peter Clausen direction = SG_MITER_TO_SG; 72861bfbdb8SLars-Peter Clausen else 72961bfbdb8SLars-Peter Clausen direction = SG_MITER_FROM_SG; 73061bfbdb8SLars-Peter Clausen 73161bfbdb8SLars-Peter Clausen sg_miter_start(&host->miter, data->sg, data->sg_len, direction); 73261bfbdb8SLars-Peter Clausen } 73361bfbdb8SLars-Peter Clausen 73461bfbdb8SLars-Peter Clausen 73561bfbdb8SLars-Peter Clausen static irqreturn_t jz_mmc_irq_worker(int irq, void *devid) 73661bfbdb8SLars-Peter Clausen { 73761bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)devid; 73861bfbdb8SLars-Peter Clausen struct mmc_command *cmd = host->req->cmd; 73961bfbdb8SLars-Peter Clausen struct mmc_request *req = host->req; 7407ca27a6fSApelete Seketeli struct mmc_data *data = cmd->data; 74161bfbdb8SLars-Peter Clausen bool timeout = false; 74261bfbdb8SLars-Peter Clausen 74361bfbdb8SLars-Peter Clausen if (cmd->error) 74461bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_DONE; 74561bfbdb8SLars-Peter Clausen 74661bfbdb8SLars-Peter Clausen switch (host->state) { 74761bfbdb8SLars-Peter Clausen case JZ4740_MMC_STATE_READ_RESPONSE: 74861bfbdb8SLars-Peter Clausen if (cmd->flags & MMC_RSP_PRESENT) 74961bfbdb8SLars-Peter Clausen jz4740_mmc_read_response(host, cmd); 75061bfbdb8SLars-Peter Clausen 7517ca27a6fSApelete Seketeli if (!data) 75261bfbdb8SLars-Peter Clausen break; 75361bfbdb8SLars-Peter Clausen 75461bfbdb8SLars-Peter Clausen jz_mmc_prepare_data_transfer(host); 75561bfbdb8SLars-Peter Clausen 75661bfbdb8SLars-Peter Clausen case JZ4740_MMC_STATE_TRANSFER_DATA: 7577ca27a6fSApelete Seketeli if (host->use_dma) { 758bb2f4592SApelete Seketeli /* Use DMA if enabled. 759bb2f4592SApelete Seketeli * Data transfer direction is defined later by 760bb2f4592SApelete Seketeli * relying on data flags in 761bb2f4592SApelete Seketeli * jz4740_mmc_prepare_dma_data() and 762bb2f4592SApelete Seketeli * jz4740_mmc_start_dma_transfer(). 7637ca27a6fSApelete Seketeli */ 7647ca27a6fSApelete Seketeli timeout = jz4740_mmc_start_dma_transfer(host, data); 7657ca27a6fSApelete Seketeli data->bytes_xfered = data->blocks * data->blksz; 7667ca27a6fSApelete Seketeli } else if (data->flags & MMC_DATA_READ) 767bb2f4592SApelete Seketeli /* Use PIO if DMA is not enabled. 768bb2f4592SApelete Seketeli * Data transfer direction was defined before 769bb2f4592SApelete Seketeli * by relying on data flags in 770bb2f4592SApelete Seketeli * jz_mmc_prepare_data_transfer(). 7717ca27a6fSApelete Seketeli */ 7727ca27a6fSApelete Seketeli timeout = jz4740_mmc_read_data(host, data); 77361bfbdb8SLars-Peter Clausen else 7747ca27a6fSApelete Seketeli timeout = jz4740_mmc_write_data(host, data); 77561bfbdb8SLars-Peter Clausen 77661bfbdb8SLars-Peter Clausen if (unlikely(timeout)) { 77761bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_TRANSFER_DATA; 77861bfbdb8SLars-Peter Clausen break; 77961bfbdb8SLars-Peter Clausen } 78061bfbdb8SLars-Peter Clausen 7817ca27a6fSApelete Seketeli jz4740_mmc_transfer_check_state(host, data); 78261bfbdb8SLars-Peter Clausen 78361bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_DATA_TRAN_DONE); 78461bfbdb8SLars-Peter Clausen if (unlikely(timeout)) { 78561bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_SEND_STOP; 78661bfbdb8SLars-Peter Clausen break; 78761bfbdb8SLars-Peter Clausen } 7886a78768aSAlex Smith jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_DATA_TRAN_DONE); 78961bfbdb8SLars-Peter Clausen 79061bfbdb8SLars-Peter Clausen case JZ4740_MMC_STATE_SEND_STOP: 79161bfbdb8SLars-Peter Clausen if (!req->stop) 79261bfbdb8SLars-Peter Clausen break; 79361bfbdb8SLars-Peter Clausen 79461bfbdb8SLars-Peter Clausen jz4740_mmc_send_command(host, req->stop); 79561bfbdb8SLars-Peter Clausen 7961acee84bSAlex Smith if (mmc_resp_type(req->stop) & MMC_RSP_BUSY) { 7971acee84bSAlex Smith timeout = jz4740_mmc_poll_irq(host, 7981acee84bSAlex Smith JZ_MMC_IRQ_PRG_DONE); 79961bfbdb8SLars-Peter Clausen if (timeout) { 80061bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_DONE; 80161bfbdb8SLars-Peter Clausen break; 80261bfbdb8SLars-Peter Clausen } 8031acee84bSAlex Smith } 80461bfbdb8SLars-Peter Clausen case JZ4740_MMC_STATE_DONE: 80561bfbdb8SLars-Peter Clausen break; 80661bfbdb8SLars-Peter Clausen } 80761bfbdb8SLars-Peter Clausen 80861bfbdb8SLars-Peter Clausen if (!timeout) 80961bfbdb8SLars-Peter Clausen jz4740_mmc_request_done(host); 81061bfbdb8SLars-Peter Clausen 81161bfbdb8SLars-Peter Clausen return IRQ_HANDLED; 81261bfbdb8SLars-Peter Clausen } 81361bfbdb8SLars-Peter Clausen 81461bfbdb8SLars-Peter Clausen static irqreturn_t jz_mmc_irq(int irq, void *devid) 81561bfbdb8SLars-Peter Clausen { 81661bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = devid; 81761bfbdb8SLars-Peter Clausen struct mmc_command *cmd = host->cmd; 8186a78768aSAlex Smith uint32_t irq_reg, status, tmp; 81961bfbdb8SLars-Peter Clausen 8206a78768aSAlex Smith status = readl(host->base + JZ_REG_MMC_STATUS); 8216a78768aSAlex Smith irq_reg = jz4740_mmc_read_irq_reg(host); 82261bfbdb8SLars-Peter Clausen 82361bfbdb8SLars-Peter Clausen tmp = irq_reg; 82461bfbdb8SLars-Peter Clausen irq_reg &= ~host->irq_mask; 82561bfbdb8SLars-Peter Clausen 82661bfbdb8SLars-Peter Clausen tmp &= ~(JZ_MMC_IRQ_TXFIFO_WR_REQ | JZ_MMC_IRQ_RXFIFO_RD_REQ | 82761bfbdb8SLars-Peter Clausen JZ_MMC_IRQ_PRG_DONE | JZ_MMC_IRQ_DATA_TRAN_DONE); 82861bfbdb8SLars-Peter Clausen 82961bfbdb8SLars-Peter Clausen if (tmp != irq_reg) 8306a78768aSAlex Smith jz4740_mmc_write_irq_reg(host, tmp & ~irq_reg); 83161bfbdb8SLars-Peter Clausen 83261bfbdb8SLars-Peter Clausen if (irq_reg & JZ_MMC_IRQ_SDIO) { 8336a78768aSAlex Smith jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_SDIO); 83461bfbdb8SLars-Peter Clausen mmc_signal_sdio_irq(host->mmc); 83561bfbdb8SLars-Peter Clausen irq_reg &= ~JZ_MMC_IRQ_SDIO; 83661bfbdb8SLars-Peter Clausen } 83761bfbdb8SLars-Peter Clausen 83861bfbdb8SLars-Peter Clausen if (host->req && cmd && irq_reg) { 83961bfbdb8SLars-Peter Clausen if (test_and_clear_bit(0, &host->waiting)) { 84061bfbdb8SLars-Peter Clausen del_timer(&host->timeout_timer); 84161bfbdb8SLars-Peter Clausen 84261bfbdb8SLars-Peter Clausen if (status & JZ_MMC_STATUS_TIMEOUT_RES) { 84361bfbdb8SLars-Peter Clausen cmd->error = -ETIMEDOUT; 84461bfbdb8SLars-Peter Clausen } else if (status & JZ_MMC_STATUS_CRC_RES_ERR) { 84561bfbdb8SLars-Peter Clausen cmd->error = -EIO; 84661bfbdb8SLars-Peter Clausen } else if (status & (JZ_MMC_STATUS_CRC_READ_ERROR | 84761bfbdb8SLars-Peter Clausen JZ_MMC_STATUS_CRC_WRITE_ERROR)) { 84861bfbdb8SLars-Peter Clausen if (cmd->data) 84961bfbdb8SLars-Peter Clausen cmd->data->error = -EIO; 85061bfbdb8SLars-Peter Clausen cmd->error = -EIO; 85161bfbdb8SLars-Peter Clausen } 85261bfbdb8SLars-Peter Clausen 85361bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, irq_reg, false); 8546a78768aSAlex Smith jz4740_mmc_write_irq_reg(host, irq_reg); 85561bfbdb8SLars-Peter Clausen 85661bfbdb8SLars-Peter Clausen return IRQ_WAKE_THREAD; 85761bfbdb8SLars-Peter Clausen } 85861bfbdb8SLars-Peter Clausen } 85961bfbdb8SLars-Peter Clausen 86061bfbdb8SLars-Peter Clausen return IRQ_HANDLED; 86161bfbdb8SLars-Peter Clausen } 86261bfbdb8SLars-Peter Clausen 86361bfbdb8SLars-Peter Clausen static int jz4740_mmc_set_clock_rate(struct jz4740_mmc_host *host, int rate) 86461bfbdb8SLars-Peter Clausen { 86561bfbdb8SLars-Peter Clausen int div = 0; 86661bfbdb8SLars-Peter Clausen int real_rate; 86761bfbdb8SLars-Peter Clausen 86861bfbdb8SLars-Peter Clausen jz4740_mmc_clock_disable(host); 8696861fce6SAlex Smith clk_set_rate(host->clk, host->mmc->f_max); 87061bfbdb8SLars-Peter Clausen 87161bfbdb8SLars-Peter Clausen real_rate = clk_get_rate(host->clk); 87261bfbdb8SLars-Peter Clausen 87361bfbdb8SLars-Peter Clausen while (real_rate > rate && div < 7) { 87461bfbdb8SLars-Peter Clausen ++div; 87561bfbdb8SLars-Peter Clausen real_rate >>= 1; 87661bfbdb8SLars-Peter Clausen } 87761bfbdb8SLars-Peter Clausen 87861bfbdb8SLars-Peter Clausen writew(div, host->base + JZ_REG_MMC_CLKRT); 87961bfbdb8SLars-Peter Clausen return real_rate; 88061bfbdb8SLars-Peter Clausen } 88161bfbdb8SLars-Peter Clausen 88261bfbdb8SLars-Peter Clausen static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req) 88361bfbdb8SLars-Peter Clausen { 88461bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = mmc_priv(mmc); 88561bfbdb8SLars-Peter Clausen 88661bfbdb8SLars-Peter Clausen host->req = req; 88761bfbdb8SLars-Peter Clausen 8886a78768aSAlex Smith jz4740_mmc_write_irq_reg(host, ~0); 88961bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, true); 89061bfbdb8SLars-Peter Clausen 89161bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_READ_RESPONSE; 89261bfbdb8SLars-Peter Clausen set_bit(0, &host->waiting); 89361bfbdb8SLars-Peter Clausen mod_timer(&host->timeout_timer, jiffies + 5*HZ); 89461bfbdb8SLars-Peter Clausen jz4740_mmc_send_command(host, req->cmd); 89561bfbdb8SLars-Peter Clausen } 89661bfbdb8SLars-Peter Clausen 89761bfbdb8SLars-Peter Clausen static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 89861bfbdb8SLars-Peter Clausen { 89961bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = mmc_priv(mmc); 90061bfbdb8SLars-Peter Clausen if (ios->clock) 90161bfbdb8SLars-Peter Clausen jz4740_mmc_set_clock_rate(host, ios->clock); 90261bfbdb8SLars-Peter Clausen 90361bfbdb8SLars-Peter Clausen switch (ios->power_mode) { 90461bfbdb8SLars-Peter Clausen case MMC_POWER_UP: 90561bfbdb8SLars-Peter Clausen jz4740_mmc_reset(host); 90661e11ebaSEzequiel Garcia if (host->pdata && gpio_is_valid(host->pdata->gpio_power)) 90761bfbdb8SLars-Peter Clausen gpio_set_value(host->pdata->gpio_power, 90861bfbdb8SLars-Peter Clausen !host->pdata->power_active_low); 90961bfbdb8SLars-Peter Clausen host->cmdat |= JZ_MMC_CMDAT_INIT; 910fca9661cSLars-Peter Clausen clk_prepare_enable(host->clk); 91161bfbdb8SLars-Peter Clausen break; 91261bfbdb8SLars-Peter Clausen case MMC_POWER_ON: 91361bfbdb8SLars-Peter Clausen break; 91461bfbdb8SLars-Peter Clausen default: 91561e11ebaSEzequiel Garcia if (host->pdata && gpio_is_valid(host->pdata->gpio_power)) 91661bfbdb8SLars-Peter Clausen gpio_set_value(host->pdata->gpio_power, 91761bfbdb8SLars-Peter Clausen host->pdata->power_active_low); 918fca9661cSLars-Peter Clausen clk_disable_unprepare(host->clk); 91961bfbdb8SLars-Peter Clausen break; 92061bfbdb8SLars-Peter Clausen } 92161bfbdb8SLars-Peter Clausen 92261bfbdb8SLars-Peter Clausen switch (ios->bus_width) { 92361bfbdb8SLars-Peter Clausen case MMC_BUS_WIDTH_1: 92461bfbdb8SLars-Peter Clausen host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_4BIT; 92561bfbdb8SLars-Peter Clausen break; 92661bfbdb8SLars-Peter Clausen case MMC_BUS_WIDTH_4: 92761bfbdb8SLars-Peter Clausen host->cmdat |= JZ_MMC_CMDAT_BUS_WIDTH_4BIT; 92861bfbdb8SLars-Peter Clausen break; 92961bfbdb8SLars-Peter Clausen default: 93061bfbdb8SLars-Peter Clausen break; 93161bfbdb8SLars-Peter Clausen } 93261bfbdb8SLars-Peter Clausen } 93361bfbdb8SLars-Peter Clausen 93461bfbdb8SLars-Peter Clausen static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) 93561bfbdb8SLars-Peter Clausen { 93661bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = mmc_priv(mmc); 93761bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_SDIO, enable); 93861bfbdb8SLars-Peter Clausen } 93961bfbdb8SLars-Peter Clausen 94061bfbdb8SLars-Peter Clausen static const struct mmc_host_ops jz4740_mmc_ops = { 94161bfbdb8SLars-Peter Clausen .request = jz4740_mmc_request, 942bb2f4592SApelete Seketeli .pre_req = jz4740_mmc_pre_request, 943bb2f4592SApelete Seketeli .post_req = jz4740_mmc_post_request, 94461bfbdb8SLars-Peter Clausen .set_ios = jz4740_mmc_set_ios, 94558e300afSLars-Peter Clausen .get_ro = mmc_gpio_get_ro, 94658e300afSLars-Peter Clausen .get_cd = mmc_gpio_get_cd, 94761bfbdb8SLars-Peter Clausen .enable_sdio_irq = jz4740_mmc_enable_sdio_irq, 94861bfbdb8SLars-Peter Clausen }; 94961bfbdb8SLars-Peter Clausen 950c3be1efdSBill Pemberton static int jz4740_mmc_request_gpio(struct device *dev, int gpio, 95161bfbdb8SLars-Peter Clausen const char *name, bool output, int value) 95261bfbdb8SLars-Peter Clausen { 95361bfbdb8SLars-Peter Clausen int ret; 95461bfbdb8SLars-Peter Clausen 95561bfbdb8SLars-Peter Clausen if (!gpio_is_valid(gpio)) 95661bfbdb8SLars-Peter Clausen return 0; 95761bfbdb8SLars-Peter Clausen 95861bfbdb8SLars-Peter Clausen ret = gpio_request(gpio, name); 95961bfbdb8SLars-Peter Clausen if (ret) { 96061bfbdb8SLars-Peter Clausen dev_err(dev, "Failed to request %s gpio: %d\n", name, ret); 96161bfbdb8SLars-Peter Clausen return ret; 96261bfbdb8SLars-Peter Clausen } 96361bfbdb8SLars-Peter Clausen 96461bfbdb8SLars-Peter Clausen if (output) 96561bfbdb8SLars-Peter Clausen gpio_direction_output(gpio, value); 96661bfbdb8SLars-Peter Clausen else 96761bfbdb8SLars-Peter Clausen gpio_direction_input(gpio); 96861bfbdb8SLars-Peter Clausen 96961bfbdb8SLars-Peter Clausen return 0; 97061bfbdb8SLars-Peter Clausen } 97161bfbdb8SLars-Peter Clausen 97258e300afSLars-Peter Clausen static int jz4740_mmc_request_gpios(struct mmc_host *mmc, 97358e300afSLars-Peter Clausen struct platform_device *pdev) 97461bfbdb8SLars-Peter Clausen { 97539e9ef1dSEzequiel Garcia struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev); 97658e300afSLars-Peter Clausen int ret = 0; 97761bfbdb8SLars-Peter Clausen 97861bfbdb8SLars-Peter Clausen if (!pdata) 97961bfbdb8SLars-Peter Clausen return 0; 98061bfbdb8SLars-Peter Clausen 98158e300afSLars-Peter Clausen if (!pdata->card_detect_active_low) 98258e300afSLars-Peter Clausen mmc->caps2 |= MMC_CAP2_CD_ACTIVE_HIGH; 98358e300afSLars-Peter Clausen if (!pdata->read_only_active_low) 98458e300afSLars-Peter Clausen mmc->caps2 |= MMC_CAP2_RO_ACTIVE_HIGH; 98558e300afSLars-Peter Clausen 98658e300afSLars-Peter Clausen if (gpio_is_valid(pdata->gpio_card_detect)) { 987214fc309SLaurent Pinchart ret = mmc_gpio_request_cd(mmc, pdata->gpio_card_detect, 0); 98861bfbdb8SLars-Peter Clausen if (ret) 98961bfbdb8SLars-Peter Clausen return ret; 99061bfbdb8SLars-Peter Clausen } 99161bfbdb8SLars-Peter Clausen 99258e300afSLars-Peter Clausen if (gpio_is_valid(pdata->gpio_read_only)) { 99358e300afSLars-Peter Clausen ret = mmc_gpio_request_ro(mmc, pdata->gpio_read_only); 99458e300afSLars-Peter Clausen if (ret) 99558e300afSLars-Peter Clausen return ret; 99661bfbdb8SLars-Peter Clausen } 99761bfbdb8SLars-Peter Clausen 99858e300afSLars-Peter Clausen return jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_power, 99958e300afSLars-Peter Clausen "MMC read only", true, pdata->power_active_low); 100061bfbdb8SLars-Peter Clausen } 100161bfbdb8SLars-Peter Clausen 100261bfbdb8SLars-Peter Clausen static void jz4740_mmc_free_gpios(struct platform_device *pdev) 100361bfbdb8SLars-Peter Clausen { 100439e9ef1dSEzequiel Garcia struct jz4740_mmc_platform_data *pdata = dev_get_platdata(&pdev->dev); 100561bfbdb8SLars-Peter Clausen 100661bfbdb8SLars-Peter Clausen if (!pdata) 100761bfbdb8SLars-Peter Clausen return; 100861bfbdb8SLars-Peter Clausen 100961bfbdb8SLars-Peter Clausen if (gpio_is_valid(pdata->gpio_power)) 101061bfbdb8SLars-Peter Clausen gpio_free(pdata->gpio_power); 101161bfbdb8SLars-Peter Clausen } 101261bfbdb8SLars-Peter Clausen 101361e11ebaSEzequiel Garcia static const struct of_device_id jz4740_mmc_of_match[] = { 101461e11ebaSEzequiel Garcia { .compatible = "ingenic,jz4740-mmc", .data = (void *) JZ_MMC_JZ4740 }, 1015a0c938b5SPaul Cercueil { .compatible = "ingenic,jz4725b-mmc", .data = (void *)JZ_MMC_JZ4725B }, 10166a78768aSAlex Smith { .compatible = "ingenic,jz4780-mmc", .data = (void *) JZ_MMC_JZ4780 }, 101761e11ebaSEzequiel Garcia {}, 101861e11ebaSEzequiel Garcia }; 101961e11ebaSEzequiel Garcia MODULE_DEVICE_TABLE(of, jz4740_mmc_of_match); 102061e11ebaSEzequiel Garcia 1021c3be1efdSBill Pemberton static int jz4740_mmc_probe(struct platform_device* pdev) 102261bfbdb8SLars-Peter Clausen { 102361bfbdb8SLars-Peter Clausen int ret; 102461bfbdb8SLars-Peter Clausen struct mmc_host *mmc; 102561bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host; 102661e11ebaSEzequiel Garcia const struct of_device_id *match; 102761bfbdb8SLars-Peter Clausen struct jz4740_mmc_platform_data *pdata; 102861bfbdb8SLars-Peter Clausen 102939e9ef1dSEzequiel Garcia pdata = dev_get_platdata(&pdev->dev); 103061bfbdb8SLars-Peter Clausen 103161bfbdb8SLars-Peter Clausen mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev); 103261bfbdb8SLars-Peter Clausen if (!mmc) { 103361bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to alloc mmc host structure\n"); 103461bfbdb8SLars-Peter Clausen return -ENOMEM; 103561bfbdb8SLars-Peter Clausen } 103661bfbdb8SLars-Peter Clausen 103761bfbdb8SLars-Peter Clausen host = mmc_priv(mmc); 103861bfbdb8SLars-Peter Clausen host->pdata = pdata; 103961bfbdb8SLars-Peter Clausen 104061e11ebaSEzequiel Garcia match = of_match_device(jz4740_mmc_of_match, &pdev->dev); 104161e11ebaSEzequiel Garcia if (match) { 104261e11ebaSEzequiel Garcia host->version = (enum jz4740_mmc_version)match->data; 104361e11ebaSEzequiel Garcia ret = mmc_of_parse(mmc); 104461e11ebaSEzequiel Garcia if (ret) { 104561e11ebaSEzequiel Garcia if (ret != -EPROBE_DEFER) 104661e11ebaSEzequiel Garcia dev_err(&pdev->dev, 104761e11ebaSEzequiel Garcia "could not parse of data: %d\n", ret); 104861e11ebaSEzequiel Garcia goto err_free_host; 104961e11ebaSEzequiel Garcia } 105061e11ebaSEzequiel Garcia } else { 105161e11ebaSEzequiel Garcia /* JZ4740 should be the only one using legacy probe */ 105261e11ebaSEzequiel Garcia host->version = JZ_MMC_JZ4740; 105361e11ebaSEzequiel Garcia mmc->caps |= MMC_CAP_SDIO_IRQ; 105461e11ebaSEzequiel Garcia if (!(pdata && pdata->data_1bit)) 105561e11ebaSEzequiel Garcia mmc->caps |= MMC_CAP_4_BIT_DATA; 105661e11ebaSEzequiel Garcia ret = jz4740_mmc_request_gpios(mmc, pdev); 105761e11ebaSEzequiel Garcia if (ret) 105861e11ebaSEzequiel Garcia goto err_free_host; 105961e11ebaSEzequiel Garcia } 106061e11ebaSEzequiel Garcia 106161bfbdb8SLars-Peter Clausen host->irq = platform_get_irq(pdev, 0); 106261bfbdb8SLars-Peter Clausen if (host->irq < 0) { 106361bfbdb8SLars-Peter Clausen ret = host->irq; 106461bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); 106561bfbdb8SLars-Peter Clausen goto err_free_host; 106661bfbdb8SLars-Peter Clausen } 106761bfbdb8SLars-Peter Clausen 1068017d84bdSLars-Peter Clausen host->clk = devm_clk_get(&pdev->dev, "mmc"); 10693119cbdaSJamie Iles if (IS_ERR(host->clk)) { 10703119cbdaSJamie Iles ret = PTR_ERR(host->clk); 107161bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to get mmc clock\n"); 107261bfbdb8SLars-Peter Clausen goto err_free_host; 107361bfbdb8SLars-Peter Clausen } 107461bfbdb8SLars-Peter Clausen 10757ca27a6fSApelete Seketeli host->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 10767ca27a6fSApelete Seketeli host->base = devm_ioremap_resource(&pdev->dev, host->mem_res); 10773e7e8c18SWei Yongjun if (IS_ERR(host->base)) { 10783e7e8c18SWei Yongjun ret = PTR_ERR(host->base); 10797ca27a6fSApelete Seketeli dev_err(&pdev->dev, "Failed to ioremap base memory\n"); 1080017d84bdSLars-Peter Clausen goto err_free_host; 108161bfbdb8SLars-Peter Clausen } 108261bfbdb8SLars-Peter Clausen 108361bfbdb8SLars-Peter Clausen mmc->ops = &jz4740_mmc_ops; 108461e11ebaSEzequiel Garcia if (!mmc->f_max) 108561bfbdb8SLars-Peter Clausen mmc->f_max = JZ_MMC_CLK_RATE; 108661e11ebaSEzequiel Garcia mmc->f_min = mmc->f_max / 128; 108761bfbdb8SLars-Peter Clausen mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; 108861bfbdb8SLars-Peter Clausen 108961bfbdb8SLars-Peter Clausen mmc->max_blk_size = (1 << 10) - 1; 109061bfbdb8SLars-Peter Clausen mmc->max_blk_count = (1 << 15) - 1; 109161bfbdb8SLars-Peter Clausen mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; 109261bfbdb8SLars-Peter Clausen 1093a36274e0SMartin K. Petersen mmc->max_segs = 128; 109461bfbdb8SLars-Peter Clausen mmc->max_seg_size = mmc->max_req_size; 109561bfbdb8SLars-Peter Clausen 109661bfbdb8SLars-Peter Clausen host->mmc = mmc; 109761bfbdb8SLars-Peter Clausen host->pdev = pdev; 109861bfbdb8SLars-Peter Clausen spin_lock_init(&host->lock); 10996a78768aSAlex Smith host->irq_mask = ~0; 110061bfbdb8SLars-Peter Clausen 1101436a3cfdSZubair Lutfullah Kakakhel jz4740_mmc_reset(host); 1102436a3cfdSZubair Lutfullah Kakakhel 110361bfbdb8SLars-Peter Clausen ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0, 110461bfbdb8SLars-Peter Clausen dev_name(&pdev->dev), host); 110561bfbdb8SLars-Peter Clausen if (ret) { 110661bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); 110758e300afSLars-Peter Clausen goto err_free_gpios; 110861bfbdb8SLars-Peter Clausen } 110961bfbdb8SLars-Peter Clausen 111061bfbdb8SLars-Peter Clausen jz4740_mmc_clock_disable(host); 11112ee4f620SKees Cook timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0); 111261bfbdb8SLars-Peter Clausen 11137e8466e2SPaul Cercueil ret = jz4740_mmc_acquire_dma_channels(host); 11147e8466e2SPaul Cercueil if (ret == -EPROBE_DEFER) 11157e8466e2SPaul Cercueil goto err_free_irq; 11167e8466e2SPaul Cercueil host->use_dma = !ret; 11177ca27a6fSApelete Seketeli 111861bfbdb8SLars-Peter Clausen platform_set_drvdata(pdev, host); 111961bfbdb8SLars-Peter Clausen ret = mmc_add_host(mmc); 112061bfbdb8SLars-Peter Clausen 112161bfbdb8SLars-Peter Clausen if (ret) { 112261bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret); 11237e8466e2SPaul Cercueil goto err_release_dma; 112461bfbdb8SLars-Peter Clausen } 112561bfbdb8SLars-Peter Clausen dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n"); 112661bfbdb8SLars-Peter Clausen 11277ca27a6fSApelete Seketeli dev_info(&pdev->dev, "Using %s, %d-bit mode\n", 11287ca27a6fSApelete Seketeli host->use_dma ? "DMA" : "PIO", 11297ca27a6fSApelete Seketeli (mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1); 11307ca27a6fSApelete Seketeli 113161bfbdb8SLars-Peter Clausen return 0; 113261bfbdb8SLars-Peter Clausen 11337e8466e2SPaul Cercueil err_release_dma: 11347e8466e2SPaul Cercueil if (host->use_dma) 11357e8466e2SPaul Cercueil jz4740_mmc_release_dma_channels(host); 113661bfbdb8SLars-Peter Clausen err_free_irq: 113761bfbdb8SLars-Peter Clausen free_irq(host->irq, host); 113861bfbdb8SLars-Peter Clausen err_free_gpios: 113961bfbdb8SLars-Peter Clausen jz4740_mmc_free_gpios(pdev); 114061bfbdb8SLars-Peter Clausen err_free_host: 114161bfbdb8SLars-Peter Clausen mmc_free_host(mmc); 114261bfbdb8SLars-Peter Clausen 114361bfbdb8SLars-Peter Clausen return ret; 114461bfbdb8SLars-Peter Clausen } 114561bfbdb8SLars-Peter Clausen 11466e0ee714SBill Pemberton static int jz4740_mmc_remove(struct platform_device *pdev) 114761bfbdb8SLars-Peter Clausen { 114861bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = platform_get_drvdata(pdev); 114961bfbdb8SLars-Peter Clausen 115061bfbdb8SLars-Peter Clausen del_timer_sync(&host->timeout_timer); 115161bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, 0xff, false); 115261bfbdb8SLars-Peter Clausen jz4740_mmc_reset(host); 115361bfbdb8SLars-Peter Clausen 115461bfbdb8SLars-Peter Clausen mmc_remove_host(host->mmc); 115561bfbdb8SLars-Peter Clausen 115661bfbdb8SLars-Peter Clausen free_irq(host->irq, host); 115761bfbdb8SLars-Peter Clausen 115861bfbdb8SLars-Peter Clausen jz4740_mmc_free_gpios(pdev); 115961bfbdb8SLars-Peter Clausen 11607ca27a6fSApelete Seketeli if (host->use_dma) 11617ca27a6fSApelete Seketeli jz4740_mmc_release_dma_channels(host); 11627ca27a6fSApelete Seketeli 116361bfbdb8SLars-Peter Clausen mmc_free_host(host->mmc); 116461bfbdb8SLars-Peter Clausen 116561bfbdb8SLars-Peter Clausen return 0; 116661bfbdb8SLars-Peter Clausen } 116761bfbdb8SLars-Peter Clausen 11685d5c0350SLars-Peter Clausen #ifdef CONFIG_PM_SLEEP 116961bfbdb8SLars-Peter Clausen 117061bfbdb8SLars-Peter Clausen static int jz4740_mmc_suspend(struct device *dev) 117161bfbdb8SLars-Peter Clausen { 1172fa5ed6bcSPaul Cercueil return pinctrl_pm_select_sleep_state(dev); 117361bfbdb8SLars-Peter Clausen } 117461bfbdb8SLars-Peter Clausen 117561bfbdb8SLars-Peter Clausen static int jz4740_mmc_resume(struct device *dev) 117661bfbdb8SLars-Peter Clausen { 1177fa5ed6bcSPaul Cercueil return pinctrl_pm_select_default_state(dev); 117861bfbdb8SLars-Peter Clausen } 117961bfbdb8SLars-Peter Clausen 11805d5c0350SLars-Peter Clausen static SIMPLE_DEV_PM_OPS(jz4740_mmc_pm_ops, jz4740_mmc_suspend, 11815d5c0350SLars-Peter Clausen jz4740_mmc_resume); 118261bfbdb8SLars-Peter Clausen #define JZ4740_MMC_PM_OPS (&jz4740_mmc_pm_ops) 118361bfbdb8SLars-Peter Clausen #else 118461bfbdb8SLars-Peter Clausen #define JZ4740_MMC_PM_OPS NULL 118561bfbdb8SLars-Peter Clausen #endif 118661bfbdb8SLars-Peter Clausen 118761bfbdb8SLars-Peter Clausen static struct platform_driver jz4740_mmc_driver = { 118861bfbdb8SLars-Peter Clausen .probe = jz4740_mmc_probe, 11890433c143SBill Pemberton .remove = jz4740_mmc_remove, 119061bfbdb8SLars-Peter Clausen .driver = { 119161bfbdb8SLars-Peter Clausen .name = "jz4740-mmc", 119261e11ebaSEzequiel Garcia .of_match_table = of_match_ptr(jz4740_mmc_of_match), 119361bfbdb8SLars-Peter Clausen .pm = JZ4740_MMC_PM_OPS, 119461bfbdb8SLars-Peter Clausen }, 119561bfbdb8SLars-Peter Clausen }; 119661bfbdb8SLars-Peter Clausen 1197d1f81a64SAxel Lin module_platform_driver(jz4740_mmc_driver); 119861bfbdb8SLars-Peter Clausen 119961bfbdb8SLars-Peter Clausen MODULE_DESCRIPTION("JZ4740 SD/MMC controller driver"); 120061bfbdb8SLars-Peter Clausen MODULE_LICENSE("GPL"); 120161bfbdb8SLars-Peter Clausen MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 1202