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