xref: /openbmc/linux/drivers/mmc/host/jz4740_mmc.c (revision 67ad8238)
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>
24152b5245SPaul Cercueil #include <linux/regulator/consumer.h>
2561bfbdb8SLars-Peter Clausen #include <linux/scatterlist.h>
2661bfbdb8SLars-Peter Clausen 
2761bfbdb8SLars-Peter Clausen #include <asm/cacheflush.h>
2861bfbdb8SLars-Peter Clausen 
2961bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_STRPCL	0x00
3061bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_STATUS	0x04
3161bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_CLKRT	0x08
3261bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_CMDAT	0x0C
3361bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RESTO	0x10
3461bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RDTO		0x14
3561bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_BLKLEN	0x18
3661bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_NOB		0x1C
3761bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_SNOB		0x20
3861bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_IMASK	0x24
3961bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_IREG		0x28
4061bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_CMD		0x2C
4161bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_ARG		0x30
4261bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RESP_FIFO	0x34
4361bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RXFIFO	0x38
4461bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_TXFIFO	0x3C
4580fe4e90SZhou Yanjie #define JZ_REG_MMC_LPM		0x40
466a78768aSAlex Smith #define JZ_REG_MMC_DMAC		0x44
4761bfbdb8SLars-Peter Clausen 
4861bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7)
4961bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_EXIT_TRANSFER BIT(6)
5061bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_START_READWAIT BIT(5)
5161bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_STOP_READWAIT BIT(4)
5261bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_RESET BIT(3)
5361bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_START_OP BIT(2)
5461bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_CLOCK_CONTROL (BIT(1) | BIT(0))
5561bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_CLOCK_STOP BIT(0)
5661bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_CLOCK_START BIT(1)
5761bfbdb8SLars-Peter Clausen 
5861bfbdb8SLars-Peter Clausen 
5961bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_IS_RESETTING BIT(15)
6061bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_SDIO_INT_ACTIVE BIT(14)
6161bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_PRG_DONE BIT(13)
6261bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_TRAN_DONE BIT(12)
6361bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_END_CMD_RES BIT(11)
6461bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_FIFO_AFULL BIT(10)
6561bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_IS_READWAIT BIT(9)
6661bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CLK_EN BIT(8)
6761bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_FIFO_FULL BIT(7)
6861bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_FIFO_EMPTY BIT(6)
6961bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CRC_RES_ERR BIT(5)
7061bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CRC_READ_ERROR BIT(4)
7161bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_TIMEOUT_WRITE BIT(3)
7261bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CRC_WRITE_ERROR BIT(2)
7361bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_TIMEOUT_RES BIT(1)
7461bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_TIMEOUT_READ BIT(0)
7561bfbdb8SLars-Peter Clausen 
7661bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_READ_ERROR_MASK (BIT(4) | BIT(0))
7761bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_WRITE_ERROR_MASK (BIT(3) | BIT(2))
7861bfbdb8SLars-Peter Clausen 
7961bfbdb8SLars-Peter Clausen 
8061bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_IO_ABORT BIT(11)
8161bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_BUS_WIDTH_4BIT BIT(10)
82a02f8f48SZhou Yanjie #define JZ_MMC_CMDAT_BUS_WIDTH_8BIT (BIT(10) | BIT(9))
83a02f8f48SZhou Yanjie #define	JZ_MMC_CMDAT_BUS_WIDTH_MASK (BIT(10) | BIT(9))
8461bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_DMA_EN BIT(8)
8561bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_INIT BIT(7)
8661bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_BUSY BIT(6)
8761bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_STREAM BIT(5)
8861bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_WRITE BIT(4)
8961bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_DATA_EN BIT(3)
9061bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RESPONSE_FORMAT (BIT(2) | BIT(1) | BIT(0))
9161bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RSP_R1 1
9261bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RSP_R2 2
9361bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RSP_R3 3
9461bfbdb8SLars-Peter Clausen 
9561bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_SDIO BIT(7)
9661bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_TXFIFO_WR_REQ BIT(6)
9761bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_RXFIFO_RD_REQ BIT(5)
9861bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_END_CMD_RES BIT(2)
9961bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_PRG_DONE BIT(1)
10061bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_DATA_TRAN_DONE BIT(0)
10161bfbdb8SLars-Peter Clausen 
1026a78768aSAlex Smith #define JZ_MMC_DMAC_DMA_SEL BIT(1)
1036a78768aSAlex Smith #define JZ_MMC_DMAC_DMA_EN BIT(0)
10461bfbdb8SLars-Peter Clausen 
10580fe4e90SZhou Yanjie #define	JZ_MMC_LPM_DRV_RISING BIT(31)
10680fe4e90SZhou Yanjie #define	JZ_MMC_LPM_DRV_RISING_QTR_PHASE_DLY BIT(31)
10780fe4e90SZhou Yanjie #define	JZ_MMC_LPM_DRV_RISING_1NS_DLY BIT(30)
10880fe4e90SZhou Yanjie #define	JZ_MMC_LPM_SMP_RISING_QTR_OR_HALF_PHASE_DLY BIT(29)
10980fe4e90SZhou Yanjie #define	JZ_MMC_LPM_LOW_POWER_MODE_EN BIT(0)
11080fe4e90SZhou Yanjie 
11161bfbdb8SLars-Peter Clausen #define JZ_MMC_CLK_RATE 24000000
112d422f8b9SUlf Hansson #define JZ_MMC_REQ_TIMEOUT_MS 5000
11361bfbdb8SLars-Peter Clausen 
11461e11ebaSEzequiel Garcia enum jz4740_mmc_version {
11561e11ebaSEzequiel Garcia 	JZ_MMC_JZ4740,
116a0c938b5SPaul Cercueil 	JZ_MMC_JZ4725B,
1172af2af99SZhou Yanjie 	JZ_MMC_JZ4760,
1186a78768aSAlex Smith 	JZ_MMC_JZ4780,
119fea5fcc2SZhou Yanjie 	JZ_MMC_X1000,
12061e11ebaSEzequiel Garcia };
12161e11ebaSEzequiel Garcia 
12261bfbdb8SLars-Peter Clausen enum jz4740_mmc_state {
12361bfbdb8SLars-Peter Clausen 	JZ4740_MMC_STATE_READ_RESPONSE,
12461bfbdb8SLars-Peter Clausen 	JZ4740_MMC_STATE_TRANSFER_DATA,
12561bfbdb8SLars-Peter Clausen 	JZ4740_MMC_STATE_SEND_STOP,
12661bfbdb8SLars-Peter Clausen 	JZ4740_MMC_STATE_DONE,
12761bfbdb8SLars-Peter Clausen };
12861bfbdb8SLars-Peter Clausen 
12996e03fffSEzequiel Garcia /*
13096e03fffSEzequiel Garcia  * The MMC core allows to prepare a mmc_request while another mmc_request
13196e03fffSEzequiel Garcia  * is in-flight. This is used via the pre_req/post_req hooks.
13296e03fffSEzequiel Garcia  * This driver uses the pre_req/post_req hooks to map/unmap the mmc_request.
13396e03fffSEzequiel Garcia  * Following what other drivers do (sdhci, dw_mmc) we use the following cookie
13496e03fffSEzequiel Garcia  * flags to keep track of the mmc_request mapping state.
13596e03fffSEzequiel Garcia  *
13696e03fffSEzequiel Garcia  * COOKIE_UNMAPPED: the request is not mapped.
13796e03fffSEzequiel Garcia  * COOKIE_PREMAPPED: the request was mapped in pre_req,
13896e03fffSEzequiel Garcia  * and should be unmapped in post_req.
13996e03fffSEzequiel Garcia  * COOKIE_MAPPED: the request was mapped in the irq handler,
14096e03fffSEzequiel Garcia  * and should be unmapped before mmc_request_done is called..
14196e03fffSEzequiel Garcia  */
14296e03fffSEzequiel Garcia enum jz4780_cookie {
14396e03fffSEzequiel Garcia 	COOKIE_UNMAPPED = 0,
14496e03fffSEzequiel Garcia 	COOKIE_PREMAPPED,
14596e03fffSEzequiel Garcia 	COOKIE_MAPPED,
146bb2f4592SApelete Seketeli };
147bb2f4592SApelete Seketeli 
14861bfbdb8SLars-Peter Clausen struct jz4740_mmc_host {
14961bfbdb8SLars-Peter Clausen 	struct mmc_host *mmc;
15061bfbdb8SLars-Peter Clausen 	struct platform_device *pdev;
15161bfbdb8SLars-Peter Clausen 	struct clk *clk;
15261bfbdb8SLars-Peter Clausen 
15361e11ebaSEzequiel Garcia 	enum jz4740_mmc_version version;
15461e11ebaSEzequiel Garcia 
15561bfbdb8SLars-Peter Clausen 	int 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 
162152b5245SPaul Cercueil 	bool vqmmc_enabled;
163152b5245SPaul Cercueil 
16461bfbdb8SLars-Peter Clausen 	unsigned long waiting;
16561bfbdb8SLars-Peter Clausen 
16661bfbdb8SLars-Peter Clausen 	uint32_t cmdat;
16761bfbdb8SLars-Peter Clausen 
1686a78768aSAlex Smith 	uint32_t irq_mask;
16961bfbdb8SLars-Peter Clausen 
17061bfbdb8SLars-Peter Clausen 	spinlock_t lock;
17161bfbdb8SLars-Peter Clausen 
17261bfbdb8SLars-Peter Clausen 	struct timer_list timeout_timer;
17361bfbdb8SLars-Peter Clausen 	struct sg_mapping_iter miter;
17461bfbdb8SLars-Peter Clausen 	enum jz4740_mmc_state state;
1757ca27a6fSApelete Seketeli 
1767ca27a6fSApelete Seketeli 	/* DMA support */
1777ca27a6fSApelete Seketeli 	struct dma_chan *dma_rx;
1787ca27a6fSApelete Seketeli 	struct dma_chan *dma_tx;
1797ca27a6fSApelete Seketeli 	bool use_dma;
1807ca27a6fSApelete Seketeli 
1817ca27a6fSApelete Seketeli /* The DMA trigger level is 8 words, that is to say, the DMA read
1827ca27a6fSApelete Seketeli  * trigger is when data words in MSC_RXFIFO is >= 8 and the DMA write
1837ca27a6fSApelete Seketeli  * trigger is when data words in MSC_TXFIFO is < 8.
1847ca27a6fSApelete Seketeli  */
1857ca27a6fSApelete Seketeli #define JZ4740_MMC_FIFO_HALF_SIZE 8
18661bfbdb8SLars-Peter Clausen };
18761bfbdb8SLars-Peter Clausen 
jz4740_mmc_write_irq_mask(struct jz4740_mmc_host * host,uint32_t val)1886a78768aSAlex Smith static void jz4740_mmc_write_irq_mask(struct jz4740_mmc_host *host,
1896a78768aSAlex Smith 				      uint32_t val)
1906a78768aSAlex Smith {
191a0c938b5SPaul Cercueil 	if (host->version >= JZ_MMC_JZ4725B)
1926a78768aSAlex Smith 		return writel(val, host->base + JZ_REG_MMC_IMASK);
1936a78768aSAlex Smith 	else
1946a78768aSAlex Smith 		return writew(val, host->base + JZ_REG_MMC_IMASK);
1956a78768aSAlex Smith }
1966a78768aSAlex Smith 
jz4740_mmc_write_irq_reg(struct jz4740_mmc_host * host,uint32_t val)1976a78768aSAlex Smith static void jz4740_mmc_write_irq_reg(struct jz4740_mmc_host *host,
1986a78768aSAlex Smith 				     uint32_t val)
1996a78768aSAlex Smith {
2006a78768aSAlex Smith 	if (host->version >= JZ_MMC_JZ4780)
20165af9866SPaul Cercueil 		writel(val, host->base + JZ_REG_MMC_IREG);
2026a78768aSAlex Smith 	else
20365af9866SPaul Cercueil 		writew(val, host->base + JZ_REG_MMC_IREG);
2046a78768aSAlex Smith }
2056a78768aSAlex Smith 
jz4740_mmc_read_irq_reg(struct jz4740_mmc_host * host)2066a78768aSAlex Smith static uint32_t jz4740_mmc_read_irq_reg(struct jz4740_mmc_host *host)
2076a78768aSAlex Smith {
2086a78768aSAlex Smith 	if (host->version >= JZ_MMC_JZ4780)
2096a78768aSAlex Smith 		return readl(host->base + JZ_REG_MMC_IREG);
2106a78768aSAlex Smith 	else
2116a78768aSAlex Smith 		return readw(host->base + JZ_REG_MMC_IREG);
2126a78768aSAlex Smith }
2136a78768aSAlex Smith 
2147ca27a6fSApelete Seketeli /*----------------------------------------------------------------------------*/
2157ca27a6fSApelete Seketeli /* DMA infrastructure */
2167ca27a6fSApelete Seketeli 
jz4740_mmc_release_dma_channels(struct jz4740_mmc_host * host)2177ca27a6fSApelete Seketeli static void jz4740_mmc_release_dma_channels(struct jz4740_mmc_host *host)
2187ca27a6fSApelete Seketeli {
2197ca27a6fSApelete Seketeli 	if (!host->use_dma)
2207ca27a6fSApelete Seketeli 		return;
2217ca27a6fSApelete Seketeli 
2227ca27a6fSApelete Seketeli 	dma_release_channel(host->dma_tx);
223a474e52cSPaul Cercueil 	if (host->dma_rx)
2247ca27a6fSApelete Seketeli 		dma_release_channel(host->dma_rx);
2257ca27a6fSApelete Seketeli }
2267ca27a6fSApelete Seketeli 
jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host * host)2277ca27a6fSApelete Seketeli static int jz4740_mmc_acquire_dma_channels(struct jz4740_mmc_host *host)
2287ca27a6fSApelete Seketeli {
229a474e52cSPaul Cercueil 	struct device *dev = mmc_dev(host->mmc);
230a474e52cSPaul Cercueil 
231a474e52cSPaul Cercueil 	host->dma_tx = dma_request_chan(dev, "tx-rx");
232a474e52cSPaul Cercueil 	if (!IS_ERR(host->dma_tx))
233a474e52cSPaul Cercueil 		return 0;
234a474e52cSPaul Cercueil 
235a474e52cSPaul Cercueil 	if (PTR_ERR(host->dma_tx) != -ENODEV) {
236a474e52cSPaul Cercueil 		dev_err(dev, "Failed to get dma tx-rx channel\n");
237a474e52cSPaul Cercueil 		return PTR_ERR(host->dma_tx);
238a474e52cSPaul Cercueil 	}
239a474e52cSPaul Cercueil 
240fb0ce9ddSEzequiel Garcia 	host->dma_tx = dma_request_chan(mmc_dev(host->mmc), "tx");
241fb0ce9ddSEzequiel Garcia 	if (IS_ERR(host->dma_tx)) {
2427ca27a6fSApelete Seketeli 		dev_err(mmc_dev(host->mmc), "Failed to get dma_tx channel\n");
243fb0ce9ddSEzequiel Garcia 		return PTR_ERR(host->dma_tx);
2447ca27a6fSApelete Seketeli 	}
2457ca27a6fSApelete Seketeli 
246fb0ce9ddSEzequiel Garcia 	host->dma_rx = dma_request_chan(mmc_dev(host->mmc), "rx");
247fb0ce9ddSEzequiel Garcia 	if (IS_ERR(host->dma_rx)) {
2487ca27a6fSApelete Seketeli 		dev_err(mmc_dev(host->mmc), "Failed to get dma_rx channel\n");
249fb0ce9ddSEzequiel Garcia 		dma_release_channel(host->dma_tx);
250fb0ce9ddSEzequiel Garcia 		return PTR_ERR(host->dma_rx);
2517ca27a6fSApelete Seketeli 	}
2527ca27a6fSApelete Seketeli 
253afadb04fSAidan MacDonald 	/*
254afadb04fSAidan MacDonald 	 * Limit the maximum segment size in any SG entry according to
255afadb04fSAidan MacDonald 	 * the parameters of the DMA engine device.
256afadb04fSAidan MacDonald 	 */
257afadb04fSAidan MacDonald 	if (host->dma_tx) {
258afadb04fSAidan MacDonald 		struct device *dev = host->dma_tx->device->dev;
259afadb04fSAidan MacDonald 		unsigned int max_seg_size = dma_get_max_seg_size(dev);
260afadb04fSAidan MacDonald 
261afadb04fSAidan MacDonald 		if (max_seg_size < host->mmc->max_seg_size)
262afadb04fSAidan MacDonald 			host->mmc->max_seg_size = max_seg_size;
263afadb04fSAidan MacDonald 	}
264afadb04fSAidan MacDonald 
265afadb04fSAidan MacDonald 	if (host->dma_rx) {
266afadb04fSAidan MacDonald 		struct device *dev = host->dma_rx->device->dev;
267afadb04fSAidan MacDonald 		unsigned int max_seg_size = dma_get_max_seg_size(dev);
268afadb04fSAidan MacDonald 
269afadb04fSAidan MacDonald 		if (max_seg_size < host->mmc->max_seg_size)
270afadb04fSAidan MacDonald 			host->mmc->max_seg_size = max_seg_size;
271afadb04fSAidan MacDonald 	}
272afadb04fSAidan MacDonald 
2737ca27a6fSApelete Seketeli 	return 0;
2747ca27a6fSApelete Seketeli }
2757ca27a6fSApelete Seketeli 
jz4740_mmc_get_dma_chan(struct jz4740_mmc_host * host,struct mmc_data * data)276bb2f4592SApelete Seketeli static inline struct dma_chan *jz4740_mmc_get_dma_chan(struct jz4740_mmc_host *host,
277bb2f4592SApelete Seketeli 						       struct mmc_data *data)
278bb2f4592SApelete Seketeli {
279a474e52cSPaul Cercueil 	if ((data->flags & MMC_DATA_READ) && host->dma_rx)
280a474e52cSPaul Cercueil 		return host->dma_rx;
281a474e52cSPaul Cercueil 	else
282a474e52cSPaul Cercueil 		return host->dma_tx;
283bb2f4592SApelete Seketeli }
284bb2f4592SApelete Seketeli 
jz4740_mmc_dma_unmap(struct jz4740_mmc_host * host,struct mmc_data * data)2857ca27a6fSApelete Seketeli static void jz4740_mmc_dma_unmap(struct jz4740_mmc_host *host,
2867ca27a6fSApelete Seketeli 				 struct mmc_data *data)
2877ca27a6fSApelete Seketeli {
288bb2f4592SApelete Seketeli 	struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
289feeef096SHeiner Kallweit 	enum dma_data_direction dir = mmc_get_dma_dir(data);
2907ca27a6fSApelete Seketeli 
2917ca27a6fSApelete Seketeli 	dma_unmap_sg(chan->device->dev, data->sg, data->sg_len, dir);
29296e03fffSEzequiel Garcia 	data->host_cookie = COOKIE_UNMAPPED;
2937ca27a6fSApelete Seketeli }
2947ca27a6fSApelete Seketeli 
29596e03fffSEzequiel Garcia /* Prepares DMA data for current or next transfer.
29696e03fffSEzequiel Garcia  * A request can be in-flight when this is called.
29796e03fffSEzequiel Garcia  */
jz4740_mmc_prepare_dma_data(struct jz4740_mmc_host * host,struct mmc_data * data,int cookie)298bb2f4592SApelete Seketeli static int jz4740_mmc_prepare_dma_data(struct jz4740_mmc_host *host,
299bb2f4592SApelete Seketeli 				       struct mmc_data *data,
30096e03fffSEzequiel Garcia 				       int cookie)
301bb2f4592SApelete Seketeli {
30296e03fffSEzequiel Garcia 	struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
303feeef096SHeiner Kallweit 	enum dma_data_direction dir = mmc_get_dma_dir(data);
3048827f85eSJack Wang 	unsigned int sg_count;
305bb2f4592SApelete Seketeli 
30696e03fffSEzequiel Garcia 	if (data->host_cookie == COOKIE_PREMAPPED)
30796e03fffSEzequiel Garcia 		return data->sg_count;
308bb2f4592SApelete Seketeli 
30996e03fffSEzequiel Garcia 	sg_count = dma_map_sg(chan->device->dev,
310bb2f4592SApelete Seketeli 			data->sg,
311bb2f4592SApelete Seketeli 			data->sg_len,
312bb2f4592SApelete Seketeli 			dir);
313bb2f4592SApelete Seketeli 
3148827f85eSJack Wang 	if (!sg_count) {
315bb2f4592SApelete Seketeli 		dev_err(mmc_dev(host->mmc),
316bb2f4592SApelete Seketeli 			"Failed to map scatterlist for DMA operation\n");
317bb2f4592SApelete Seketeli 		return -EINVAL;
318bb2f4592SApelete Seketeli 	}
319bb2f4592SApelete Seketeli 
32096e03fffSEzequiel Garcia 	data->sg_count = sg_count;
32196e03fffSEzequiel Garcia 	data->host_cookie = cookie;
322bb2f4592SApelete Seketeli 
32396e03fffSEzequiel Garcia 	return data->sg_count;
324bb2f4592SApelete Seketeli }
325bb2f4592SApelete Seketeli 
jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host * host,struct mmc_data * data)3267ca27a6fSApelete Seketeli static int jz4740_mmc_start_dma_transfer(struct jz4740_mmc_host *host,
3277ca27a6fSApelete Seketeli 					 struct mmc_data *data)
3287ca27a6fSApelete Seketeli {
32996e03fffSEzequiel Garcia 	struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
3307ca27a6fSApelete Seketeli 	struct dma_async_tx_descriptor *desc;
3317ca27a6fSApelete Seketeli 	struct dma_slave_config conf = {
3327ca27a6fSApelete Seketeli 		.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
3337ca27a6fSApelete Seketeli 		.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
3347ca27a6fSApelete Seketeli 		.src_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
3357ca27a6fSApelete Seketeli 		.dst_maxburst = JZ4740_MMC_FIFO_HALF_SIZE,
3367ca27a6fSApelete Seketeli 	};
33796e03fffSEzequiel Garcia 	int sg_count;
3387ca27a6fSApelete Seketeli 
339bb2f4592SApelete Seketeli 	if (data->flags & MMC_DATA_WRITE) {
3407ca27a6fSApelete Seketeli 		conf.direction = DMA_MEM_TO_DEV;
3417ca27a6fSApelete Seketeli 		conf.dst_addr = host->mem_res->start + JZ_REG_MMC_TXFIFO;
3427ca27a6fSApelete Seketeli 	} else {
3437ca27a6fSApelete Seketeli 		conf.direction = DMA_DEV_TO_MEM;
3447ca27a6fSApelete Seketeli 		conf.src_addr = host->mem_res->start + JZ_REG_MMC_RXFIFO;
3457ca27a6fSApelete Seketeli 	}
3467ca27a6fSApelete Seketeli 
34796e03fffSEzequiel Garcia 	sg_count = jz4740_mmc_prepare_dma_data(host, data, COOKIE_MAPPED);
34896e03fffSEzequiel Garcia 	if (sg_count < 0)
34996e03fffSEzequiel Garcia 		return sg_count;
3507ca27a6fSApelete Seketeli 
3517ca27a6fSApelete Seketeli 	dmaengine_slave_config(chan, &conf);
35296e03fffSEzequiel Garcia 	desc = dmaengine_prep_slave_sg(chan, data->sg, sg_count,
3537ca27a6fSApelete Seketeli 			conf.direction,
3547ca27a6fSApelete Seketeli 			DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
3557ca27a6fSApelete Seketeli 	if (!desc) {
3567ca27a6fSApelete Seketeli 		dev_err(mmc_dev(host->mmc),
3577ca27a6fSApelete Seketeli 			"Failed to allocate DMA %s descriptor",
3587ca27a6fSApelete Seketeli 			 conf.direction == DMA_MEM_TO_DEV ? "TX" : "RX");
3597ca27a6fSApelete Seketeli 		goto dma_unmap;
3607ca27a6fSApelete Seketeli 	}
3617ca27a6fSApelete Seketeli 
3627ca27a6fSApelete Seketeli 	dmaengine_submit(desc);
3637ca27a6fSApelete Seketeli 	dma_async_issue_pending(chan);
3647ca27a6fSApelete Seketeli 
3657ca27a6fSApelete Seketeli 	return 0;
3667ca27a6fSApelete Seketeli 
3677ca27a6fSApelete Seketeli dma_unmap:
36896e03fffSEzequiel Garcia 	if (data->host_cookie == COOKIE_MAPPED)
3697ca27a6fSApelete Seketeli 		jz4740_mmc_dma_unmap(host, data);
3707ca27a6fSApelete Seketeli 	return -ENOMEM;
3717ca27a6fSApelete Seketeli }
3727ca27a6fSApelete Seketeli 
jz4740_mmc_pre_request(struct mmc_host * mmc,struct mmc_request * mrq)373bb2f4592SApelete Seketeli static void jz4740_mmc_pre_request(struct mmc_host *mmc,
374d3c6aac3SLinus Walleij 				   struct mmc_request *mrq)
375bb2f4592SApelete Seketeli {
376bb2f4592SApelete Seketeli 	struct jz4740_mmc_host *host = mmc_priv(mmc);
377bb2f4592SApelete Seketeli 	struct mmc_data *data = mrq->data;
378bb2f4592SApelete Seketeli 
37996e03fffSEzequiel Garcia 	if (!host->use_dma)
38096e03fffSEzequiel Garcia 		return;
381bb2f4592SApelete Seketeli 
38296e03fffSEzequiel Garcia 	data->host_cookie = COOKIE_UNMAPPED;
38396e03fffSEzequiel Garcia 	if (jz4740_mmc_prepare_dma_data(host, data, COOKIE_PREMAPPED) < 0)
38496e03fffSEzequiel Garcia 		data->host_cookie = COOKIE_UNMAPPED;
385bb2f4592SApelete Seketeli }
386bb2f4592SApelete Seketeli 
jz4740_mmc_post_request(struct mmc_host * mmc,struct mmc_request * mrq,int err)387bb2f4592SApelete Seketeli static void jz4740_mmc_post_request(struct mmc_host *mmc,
388bb2f4592SApelete Seketeli 				    struct mmc_request *mrq,
389bb2f4592SApelete Seketeli 				    int err)
390bb2f4592SApelete Seketeli {
391bb2f4592SApelete Seketeli 	struct jz4740_mmc_host *host = mmc_priv(mmc);
392bb2f4592SApelete Seketeli 	struct mmc_data *data = mrq->data;
393bb2f4592SApelete Seketeli 
39496e03fffSEzequiel Garcia 	if (data && data->host_cookie != COOKIE_UNMAPPED)
395bb2f4592SApelete Seketeli 		jz4740_mmc_dma_unmap(host, data);
396bb2f4592SApelete Seketeli 
397bb2f4592SApelete Seketeli 	if (err) {
398bb2f4592SApelete Seketeli 		struct dma_chan *chan = jz4740_mmc_get_dma_chan(host, data);
399bb2f4592SApelete Seketeli 
400bb2f4592SApelete Seketeli 		dmaengine_terminate_all(chan);
401bb2f4592SApelete Seketeli 	}
402bb2f4592SApelete Seketeli }
403bb2f4592SApelete Seketeli 
4047ca27a6fSApelete Seketeli /*----------------------------------------------------------------------------*/
4057ca27a6fSApelete Seketeli 
jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host * host,unsigned int irq,bool enabled)40661bfbdb8SLars-Peter Clausen static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host,
40761bfbdb8SLars-Peter Clausen 	unsigned int irq, bool enabled)
40861bfbdb8SLars-Peter Clausen {
40961bfbdb8SLars-Peter Clausen 	unsigned long flags;
41061bfbdb8SLars-Peter Clausen 
41161bfbdb8SLars-Peter Clausen 	spin_lock_irqsave(&host->lock, flags);
41261bfbdb8SLars-Peter Clausen 	if (enabled)
41361bfbdb8SLars-Peter Clausen 		host->irq_mask &= ~irq;
41461bfbdb8SLars-Peter Clausen 	else
41561bfbdb8SLars-Peter Clausen 		host->irq_mask |= irq;
41661bfbdb8SLars-Peter Clausen 
4176a78768aSAlex Smith 	jz4740_mmc_write_irq_mask(host, host->irq_mask);
418a04f0017SAlex Smith 	spin_unlock_irqrestore(&host->lock, flags);
41961bfbdb8SLars-Peter Clausen }
42061bfbdb8SLars-Peter Clausen 
jz4740_mmc_clock_enable(struct jz4740_mmc_host * host,bool start_transfer)42161bfbdb8SLars-Peter Clausen static void jz4740_mmc_clock_enable(struct jz4740_mmc_host *host,
42261bfbdb8SLars-Peter Clausen 	bool start_transfer)
42361bfbdb8SLars-Peter Clausen {
42461bfbdb8SLars-Peter Clausen 	uint16_t val = JZ_MMC_STRPCL_CLOCK_START;
42561bfbdb8SLars-Peter Clausen 
42661bfbdb8SLars-Peter Clausen 	if (start_transfer)
42761bfbdb8SLars-Peter Clausen 		val |= JZ_MMC_STRPCL_START_OP;
42861bfbdb8SLars-Peter Clausen 
42961bfbdb8SLars-Peter Clausen 	writew(val, host->base + JZ_REG_MMC_STRPCL);
43061bfbdb8SLars-Peter Clausen }
43161bfbdb8SLars-Peter Clausen 
jz4740_mmc_clock_disable(struct jz4740_mmc_host * host)43261bfbdb8SLars-Peter Clausen static void jz4740_mmc_clock_disable(struct jz4740_mmc_host *host)
43361bfbdb8SLars-Peter Clausen {
43461bfbdb8SLars-Peter Clausen 	uint32_t status;
43561bfbdb8SLars-Peter Clausen 	unsigned int timeout = 1000;
43661bfbdb8SLars-Peter Clausen 
43761bfbdb8SLars-Peter Clausen 	writew(JZ_MMC_STRPCL_CLOCK_STOP, host->base + JZ_REG_MMC_STRPCL);
43861bfbdb8SLars-Peter Clausen 	do {
43961bfbdb8SLars-Peter Clausen 		status = readl(host->base + JZ_REG_MMC_STATUS);
44061bfbdb8SLars-Peter Clausen 	} while (status & JZ_MMC_STATUS_CLK_EN && --timeout);
44161bfbdb8SLars-Peter Clausen }
44261bfbdb8SLars-Peter Clausen 
jz4740_mmc_reset(struct jz4740_mmc_host * host)44361bfbdb8SLars-Peter Clausen static void jz4740_mmc_reset(struct jz4740_mmc_host *host)
44461bfbdb8SLars-Peter Clausen {
44561bfbdb8SLars-Peter Clausen 	uint32_t status;
44661bfbdb8SLars-Peter Clausen 	unsigned int timeout = 1000;
44761bfbdb8SLars-Peter Clausen 
44861bfbdb8SLars-Peter Clausen 	writew(JZ_MMC_STRPCL_RESET, host->base + JZ_REG_MMC_STRPCL);
44961bfbdb8SLars-Peter Clausen 	udelay(10);
45061bfbdb8SLars-Peter Clausen 	do {
45161bfbdb8SLars-Peter Clausen 		status = readl(host->base + JZ_REG_MMC_STATUS);
45261bfbdb8SLars-Peter Clausen 	} while (status & JZ_MMC_STATUS_IS_RESETTING && --timeout);
45361bfbdb8SLars-Peter Clausen }
45461bfbdb8SLars-Peter Clausen 
jz4740_mmc_request_done(struct jz4740_mmc_host * host)45561bfbdb8SLars-Peter Clausen static void jz4740_mmc_request_done(struct jz4740_mmc_host *host)
45661bfbdb8SLars-Peter Clausen {
45761bfbdb8SLars-Peter Clausen 	struct mmc_request *req;
45896e03fffSEzequiel Garcia 	struct mmc_data *data;
45961bfbdb8SLars-Peter Clausen 
46061bfbdb8SLars-Peter Clausen 	req = host->req;
46196e03fffSEzequiel Garcia 	data = req->data;
46261bfbdb8SLars-Peter Clausen 	host->req = NULL;
46361bfbdb8SLars-Peter Clausen 
46496e03fffSEzequiel Garcia 	if (data && data->host_cookie == COOKIE_MAPPED)
46596e03fffSEzequiel Garcia 		jz4740_mmc_dma_unmap(host, data);
46661bfbdb8SLars-Peter Clausen 	mmc_request_done(host->mmc, req);
46761bfbdb8SLars-Peter Clausen }
46861bfbdb8SLars-Peter Clausen 
jz4740_mmc_poll_irq(struct jz4740_mmc_host * host,unsigned int irq)46961bfbdb8SLars-Peter Clausen static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host,
47061bfbdb8SLars-Peter Clausen 	unsigned int irq)
47161bfbdb8SLars-Peter Clausen {
47261bfbdb8SLars-Peter Clausen 	unsigned int timeout = 0x800;
4736a78768aSAlex Smith 	uint32_t status;
47461bfbdb8SLars-Peter Clausen 
47561bfbdb8SLars-Peter Clausen 	do {
4766a78768aSAlex Smith 		status = jz4740_mmc_read_irq_reg(host);
47761bfbdb8SLars-Peter Clausen 	} while (!(status & irq) && --timeout);
47861bfbdb8SLars-Peter Clausen 
47961bfbdb8SLars-Peter Clausen 	if (timeout == 0) {
48061bfbdb8SLars-Peter Clausen 		set_bit(0, &host->waiting);
481d422f8b9SUlf Hansson 		mod_timer(&host->timeout_timer,
482d422f8b9SUlf Hansson 			  jiffies + msecs_to_jiffies(JZ_MMC_REQ_TIMEOUT_MS));
48361bfbdb8SLars-Peter Clausen 		jz4740_mmc_set_irq_enabled(host, irq, true);
48461bfbdb8SLars-Peter Clausen 		return true;
48561bfbdb8SLars-Peter Clausen 	}
48661bfbdb8SLars-Peter Clausen 
48761bfbdb8SLars-Peter Clausen 	return false;
48861bfbdb8SLars-Peter Clausen }
48961bfbdb8SLars-Peter Clausen 
jz4740_mmc_transfer_check_state(struct jz4740_mmc_host * host,struct mmc_data * data)49061bfbdb8SLars-Peter Clausen static void jz4740_mmc_transfer_check_state(struct jz4740_mmc_host *host,
49161bfbdb8SLars-Peter Clausen 	struct mmc_data *data)
49261bfbdb8SLars-Peter Clausen {
49361bfbdb8SLars-Peter Clausen 	int status;
49461bfbdb8SLars-Peter Clausen 
49561bfbdb8SLars-Peter Clausen 	status = readl(host->base + JZ_REG_MMC_STATUS);
49661bfbdb8SLars-Peter Clausen 	if (status & JZ_MMC_STATUS_WRITE_ERROR_MASK) {
49761bfbdb8SLars-Peter Clausen 		if (status & (JZ_MMC_STATUS_TIMEOUT_WRITE)) {
49861bfbdb8SLars-Peter Clausen 			host->req->cmd->error = -ETIMEDOUT;
49961bfbdb8SLars-Peter Clausen 			data->error = -ETIMEDOUT;
50061bfbdb8SLars-Peter Clausen 		} else {
50161bfbdb8SLars-Peter Clausen 			host->req->cmd->error = -EIO;
50261bfbdb8SLars-Peter Clausen 			data->error = -EIO;
50361bfbdb8SLars-Peter Clausen 		}
5048a489aa1SPaul Cercueil 	} else if (status & JZ_MMC_STATUS_READ_ERROR_MASK) {
5058a489aa1SPaul Cercueil 		if (status & (JZ_MMC_STATUS_TIMEOUT_READ)) {
5068a489aa1SPaul Cercueil 			host->req->cmd->error = -ETIMEDOUT;
5078a489aa1SPaul Cercueil 			data->error = -ETIMEDOUT;
5088a489aa1SPaul Cercueil 		} else {
5098a489aa1SPaul Cercueil 			host->req->cmd->error = -EIO;
5108a489aa1SPaul Cercueil 			data->error = -EIO;
5118a489aa1SPaul Cercueil 		}
51261bfbdb8SLars-Peter Clausen 	}
51361bfbdb8SLars-Peter Clausen }
51461bfbdb8SLars-Peter Clausen 
jz4740_mmc_write_data(struct jz4740_mmc_host * host,struct mmc_data * data)51561bfbdb8SLars-Peter Clausen static bool jz4740_mmc_write_data(struct jz4740_mmc_host *host,
51661bfbdb8SLars-Peter Clausen 	struct mmc_data *data)
51761bfbdb8SLars-Peter Clausen {
51861bfbdb8SLars-Peter Clausen 	struct sg_mapping_iter *miter = &host->miter;
51961bfbdb8SLars-Peter Clausen 	void __iomem *fifo_addr = host->base + JZ_REG_MMC_TXFIFO;
52061bfbdb8SLars-Peter Clausen 	uint32_t *buf;
52161bfbdb8SLars-Peter Clausen 	bool timeout;
52261bfbdb8SLars-Peter Clausen 	size_t i, j;
52361bfbdb8SLars-Peter Clausen 
52461bfbdb8SLars-Peter Clausen 	while (sg_miter_next(miter)) {
52561bfbdb8SLars-Peter Clausen 		buf = miter->addr;
52661bfbdb8SLars-Peter Clausen 		i = miter->length / 4;
52761bfbdb8SLars-Peter Clausen 		j = i / 8;
52861bfbdb8SLars-Peter Clausen 		i = i & 0x7;
52961bfbdb8SLars-Peter Clausen 		while (j) {
53061bfbdb8SLars-Peter Clausen 			timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ);
53161bfbdb8SLars-Peter Clausen 			if (unlikely(timeout))
53261bfbdb8SLars-Peter Clausen 				goto poll_timeout;
53361bfbdb8SLars-Peter Clausen 
53461bfbdb8SLars-Peter Clausen 			writel(buf[0], fifo_addr);
53561bfbdb8SLars-Peter Clausen 			writel(buf[1], fifo_addr);
53661bfbdb8SLars-Peter Clausen 			writel(buf[2], fifo_addr);
53761bfbdb8SLars-Peter Clausen 			writel(buf[3], fifo_addr);
53861bfbdb8SLars-Peter Clausen 			writel(buf[4], fifo_addr);
53961bfbdb8SLars-Peter Clausen 			writel(buf[5], fifo_addr);
54061bfbdb8SLars-Peter Clausen 			writel(buf[6], fifo_addr);
54161bfbdb8SLars-Peter Clausen 			writel(buf[7], fifo_addr);
54261bfbdb8SLars-Peter Clausen 			buf += 8;
54361bfbdb8SLars-Peter Clausen 			--j;
54461bfbdb8SLars-Peter Clausen 		}
54561bfbdb8SLars-Peter Clausen 		if (unlikely(i)) {
54661bfbdb8SLars-Peter Clausen 			timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ);
54761bfbdb8SLars-Peter Clausen 			if (unlikely(timeout))
54861bfbdb8SLars-Peter Clausen 				goto poll_timeout;
54961bfbdb8SLars-Peter Clausen 
55061bfbdb8SLars-Peter Clausen 			while (i) {
55161bfbdb8SLars-Peter Clausen 				writel(*buf, fifo_addr);
55261bfbdb8SLars-Peter Clausen 				++buf;
55361bfbdb8SLars-Peter Clausen 				--i;
55461bfbdb8SLars-Peter Clausen 			}
55561bfbdb8SLars-Peter Clausen 		}
55661bfbdb8SLars-Peter Clausen 		data->bytes_xfered += miter->length;
55761bfbdb8SLars-Peter Clausen 	}
55861bfbdb8SLars-Peter Clausen 	sg_miter_stop(miter);
55961bfbdb8SLars-Peter Clausen 
56061bfbdb8SLars-Peter Clausen 	return false;
56161bfbdb8SLars-Peter Clausen 
56261bfbdb8SLars-Peter Clausen poll_timeout:
56361bfbdb8SLars-Peter Clausen 	miter->consumed = (void *)buf - miter->addr;
56461bfbdb8SLars-Peter Clausen 	data->bytes_xfered += miter->consumed;
56561bfbdb8SLars-Peter Clausen 	sg_miter_stop(miter);
56661bfbdb8SLars-Peter Clausen 
56761bfbdb8SLars-Peter Clausen 	return true;
56861bfbdb8SLars-Peter Clausen }
56961bfbdb8SLars-Peter Clausen 
jz4740_mmc_read_data(struct jz4740_mmc_host * host,struct mmc_data * data)57061bfbdb8SLars-Peter Clausen static bool jz4740_mmc_read_data(struct jz4740_mmc_host *host,
57161bfbdb8SLars-Peter Clausen 				struct mmc_data *data)
57261bfbdb8SLars-Peter Clausen {
57361bfbdb8SLars-Peter Clausen 	struct sg_mapping_iter *miter = &host->miter;
57461bfbdb8SLars-Peter Clausen 	void __iomem *fifo_addr = host->base + JZ_REG_MMC_RXFIFO;
57561bfbdb8SLars-Peter Clausen 	uint32_t *buf;
57661bfbdb8SLars-Peter Clausen 	uint32_t d;
5776a78768aSAlex Smith 	uint32_t status;
57861bfbdb8SLars-Peter Clausen 	size_t i, j;
57961bfbdb8SLars-Peter Clausen 	unsigned int timeout;
58061bfbdb8SLars-Peter Clausen 
58161bfbdb8SLars-Peter Clausen 	while (sg_miter_next(miter)) {
58261bfbdb8SLars-Peter Clausen 		buf = miter->addr;
58361bfbdb8SLars-Peter Clausen 		i = miter->length;
58461bfbdb8SLars-Peter Clausen 		j = i / 32;
58561bfbdb8SLars-Peter Clausen 		i = i & 0x1f;
58661bfbdb8SLars-Peter Clausen 		while (j) {
58761bfbdb8SLars-Peter Clausen 			timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ);
58861bfbdb8SLars-Peter Clausen 			if (unlikely(timeout))
58961bfbdb8SLars-Peter Clausen 				goto poll_timeout;
59061bfbdb8SLars-Peter Clausen 
59161bfbdb8SLars-Peter Clausen 			buf[0] = readl(fifo_addr);
59261bfbdb8SLars-Peter Clausen 			buf[1] = readl(fifo_addr);
59361bfbdb8SLars-Peter Clausen 			buf[2] = readl(fifo_addr);
59461bfbdb8SLars-Peter Clausen 			buf[3] = readl(fifo_addr);
59561bfbdb8SLars-Peter Clausen 			buf[4] = readl(fifo_addr);
59661bfbdb8SLars-Peter Clausen 			buf[5] = readl(fifo_addr);
59761bfbdb8SLars-Peter Clausen 			buf[6] = readl(fifo_addr);
59861bfbdb8SLars-Peter Clausen 			buf[7] = readl(fifo_addr);
59961bfbdb8SLars-Peter Clausen 
60061bfbdb8SLars-Peter Clausen 			buf += 8;
60161bfbdb8SLars-Peter Clausen 			--j;
60261bfbdb8SLars-Peter Clausen 		}
60361bfbdb8SLars-Peter Clausen 
60461bfbdb8SLars-Peter Clausen 		if (unlikely(i)) {
60561bfbdb8SLars-Peter Clausen 			timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ);
60661bfbdb8SLars-Peter Clausen 			if (unlikely(timeout))
60761bfbdb8SLars-Peter Clausen 				goto poll_timeout;
60861bfbdb8SLars-Peter Clausen 
60961bfbdb8SLars-Peter Clausen 			while (i >= 4) {
61061bfbdb8SLars-Peter Clausen 				*buf++ = readl(fifo_addr);
61161bfbdb8SLars-Peter Clausen 				i -= 4;
61261bfbdb8SLars-Peter Clausen 			}
61361bfbdb8SLars-Peter Clausen 			if (unlikely(i > 0)) {
61461bfbdb8SLars-Peter Clausen 				d = readl(fifo_addr);
61561bfbdb8SLars-Peter Clausen 				memcpy(buf, &d, i);
61661bfbdb8SLars-Peter Clausen 			}
61761bfbdb8SLars-Peter Clausen 		}
61861bfbdb8SLars-Peter Clausen 		data->bytes_xfered += miter->length;
61961bfbdb8SLars-Peter Clausen 	}
62061bfbdb8SLars-Peter Clausen 	sg_miter_stop(miter);
62161bfbdb8SLars-Peter Clausen 
62261bfbdb8SLars-Peter Clausen 	/* For whatever reason there is sometime one word more in the fifo then
62361bfbdb8SLars-Peter Clausen 	 * requested */
62461bfbdb8SLars-Peter Clausen 	timeout = 1000;
62561bfbdb8SLars-Peter Clausen 	status = readl(host->base + JZ_REG_MMC_STATUS);
62661bfbdb8SLars-Peter Clausen 	while (!(status & JZ_MMC_STATUS_DATA_FIFO_EMPTY) && --timeout) {
62761bfbdb8SLars-Peter Clausen 		d = readl(fifo_addr);
62861bfbdb8SLars-Peter Clausen 		status = readl(host->base + JZ_REG_MMC_STATUS);
62961bfbdb8SLars-Peter Clausen 	}
63061bfbdb8SLars-Peter Clausen 
63161bfbdb8SLars-Peter Clausen 	return false;
63261bfbdb8SLars-Peter Clausen 
63361bfbdb8SLars-Peter Clausen poll_timeout:
63461bfbdb8SLars-Peter Clausen 	miter->consumed = (void *)buf - miter->addr;
63561bfbdb8SLars-Peter Clausen 	data->bytes_xfered += miter->consumed;
63661bfbdb8SLars-Peter Clausen 	sg_miter_stop(miter);
63761bfbdb8SLars-Peter Clausen 
63861bfbdb8SLars-Peter Clausen 	return true;
63961bfbdb8SLars-Peter Clausen }
64061bfbdb8SLars-Peter Clausen 
jz4740_mmc_timeout(struct timer_list * t)6412ee4f620SKees Cook static void jz4740_mmc_timeout(struct timer_list *t)
64261bfbdb8SLars-Peter Clausen {
6432ee4f620SKees Cook 	struct jz4740_mmc_host *host = from_timer(host, t, timeout_timer);
64461bfbdb8SLars-Peter Clausen 
64561bfbdb8SLars-Peter Clausen 	if (!test_and_clear_bit(0, &host->waiting))
64661bfbdb8SLars-Peter Clausen 		return;
64761bfbdb8SLars-Peter Clausen 
64861bfbdb8SLars-Peter Clausen 	jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, false);
64961bfbdb8SLars-Peter Clausen 
65061bfbdb8SLars-Peter Clausen 	host->req->cmd->error = -ETIMEDOUT;
65161bfbdb8SLars-Peter Clausen 	jz4740_mmc_request_done(host);
65261bfbdb8SLars-Peter Clausen }
65361bfbdb8SLars-Peter Clausen 
jz4740_mmc_read_response(struct jz4740_mmc_host * host,struct mmc_command * cmd)65461bfbdb8SLars-Peter Clausen static void jz4740_mmc_read_response(struct jz4740_mmc_host *host,
65561bfbdb8SLars-Peter Clausen 	struct mmc_command *cmd)
65661bfbdb8SLars-Peter Clausen {
65761bfbdb8SLars-Peter Clausen 	int i;
65861bfbdb8SLars-Peter Clausen 	uint16_t tmp;
65961bfbdb8SLars-Peter Clausen 	void __iomem *fifo_addr = host->base + JZ_REG_MMC_RESP_FIFO;
66061bfbdb8SLars-Peter Clausen 
66161bfbdb8SLars-Peter Clausen 	if (cmd->flags & MMC_RSP_136) {
66261bfbdb8SLars-Peter Clausen 		tmp = readw(fifo_addr);
66361bfbdb8SLars-Peter Clausen 		for (i = 0; i < 4; ++i) {
66461bfbdb8SLars-Peter Clausen 			cmd->resp[i] = tmp << 24;
66561bfbdb8SLars-Peter Clausen 			tmp = readw(fifo_addr);
66661bfbdb8SLars-Peter Clausen 			cmd->resp[i] |= tmp << 8;
66761bfbdb8SLars-Peter Clausen 			tmp = readw(fifo_addr);
66861bfbdb8SLars-Peter Clausen 			cmd->resp[i] |= tmp >> 8;
66961bfbdb8SLars-Peter Clausen 		}
67061bfbdb8SLars-Peter Clausen 	} else {
67161bfbdb8SLars-Peter Clausen 		cmd->resp[0] = readw(fifo_addr) << 24;
67261bfbdb8SLars-Peter Clausen 		cmd->resp[0] |= readw(fifo_addr) << 8;
67361bfbdb8SLars-Peter Clausen 		cmd->resp[0] |= readw(fifo_addr) & 0xff;
67461bfbdb8SLars-Peter Clausen 	}
67561bfbdb8SLars-Peter Clausen }
67661bfbdb8SLars-Peter Clausen 
jz4740_mmc_send_command(struct jz4740_mmc_host * host,struct mmc_command * cmd)67761bfbdb8SLars-Peter Clausen static void jz4740_mmc_send_command(struct jz4740_mmc_host *host,
67861bfbdb8SLars-Peter Clausen 	struct mmc_command *cmd)
67961bfbdb8SLars-Peter Clausen {
68061bfbdb8SLars-Peter Clausen 	uint32_t cmdat = host->cmdat;
68161bfbdb8SLars-Peter Clausen 
68261bfbdb8SLars-Peter Clausen 	host->cmdat &= ~JZ_MMC_CMDAT_INIT;
68361bfbdb8SLars-Peter Clausen 	jz4740_mmc_clock_disable(host);
68461bfbdb8SLars-Peter Clausen 
68561bfbdb8SLars-Peter Clausen 	host->cmd = cmd;
68661bfbdb8SLars-Peter Clausen 
68761bfbdb8SLars-Peter Clausen 	if (cmd->flags & MMC_RSP_BUSY)
68861bfbdb8SLars-Peter Clausen 		cmdat |= JZ_MMC_CMDAT_BUSY;
68961bfbdb8SLars-Peter Clausen 
69061bfbdb8SLars-Peter Clausen 	switch (mmc_resp_type(cmd)) {
69161bfbdb8SLars-Peter Clausen 	case MMC_RSP_R1B:
69261bfbdb8SLars-Peter Clausen 	case MMC_RSP_R1:
69361bfbdb8SLars-Peter Clausen 		cmdat |= JZ_MMC_CMDAT_RSP_R1;
69461bfbdb8SLars-Peter Clausen 		break;
69561bfbdb8SLars-Peter Clausen 	case MMC_RSP_R2:
69661bfbdb8SLars-Peter Clausen 		cmdat |= JZ_MMC_CMDAT_RSP_R2;
69761bfbdb8SLars-Peter Clausen 		break;
69861bfbdb8SLars-Peter Clausen 	case MMC_RSP_R3:
69961bfbdb8SLars-Peter Clausen 		cmdat |= JZ_MMC_CMDAT_RSP_R3;
70061bfbdb8SLars-Peter Clausen 		break;
70161bfbdb8SLars-Peter Clausen 	default:
70261bfbdb8SLars-Peter Clausen 		break;
70361bfbdb8SLars-Peter Clausen 	}
70461bfbdb8SLars-Peter Clausen 
70561bfbdb8SLars-Peter Clausen 	if (cmd->data) {
70661bfbdb8SLars-Peter Clausen 		cmdat |= JZ_MMC_CMDAT_DATA_EN;
70761bfbdb8SLars-Peter Clausen 		if (cmd->data->flags & MMC_DATA_WRITE)
70861bfbdb8SLars-Peter Clausen 			cmdat |= JZ_MMC_CMDAT_WRITE;
7096a78768aSAlex Smith 		if (host->use_dma) {
7106a78768aSAlex Smith 			/*
711d1c777eeS周琰杰 (Zhou Yanjie) 			 * The JZ4780's MMC controller has integrated DMA ability
7126a78768aSAlex Smith 			 * in addition to being able to use the external DMA
7136a78768aSAlex Smith 			 * controller. It moves DMA control bits to a separate
7146a78768aSAlex Smith 			 * register. The DMA_SEL bit chooses the external
7156a78768aSAlex Smith 			 * controller over the integrated one. Earlier SoCs
7166a78768aSAlex Smith 			 * can only use the external controller, and have a
7176a78768aSAlex Smith 			 * single DMA enable bit in CMDAT.
7186a78768aSAlex Smith 			 */
7196a78768aSAlex Smith 			if (host->version >= JZ_MMC_JZ4780) {
7206a78768aSAlex Smith 				writel(JZ_MMC_DMAC_DMA_EN | JZ_MMC_DMAC_DMA_SEL,
7216a78768aSAlex Smith 				       host->base + JZ_REG_MMC_DMAC);
7226a78768aSAlex Smith 			} else {
7237ca27a6fSApelete Seketeli 				cmdat |= JZ_MMC_CMDAT_DMA_EN;
7246a78768aSAlex Smith 			}
7256a78768aSAlex Smith 		} else if (host->version >= JZ_MMC_JZ4780) {
7266a78768aSAlex Smith 			writel(0, host->base + JZ_REG_MMC_DMAC);
7276a78768aSAlex Smith 		}
72861bfbdb8SLars-Peter Clausen 
72961bfbdb8SLars-Peter Clausen 		writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN);
73061bfbdb8SLars-Peter Clausen 		writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB);
73161bfbdb8SLars-Peter Clausen 	}
73261bfbdb8SLars-Peter Clausen 
73361bfbdb8SLars-Peter Clausen 	writeb(cmd->opcode, host->base + JZ_REG_MMC_CMD);
73461bfbdb8SLars-Peter Clausen 	writel(cmd->arg, host->base + JZ_REG_MMC_ARG);
73561bfbdb8SLars-Peter Clausen 	writel(cmdat, host->base + JZ_REG_MMC_CMDAT);
73661bfbdb8SLars-Peter Clausen 
73761bfbdb8SLars-Peter Clausen 	jz4740_mmc_clock_enable(host, 1);
73861bfbdb8SLars-Peter Clausen }
73961bfbdb8SLars-Peter Clausen 
jz_mmc_prepare_data_transfer(struct jz4740_mmc_host * host)74061bfbdb8SLars-Peter Clausen static void jz_mmc_prepare_data_transfer(struct jz4740_mmc_host *host)
74161bfbdb8SLars-Peter Clausen {
74261bfbdb8SLars-Peter Clausen 	struct mmc_command *cmd = host->req->cmd;
74361bfbdb8SLars-Peter Clausen 	struct mmc_data *data = cmd->data;
74461bfbdb8SLars-Peter Clausen 	int direction;
74561bfbdb8SLars-Peter Clausen 
74661bfbdb8SLars-Peter Clausen 	if (data->flags & MMC_DATA_READ)
74761bfbdb8SLars-Peter Clausen 		direction = SG_MITER_TO_SG;
74861bfbdb8SLars-Peter Clausen 	else
74961bfbdb8SLars-Peter Clausen 		direction = SG_MITER_FROM_SG;
75061bfbdb8SLars-Peter Clausen 
75161bfbdb8SLars-Peter Clausen 	sg_miter_start(&host->miter, data->sg, data->sg_len, direction);
75261bfbdb8SLars-Peter Clausen }
75361bfbdb8SLars-Peter Clausen 
75461bfbdb8SLars-Peter Clausen 
jz_mmc_irq_worker(int irq,void * devid)75561bfbdb8SLars-Peter Clausen static irqreturn_t jz_mmc_irq_worker(int irq, void *devid)
75661bfbdb8SLars-Peter Clausen {
75761bfbdb8SLars-Peter Clausen 	struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)devid;
75861bfbdb8SLars-Peter Clausen 	struct mmc_command *cmd = host->req->cmd;
75961bfbdb8SLars-Peter Clausen 	struct mmc_request *req = host->req;
7607ca27a6fSApelete Seketeli 	struct mmc_data *data = cmd->data;
76161bfbdb8SLars-Peter Clausen 	bool timeout = false;
76261bfbdb8SLars-Peter Clausen 
76361bfbdb8SLars-Peter Clausen 	if (cmd->error)
76461bfbdb8SLars-Peter Clausen 		host->state = JZ4740_MMC_STATE_DONE;
76561bfbdb8SLars-Peter Clausen 
76661bfbdb8SLars-Peter Clausen 	switch (host->state) {
76761bfbdb8SLars-Peter Clausen 	case JZ4740_MMC_STATE_READ_RESPONSE:
76861bfbdb8SLars-Peter Clausen 		if (cmd->flags & MMC_RSP_PRESENT)
76961bfbdb8SLars-Peter Clausen 			jz4740_mmc_read_response(host, cmd);
77061bfbdb8SLars-Peter Clausen 
7717ca27a6fSApelete Seketeli 		if (!data)
77261bfbdb8SLars-Peter Clausen 			break;
77361bfbdb8SLars-Peter Clausen 
77461bfbdb8SLars-Peter Clausen 		jz_mmc_prepare_data_transfer(host);
775df561f66SGustavo A. R. Silva 		fallthrough;
77661bfbdb8SLars-Peter Clausen 
77761bfbdb8SLars-Peter Clausen 	case JZ4740_MMC_STATE_TRANSFER_DATA:
7787ca27a6fSApelete Seketeli 		if (host->use_dma) {
779bb2f4592SApelete Seketeli 			/* Use DMA if enabled.
780bb2f4592SApelete Seketeli 			 * Data transfer direction is defined later by
781bb2f4592SApelete Seketeli 			 * relying on data flags in
782bb2f4592SApelete Seketeli 			 * jz4740_mmc_prepare_dma_data() and
783bb2f4592SApelete Seketeli 			 * jz4740_mmc_start_dma_transfer().
7847ca27a6fSApelete Seketeli 			 */
7857ca27a6fSApelete Seketeli 			timeout = jz4740_mmc_start_dma_transfer(host, data);
7867ca27a6fSApelete Seketeli 			data->bytes_xfered = data->blocks * data->blksz;
7877ca27a6fSApelete Seketeli 		} else if (data->flags & MMC_DATA_READ)
788bb2f4592SApelete Seketeli 			/* Use PIO if DMA is not enabled.
789bb2f4592SApelete Seketeli 			 * Data transfer direction was defined before
790bb2f4592SApelete Seketeli 			 * by relying on data flags in
791bb2f4592SApelete Seketeli 			 * jz_mmc_prepare_data_transfer().
7927ca27a6fSApelete Seketeli 			 */
7937ca27a6fSApelete Seketeli 			timeout = jz4740_mmc_read_data(host, data);
79461bfbdb8SLars-Peter Clausen 		else
7957ca27a6fSApelete Seketeli 			timeout = jz4740_mmc_write_data(host, data);
79661bfbdb8SLars-Peter Clausen 
79761bfbdb8SLars-Peter Clausen 		if (unlikely(timeout)) {
79861bfbdb8SLars-Peter Clausen 			host->state = JZ4740_MMC_STATE_TRANSFER_DATA;
79961bfbdb8SLars-Peter Clausen 			break;
80061bfbdb8SLars-Peter Clausen 		}
80161bfbdb8SLars-Peter Clausen 
8027ca27a6fSApelete Seketeli 		jz4740_mmc_transfer_check_state(host, data);
80361bfbdb8SLars-Peter Clausen 
80461bfbdb8SLars-Peter Clausen 		timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_DATA_TRAN_DONE);
80561bfbdb8SLars-Peter Clausen 		if (unlikely(timeout)) {
80661bfbdb8SLars-Peter Clausen 			host->state = JZ4740_MMC_STATE_SEND_STOP;
80761bfbdb8SLars-Peter Clausen 			break;
80861bfbdb8SLars-Peter Clausen 		}
8096a78768aSAlex Smith 		jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_DATA_TRAN_DONE);
810df561f66SGustavo A. R. Silva 		fallthrough;
81161bfbdb8SLars-Peter Clausen 
81261bfbdb8SLars-Peter Clausen 	case JZ4740_MMC_STATE_SEND_STOP:
81361bfbdb8SLars-Peter Clausen 		if (!req->stop)
81461bfbdb8SLars-Peter Clausen 			break;
81561bfbdb8SLars-Peter Clausen 
81661bfbdb8SLars-Peter Clausen 		jz4740_mmc_send_command(host, req->stop);
81761bfbdb8SLars-Peter Clausen 
8181acee84bSAlex Smith 		if (mmc_resp_type(req->stop) & MMC_RSP_BUSY) {
8191acee84bSAlex Smith 			timeout = jz4740_mmc_poll_irq(host,
8201acee84bSAlex Smith 						      JZ_MMC_IRQ_PRG_DONE);
82161bfbdb8SLars-Peter Clausen 			if (timeout) {
82261bfbdb8SLars-Peter Clausen 				host->state = JZ4740_MMC_STATE_DONE;
82361bfbdb8SLars-Peter Clausen 				break;
82461bfbdb8SLars-Peter Clausen 			}
8251acee84bSAlex Smith 		}
826f95deaecSGustavo A. R. Silva 		fallthrough;
827f95deaecSGustavo A. R. Silva 
82861bfbdb8SLars-Peter Clausen 	case JZ4740_MMC_STATE_DONE:
82961bfbdb8SLars-Peter Clausen 		break;
83061bfbdb8SLars-Peter Clausen 	}
83161bfbdb8SLars-Peter Clausen 
83261bfbdb8SLars-Peter Clausen 	if (!timeout)
83361bfbdb8SLars-Peter Clausen 		jz4740_mmc_request_done(host);
83461bfbdb8SLars-Peter Clausen 
83561bfbdb8SLars-Peter Clausen 	return IRQ_HANDLED;
83661bfbdb8SLars-Peter Clausen }
83761bfbdb8SLars-Peter Clausen 
jz_mmc_irq(int irq,void * devid)83861bfbdb8SLars-Peter Clausen static irqreturn_t jz_mmc_irq(int irq, void *devid)
83961bfbdb8SLars-Peter Clausen {
84061bfbdb8SLars-Peter Clausen 	struct jz4740_mmc_host *host = devid;
84161bfbdb8SLars-Peter Clausen 	struct mmc_command *cmd = host->cmd;
8426a78768aSAlex Smith 	uint32_t irq_reg, status, tmp;
84361bfbdb8SLars-Peter Clausen 
8446a78768aSAlex Smith 	status = readl(host->base + JZ_REG_MMC_STATUS);
8456a78768aSAlex Smith 	irq_reg = jz4740_mmc_read_irq_reg(host);
84661bfbdb8SLars-Peter Clausen 
84761bfbdb8SLars-Peter Clausen 	tmp = irq_reg;
84861bfbdb8SLars-Peter Clausen 	irq_reg &= ~host->irq_mask;
84961bfbdb8SLars-Peter Clausen 
85061bfbdb8SLars-Peter Clausen 	tmp &= ~(JZ_MMC_IRQ_TXFIFO_WR_REQ | JZ_MMC_IRQ_RXFIFO_RD_REQ |
85161bfbdb8SLars-Peter Clausen 		JZ_MMC_IRQ_PRG_DONE | JZ_MMC_IRQ_DATA_TRAN_DONE);
85261bfbdb8SLars-Peter Clausen 
85361bfbdb8SLars-Peter Clausen 	if (tmp != irq_reg)
8546a78768aSAlex Smith 		jz4740_mmc_write_irq_reg(host, tmp & ~irq_reg);
85561bfbdb8SLars-Peter Clausen 
85661bfbdb8SLars-Peter Clausen 	if (irq_reg & JZ_MMC_IRQ_SDIO) {
8576a78768aSAlex Smith 		jz4740_mmc_write_irq_reg(host, JZ_MMC_IRQ_SDIO);
85861bfbdb8SLars-Peter Clausen 		mmc_signal_sdio_irq(host->mmc);
85961bfbdb8SLars-Peter Clausen 		irq_reg &= ~JZ_MMC_IRQ_SDIO;
86061bfbdb8SLars-Peter Clausen 	}
86161bfbdb8SLars-Peter Clausen 
86261bfbdb8SLars-Peter Clausen 	if (host->req && cmd && irq_reg) {
86361bfbdb8SLars-Peter Clausen 		if (test_and_clear_bit(0, &host->waiting)) {
86461bfbdb8SLars-Peter Clausen 			del_timer(&host->timeout_timer);
86561bfbdb8SLars-Peter Clausen 
86661bfbdb8SLars-Peter Clausen 			if (status & JZ_MMC_STATUS_TIMEOUT_RES) {
86761bfbdb8SLars-Peter Clausen 				cmd->error = -ETIMEDOUT;
86861bfbdb8SLars-Peter Clausen 			} else if (status & JZ_MMC_STATUS_CRC_RES_ERR) {
86961bfbdb8SLars-Peter Clausen 				cmd->error = -EIO;
87061bfbdb8SLars-Peter Clausen 			} else if (status & (JZ_MMC_STATUS_CRC_READ_ERROR |
87161bfbdb8SLars-Peter Clausen 				    JZ_MMC_STATUS_CRC_WRITE_ERROR)) {
87261bfbdb8SLars-Peter Clausen 				if (cmd->data)
87361bfbdb8SLars-Peter Clausen 					cmd->data->error = -EIO;
87461bfbdb8SLars-Peter Clausen 				cmd->error = -EIO;
87561bfbdb8SLars-Peter Clausen 			}
87661bfbdb8SLars-Peter Clausen 
87761bfbdb8SLars-Peter Clausen 			jz4740_mmc_set_irq_enabled(host, irq_reg, false);
8786a78768aSAlex Smith 			jz4740_mmc_write_irq_reg(host, irq_reg);
87961bfbdb8SLars-Peter Clausen 
88061bfbdb8SLars-Peter Clausen 			return IRQ_WAKE_THREAD;
88161bfbdb8SLars-Peter Clausen 		}
88261bfbdb8SLars-Peter Clausen 	}
88361bfbdb8SLars-Peter Clausen 
88461bfbdb8SLars-Peter Clausen 	return IRQ_HANDLED;
88561bfbdb8SLars-Peter Clausen }
88661bfbdb8SLars-Peter Clausen 
jz4740_mmc_set_clock_rate(struct jz4740_mmc_host * host,int rate)88761bfbdb8SLars-Peter Clausen static int jz4740_mmc_set_clock_rate(struct jz4740_mmc_host *host, int rate)
88861bfbdb8SLars-Peter Clausen {
88961bfbdb8SLars-Peter Clausen 	int div = 0;
89061bfbdb8SLars-Peter Clausen 	int real_rate;
89161bfbdb8SLars-Peter Clausen 
89261bfbdb8SLars-Peter Clausen 	jz4740_mmc_clock_disable(host);
8936861fce6SAlex Smith 	clk_set_rate(host->clk, host->mmc->f_max);
89461bfbdb8SLars-Peter Clausen 
89561bfbdb8SLars-Peter Clausen 	real_rate = clk_get_rate(host->clk);
89661bfbdb8SLars-Peter Clausen 
89761bfbdb8SLars-Peter Clausen 	while (real_rate > rate && div < 7) {
89861bfbdb8SLars-Peter Clausen 		++div;
89961bfbdb8SLars-Peter Clausen 		real_rate >>= 1;
90061bfbdb8SLars-Peter Clausen 	}
90161bfbdb8SLars-Peter Clausen 
90261bfbdb8SLars-Peter Clausen 	writew(div, host->base + JZ_REG_MMC_CLKRT);
90380fe4e90SZhou Yanjie 
90480fe4e90SZhou Yanjie 	if (real_rate > 25000000) {
905d1c777eeS周琰杰 (Zhou Yanjie) 		if (host->version >= JZ_MMC_JZ4780) {
90680fe4e90SZhou Yanjie 			writel(JZ_MMC_LPM_DRV_RISING_QTR_PHASE_DLY |
90780fe4e90SZhou Yanjie 				   JZ_MMC_LPM_SMP_RISING_QTR_OR_HALF_PHASE_DLY |
90880fe4e90SZhou Yanjie 				   JZ_MMC_LPM_LOW_POWER_MODE_EN,
90980fe4e90SZhou Yanjie 				   host->base + JZ_REG_MMC_LPM);
91080fe4e90SZhou Yanjie 		} else if (host->version >= JZ_MMC_JZ4760) {
91180fe4e90SZhou Yanjie 			writel(JZ_MMC_LPM_DRV_RISING |
91280fe4e90SZhou Yanjie 				   JZ_MMC_LPM_LOW_POWER_MODE_EN,
91380fe4e90SZhou Yanjie 				   host->base + JZ_REG_MMC_LPM);
91480fe4e90SZhou Yanjie 		} else if (host->version >= JZ_MMC_JZ4725B)
91580fe4e90SZhou Yanjie 			writel(JZ_MMC_LPM_LOW_POWER_MODE_EN,
91680fe4e90SZhou Yanjie 				   host->base + JZ_REG_MMC_LPM);
91780fe4e90SZhou Yanjie 	}
91880fe4e90SZhou Yanjie 
91961bfbdb8SLars-Peter Clausen 	return real_rate;
92061bfbdb8SLars-Peter Clausen }
92161bfbdb8SLars-Peter Clausen 
jz4740_mmc_request(struct mmc_host * mmc,struct mmc_request * req)92261bfbdb8SLars-Peter Clausen static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req)
92361bfbdb8SLars-Peter Clausen {
92461bfbdb8SLars-Peter Clausen 	struct jz4740_mmc_host *host = mmc_priv(mmc);
92561bfbdb8SLars-Peter Clausen 
92661bfbdb8SLars-Peter Clausen 	host->req = req;
92761bfbdb8SLars-Peter Clausen 
9286a78768aSAlex Smith 	jz4740_mmc_write_irq_reg(host, ~0);
92961bfbdb8SLars-Peter Clausen 	jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, true);
93061bfbdb8SLars-Peter Clausen 
93161bfbdb8SLars-Peter Clausen 	host->state = JZ4740_MMC_STATE_READ_RESPONSE;
93261bfbdb8SLars-Peter Clausen 	set_bit(0, &host->waiting);
933d422f8b9SUlf Hansson 	mod_timer(&host->timeout_timer,
934d422f8b9SUlf Hansson 		  jiffies + msecs_to_jiffies(JZ_MMC_REQ_TIMEOUT_MS));
93561bfbdb8SLars-Peter Clausen 	jz4740_mmc_send_command(host, req->cmd);
93661bfbdb8SLars-Peter Clausen }
93761bfbdb8SLars-Peter Clausen 
jz4740_mmc_set_ios(struct mmc_host * mmc,struct mmc_ios * ios)93861bfbdb8SLars-Peter Clausen static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
93961bfbdb8SLars-Peter Clausen {
94061bfbdb8SLars-Peter Clausen 	struct jz4740_mmc_host *host = mmc_priv(mmc);
941152b5245SPaul Cercueil 	int ret;
942152b5245SPaul Cercueil 
94361bfbdb8SLars-Peter Clausen 	if (ios->clock)
94461bfbdb8SLars-Peter Clausen 		jz4740_mmc_set_clock_rate(host, ios->clock);
94561bfbdb8SLars-Peter Clausen 
94661bfbdb8SLars-Peter Clausen 	switch (ios->power_mode) {
94761bfbdb8SLars-Peter Clausen 	case MMC_POWER_UP:
94861bfbdb8SLars-Peter Clausen 		jz4740_mmc_reset(host);
94905395527SPaul Cercueil 		if (!IS_ERR(mmc->supply.vmmc))
95005395527SPaul Cercueil 			mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
95161bfbdb8SLars-Peter Clausen 		host->cmdat |= JZ_MMC_CMDAT_INIT;
952fca9661cSLars-Peter Clausen 		clk_prepare_enable(host->clk);
95361bfbdb8SLars-Peter Clausen 		break;
95461bfbdb8SLars-Peter Clausen 	case MMC_POWER_ON:
955152b5245SPaul Cercueil 		if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
956152b5245SPaul Cercueil 			ret = regulator_enable(mmc->supply.vqmmc);
957152b5245SPaul Cercueil 			if (ret)
958152b5245SPaul Cercueil 				dev_err(&host->pdev->dev, "Failed to set vqmmc power!\n");
959152b5245SPaul Cercueil 			else
960152b5245SPaul Cercueil 				host->vqmmc_enabled = true;
961152b5245SPaul Cercueil 		}
96261bfbdb8SLars-Peter Clausen 		break;
963152b5245SPaul Cercueil 	case MMC_POWER_OFF:
96405395527SPaul Cercueil 		if (!IS_ERR(mmc->supply.vmmc))
96505395527SPaul Cercueil 			mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
966152b5245SPaul Cercueil 		if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) {
967152b5245SPaul Cercueil 			regulator_disable(mmc->supply.vqmmc);
968152b5245SPaul Cercueil 			host->vqmmc_enabled = false;
969152b5245SPaul Cercueil 		}
970fca9661cSLars-Peter Clausen 		clk_disable_unprepare(host->clk);
97161bfbdb8SLars-Peter Clausen 		break;
972152b5245SPaul Cercueil 	default:
973152b5245SPaul Cercueil 		break;
97461bfbdb8SLars-Peter Clausen 	}
97561bfbdb8SLars-Peter Clausen 
97661bfbdb8SLars-Peter Clausen 	switch (ios->bus_width) {
97761bfbdb8SLars-Peter Clausen 	case MMC_BUS_WIDTH_1:
978a02f8f48SZhou Yanjie 		host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_MASK;
97961bfbdb8SLars-Peter Clausen 		break;
98061bfbdb8SLars-Peter Clausen 	case MMC_BUS_WIDTH_4:
981a02f8f48SZhou Yanjie 		host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_MASK;
98261bfbdb8SLars-Peter Clausen 		host->cmdat |= JZ_MMC_CMDAT_BUS_WIDTH_4BIT;
98361bfbdb8SLars-Peter Clausen 		break;
984a02f8f48SZhou Yanjie 	case MMC_BUS_WIDTH_8:
985a02f8f48SZhou Yanjie 		host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_MASK;
986a02f8f48SZhou Yanjie 		host->cmdat |= JZ_MMC_CMDAT_BUS_WIDTH_8BIT;
987a02f8f48SZhou Yanjie 		break;
98861bfbdb8SLars-Peter Clausen 	default:
98961bfbdb8SLars-Peter Clausen 		break;
99061bfbdb8SLars-Peter Clausen 	}
99161bfbdb8SLars-Peter Clausen }
99261bfbdb8SLars-Peter Clausen 
jz4740_mmc_enable_sdio_irq(struct mmc_host * mmc,int enable)99361bfbdb8SLars-Peter Clausen static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
99461bfbdb8SLars-Peter Clausen {
99561bfbdb8SLars-Peter Clausen 	struct jz4740_mmc_host *host = mmc_priv(mmc);
99661bfbdb8SLars-Peter Clausen 	jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_SDIO, enable);
99761bfbdb8SLars-Peter Clausen }
99861bfbdb8SLars-Peter Clausen 
jz4740_voltage_switch(struct mmc_host * mmc,struct mmc_ios * ios)999152b5245SPaul Cercueil static int jz4740_voltage_switch(struct mmc_host *mmc, struct mmc_ios *ios)
1000152b5245SPaul Cercueil {
1001152b5245SPaul Cercueil 	int ret;
1002152b5245SPaul Cercueil 
1003152b5245SPaul Cercueil 	/* vqmmc regulator is available */
1004152b5245SPaul Cercueil 	if (!IS_ERR(mmc->supply.vqmmc)) {
1005152b5245SPaul Cercueil 		ret = mmc_regulator_set_vqmmc(mmc, ios);
1006152b5245SPaul Cercueil 		return ret < 0 ? ret : 0;
1007152b5245SPaul Cercueil 	}
1008152b5245SPaul Cercueil 
1009152b5245SPaul Cercueil 	/* no vqmmc regulator, assume fixed regulator at 3/3.3V */
1010152b5245SPaul Cercueil 	if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
1011152b5245SPaul Cercueil 		return 0;
1012152b5245SPaul Cercueil 
1013152b5245SPaul Cercueil 	return -EINVAL;
1014152b5245SPaul Cercueil }
1015152b5245SPaul Cercueil 
101661bfbdb8SLars-Peter Clausen static const struct mmc_host_ops jz4740_mmc_ops = {
101761bfbdb8SLars-Peter Clausen 	.request	= jz4740_mmc_request,
1018bb2f4592SApelete Seketeli 	.pre_req	= jz4740_mmc_pre_request,
1019bb2f4592SApelete Seketeli 	.post_req	= jz4740_mmc_post_request,
102061bfbdb8SLars-Peter Clausen 	.set_ios	= jz4740_mmc_set_ios,
102158e300afSLars-Peter Clausen 	.get_ro		= mmc_gpio_get_ro,
102258e300afSLars-Peter Clausen 	.get_cd		= mmc_gpio_get_cd,
102361bfbdb8SLars-Peter Clausen 	.enable_sdio_irq = jz4740_mmc_enable_sdio_irq,
1024152b5245SPaul Cercueil 	.start_signal_voltage_switch = jz4740_voltage_switch,
102561bfbdb8SLars-Peter Clausen };
102661bfbdb8SLars-Peter Clausen 
102761e11ebaSEzequiel Garcia static const struct of_device_id jz4740_mmc_of_match[] = {
102861e11ebaSEzequiel Garcia 	{ .compatible = "ingenic,jz4740-mmc", .data = (void *) JZ_MMC_JZ4740 },
1029a0c938b5SPaul Cercueil 	{ .compatible = "ingenic,jz4725b-mmc", .data = (void *)JZ_MMC_JZ4725B },
10302af2af99SZhou Yanjie 	{ .compatible = "ingenic,jz4760-mmc", .data = (void *) JZ_MMC_JZ4760 },
1031d1c777eeS周琰杰 (Zhou Yanjie) 	{ .compatible = "ingenic,jz4775-mmc", .data = (void *) JZ_MMC_JZ4780 },
10326a78768aSAlex Smith 	{ .compatible = "ingenic,jz4780-mmc", .data = (void *) JZ_MMC_JZ4780 },
1033fea5fcc2SZhou Yanjie 	{ .compatible = "ingenic,x1000-mmc", .data = (void *) JZ_MMC_X1000 },
103461e11ebaSEzequiel Garcia 	{},
103561e11ebaSEzequiel Garcia };
103661e11ebaSEzequiel Garcia MODULE_DEVICE_TABLE(of, jz4740_mmc_of_match);
103761e11ebaSEzequiel Garcia 
jz4740_mmc_probe(struct platform_device * pdev)1038c3be1efdSBill Pemberton static int jz4740_mmc_probe(struct platform_device* pdev)
103961bfbdb8SLars-Peter Clausen {
104061bfbdb8SLars-Peter Clausen 	int ret;
104161bfbdb8SLars-Peter Clausen 	struct mmc_host *mmc;
104261bfbdb8SLars-Peter Clausen 	struct jz4740_mmc_host *host;
104361e11ebaSEzequiel Garcia 	const struct of_device_id *match;
104461bfbdb8SLars-Peter Clausen 
104561bfbdb8SLars-Peter Clausen 	mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev);
104661bfbdb8SLars-Peter Clausen 	if (!mmc) {
104761bfbdb8SLars-Peter Clausen 		dev_err(&pdev->dev, "Failed to alloc mmc host structure\n");
104861bfbdb8SLars-Peter Clausen 		return -ENOMEM;
104961bfbdb8SLars-Peter Clausen 	}
105061bfbdb8SLars-Peter Clausen 
105161bfbdb8SLars-Peter Clausen 	host = mmc_priv(mmc);
105261bfbdb8SLars-Peter Clausen 
105361e11ebaSEzequiel Garcia 	match = of_match_device(jz4740_mmc_of_match, &pdev->dev);
105461e11ebaSEzequiel Garcia 	if (match) {
105561e11ebaSEzequiel Garcia 		host->version = (enum jz4740_mmc_version)match->data;
105605395527SPaul Cercueil 	} else {
105705395527SPaul Cercueil 		/* JZ4740 should be the only one using legacy probe */
105805395527SPaul Cercueil 		host->version = JZ_MMC_JZ4740;
105905395527SPaul Cercueil 	}
106005395527SPaul Cercueil 
106161e11ebaSEzequiel Garcia 	ret = mmc_of_parse(mmc);
106261e11ebaSEzequiel Garcia 	if (ret) {
1063295208a6SKrzysztof Kozlowski 		dev_err_probe(&pdev->dev, ret, "could not parse device properties\n");
106461e11ebaSEzequiel Garcia 		goto err_free_host;
106561e11ebaSEzequiel Garcia 	}
106605395527SPaul Cercueil 
106705395527SPaul Cercueil 	mmc_regulator_get_supply(mmc);
106861e11ebaSEzequiel Garcia 
106961bfbdb8SLars-Peter Clausen 	host->irq = platform_get_irq(pdev, 0);
107061bfbdb8SLars-Peter Clausen 	if (host->irq < 0) {
107161bfbdb8SLars-Peter Clausen 		ret = host->irq;
107261bfbdb8SLars-Peter Clausen 		goto err_free_host;
107361bfbdb8SLars-Peter Clausen 	}
107461bfbdb8SLars-Peter Clausen 
1075017d84bdSLars-Peter Clausen 	host->clk = devm_clk_get(&pdev->dev, "mmc");
10763119cbdaSJamie Iles 	if (IS_ERR(host->clk)) {
10773119cbdaSJamie Iles 		ret = PTR_ERR(host->clk);
107861bfbdb8SLars-Peter Clausen 		dev_err(&pdev->dev, "Failed to get mmc clock\n");
107961bfbdb8SLars-Peter Clausen 		goto err_free_host;
108061bfbdb8SLars-Peter Clausen 	}
108161bfbdb8SLars-Peter Clausen 
1082e3d16727SYang Li 	host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &host->mem_res);
10833e7e8c18SWei Yongjun 	if (IS_ERR(host->base)) {
10843e7e8c18SWei Yongjun 		ret = PTR_ERR(host->base);
1085017d84bdSLars-Peter Clausen 		goto err_free_host;
108661bfbdb8SLars-Peter Clausen 	}
108761bfbdb8SLars-Peter Clausen 
108861bfbdb8SLars-Peter Clausen 	mmc->ops = &jz4740_mmc_ops;
108961e11ebaSEzequiel Garcia 	if (!mmc->f_max)
109061bfbdb8SLars-Peter Clausen 		mmc->f_max = JZ_MMC_CLK_RATE;
10913f18c504SPaul Cercueil 
10923f18c504SPaul Cercueil 	/*
10933f18c504SPaul Cercueil 	 * There seems to be a problem with this driver on the JZ4760 and
10943f18c504SPaul Cercueil 	 * JZ4760B SoCs. There, when using the maximum rate supported (50 MHz),
10953f18c504SPaul Cercueil 	 * the communication fails with many SD cards.
10963f18c504SPaul Cercueil 	 * Until this bug is sorted out, limit the maximum rate to 24 MHz.
10973f18c504SPaul Cercueil 	 */
10983f18c504SPaul Cercueil 	if (host->version == JZ_MMC_JZ4760 && mmc->f_max > JZ_MMC_CLK_RATE)
10993f18c504SPaul Cercueil 		mmc->f_max = JZ_MMC_CLK_RATE;
11003f18c504SPaul Cercueil 
110161e11ebaSEzequiel Garcia 	mmc->f_min = mmc->f_max / 128;
110261bfbdb8SLars-Peter Clausen 	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
110361bfbdb8SLars-Peter Clausen 
1104d422f8b9SUlf Hansson 	/*
1105d422f8b9SUlf Hansson 	 * We use a fixed timeout of 5s, hence inform the core about it. A
1106d422f8b9SUlf Hansson 	 * future improvement should instead respect the cmd->busy_timeout.
1107d422f8b9SUlf Hansson 	 */
1108d422f8b9SUlf Hansson 	mmc->max_busy_timeout = JZ_MMC_REQ_TIMEOUT_MS;
1109d422f8b9SUlf Hansson 
111061bfbdb8SLars-Peter Clausen 	mmc->max_blk_size = (1 << 10) - 1;
111161bfbdb8SLars-Peter Clausen 	mmc->max_blk_count = (1 << 15) - 1;
111261bfbdb8SLars-Peter Clausen 	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
111361bfbdb8SLars-Peter Clausen 
1114a36274e0SMartin K. Petersen 	mmc->max_segs = 128;
111561bfbdb8SLars-Peter Clausen 	mmc->max_seg_size = mmc->max_req_size;
111661bfbdb8SLars-Peter Clausen 
111761bfbdb8SLars-Peter Clausen 	host->mmc = mmc;
111861bfbdb8SLars-Peter Clausen 	host->pdev = pdev;
111961bfbdb8SLars-Peter Clausen 	spin_lock_init(&host->lock);
11206a78768aSAlex Smith 	host->irq_mask = ~0;
112161bfbdb8SLars-Peter Clausen 
1122436a3cfdSZubair Lutfullah Kakakhel 	jz4740_mmc_reset(host);
1123436a3cfdSZubair Lutfullah Kakakhel 
112461bfbdb8SLars-Peter Clausen 	ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0,
112561bfbdb8SLars-Peter Clausen 			dev_name(&pdev->dev), host);
112661bfbdb8SLars-Peter Clausen 	if (ret) {
112761bfbdb8SLars-Peter Clausen 		dev_err(&pdev->dev, "Failed to request irq: %d\n", ret);
11280f6f3235SLinus Walleij 		goto err_free_host;
112961bfbdb8SLars-Peter Clausen 	}
113061bfbdb8SLars-Peter Clausen 
113161bfbdb8SLars-Peter Clausen 	jz4740_mmc_clock_disable(host);
11322ee4f620SKees Cook 	timer_setup(&host->timeout_timer, jz4740_mmc_timeout, 0);
113361bfbdb8SLars-Peter Clausen 
11347e8466e2SPaul Cercueil 	ret = jz4740_mmc_acquire_dma_channels(host);
11357e8466e2SPaul Cercueil 	if (ret == -EPROBE_DEFER)
11367e8466e2SPaul Cercueil 		goto err_free_irq;
11377e8466e2SPaul Cercueil 	host->use_dma = !ret;
11387ca27a6fSApelete Seketeli 
113961bfbdb8SLars-Peter Clausen 	platform_set_drvdata(pdev, host);
114061bfbdb8SLars-Peter Clausen 	ret = mmc_add_host(mmc);
114161bfbdb8SLars-Peter Clausen 
114261bfbdb8SLars-Peter Clausen 	if (ret) {
114361bfbdb8SLars-Peter Clausen 		dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret);
11447e8466e2SPaul Cercueil 		goto err_release_dma;
114561bfbdb8SLars-Peter Clausen 	}
1146fea5fcc2SZhou Yanjie 	dev_info(&pdev->dev, "Ingenic SD/MMC card driver registered\n");
114761bfbdb8SLars-Peter Clausen 
11487ca27a6fSApelete Seketeli 	dev_info(&pdev->dev, "Using %s, %d-bit mode\n",
11497ca27a6fSApelete Seketeli 		 host->use_dma ? "DMA" : "PIO",
1150a02f8f48SZhou Yanjie 		 (mmc->caps & MMC_CAP_8_BIT_DATA) ? 8 :
1151a02f8f48SZhou Yanjie 		 ((mmc->caps & MMC_CAP_4_BIT_DATA) ? 4 : 1));
11527ca27a6fSApelete Seketeli 
115361bfbdb8SLars-Peter Clausen 	return 0;
115461bfbdb8SLars-Peter Clausen 
11557e8466e2SPaul Cercueil err_release_dma:
11567e8466e2SPaul Cercueil 	if (host->use_dma)
11577e8466e2SPaul Cercueil 		jz4740_mmc_release_dma_channels(host);
115861bfbdb8SLars-Peter Clausen err_free_irq:
115961bfbdb8SLars-Peter Clausen 	free_irq(host->irq, host);
116061bfbdb8SLars-Peter Clausen err_free_host:
116161bfbdb8SLars-Peter Clausen 	mmc_free_host(mmc);
116261bfbdb8SLars-Peter Clausen 
116361bfbdb8SLars-Peter Clausen 	return ret;
116461bfbdb8SLars-Peter Clausen }
116561bfbdb8SLars-Peter Clausen 
jz4740_mmc_remove(struct platform_device * pdev)1166*67ad8238SYangtao Li static void jz4740_mmc_remove(struct platform_device *pdev)
116761bfbdb8SLars-Peter Clausen {
116861bfbdb8SLars-Peter Clausen 	struct jz4740_mmc_host *host = platform_get_drvdata(pdev);
116961bfbdb8SLars-Peter Clausen 
117061bfbdb8SLars-Peter Clausen 	del_timer_sync(&host->timeout_timer);
117161bfbdb8SLars-Peter Clausen 	jz4740_mmc_set_irq_enabled(host, 0xff, false);
117261bfbdb8SLars-Peter Clausen 	jz4740_mmc_reset(host);
117361bfbdb8SLars-Peter Clausen 
117461bfbdb8SLars-Peter Clausen 	mmc_remove_host(host->mmc);
117561bfbdb8SLars-Peter Clausen 
117661bfbdb8SLars-Peter Clausen 	free_irq(host->irq, host);
117761bfbdb8SLars-Peter Clausen 
11787ca27a6fSApelete Seketeli 	if (host->use_dma)
11797ca27a6fSApelete Seketeli 		jz4740_mmc_release_dma_channels(host);
11807ca27a6fSApelete Seketeli 
118161bfbdb8SLars-Peter Clausen 	mmc_free_host(host->mmc);
118261bfbdb8SLars-Peter Clausen }
118361bfbdb8SLars-Peter Clausen 
jz4740_mmc_suspend(struct device * dev)1184e0d64eccSPaul Cercueil static int jz4740_mmc_suspend(struct device *dev)
118561bfbdb8SLars-Peter Clausen {
1186fa5ed6bcSPaul Cercueil 	return pinctrl_pm_select_sleep_state(dev);
118761bfbdb8SLars-Peter Clausen }
118861bfbdb8SLars-Peter Clausen 
jz4740_mmc_resume(struct device * dev)1189e0d64eccSPaul Cercueil static int jz4740_mmc_resume(struct device *dev)
119061bfbdb8SLars-Peter Clausen {
1191a62ff540SUlf Hansson 	return pinctrl_select_default_state(dev);
119261bfbdb8SLars-Peter Clausen }
119361bfbdb8SLars-Peter Clausen 
119452cc1d7fSPaul Cercueil static DEFINE_SIMPLE_DEV_PM_OPS(jz4740_mmc_pm_ops, jz4740_mmc_suspend,
11955d5c0350SLars-Peter Clausen 				jz4740_mmc_resume);
119661bfbdb8SLars-Peter Clausen 
119761bfbdb8SLars-Peter Clausen static struct platform_driver jz4740_mmc_driver = {
119861bfbdb8SLars-Peter Clausen 	.probe = jz4740_mmc_probe,
1199*67ad8238SYangtao Li 	.remove_new = jz4740_mmc_remove,
120061bfbdb8SLars-Peter Clausen 	.driver = {
120161bfbdb8SLars-Peter Clausen 		.name = "jz4740-mmc",
120221b2cec6SDouglas Anderson 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
120361e11ebaSEzequiel Garcia 		.of_match_table = of_match_ptr(jz4740_mmc_of_match),
1204e0d64eccSPaul Cercueil 		.pm = pm_sleep_ptr(&jz4740_mmc_pm_ops),
120561bfbdb8SLars-Peter Clausen 	},
120661bfbdb8SLars-Peter Clausen };
120761bfbdb8SLars-Peter Clausen 
1208d1f81a64SAxel Lin module_platform_driver(jz4740_mmc_driver);
120961bfbdb8SLars-Peter Clausen 
121061bfbdb8SLars-Peter Clausen MODULE_DESCRIPTION("JZ4740 SD/MMC controller driver");
121161bfbdb8SLars-Peter Clausen MODULE_LICENSE("GPL");
121261bfbdb8SLars-Peter Clausen MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
1213