18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> 48c2ecf20Sopenharmony_ci * Copyright (C) 2013, Imagination Technologies 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * JZ4740 SD/MMC controller driver 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/bitops.h> 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/dmaengine.h> 138c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/irq.h> 188c2ecf20Sopenharmony_ci#include <linux/mmc/host.h> 198c2ecf20Sopenharmony_ci#include <linux/mmc/slot-gpio.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/of_device.h> 228c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 248c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define JZ_REG_MMC_STRPCL 0x00 298c2ecf20Sopenharmony_ci#define JZ_REG_MMC_STATUS 0x04 308c2ecf20Sopenharmony_ci#define JZ_REG_MMC_CLKRT 0x08 318c2ecf20Sopenharmony_ci#define JZ_REG_MMC_CMDAT 0x0C 328c2ecf20Sopenharmony_ci#define JZ_REG_MMC_RESTO 0x10 338c2ecf20Sopenharmony_ci#define JZ_REG_MMC_RDTO 0x14 348c2ecf20Sopenharmony_ci#define JZ_REG_MMC_BLKLEN 0x18 358c2ecf20Sopenharmony_ci#define JZ_REG_MMC_NOB 0x1C 368c2ecf20Sopenharmony_ci#define JZ_REG_MMC_SNOB 0x20 378c2ecf20Sopenharmony_ci#define JZ_REG_MMC_IMASK 0x24 388c2ecf20Sopenharmony_ci#define JZ_REG_MMC_IREG 0x28 398c2ecf20Sopenharmony_ci#define JZ_REG_MMC_CMD 0x2C 408c2ecf20Sopenharmony_ci#define JZ_REG_MMC_ARG 0x30 418c2ecf20Sopenharmony_ci#define JZ_REG_MMC_RESP_FIFO 0x34 428c2ecf20Sopenharmony_ci#define JZ_REG_MMC_RXFIFO 0x38 438c2ecf20Sopenharmony_ci#define JZ_REG_MMC_TXFIFO 0x3C 448c2ecf20Sopenharmony_ci#define JZ_REG_MMC_LPM 0x40 458c2ecf20Sopenharmony_ci#define JZ_REG_MMC_DMAC 0x44 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7) 488c2ecf20Sopenharmony_ci#define JZ_MMC_STRPCL_EXIT_TRANSFER BIT(6) 498c2ecf20Sopenharmony_ci#define JZ_MMC_STRPCL_START_READWAIT BIT(5) 508c2ecf20Sopenharmony_ci#define JZ_MMC_STRPCL_STOP_READWAIT BIT(4) 518c2ecf20Sopenharmony_ci#define JZ_MMC_STRPCL_RESET BIT(3) 528c2ecf20Sopenharmony_ci#define JZ_MMC_STRPCL_START_OP BIT(2) 538c2ecf20Sopenharmony_ci#define JZ_MMC_STRPCL_CLOCK_CONTROL (BIT(1) | BIT(0)) 548c2ecf20Sopenharmony_ci#define JZ_MMC_STRPCL_CLOCK_STOP BIT(0) 558c2ecf20Sopenharmony_ci#define JZ_MMC_STRPCL_CLOCK_START BIT(1) 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_IS_RESETTING BIT(15) 598c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_SDIO_INT_ACTIVE BIT(14) 608c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_PRG_DONE BIT(13) 618c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_DATA_TRAN_DONE BIT(12) 628c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_END_CMD_RES BIT(11) 638c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_DATA_FIFO_AFULL BIT(10) 648c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_IS_READWAIT BIT(9) 658c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_CLK_EN BIT(8) 668c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_DATA_FIFO_FULL BIT(7) 678c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_DATA_FIFO_EMPTY BIT(6) 688c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_CRC_RES_ERR BIT(5) 698c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_CRC_READ_ERROR BIT(4) 708c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_TIMEOUT_WRITE BIT(3) 718c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_CRC_WRITE_ERROR BIT(2) 728c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_TIMEOUT_RES BIT(1) 738c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_TIMEOUT_READ BIT(0) 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_READ_ERROR_MASK (BIT(4) | BIT(0)) 768c2ecf20Sopenharmony_ci#define JZ_MMC_STATUS_WRITE_ERROR_MASK (BIT(3) | BIT(2)) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define JZ_MMC_CMDAT_IO_ABORT BIT(11) 808c2ecf20Sopenharmony_ci#define JZ_MMC_CMDAT_BUS_WIDTH_4BIT BIT(10) 818c2ecf20Sopenharmony_ci#define JZ_MMC_CMDAT_BUS_WIDTH_8BIT (BIT(10) | BIT(9)) 828c2ecf20Sopenharmony_ci#define JZ_MMC_CMDAT_BUS_WIDTH_MASK (BIT(10) | BIT(9)) 838c2ecf20Sopenharmony_ci#define JZ_MMC_CMDAT_DMA_EN BIT(8) 848c2ecf20Sopenharmony_ci#define JZ_MMC_CMDAT_INIT BIT(7) 858c2ecf20Sopenharmony_ci#define JZ_MMC_CMDAT_BUSY BIT(6) 868c2ecf20Sopenharmony_ci#define JZ_MMC_CMDAT_STREAM BIT(5) 878c2ecf20Sopenharmony_ci#define JZ_MMC_CMDAT_WRITE BIT(4) 888c2ecf20Sopenharmony_ci#define JZ_MMC_CMDAT_DATA_EN BIT(3) 898c2ecf20Sopenharmony_ci#define JZ_MMC_CMDAT_RESPONSE_FORMAT (BIT(2) | BIT(1) | BIT(0)) 908c2ecf20Sopenharmony_ci#define JZ_MMC_CMDAT_RSP_R1 1 918c2ecf20Sopenharmony_ci#define JZ_MMC_CMDAT_RSP_R2 2 928c2ecf20Sopenharmony_ci#define JZ_MMC_CMDAT_RSP_R3 3 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci#define JZ_MMC_IRQ_SDIO BIT(7) 958c2ecf20Sopenharmony_ci#define JZ_MMC_IRQ_TXFIFO_WR_REQ BIT(6) 968c2ecf20Sopenharmony_ci#define JZ_MMC_IRQ_RXFIFO_RD_REQ BIT(5) 978c2ecf20Sopenharmony_ci#define JZ_MMC_IRQ_END_CMD_RES BIT(2) 988c2ecf20Sopenharmony_ci#define JZ_MMC_IRQ_PRG_DONE BIT(1) 998c2ecf20Sopenharmony_ci#define JZ_MMC_IRQ_DATA_TRAN_DONE BIT(0) 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#define JZ_MMC_DMAC_DMA_SEL BIT(1) 1028c2ecf20Sopenharmony_ci#define JZ_MMC_DMAC_DMA_EN BIT(0) 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define JZ_MMC_LPM_DRV_RISING BIT(31) 1058c2ecf20Sopenharmony_ci#define JZ_MMC_LPM_DRV_RISING_QTR_PHASE_DLY BIT(31) 1068c2ecf20Sopenharmony_ci#define JZ_MMC_LPM_DRV_RISING_1NS_DLY BIT(30) 1078c2ecf20Sopenharmony_ci#define JZ_MMC_LPM_SMP_RISING_QTR_OR_HALF_PHASE_DLY BIT(29) 1088c2ecf20Sopenharmony_ci#define JZ_MMC_LPM_LOW_POWER_MODE_EN BIT(0) 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci#define JZ_MMC_CLK_RATE 24000000 1118c2ecf20Sopenharmony_ci#define JZ_MMC_REQ_TIMEOUT_MS 5000 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cienum jz4740_mmc_version { 1148c2ecf20Sopenharmony_ci JZ_MMC_JZ4740, 1158c2ecf20Sopenharmony_ci JZ_MMC_JZ4725B, 1168c2ecf20Sopenharmony_ci JZ_MMC_JZ4760, 1178c2ecf20Sopenharmony_ci JZ_MMC_JZ4780, 1188c2ecf20Sopenharmony_ci JZ_MMC_X1000, 1198c2ecf20Sopenharmony_ci}; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cienum jz4740_mmc_state { 1228c2ecf20Sopenharmony_ci JZ4740_MMC_STATE_READ_RESPONSE, 1238c2ecf20Sopenharmony_ci JZ4740_MMC_STATE_TRANSFER_DATA, 1248c2ecf20Sopenharmony_ci JZ4740_MMC_STATE_SEND_STOP, 1258c2ecf20Sopenharmony_ci JZ4740_MMC_STATE_DONE, 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* 1298c2ecf20Sopenharmony_ci * The MMC core allows to prepare a mmc_request while another mmc_request 1308c2ecf20Sopenharmony_ci * is in-flight. This is used via the pre_req/post_req hooks. 1318c2ecf20Sopenharmony_ci * This driver uses the pre_req/post_req hooks to map/unmap the mmc_request. 1328c2ecf20Sopenharmony_ci * Following what other drivers do (sdhci, dw_mmc) we use the following cookie 1338c2ecf20Sopenharmony_ci * flags to keep track of the mmc_request mapping state. 1348c2ecf20Sopenharmony_ci * 1358c2ecf20Sopenharmony_ci * COOKIE_UNMAPPED: the request is not mapped. 1368c2ecf20Sopenharmony_ci * COOKIE_PREMAPPED: the request was mapped in pre_req, 1378c2ecf20Sopenharmony_ci * and should be unmapped in post_req. 1388c2ecf20Sopenharmony_ci * COOKIE_MAPPED: the request was mapped in the irq handler, 1398c2ecf20Sopenharmony_ci * and should be unmapped before mmc_request_done is called.. 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_cienum jz4780_cookie { 1428c2ecf20Sopenharmony_ci COOKIE_UNMAPPED = 0, 1438c2ecf20Sopenharmony_ci COOKIE_PREMAPPED, 1448c2ecf20Sopenharmony_ci COOKIE_MAPPED, 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistruct jz4740_mmc_host { 1488c2ecf20Sopenharmony_ci struct mmc_host *mmc; 1498c2ecf20Sopenharmony_ci struct platform_device *pdev; 1508c2ecf20Sopenharmony_ci struct clk *clk; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci enum jz4740_mmc_version version; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci int irq; 1558c2ecf20Sopenharmony_ci int card_detect_irq; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci void __iomem *base; 1588c2ecf20Sopenharmony_ci struct resource *mem_res; 1598c2ecf20Sopenharmony_ci struct mmc_request *req; 1608c2ecf20Sopenharmony_ci struct mmc_command *cmd; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci unsigned long waiting; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci uint32_t cmdat; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci uint32_t irq_mask; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci spinlock_t lock; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci struct timer_list timeout_timer; 1718c2ecf20Sopenharmony_ci struct sg_mapping_iter miter; 1728c2ecf20Sopenharmony_ci enum jz4740_mmc_state state; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* DMA support */ 1758c2ecf20Sopenharmony_ci struct dma_chan *dma_rx; 1768c2ecf20Sopenharmony_ci struct dma_chan *dma_tx; 1778c2ecf20Sopenharmony_ci bool use_dma; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* The DMA trigger level is 8 words, that is to say, the DMA read 1808c2ecf20Sopenharmony_ci * trigger is when data words in MSC_RXFIFO is >= 8 and the DMA write 1818c2ecf20Sopenharmony_ci * trigger is when data words in MSC_TXFIFO is < 8. 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_ci#define JZ4740_MMC_FIFO_HALF_SIZE 8 1848c2ecf20Sopenharmony_ci}; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic void jz4740_mmc_write_irq_mask(struct jz4740_mmc_host *host, 1878c2ecf20Sopenharmony_ci uint32_t val) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci if (host->version >= JZ_MMC_JZ4725B) 1908c2ecf20Sopenharmony_ci return writel(val, host->base + JZ_REG_MMC_IMASK); 1918c2ecf20Sopenharmony_ci else 1928c2ecf20Sopenharmony_ci return writew(val, host->base + JZ_REG_MMC_IMASK); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic void jz4740_mmc_write_irq_reg(struct jz4740_mmc_host *host, 1968c2ecf20Sopenharmony_ci uint32_t val) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci if (host->version >= JZ_MMC_JZ4780) 1998c2ecf20Sopenharmony_ci writel(val, host->base + JZ_REG_MMC_IREG); 2008c2ecf20Sopenharmony_ci else 2018c2ecf20Sopenharmony_ci writew(val, host->base + JZ_REG_MMC_IREG); 2028c2ecf20Sopenharmony_ci} 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic uint32_t jz4740_mmc_read_irq_reg(struct jz4740_mmc_host *host) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci if (host->version >= JZ_MMC_JZ4780) 2078c2ecf20Sopenharmony_ci return readl(host->base + JZ_REG_MMC_IREG); 2088c2ecf20Sopenharmony_ci else 2098c2ecf20Sopenharmony_ci return readw(host->base + JZ_REG_MMC_IREG); 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci/*----------------------------------------------------------------------------*/ 2138c2ecf20Sopenharmony_ci/* DMA infrastructure */ 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic void jz4740_mmc_release_dma_channels(struct jz4740_mmc_host *host) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci if (!host->use_dma) 2188c2ecf20Sopenharmony_ci return; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci dma_release_channel(host->dma_tx); 2218c2ecf20Sopenharmony_ci dma_release_channel(host->dma_rx); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci host->dma_tx = dma_request_chan(mmc_dev(host->mmc), "tx"); 2278c2ecf20Sopenharmony_ci if (IS_ERR(host->dma_tx)) { 2288c2ecf20Sopenharmony_ci dev_err(mmc_dev(host->mmc), "Failed to get dma_tx channel\n"); 2298c2ecf20Sopenharmony_ci return PTR_ERR(host->dma_tx); 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci host->dma_rx = dma_request_chan(mmc_dev(host->mmc), "rx"); 2338c2ecf20Sopenharmony_ci if (IS_ERR(host->dma_rx)) { 2348c2ecf20Sopenharmony_ci dev_err(mmc_dev(host->mmc), "Failed to get dma_rx channel\n"); 2358c2ecf20Sopenharmony_ci dma_release_channel(host->dma_tx); 2368c2ecf20Sopenharmony_ci return PTR_ERR(host->dma_rx); 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* 2408c2ecf20Sopenharmony_ci * Limit the maximum segment size in any SG entry according to 2418c2ecf20Sopenharmony_ci * the parameters of the DMA engine device. 2428c2ecf20Sopenharmony_ci */ 2438c2ecf20Sopenharmony_ci if (host->dma_tx) { 2448c2ecf20Sopenharmony_ci struct device *dev = host->dma_tx->device->dev; 2458c2ecf20Sopenharmony_ci unsigned int max_seg_size = dma_get_max_seg_size(dev); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci if (max_seg_size < host->mmc->max_seg_size) 2488c2ecf20Sopenharmony_ci host->mmc->max_seg_size = max_seg_size; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (host->dma_rx) { 2528c2ecf20Sopenharmony_ci struct device *dev = host->dma_rx->device->dev; 2538c2ecf20Sopenharmony_ci unsigned int max_seg_size = dma_get_max_seg_size(dev); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (max_seg_size < host->mmc->max_seg_size) 2568c2ecf20Sopenharmony_ci host->mmc->max_seg_size = max_seg_size; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic inline struct dma_chan *jz4740_mmc_get_dma_chan(struct jz4740_mmc_host *host, 2638c2ecf20Sopenharmony_ci struct mmc_data *data) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci return (data->flags & MMC_DATA_READ) ? host->dma_rx : host->dma_tx; 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic void jz4740_mmc_dma_unmap(struct jz4740_mmc_host *host, 2698c2ecf20Sopenharmony_ci struct mmc_data *data) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 2728c2ecf20Sopenharmony_ci enum dma_data_direction dir = mmc_get_dma_dir(data); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir); 2758c2ecf20Sopenharmony_ci data->host_cookie = COOKIE_UNMAPPED; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/* Prepares DMA data for current or next transfer. 2798c2ecf20Sopenharmony_ci * A request can be in-flight when this is called. 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_cistatic int jz4740_mmc_prepare_dma_data(struct jz4740_mmc_host *host, 2828c2ecf20Sopenharmony_ci struct mmc_data *data, 2838c2ecf20Sopenharmony_ci int cookie) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 2868c2ecf20Sopenharmony_ci enum dma_data_direction dir = mmc_get_dma_dir(data); 2878c2ecf20Sopenharmony_ci int sg_count; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (data->host_cookie == COOKIE_PREMAPPED) 2908c2ecf20Sopenharmony_ci return data->sg_count; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci sg_count = dma_map_sg(chan->device->dev, 2938c2ecf20Sopenharmony_ci data->sg, 2948c2ecf20Sopenharmony_ci data->sg_len, 2958c2ecf20Sopenharmony_ci dir); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci if (sg_count <= 0) { 2988c2ecf20Sopenharmony_ci dev_err(mmc_dev(host->mmc), 2998c2ecf20Sopenharmony_ci "Failed to map scatterlist for DMA operation\n"); 3008c2ecf20Sopenharmony_ci return -EINVAL; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci data->sg_count = sg_count; 3048c2ecf20Sopenharmony_ci data->host_cookie = cookie; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return data->sg_count; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host, 3108c2ecf20Sopenharmony_ci struct mmc_data *data) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 3138c2ecf20Sopenharmony_ci struct dma_async_tx_descriptor *desc; 3148c2ecf20Sopenharmony_ci struct dma_slave_config conf = { 3158c2ecf20Sopenharmony_ci .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 3168c2ecf20Sopenharmony_ci .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 3178c2ecf20Sopenharmony_ci .src_maxburst = JZ4740_MMC_FIFO_HALF_SIZE, 3188c2ecf20Sopenharmony_ci .dst_maxburst = JZ4740_MMC_FIFO_HALF_SIZE, 3198c2ecf20Sopenharmony_ci }; 3208c2ecf20Sopenharmony_ci int sg_count; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci if (data->flags & MMC_DATA_WRITE) { 3238c2ecf20Sopenharmony_ci conf.direction = DMA_MEM_TO_DEV; 3248c2ecf20Sopenharmony_ci conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO; 3258c2ecf20Sopenharmony_ci } else { 3268c2ecf20Sopenharmony_ci conf.direction = DMA_DEV_TO_MEM; 3278c2ecf20Sopenharmony_ci conf.src_addr = host->mem_res->start + JZ_REG_MMC_RXFIFO; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci sg_count = jz4740_mmc_prepare_dma_data(host, data, COOKIE_MAPPED); 3318c2ecf20Sopenharmony_ci if (sg_count < 0) 3328c2ecf20Sopenharmony_ci return sg_count; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci dmaengine_slave_config(chan, &conf); 3358c2ecf20Sopenharmony_ci desc = dmaengine_prep_slave_sg(chan, data->sg, sg_count, 3368c2ecf20Sopenharmony_ci conf.direction, 3378c2ecf20Sopenharmony_ci DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 3388c2ecf20Sopenharmony_ci if (!desc) { 3398c2ecf20Sopenharmony_ci dev_err(mmc_dev(host->mmc), 3408c2ecf20Sopenharmony_ci "Failed to allocate DMA %s descriptor", 3418c2ecf20Sopenharmony_ci conf.direction == DMA_MEM_TO_DEV ? "TX" : "RX"); 3428c2ecf20Sopenharmony_ci goto dma_unmap; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci dmaengine_submit(desc); 3468c2ecf20Sopenharmony_ci dma_async_issue_pending(chan); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cidma_unmap: 3518c2ecf20Sopenharmony_ci if (data->host_cookie == COOKIE_MAPPED) 3528c2ecf20Sopenharmony_ci jz4740_mmc_dma_unmap(host, data); 3538c2ecf20Sopenharmony_ci return -ENOMEM; 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic void jz4740_mmc_pre_request(struct mmc_host *mmc, 3578c2ecf20Sopenharmony_ci struct mmc_request *mrq) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci struct jz4740_mmc_host *host = mmc_priv(mmc); 3608c2ecf20Sopenharmony_ci struct mmc_data *data = mrq->data; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (!host->use_dma) 3638c2ecf20Sopenharmony_ci return; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci data->host_cookie = COOKIE_UNMAPPED; 3668c2ecf20Sopenharmony_ci if (jz4740_mmc_prepare_dma_data(host, data, COOKIE_PREMAPPED) < 0) 3678c2ecf20Sopenharmony_ci data->host_cookie = COOKIE_UNMAPPED; 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic void jz4740_mmc_post_request(struct mmc_host *mmc, 3718c2ecf20Sopenharmony_ci struct mmc_request *mrq, 3728c2ecf20Sopenharmony_ci int err) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct jz4740_mmc_host *host = mmc_priv(mmc); 3758c2ecf20Sopenharmony_ci struct mmc_data *data = mrq->data; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci if (data && data->host_cookie != COOKIE_UNMAPPED) 3788c2ecf20Sopenharmony_ci jz4740_mmc_dma_unmap(host, data); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (err) { 3818c2ecf20Sopenharmony_ci struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci dmaengine_terminate_all(chan); 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci/*----------------------------------------------------------------------------*/ 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cistatic void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host, 3908c2ecf20Sopenharmony_ci unsigned int irq, bool enabled) 3918c2ecf20Sopenharmony_ci{ 3928c2ecf20Sopenharmony_ci unsigned long flags; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci spin_lock_irqsave(&host->lock, flags); 3958c2ecf20Sopenharmony_ci if (enabled) 3968c2ecf20Sopenharmony_ci host->irq_mask &= ~irq; 3978c2ecf20Sopenharmony_ci else 3988c2ecf20Sopenharmony_ci host->irq_mask |= irq; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci jz4740_mmc_write_irq_mask(host, host->irq_mask); 4018c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&host->lock, flags); 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic void jz4740_mmc_clock_enable(struct jz4740_mmc_host *host, 4058c2ecf20Sopenharmony_ci bool start_transfer) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci uint16_t val = JZ_MMC_STRPCL_CLOCK_START; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (start_transfer) 4108c2ecf20Sopenharmony_ci val |= JZ_MMC_STRPCL_START_OP; 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci writew(val, host->base + JZ_REG_MMC_STRPCL); 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic void jz4740_mmc_clock_disable(struct jz4740_mmc_host *host) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci uint32_t status; 4188c2ecf20Sopenharmony_ci unsigned int timeout = 1000; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci writew(JZ_MMC_STRPCL_CLOCK_STOP, host->base + JZ_REG_MMC_STRPCL); 4218c2ecf20Sopenharmony_ci do { 4228c2ecf20Sopenharmony_ci status = readl(host->base + JZ_REG_MMC_STATUS); 4238c2ecf20Sopenharmony_ci } while (status & JZ_MMC_STATUS_CLK_EN && --timeout); 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic void jz4740_mmc_reset(struct jz4740_mmc_host *host) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci uint32_t status; 4298c2ecf20Sopenharmony_ci unsigned int timeout = 1000; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci writew(JZ_MMC_STRPCL_RESET, host->base + JZ_REG_MMC_STRPCL); 4328c2ecf20Sopenharmony_ci udelay(10); 4338c2ecf20Sopenharmony_ci do { 4348c2ecf20Sopenharmony_ci status = readl(host->base + JZ_REG_MMC_STATUS); 4358c2ecf20Sopenharmony_ci } while (status & JZ_MMC_STATUS_IS_RESETTING && --timeout); 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic void jz4740_mmc_request_done(struct jz4740_mmc_host *host) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci struct mmc_request *req; 4418c2ecf20Sopenharmony_ci struct mmc_data *data; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci req = host->req; 4448c2ecf20Sopenharmony_ci data = req->data; 4458c2ecf20Sopenharmony_ci host->req = NULL; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (data && data->host_cookie == COOKIE_MAPPED) 4488c2ecf20Sopenharmony_ci jz4740_mmc_dma_unmap(host, data); 4498c2ecf20Sopenharmony_ci mmc_request_done(host->mmc, req); 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host, 4538c2ecf20Sopenharmony_ci unsigned int irq) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci unsigned int timeout = 0x800; 4568c2ecf20Sopenharmony_ci uint32_t status; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci do { 4598c2ecf20Sopenharmony_ci status = jz4740_mmc_read_irq_reg(host); 4608c2ecf20Sopenharmony_ci } while (!(status & irq) && --timeout); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (timeout == 0) { 4638c2ecf20Sopenharmony_ci set_bit(0, &host->waiting); 4648c2ecf20Sopenharmony_ci mod_timer(&host->timeout_timer, 4658c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(JZ_MMC_REQ_TIMEOUT_MS)); 4668c2ecf20Sopenharmony_ci jz4740_mmc_set_irq_enabled(host, irq, true); 4678c2ecf20Sopenharmony_ci return true; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return false; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic void jz4740_mmc_transfer_check_state(struct jz4740_mmc_host *host, 4748c2ecf20Sopenharmony_ci struct mmc_data *data) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci int status; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci status = readl(host->base + JZ_REG_MMC_STATUS); 4798c2ecf20Sopenharmony_ci if (status & JZ_MMC_STATUS_WRITE_ERROR_MASK) { 4808c2ecf20Sopenharmony_ci if (status & (JZ_MMC_STATUS_TIMEOUT_WRITE)) { 4818c2ecf20Sopenharmony_ci host->req->cmd->error = -ETIMEDOUT; 4828c2ecf20Sopenharmony_ci data->error = -ETIMEDOUT; 4838c2ecf20Sopenharmony_ci } else { 4848c2ecf20Sopenharmony_ci host->req->cmd->error = -EIO; 4858c2ecf20Sopenharmony_ci data->error = -EIO; 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci } else if (status & JZ_MMC_STATUS_READ_ERROR_MASK) { 4888c2ecf20Sopenharmony_ci if (status & (JZ_MMC_STATUS_TIMEOUT_READ)) { 4898c2ecf20Sopenharmony_ci host->req->cmd->error = -ETIMEDOUT; 4908c2ecf20Sopenharmony_ci data->error = -ETIMEDOUT; 4918c2ecf20Sopenharmony_ci } else { 4928c2ecf20Sopenharmony_ci host->req->cmd->error = -EIO; 4938c2ecf20Sopenharmony_ci data->error = -EIO; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cistatic bool jz4740_mmc_write_data(struct jz4740_mmc_host *host, 4998c2ecf20Sopenharmony_ci struct mmc_data *data) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci struct sg_mapping_iter *miter = &host->miter; 5028c2ecf20Sopenharmony_ci void __iomem *fifo_addr = host->base + JZ_REG_MMC_TXFIFO; 5038c2ecf20Sopenharmony_ci uint32_t *buf; 5048c2ecf20Sopenharmony_ci bool timeout; 5058c2ecf20Sopenharmony_ci size_t i, j; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci while (sg_miter_next(miter)) { 5088c2ecf20Sopenharmony_ci buf = miter->addr; 5098c2ecf20Sopenharmony_ci i = miter->length / 4; 5108c2ecf20Sopenharmony_ci j = i / 8; 5118c2ecf20Sopenharmony_ci i = i & 0x7; 5128c2ecf20Sopenharmony_ci while (j) { 5138c2ecf20Sopenharmony_ci timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); 5148c2ecf20Sopenharmony_ci if (unlikely(timeout)) 5158c2ecf20Sopenharmony_ci goto poll_timeout; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci writel(buf[0], fifo_addr); 5188c2ecf20Sopenharmony_ci writel(buf[1], fifo_addr); 5198c2ecf20Sopenharmony_ci writel(buf[2], fifo_addr); 5208c2ecf20Sopenharmony_ci writel(buf[3], fifo_addr); 5218c2ecf20Sopenharmony_ci writel(buf[4], fifo_addr); 5228c2ecf20Sopenharmony_ci writel(buf[5], fifo_addr); 5238c2ecf20Sopenharmony_ci writel(buf[6], fifo_addr); 5248c2ecf20Sopenharmony_ci writel(buf[7], fifo_addr); 5258c2ecf20Sopenharmony_ci buf += 8; 5268c2ecf20Sopenharmony_ci --j; 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci if (unlikely(i)) { 5298c2ecf20Sopenharmony_ci timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); 5308c2ecf20Sopenharmony_ci if (unlikely(timeout)) 5318c2ecf20Sopenharmony_ci goto poll_timeout; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci while (i) { 5348c2ecf20Sopenharmony_ci writel(*buf, fifo_addr); 5358c2ecf20Sopenharmony_ci ++buf; 5368c2ecf20Sopenharmony_ci --i; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci data->bytes_xfered += miter->length; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci sg_miter_stop(miter); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci return false; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cipoll_timeout: 5468c2ecf20Sopenharmony_ci miter->consumed = (void *)buf - miter->addr; 5478c2ecf20Sopenharmony_ci data->bytes_xfered += miter->consumed; 5488c2ecf20Sopenharmony_ci sg_miter_stop(miter); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci return true; 5518c2ecf20Sopenharmony_ci} 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic bool jz4740_mmc_read_data(struct jz4740_mmc_host *host, 5548c2ecf20Sopenharmony_ci struct mmc_data *data) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct sg_mapping_iter *miter = &host->miter; 5578c2ecf20Sopenharmony_ci void __iomem *fifo_addr = host->base + JZ_REG_MMC_RXFIFO; 5588c2ecf20Sopenharmony_ci uint32_t *buf; 5598c2ecf20Sopenharmony_ci uint32_t d; 5608c2ecf20Sopenharmony_ci uint32_t status; 5618c2ecf20Sopenharmony_ci size_t i, j; 5628c2ecf20Sopenharmony_ci unsigned int timeout; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci while (sg_miter_next(miter)) { 5658c2ecf20Sopenharmony_ci buf = miter->addr; 5668c2ecf20Sopenharmony_ci i = miter->length; 5678c2ecf20Sopenharmony_ci j = i / 32; 5688c2ecf20Sopenharmony_ci i = i & 0x1f; 5698c2ecf20Sopenharmony_ci while (j) { 5708c2ecf20Sopenharmony_ci timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); 5718c2ecf20Sopenharmony_ci if (unlikely(timeout)) 5728c2ecf20Sopenharmony_ci goto poll_timeout; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci buf[0] = readl(fifo_addr); 5758c2ecf20Sopenharmony_ci buf[1] = readl(fifo_addr); 5768c2ecf20Sopenharmony_ci buf[2] = readl(fifo_addr); 5778c2ecf20Sopenharmony_ci buf[3] = readl(fifo_addr); 5788c2ecf20Sopenharmony_ci buf[4] = readl(fifo_addr); 5798c2ecf20Sopenharmony_ci buf[5] = readl(fifo_addr); 5808c2ecf20Sopenharmony_ci buf[6] = readl(fifo_addr); 5818c2ecf20Sopenharmony_ci buf[7] = readl(fifo_addr); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci buf += 8; 5848c2ecf20Sopenharmony_ci --j; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if (unlikely(i)) { 5888c2ecf20Sopenharmony_ci timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); 5898c2ecf20Sopenharmony_ci if (unlikely(timeout)) 5908c2ecf20Sopenharmony_ci goto poll_timeout; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci while (i >= 4) { 5938c2ecf20Sopenharmony_ci *buf++ = readl(fifo_addr); 5948c2ecf20Sopenharmony_ci i -= 4; 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci if (unlikely(i > 0)) { 5978c2ecf20Sopenharmony_ci d = readl(fifo_addr); 5988c2ecf20Sopenharmony_ci memcpy(buf, &d, i); 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci data->bytes_xfered += miter->length; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci /* This can go away once MIPS implements 6048c2ecf20Sopenharmony_ci * flush_kernel_dcache_page */ 6058c2ecf20Sopenharmony_ci flush_dcache_page(miter->page); 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci sg_miter_stop(miter); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci /* For whatever reason there is sometime one word more in the fifo then 6108c2ecf20Sopenharmony_ci * requested */ 6118c2ecf20Sopenharmony_ci timeout = 1000; 6128c2ecf20Sopenharmony_ci status = readl(host->base + JZ_REG_MMC_STATUS); 6138c2ecf20Sopenharmony_ci while (!(status & JZ_MMC_STATUS_DATA_FIFO_EMPTY) && --timeout) { 6148c2ecf20Sopenharmony_ci d = readl(fifo_addr); 6158c2ecf20Sopenharmony_ci status = readl(host->base + JZ_REG_MMC_STATUS); 6168c2ecf20Sopenharmony_ci } 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci return false; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cipoll_timeout: 6218c2ecf20Sopenharmony_ci miter->consumed = (void *)buf - miter->addr; 6228c2ecf20Sopenharmony_ci data->bytes_xfered += miter->consumed; 6238c2ecf20Sopenharmony_ci sg_miter_stop(miter); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci return true; 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_cistatic void jz4740_mmc_timeout(struct timer_list *t) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci struct jz4740_mmc_host *host = from_timer(host, t, timeout_timer); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (!test_and_clear_bit(0, &host->waiting)) 6338c2ecf20Sopenharmony_ci return; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, false); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci host->req->cmd->error = -ETIMEDOUT; 6388c2ecf20Sopenharmony_ci jz4740_mmc_request_done(host); 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cistatic void jz4740_mmc_read_response(struct jz4740_mmc_host *host, 6428c2ecf20Sopenharmony_ci struct mmc_command *cmd) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci int i; 6458c2ecf20Sopenharmony_ci uint16_t tmp; 6468c2ecf20Sopenharmony_ci void __iomem *fifo_addr = host->base + JZ_REG_MMC_RESP_FIFO; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci if (cmd->flags & MMC_RSP_136) { 6498c2ecf20Sopenharmony_ci tmp = readw(fifo_addr); 6508c2ecf20Sopenharmony_ci for (i = 0; i < 4; ++i) { 6518c2ecf20Sopenharmony_ci cmd->resp[i] = tmp << 24; 6528c2ecf20Sopenharmony_ci tmp = readw(fifo_addr); 6538c2ecf20Sopenharmony_ci cmd->resp[i] |= tmp << 8; 6548c2ecf20Sopenharmony_ci tmp = readw(fifo_addr); 6558c2ecf20Sopenharmony_ci cmd->resp[i] |= tmp >> 8; 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci } else { 6588c2ecf20Sopenharmony_ci cmd->resp[0] = readw(fifo_addr) << 24; 6598c2ecf20Sopenharmony_ci cmd->resp[0] |= readw(fifo_addr) << 8; 6608c2ecf20Sopenharmony_ci cmd->resp[0] |= readw(fifo_addr) & 0xff; 6618c2ecf20Sopenharmony_ci } 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic void jz4740_mmc_send_command(struct jz4740_mmc_host *host, 6658c2ecf20Sopenharmony_ci struct mmc_command *cmd) 6668c2ecf20Sopenharmony_ci{ 6678c2ecf20Sopenharmony_ci uint32_t cmdat = host->cmdat; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci host->cmdat &= ~JZ_MMC_CMDAT_INIT; 6708c2ecf20Sopenharmony_ci jz4740_mmc_clock_disable(host); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci host->cmd = cmd; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci if (cmd->flags & MMC_RSP_BUSY) 6758c2ecf20Sopenharmony_ci cmdat |= JZ_MMC_CMDAT_BUSY; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci switch (mmc_resp_type(cmd)) { 6788c2ecf20Sopenharmony_ci case MMC_RSP_R1B: 6798c2ecf20Sopenharmony_ci case MMC_RSP_R1: 6808c2ecf20Sopenharmony_ci cmdat |= JZ_MMC_CMDAT_RSP_R1; 6818c2ecf20Sopenharmony_ci break; 6828c2ecf20Sopenharmony_ci case MMC_RSP_R2: 6838c2ecf20Sopenharmony_ci cmdat |= JZ_MMC_CMDAT_RSP_R2; 6848c2ecf20Sopenharmony_ci break; 6858c2ecf20Sopenharmony_ci case MMC_RSP_R3: 6868c2ecf20Sopenharmony_ci cmdat |= JZ_MMC_CMDAT_RSP_R3; 6878c2ecf20Sopenharmony_ci break; 6888c2ecf20Sopenharmony_ci default: 6898c2ecf20Sopenharmony_ci break; 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci if (cmd->data) { 6938c2ecf20Sopenharmony_ci cmdat |= JZ_MMC_CMDAT_DATA_EN; 6948c2ecf20Sopenharmony_ci if (cmd->data->flags & MMC_DATA_WRITE) 6958c2ecf20Sopenharmony_ci cmdat |= JZ_MMC_CMDAT_WRITE; 6968c2ecf20Sopenharmony_ci if (host->use_dma) { 6978c2ecf20Sopenharmony_ci /* 6988c2ecf20Sopenharmony_ci * The 4780's MMC controller has integrated DMA ability 6998c2ecf20Sopenharmony_ci * in addition to being able to use the external DMA 7008c2ecf20Sopenharmony_ci * controller. It moves DMA control bits to a separate 7018c2ecf20Sopenharmony_ci * register. The DMA_SEL bit chooses the external 7028c2ecf20Sopenharmony_ci * controller over the integrated one. Earlier SoCs 7038c2ecf20Sopenharmony_ci * can only use the external controller, and have a 7048c2ecf20Sopenharmony_ci * single DMA enable bit in CMDAT. 7058c2ecf20Sopenharmony_ci */ 7068c2ecf20Sopenharmony_ci if (host->version >= JZ_MMC_JZ4780) { 7078c2ecf20Sopenharmony_ci writel(JZ_MMC_DMAC_DMA_EN | JZ_MMC_DMAC_DMA_SEL, 7088c2ecf20Sopenharmony_ci host->base + JZ_REG_MMC_DMAC); 7098c2ecf20Sopenharmony_ci } else { 7108c2ecf20Sopenharmony_ci cmdat |= JZ_MMC_CMDAT_DMA_EN; 7118c2ecf20Sopenharmony_ci } 7128c2ecf20Sopenharmony_ci } else if (host->version >= JZ_MMC_JZ4780) { 7138c2ecf20Sopenharmony_ci writel(0, host->base + JZ_REG_MMC_DMAC); 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN); 7178c2ecf20Sopenharmony_ci writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB); 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci writeb(cmd->opcode, host->base + JZ_REG_MMC_CMD); 7218c2ecf20Sopenharmony_ci writel(cmd->arg, host->base + JZ_REG_MMC_ARG); 7228c2ecf20Sopenharmony_ci writel(cmdat, host->base + JZ_REG_MMC_CMDAT); 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci jz4740_mmc_clock_enable(host, 1); 7258c2ecf20Sopenharmony_ci} 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_cistatic void jz_mmc_prepare_data_transfer(struct jz4740_mmc_host *host) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci struct mmc_command *cmd = host->req->cmd; 7308c2ecf20Sopenharmony_ci struct mmc_data *data = cmd->data; 7318c2ecf20Sopenharmony_ci int direction; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci if (data->flags & MMC_DATA_READ) 7348c2ecf20Sopenharmony_ci direction = SG_MITER_TO_SG; 7358c2ecf20Sopenharmony_ci else 7368c2ecf20Sopenharmony_ci direction = SG_MITER_FROM_SG; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci sg_miter_start(&host->miter, data->sg, data->sg_len, direction); 7398c2ecf20Sopenharmony_ci} 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_cistatic irqreturn_t jz_mmc_irq_worker(int irq, void *devid) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)devid; 7458c2ecf20Sopenharmony_ci struct mmc_command *cmd = host->req->cmd; 7468c2ecf20Sopenharmony_ci struct mmc_request *req = host->req; 7478c2ecf20Sopenharmony_ci struct mmc_data *data = cmd->data; 7488c2ecf20Sopenharmony_ci bool timeout = false; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci if (cmd->error) 7518c2ecf20Sopenharmony_ci host->state = JZ4740_MMC_STATE_DONE; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci switch (host->state) { 7548c2ecf20Sopenharmony_ci case JZ4740_MMC_STATE_READ_RESPONSE: 7558c2ecf20Sopenharmony_ci if (cmd->flags & MMC_RSP_PRESENT) 7568c2ecf20Sopenharmony_ci jz4740_mmc_read_response(host, cmd); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (!data) 7598c2ecf20Sopenharmony_ci break; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci jz_mmc_prepare_data_transfer(host); 7628c2ecf20Sopenharmony_ci fallthrough; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci case JZ4740_MMC_STATE_TRANSFER_DATA: 7658c2ecf20Sopenharmony_ci if (host->use_dma) { 7668c2ecf20Sopenharmony_ci /* Use DMA if enabled. 7678c2ecf20Sopenharmony_ci * Data transfer direction is defined later by 7688c2ecf20Sopenharmony_ci * relying on data flags in 7698c2ecf20Sopenharmony_ci * jz4740_mmc_prepare_dma_data() and 7708c2ecf20Sopenharmony_ci * jz4740_mmc_start_dma_transfer(). 7718c2ecf20Sopenharmony_ci */ 7728c2ecf20Sopenharmony_ci timeout = jz4740_mmc_start_dma_transfer(host, data); 7738c2ecf20Sopenharmony_ci data->bytes_xfered = data->blocks * data->blksz; 7748c2ecf20Sopenharmony_ci } else if (data->flags & MMC_DATA_READ) 7758c2ecf20Sopenharmony_ci /* Use PIO if DMA is not enabled. 7768c2ecf20Sopenharmony_ci * Data transfer direction was defined before 7778c2ecf20Sopenharmony_ci * by relying on data flags in 7788c2ecf20Sopenharmony_ci * jz_mmc_prepare_data_transfer(). 7798c2ecf20Sopenharmony_ci */ 7808c2ecf20Sopenharmony_ci timeout = jz4740_mmc_read_data(host, data); 7818c2ecf20Sopenharmony_ci else 7828c2ecf20Sopenharmony_ci timeout = jz4740_mmc_write_data(host, data); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci if (unlikely(timeout)) { 7858c2ecf20Sopenharmony_ci host->state = JZ4740_MMC_STATE_TRANSFER_DATA; 7868c2ecf20Sopenharmony_ci break; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci jz4740_mmc_transfer_check_state(host, data); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_DATA_TRAN_DONE); 7928c2ecf20Sopenharmony_ci if (unlikely(timeout)) { 7938c2ecf20Sopenharmony_ci host->state = JZ4740_MMC_STATE_SEND_STOP; 7948c2ecf20Sopenharmony_ci break; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_DATA_TRAN_DONE); 7978c2ecf20Sopenharmony_ci fallthrough; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci case JZ4740_MMC_STATE_SEND_STOP: 8008c2ecf20Sopenharmony_ci if (!req->stop) 8018c2ecf20Sopenharmony_ci break; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci jz4740_mmc_send_command(host, req->stop); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if (mmc_resp_type(req->stop) & MMC_RSP_BUSY) { 8068c2ecf20Sopenharmony_ci timeout = jz4740_mmc_poll_irq(host, 8078c2ecf20Sopenharmony_ci JZ_MMC_IRQ_PRG_DONE); 8088c2ecf20Sopenharmony_ci if (timeout) { 8098c2ecf20Sopenharmony_ci host->state = JZ4740_MMC_STATE_DONE; 8108c2ecf20Sopenharmony_ci break; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci case JZ4740_MMC_STATE_DONE: 8148c2ecf20Sopenharmony_ci break; 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci if (!timeout) 8188c2ecf20Sopenharmony_ci jz4740_mmc_request_done(host); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci return IRQ_HANDLED; 8218c2ecf20Sopenharmony_ci} 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_cistatic irqreturn_t jz_mmc_irq(int irq, void *devid) 8248c2ecf20Sopenharmony_ci{ 8258c2ecf20Sopenharmony_ci struct jz4740_mmc_host *host = devid; 8268c2ecf20Sopenharmony_ci struct mmc_command *cmd = host->cmd; 8278c2ecf20Sopenharmony_ci uint32_t irq_reg, status, tmp; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci status = readl(host->base + JZ_REG_MMC_STATUS); 8308c2ecf20Sopenharmony_ci irq_reg = jz4740_mmc_read_irq_reg(host); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci tmp = irq_reg; 8338c2ecf20Sopenharmony_ci irq_reg &= ~host->irq_mask; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci tmp &= ~(JZ_MMC_IRQ_TXFIFO_WR_REQ | JZ_MMC_IRQ_RXFIFO_RD_REQ | 8368c2ecf20Sopenharmony_ci JZ_MMC_IRQ_PRG_DONE | JZ_MMC_IRQ_DATA_TRAN_DONE); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci if (tmp != irq_reg) 8398c2ecf20Sopenharmony_ci jz4740_mmc_write_irq_reg(host, tmp & ~irq_reg); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci if (irq_reg & JZ_MMC_IRQ_SDIO) { 8428c2ecf20Sopenharmony_ci jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_SDIO); 8438c2ecf20Sopenharmony_ci mmc_signal_sdio_irq(host->mmc); 8448c2ecf20Sopenharmony_ci irq_reg &= ~JZ_MMC_IRQ_SDIO; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci if (host->req && cmd && irq_reg) { 8488c2ecf20Sopenharmony_ci if (test_and_clear_bit(0, &host->waiting)) { 8498c2ecf20Sopenharmony_ci del_timer(&host->timeout_timer); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci if (status & JZ_MMC_STATUS_TIMEOUT_RES) { 8528c2ecf20Sopenharmony_ci cmd->error = -ETIMEDOUT; 8538c2ecf20Sopenharmony_ci } else if (status & JZ_MMC_STATUS_CRC_RES_ERR) { 8548c2ecf20Sopenharmony_ci cmd->error = -EIO; 8558c2ecf20Sopenharmony_ci } else if (status & (JZ_MMC_STATUS_CRC_READ_ERROR | 8568c2ecf20Sopenharmony_ci JZ_MMC_STATUS_CRC_WRITE_ERROR)) { 8578c2ecf20Sopenharmony_ci if (cmd->data) 8588c2ecf20Sopenharmony_ci cmd->data->error = -EIO; 8598c2ecf20Sopenharmony_ci cmd->error = -EIO; 8608c2ecf20Sopenharmony_ci } 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci jz4740_mmc_set_irq_enabled(host, irq_reg, false); 8638c2ecf20Sopenharmony_ci jz4740_mmc_write_irq_reg(host, irq_reg); 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci return IRQ_HANDLED; 8708c2ecf20Sopenharmony_ci} 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_cistatic int jz4740_mmc_set_clock_rate(struct jz4740_mmc_host *host, int rate) 8738c2ecf20Sopenharmony_ci{ 8748c2ecf20Sopenharmony_ci int div = 0; 8758c2ecf20Sopenharmony_ci int real_rate; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci jz4740_mmc_clock_disable(host); 8788c2ecf20Sopenharmony_ci clk_set_rate(host->clk, host->mmc->f_max); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci real_rate = clk_get_rate(host->clk); 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci while (real_rate > rate && div < 7) { 8838c2ecf20Sopenharmony_ci ++div; 8848c2ecf20Sopenharmony_ci real_rate >>= 1; 8858c2ecf20Sopenharmony_ci } 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci writew(div, host->base + JZ_REG_MMC_CLKRT); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci if (real_rate > 25000000) { 8908c2ecf20Sopenharmony_ci if (host->version >= JZ_MMC_X1000) { 8918c2ecf20Sopenharmony_ci writel(JZ_MMC_LPM_DRV_RISING_QTR_PHASE_DLY | 8928c2ecf20Sopenharmony_ci JZ_MMC_LPM_SMP_RISING_QTR_OR_HALF_PHASE_DLY | 8938c2ecf20Sopenharmony_ci JZ_MMC_LPM_LOW_POWER_MODE_EN, 8948c2ecf20Sopenharmony_ci host->base + JZ_REG_MMC_LPM); 8958c2ecf20Sopenharmony_ci } else if (host->version >= JZ_MMC_JZ4760) { 8968c2ecf20Sopenharmony_ci writel(JZ_MMC_LPM_DRV_RISING | 8978c2ecf20Sopenharmony_ci JZ_MMC_LPM_LOW_POWER_MODE_EN, 8988c2ecf20Sopenharmony_ci host->base + JZ_REG_MMC_LPM); 8998c2ecf20Sopenharmony_ci } else if (host->version >= JZ_MMC_JZ4725B) 9008c2ecf20Sopenharmony_ci writel(JZ_MMC_LPM_LOW_POWER_MODE_EN, 9018c2ecf20Sopenharmony_ci host->base + JZ_REG_MMC_LPM); 9028c2ecf20Sopenharmony_ci } 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci return real_rate; 9058c2ecf20Sopenharmony_ci} 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_cistatic void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci struct jz4740_mmc_host *host = mmc_priv(mmc); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci host->req = req; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci jz4740_mmc_write_irq_reg(host, ~0); 9148c2ecf20Sopenharmony_ci jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, true); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci host->state = JZ4740_MMC_STATE_READ_RESPONSE; 9178c2ecf20Sopenharmony_ci set_bit(0, &host->waiting); 9188c2ecf20Sopenharmony_ci mod_timer(&host->timeout_timer, 9198c2ecf20Sopenharmony_ci jiffies + msecs_to_jiffies(JZ_MMC_REQ_TIMEOUT_MS)); 9208c2ecf20Sopenharmony_ci jz4740_mmc_send_command(host, req->cmd); 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_cistatic void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 9248c2ecf20Sopenharmony_ci{ 9258c2ecf20Sopenharmony_ci struct jz4740_mmc_host *host = mmc_priv(mmc); 9268c2ecf20Sopenharmony_ci if (ios->clock) 9278c2ecf20Sopenharmony_ci jz4740_mmc_set_clock_rate(host, ios->clock); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci switch (ios->power_mode) { 9308c2ecf20Sopenharmony_ci case MMC_POWER_UP: 9318c2ecf20Sopenharmony_ci jz4740_mmc_reset(host); 9328c2ecf20Sopenharmony_ci if (!IS_ERR(mmc->supply.vmmc)) 9338c2ecf20Sopenharmony_ci mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd); 9348c2ecf20Sopenharmony_ci host->cmdat |= JZ_MMC_CMDAT_INIT; 9358c2ecf20Sopenharmony_ci clk_prepare_enable(host->clk); 9368c2ecf20Sopenharmony_ci break; 9378c2ecf20Sopenharmony_ci case MMC_POWER_ON: 9388c2ecf20Sopenharmony_ci break; 9398c2ecf20Sopenharmony_ci default: 9408c2ecf20Sopenharmony_ci if (!IS_ERR(mmc->supply.vmmc)) 9418c2ecf20Sopenharmony_ci mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); 9428c2ecf20Sopenharmony_ci clk_disable_unprepare(host->clk); 9438c2ecf20Sopenharmony_ci break; 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci switch (ios->bus_width) { 9478c2ecf20Sopenharmony_ci case MMC_BUS_WIDTH_1: 9488c2ecf20Sopenharmony_ci host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_MASK; 9498c2ecf20Sopenharmony_ci break; 9508c2ecf20Sopenharmony_ci case MMC_BUS_WIDTH_4: 9518c2ecf20Sopenharmony_ci host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_MASK; 9528c2ecf20Sopenharmony_ci host->cmdat |= JZ_MMC_CMDAT_BUS_WIDTH_4BIT; 9538c2ecf20Sopenharmony_ci break; 9548c2ecf20Sopenharmony_ci case MMC_BUS_WIDTH_8: 9558c2ecf20Sopenharmony_ci host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_MASK; 9568c2ecf20Sopenharmony_ci host->cmdat |= JZ_MMC_CMDAT_BUS_WIDTH_8BIT; 9578c2ecf20Sopenharmony_ci break; 9588c2ecf20Sopenharmony_ci default: 9598c2ecf20Sopenharmony_ci break; 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci} 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_cistatic void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) 9648c2ecf20Sopenharmony_ci{ 9658c2ecf20Sopenharmony_ci struct jz4740_mmc_host *host = mmc_priv(mmc); 9668c2ecf20Sopenharmony_ci jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_SDIO, enable); 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_cistatic const struct mmc_host_ops jz4740_mmc_ops = { 9708c2ecf20Sopenharmony_ci .request = jz4740_mmc_request, 9718c2ecf20Sopenharmony_ci .pre_req = jz4740_mmc_pre_request, 9728c2ecf20Sopenharmony_ci .post_req = jz4740_mmc_post_request, 9738c2ecf20Sopenharmony_ci .set_ios = jz4740_mmc_set_ios, 9748c2ecf20Sopenharmony_ci .get_ro = mmc_gpio_get_ro, 9758c2ecf20Sopenharmony_ci .get_cd = mmc_gpio_get_cd, 9768c2ecf20Sopenharmony_ci .enable_sdio_irq = jz4740_mmc_enable_sdio_irq, 9778c2ecf20Sopenharmony_ci}; 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_cistatic const struct of_device_id jz4740_mmc_of_match[] = { 9808c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4740-mmc", .data = (void *) JZ_MMC_JZ4740 }, 9818c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4725b-mmc", .data = (void *)JZ_MMC_JZ4725B }, 9828c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4760-mmc", .data = (void *) JZ_MMC_JZ4760 }, 9838c2ecf20Sopenharmony_ci { .compatible = "ingenic,jz4780-mmc", .data = (void *) JZ_MMC_JZ4780 }, 9848c2ecf20Sopenharmony_ci { .compatible = "ingenic,x1000-mmc", .data = (void *) JZ_MMC_X1000 }, 9858c2ecf20Sopenharmony_ci {}, 9868c2ecf20Sopenharmony_ci}; 9878c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, jz4740_mmc_of_match); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_cistatic int jz4740_mmc_probe(struct platform_device* pdev) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci int ret; 9928c2ecf20Sopenharmony_ci struct mmc_host *mmc; 9938c2ecf20Sopenharmony_ci struct jz4740_mmc_host *host; 9948c2ecf20Sopenharmony_ci const struct of_device_id *match; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev); 9978c2ecf20Sopenharmony_ci if (!mmc) { 9988c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to alloc mmc host structure\n"); 9998c2ecf20Sopenharmony_ci return -ENOMEM; 10008c2ecf20Sopenharmony_ci } 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci host = mmc_priv(mmc); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci match = of_match_device(jz4740_mmc_of_match, &pdev->dev); 10058c2ecf20Sopenharmony_ci if (match) { 10068c2ecf20Sopenharmony_ci host->version = (enum jz4740_mmc_version)match->data; 10078c2ecf20Sopenharmony_ci } else { 10088c2ecf20Sopenharmony_ci /* JZ4740 should be the only one using legacy probe */ 10098c2ecf20Sopenharmony_ci host->version = JZ_MMC_JZ4740; 10108c2ecf20Sopenharmony_ci } 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci ret = mmc_of_parse(mmc); 10138c2ecf20Sopenharmony_ci if (ret) { 10148c2ecf20Sopenharmony_ci dev_err_probe(&pdev->dev, ret, "could not parse device properties\n"); 10158c2ecf20Sopenharmony_ci goto err_free_host; 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci mmc_regulator_get_supply(mmc); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci host->irq = platform_get_irq(pdev, 0); 10218c2ecf20Sopenharmony_ci if (host->irq < 0) { 10228c2ecf20Sopenharmony_ci ret = host->irq; 10238c2ecf20Sopenharmony_ci goto err_free_host; 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci host->clk = devm_clk_get(&pdev->dev, "mmc"); 10278c2ecf20Sopenharmony_ci if (IS_ERR(host->clk)) { 10288c2ecf20Sopenharmony_ci ret = PTR_ERR(host->clk); 10298c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to get mmc clock\n"); 10308c2ecf20Sopenharmony_ci goto err_free_host; 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci host->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 10348c2ecf20Sopenharmony_ci host->base = devm_ioremap_resource(&pdev->dev, host->mem_res); 10358c2ecf20Sopenharmony_ci if (IS_ERR(host->base)) { 10368c2ecf20Sopenharmony_ci ret = PTR_ERR(host->base); 10378c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to ioremap base memory\n"); 10388c2ecf20Sopenharmony_ci goto err_free_host; 10398c2ecf20Sopenharmony_ci } 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci mmc->ops = &jz4740_mmc_ops; 10428c2ecf20Sopenharmony_ci if (!mmc->f_max) 10438c2ecf20Sopenharmony_ci mmc->f_max = JZ_MMC_CLK_RATE; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci /* 10468c2ecf20Sopenharmony_ci * There seems to be a problem with this driver on the JZ4760 and 10478c2ecf20Sopenharmony_ci * JZ4760B SoCs. There, when using the maximum rate supported (50 MHz), 10488c2ecf20Sopenharmony_ci * the communication fails with many SD cards. 10498c2ecf20Sopenharmony_ci * Until this bug is sorted out, limit the maximum rate to 24 MHz. 10508c2ecf20Sopenharmony_ci */ 10518c2ecf20Sopenharmony_ci if (host->version == JZ_MMC_JZ4760 && mmc->f_max > JZ_MMC_CLK_RATE) 10528c2ecf20Sopenharmony_ci mmc->f_max = JZ_MMC_CLK_RATE; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci mmc->f_min = mmc->f_max / 128; 10558c2ecf20Sopenharmony_ci mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci /* 10588c2ecf20Sopenharmony_ci * We use a fixed timeout of 5s, hence inform the core about it. A 10598c2ecf20Sopenharmony_ci * future improvement should instead respect the cmd->busy_timeout. 10608c2ecf20Sopenharmony_ci */ 10618c2ecf20Sopenharmony_ci mmc->max_busy_timeout = JZ_MMC_REQ_TIMEOUT_MS; 10628c2ecf20Sopenharmony_ci 10638c2ecf20Sopenharmony_ci mmc->max_blk_size = (1 << 10) - 1; 10648c2ecf20Sopenharmony_ci mmc->max_blk_count = (1 << 15) - 1; 10658c2ecf20Sopenharmony_ci mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci mmc->max_segs = 128; 10688c2ecf20Sopenharmony_ci mmc->max_seg_size = mmc->max_req_size; 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci host->mmc = mmc; 10718c2ecf20Sopenharmony_ci host->pdev = pdev; 10728c2ecf20Sopenharmony_ci spin_lock_init(&host->lock); 10738c2ecf20Sopenharmony_ci host->irq_mask = ~0; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci jz4740_mmc_reset(host); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0, 10788c2ecf20Sopenharmony_ci dev_name(&pdev->dev), host); 10798c2ecf20Sopenharmony_ci if (ret) { 10808c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); 10818c2ecf20Sopenharmony_ci goto err_free_host; 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci jz4740_mmc_clock_disable(host); 10858c2ecf20Sopenharmony_ci timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0); 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci ret = jz4740_mmc_acquire_dma_channels(host); 10888c2ecf20Sopenharmony_ci if (ret == -EPROBE_DEFER) 10898c2ecf20Sopenharmony_ci goto err_free_irq; 10908c2ecf20Sopenharmony_ci host->use_dma = !ret; 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, host); 10938c2ecf20Sopenharmony_ci ret = mmc_add_host(mmc); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci if (ret) { 10968c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret); 10978c2ecf20Sopenharmony_ci goto err_release_dma; 10988c2ecf20Sopenharmony_ci } 10998c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Ingenic SD/MMC card driver registered\n"); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Using %s, %d-bit mode\n", 11028c2ecf20Sopenharmony_ci host->use_dma ? "DMA" : "PIO", 11038c2ecf20Sopenharmony_ci (mmc->caps & MMC_CAP_8_BIT_DATA) ? 8 : 11048c2ecf20Sopenharmony_ci ((mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1)); 11058c2ecf20Sopenharmony_ci 11068c2ecf20Sopenharmony_ci return 0; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_cierr_release_dma: 11098c2ecf20Sopenharmony_ci if (host->use_dma) 11108c2ecf20Sopenharmony_ci jz4740_mmc_release_dma_channels(host); 11118c2ecf20Sopenharmony_cierr_free_irq: 11128c2ecf20Sopenharmony_ci free_irq(host->irq, host); 11138c2ecf20Sopenharmony_cierr_free_host: 11148c2ecf20Sopenharmony_ci mmc_free_host(mmc); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci return ret; 11178c2ecf20Sopenharmony_ci} 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_cistatic int jz4740_mmc_remove(struct platform_device *pdev) 11208c2ecf20Sopenharmony_ci{ 11218c2ecf20Sopenharmony_ci struct jz4740_mmc_host *host = platform_get_drvdata(pdev); 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci del_timer_sync(&host->timeout_timer); 11248c2ecf20Sopenharmony_ci jz4740_mmc_set_irq_enabled(host, 0xff, false); 11258c2ecf20Sopenharmony_ci jz4740_mmc_reset(host); 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci mmc_remove_host(host->mmc); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci free_irq(host->irq, host); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci if (host->use_dma) 11328c2ecf20Sopenharmony_ci jz4740_mmc_release_dma_channels(host); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci mmc_free_host(host->mmc); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci return 0; 11378c2ecf20Sopenharmony_ci} 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_cistatic int __maybe_unused jz4740_mmc_suspend(struct device *dev) 11408c2ecf20Sopenharmony_ci{ 11418c2ecf20Sopenharmony_ci return pinctrl_pm_select_sleep_state(dev); 11428c2ecf20Sopenharmony_ci} 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_cistatic int __maybe_unused jz4740_mmc_resume(struct device *dev) 11458c2ecf20Sopenharmony_ci{ 11468c2ecf20Sopenharmony_ci return pinctrl_select_default_state(dev); 11478c2ecf20Sopenharmony_ci} 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(jz4740_mmc_pm_ops, jz4740_mmc_suspend, 11508c2ecf20Sopenharmony_ci jz4740_mmc_resume); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_cistatic struct platform_driver jz4740_mmc_driver = { 11538c2ecf20Sopenharmony_ci .probe = jz4740_mmc_probe, 11548c2ecf20Sopenharmony_ci .remove = jz4740_mmc_remove, 11558c2ecf20Sopenharmony_ci .driver = { 11568c2ecf20Sopenharmony_ci .name = "jz4740-mmc", 11578c2ecf20Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 11588c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(jz4740_mmc_of_match), 11598c2ecf20Sopenharmony_ci .pm = pm_ptr(&jz4740_mmc_pm_ops), 11608c2ecf20Sopenharmony_ci }, 11618c2ecf20Sopenharmony_ci}; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cimodule_platform_driver(jz4740_mmc_driver); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("JZ4740 SD/MMC controller driver"); 11668c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 11678c2ecf20Sopenharmony_ciMODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 1168