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