xref: /openbmc/linux/drivers/mmc/host/mvsdio.c (revision 0fbfbfba)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2236caa7cSMaen Suleiman /*
3236caa7cSMaen Suleiman  * Marvell MMC/SD/SDIO driver
4236caa7cSMaen Suleiman  *
5236caa7cSMaen Suleiman  * Authors: Maen Suleiman, Nicolas Pitre
6236caa7cSMaen Suleiman  * Copyright (C) 2008-2009 Marvell Ltd.
7236caa7cSMaen Suleiman  */
8236caa7cSMaen Suleiman 
9236caa7cSMaen Suleiman #include <linux/module.h>
10236caa7cSMaen Suleiman #include <linux/init.h>
11236caa7cSMaen Suleiman #include <linux/io.h>
12236caa7cSMaen Suleiman #include <linux/platform_device.h>
13236caa7cSMaen Suleiman #include <linux/mbus.h>
14236caa7cSMaen Suleiman #include <linux/delay.h>
15236caa7cSMaen Suleiman #include <linux/interrupt.h>
16236caa7cSMaen Suleiman #include <linux/dma-mapping.h>
17236caa7cSMaen Suleiman #include <linux/scatterlist.h>
18236caa7cSMaen Suleiman #include <linux/irq.h>
19f4f7561eSAndrew Lunn #include <linux/clk.h>
20111936ffSThomas Petazzoni #include <linux/of_irq.h>
21236caa7cSMaen Suleiman #include <linux/mmc/host.h>
223724482dSThomas Petazzoni #include <linux/mmc/slot-gpio.h>
23236caa7cSMaen Suleiman 
2487dfb311SMasahiro Yamada #include <linux/sizes.h>
25236caa7cSMaen Suleiman #include <asm/unaligned.h>
26236caa7cSMaen Suleiman 
27236caa7cSMaen Suleiman #include "mvsdio.h"
28236caa7cSMaen Suleiman 
29236caa7cSMaen Suleiman #define DRIVER_NAME	"mvsdio"
30236caa7cSMaen Suleiman 
312cd17224SSimon Baatz static int maxfreq;
32236caa7cSMaen Suleiman static int nodma;
33236caa7cSMaen Suleiman 
34236caa7cSMaen Suleiman struct mvsd_host {
35236caa7cSMaen Suleiman 	void __iomem *base;
36236caa7cSMaen Suleiman 	struct mmc_request *mrq;
37236caa7cSMaen Suleiman 	spinlock_t lock;
38236caa7cSMaen Suleiman 	unsigned int xfer_mode;
39236caa7cSMaen Suleiman 	unsigned int intr_en;
40236caa7cSMaen Suleiman 	unsigned int ctrl;
41236caa7cSMaen Suleiman 	unsigned int pio_size;
42236caa7cSMaen Suleiman 	void *pio_ptr;
43236caa7cSMaen Suleiman 	unsigned int sg_frags;
44236caa7cSMaen Suleiman 	unsigned int ns_per_clk;
45236caa7cSMaen Suleiman 	unsigned int clock;
46236caa7cSMaen Suleiman 	unsigned int base_clock;
47236caa7cSMaen Suleiman 	struct timer_list timer;
48236caa7cSMaen Suleiman 	struct mmc_host *mmc;
49236caa7cSMaen Suleiman 	struct device *dev;
50f4f7561eSAndrew Lunn 	struct clk *clk;
51236caa7cSMaen Suleiman };
52236caa7cSMaen Suleiman 
53236caa7cSMaen Suleiman #define mvsd_write(offs, val)	writel(val, iobase + (offs))
54236caa7cSMaen Suleiman #define mvsd_read(offs)		readl(iobase + (offs))
55236caa7cSMaen Suleiman 
mvsd_setup_data(struct mvsd_host * host,struct mmc_data * data)56236caa7cSMaen Suleiman static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data)
57236caa7cSMaen Suleiman {
58236caa7cSMaen Suleiman 	void __iomem *iobase = host->base;
59236caa7cSMaen Suleiman 	unsigned int tmout;
60236caa7cSMaen Suleiman 	int tmout_index;
61236caa7cSMaen Suleiman 
62a6d297f0SNicolas Pitre 	/*
63a6d297f0SNicolas Pitre 	 * Hardware weirdness.  The FIFO_EMPTY bit of the HW_STATE
64a6d297f0SNicolas Pitre 	 * register is sometimes not set before a while when some
65a6d297f0SNicolas Pitre 	 * "unusual" data block sizes are used (such as with the SWITCH
66a6d297f0SNicolas Pitre 	 * command), even despite the fact that the XFER_DONE interrupt
67a6d297f0SNicolas Pitre 	 * was raised.  And if another data transfer starts before
68a6d297f0SNicolas Pitre 	 * this bit comes to good sense (which eventually happens by
69a6d297f0SNicolas Pitre 	 * itself) then the new transfer simply fails with a timeout.
70a6d297f0SNicolas Pitre 	 */
71a6d297f0SNicolas Pitre 	if (!(mvsd_read(MVSD_HW_STATE) & (1 << 13))) {
72a6d297f0SNicolas Pitre 		unsigned long t = jiffies + HZ;
73a6d297f0SNicolas Pitre 		unsigned int hw_state,  count = 0;
74a6d297f0SNicolas Pitre 		do {
75d7fe833fSArnd Bergmann 			hw_state = mvsd_read(MVSD_HW_STATE);
76a6d297f0SNicolas Pitre 			if (time_after(jiffies, t)) {
77a6d297f0SNicolas Pitre 				dev_warn(host->dev, "FIFO_EMPTY bit missing\n");
78a6d297f0SNicolas Pitre 				break;
79a6d297f0SNicolas Pitre 			}
80a6d297f0SNicolas Pitre 			count++;
81a6d297f0SNicolas Pitre 		} while (!(hw_state & (1 << 13)));
82a6d297f0SNicolas Pitre 		dev_dbg(host->dev, "*** wait for FIFO_EMPTY bit "
83a6d297f0SNicolas Pitre 				   "(hw=0x%04x, count=%d, jiffies=%ld)\n",
84a6d297f0SNicolas Pitre 				   hw_state, count, jiffies - (t - HZ));
85a6d297f0SNicolas Pitre 	}
86a6d297f0SNicolas Pitre 
87236caa7cSMaen Suleiman 	/* If timeout=0 then maximum timeout index is used. */
88236caa7cSMaen Suleiman 	tmout = DIV_ROUND_UP(data->timeout_ns, host->ns_per_clk);
89236caa7cSMaen Suleiman 	tmout += data->timeout_clks;
90236caa7cSMaen Suleiman 	tmout_index = fls(tmout - 1) - 12;
91236caa7cSMaen Suleiman 	if (tmout_index < 0)
92236caa7cSMaen Suleiman 		tmout_index = 0;
93236caa7cSMaen Suleiman 	if (tmout_index > MVSD_HOST_CTRL_TMOUT_MAX)
94236caa7cSMaen Suleiman 		tmout_index = MVSD_HOST_CTRL_TMOUT_MAX;
95236caa7cSMaen Suleiman 
96236caa7cSMaen Suleiman 	dev_dbg(host->dev, "data %s at 0x%08x: blocks=%d blksz=%d tmout=%u (%d)\n",
97236caa7cSMaen Suleiman 		(data->flags & MMC_DATA_READ) ? "read" : "write",
98236caa7cSMaen Suleiman 		(u32)sg_virt(data->sg), data->blocks, data->blksz,
99236caa7cSMaen Suleiman 		tmout, tmout_index);
100236caa7cSMaen Suleiman 
101236caa7cSMaen Suleiman 	host->ctrl &= ~MVSD_HOST_CTRL_TMOUT_MASK;
102236caa7cSMaen Suleiman 	host->ctrl |= MVSD_HOST_CTRL_TMOUT(tmout_index);
103236caa7cSMaen Suleiman 	mvsd_write(MVSD_HOST_CTRL, host->ctrl);
104236caa7cSMaen Suleiman 	mvsd_write(MVSD_BLK_COUNT, data->blocks);
105236caa7cSMaen Suleiman 	mvsd_write(MVSD_BLK_SIZE, data->blksz);
106236caa7cSMaen Suleiman 
1073c583f70SAndrew Lunn 	if (nodma || (data->blksz | data->sg->offset) & 3 ||
1083c583f70SAndrew Lunn 	    ((!(data->flags & MMC_DATA_READ) && data->sg->offset & 0x3f))) {
109236caa7cSMaen Suleiman 		/*
110236caa7cSMaen Suleiman 		 * We cannot do DMA on a buffer which offset or size
111236caa7cSMaen Suleiman 		 * is not aligned on a 4-byte boundary.
1123c583f70SAndrew Lunn 		 *
1133c583f70SAndrew Lunn 		 * It also appears the host to card DMA can corrupt
1143c583f70SAndrew Lunn 		 * data when the buffer is not aligned on a 64 byte
1153c583f70SAndrew Lunn 		 * boundary.
116236caa7cSMaen Suleiman 		 */
117236caa7cSMaen Suleiman 		host->pio_size = data->blocks * data->blksz;
118236caa7cSMaen Suleiman 		host->pio_ptr = sg_virt(data->sg);
119236caa7cSMaen Suleiman 		if (!nodma)
120e573d698SThomas Petazzoni 			dev_dbg(host->dev, "fallback to PIO for data at 0x%p size %d\n",
121236caa7cSMaen Suleiman 				host->pio_ptr, host->pio_size);
122236caa7cSMaen Suleiman 		return 1;
123236caa7cSMaen Suleiman 	} else {
124236caa7cSMaen Suleiman 		dma_addr_t phys_addr;
125feeef096SHeiner Kallweit 
126feeef096SHeiner Kallweit 		host->sg_frags = dma_map_sg(mmc_dev(host->mmc),
127feeef096SHeiner Kallweit 					    data->sg, data->sg_len,
128feeef096SHeiner Kallweit 					    mmc_get_dma_dir(data));
129236caa7cSMaen Suleiman 		phys_addr = sg_dma_address(data->sg);
130236caa7cSMaen Suleiman 		mvsd_write(MVSD_SYS_ADDR_LOW, (u32)phys_addr & 0xffff);
131236caa7cSMaen Suleiman 		mvsd_write(MVSD_SYS_ADDR_HI,  (u32)phys_addr >> 16);
132236caa7cSMaen Suleiman 		return 0;
133236caa7cSMaen Suleiman 	}
134236caa7cSMaen Suleiman }
135236caa7cSMaen Suleiman 
mvsd_request(struct mmc_host * mmc,struct mmc_request * mrq)136236caa7cSMaen Suleiman static void mvsd_request(struct mmc_host *mmc, struct mmc_request *mrq)
137236caa7cSMaen Suleiman {
138236caa7cSMaen Suleiman 	struct mvsd_host *host = mmc_priv(mmc);
139236caa7cSMaen Suleiman 	void __iomem *iobase = host->base;
140236caa7cSMaen Suleiman 	struct mmc_command *cmd = mrq->cmd;
141236caa7cSMaen Suleiman 	u32 cmdreg = 0, xfer = 0, intr = 0;
142236caa7cSMaen Suleiman 	unsigned long flags;
1430ef89ec2SUlf Hansson 	unsigned int timeout;
144236caa7cSMaen Suleiman 
145236caa7cSMaen Suleiman 	BUG_ON(host->mrq != NULL);
146236caa7cSMaen Suleiman 	host->mrq = mrq;
147236caa7cSMaen Suleiman 
148236caa7cSMaen Suleiman 	dev_dbg(host->dev, "cmd %d (hw state 0x%04x)\n",
149236caa7cSMaen Suleiman 		cmd->opcode, mvsd_read(MVSD_HW_STATE));
150236caa7cSMaen Suleiman 
151236caa7cSMaen Suleiman 	cmdreg = MVSD_CMD_INDEX(cmd->opcode);
152236caa7cSMaen Suleiman 
153236caa7cSMaen Suleiman 	if (cmd->flags & MMC_RSP_BUSY)
154236caa7cSMaen Suleiman 		cmdreg |= MVSD_CMD_RSP_48BUSY;
155236caa7cSMaen Suleiman 	else if (cmd->flags & MMC_RSP_136)
156236caa7cSMaen Suleiman 		cmdreg |= MVSD_CMD_RSP_136;
157236caa7cSMaen Suleiman 	else if (cmd->flags & MMC_RSP_PRESENT)
158236caa7cSMaen Suleiman 		cmdreg |= MVSD_CMD_RSP_48;
159236caa7cSMaen Suleiman 	else
160236caa7cSMaen Suleiman 		cmdreg |= MVSD_CMD_RSP_NONE;
161236caa7cSMaen Suleiman 
162236caa7cSMaen Suleiman 	if (cmd->flags & MMC_RSP_CRC)
163236caa7cSMaen Suleiman 		cmdreg |= MVSD_CMD_CHECK_CMDCRC;
164236caa7cSMaen Suleiman 
165236caa7cSMaen Suleiman 	if (cmd->flags & MMC_RSP_OPCODE)
166236caa7cSMaen Suleiman 		cmdreg |= MVSD_CMD_INDX_CHECK;
167236caa7cSMaen Suleiman 
168236caa7cSMaen Suleiman 	if (cmd->flags & MMC_RSP_PRESENT) {
169236caa7cSMaen Suleiman 		cmdreg |= MVSD_UNEXPECTED_RESP;
170236caa7cSMaen Suleiman 		intr |= MVSD_NOR_UNEXP_RSP;
171236caa7cSMaen Suleiman 	}
172236caa7cSMaen Suleiman 
173236caa7cSMaen Suleiman 	if (mrq->data) {
174236caa7cSMaen Suleiman 		struct mmc_data *data = mrq->data;
175236caa7cSMaen Suleiman 		int pio;
176236caa7cSMaen Suleiman 
177236caa7cSMaen Suleiman 		cmdreg |= MVSD_CMD_DATA_PRESENT | MVSD_CMD_CHECK_DATACRC16;
178236caa7cSMaen Suleiman 		xfer |= MVSD_XFER_MODE_HW_WR_DATA_EN;
179236caa7cSMaen Suleiman 		if (data->flags & MMC_DATA_READ)
180236caa7cSMaen Suleiman 			xfer |= MVSD_XFER_MODE_TO_HOST;
181236caa7cSMaen Suleiman 
182236caa7cSMaen Suleiman 		pio = mvsd_setup_data(host, data);
183236caa7cSMaen Suleiman 		if (pio) {
184236caa7cSMaen Suleiman 			xfer |= MVSD_XFER_MODE_PIO;
185236caa7cSMaen Suleiman 			/* PIO section of mvsd_irq has comments on those bits */
186236caa7cSMaen Suleiman 			if (data->flags & MMC_DATA_WRITE)
187236caa7cSMaen Suleiman 				intr |= MVSD_NOR_TX_AVAIL;
188236caa7cSMaen Suleiman 			else if (host->pio_size > 32)
189236caa7cSMaen Suleiman 				intr |= MVSD_NOR_RX_FIFO_8W;
190236caa7cSMaen Suleiman 			else
191236caa7cSMaen Suleiman 				intr |= MVSD_NOR_RX_READY;
192236caa7cSMaen Suleiman 		}
193236caa7cSMaen Suleiman 
194236caa7cSMaen Suleiman 		if (data->stop) {
195236caa7cSMaen Suleiman 			struct mmc_command *stop = data->stop;
196236caa7cSMaen Suleiman 			u32 cmd12reg = 0;
197236caa7cSMaen Suleiman 
198236caa7cSMaen Suleiman 			mvsd_write(MVSD_AUTOCMD12_ARG_LOW, stop->arg & 0xffff);
199236caa7cSMaen Suleiman 			mvsd_write(MVSD_AUTOCMD12_ARG_HI,  stop->arg >> 16);
200236caa7cSMaen Suleiman 
201236caa7cSMaen Suleiman 			if (stop->flags & MMC_RSP_BUSY)
202236caa7cSMaen Suleiman 				cmd12reg |= MVSD_AUTOCMD12_BUSY;
203236caa7cSMaen Suleiman 			if (stop->flags & MMC_RSP_OPCODE)
204236caa7cSMaen Suleiman 				cmd12reg |= MVSD_AUTOCMD12_INDX_CHECK;
205236caa7cSMaen Suleiman 			cmd12reg |= MVSD_AUTOCMD12_INDEX(stop->opcode);
206236caa7cSMaen Suleiman 			mvsd_write(MVSD_AUTOCMD12_CMD, cmd12reg);
207236caa7cSMaen Suleiman 
208236caa7cSMaen Suleiman 			xfer |= MVSD_XFER_MODE_AUTO_CMD12;
209236caa7cSMaen Suleiman 			intr |= MVSD_NOR_AUTOCMD12_DONE;
210236caa7cSMaen Suleiman 		} else {
211236caa7cSMaen Suleiman 			intr |= MVSD_NOR_XFER_DONE;
212236caa7cSMaen Suleiman 		}
213236caa7cSMaen Suleiman 	} else {
214236caa7cSMaen Suleiman 		intr |= MVSD_NOR_CMD_DONE;
215236caa7cSMaen Suleiman 	}
216236caa7cSMaen Suleiman 
217236caa7cSMaen Suleiman 	mvsd_write(MVSD_ARG_LOW, cmd->arg & 0xffff);
218236caa7cSMaen Suleiman 	mvsd_write(MVSD_ARG_HI,  cmd->arg >> 16);
219236caa7cSMaen Suleiman 
220236caa7cSMaen Suleiman 	spin_lock_irqsave(&host->lock, flags);
221236caa7cSMaen Suleiman 
222236caa7cSMaen Suleiman 	host->xfer_mode &= MVSD_XFER_MODE_INT_CHK_EN;
223236caa7cSMaen Suleiman 	host->xfer_mode |= xfer;
224236caa7cSMaen Suleiman 	mvsd_write(MVSD_XFER_MODE, host->xfer_mode);
225236caa7cSMaen Suleiman 
226236caa7cSMaen Suleiman 	mvsd_write(MVSD_NOR_INTR_STATUS, ~MVSD_NOR_CARD_INT);
227236caa7cSMaen Suleiman 	mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff);
228236caa7cSMaen Suleiman 	mvsd_write(MVSD_CMD, cmdreg);
229236caa7cSMaen Suleiman 
230236caa7cSMaen Suleiman 	host->intr_en &= MVSD_NOR_CARD_INT;
231236caa7cSMaen Suleiman 	host->intr_en |= intr | MVSD_NOR_ERROR;
232236caa7cSMaen Suleiman 	mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
233236caa7cSMaen Suleiman 	mvsd_write(MVSD_ERR_INTR_EN, 0xffff);
234236caa7cSMaen Suleiman 
2350ef89ec2SUlf Hansson 	timeout = cmd->busy_timeout ? cmd->busy_timeout : 5000;
2360ef89ec2SUlf Hansson 	mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout));
237236caa7cSMaen Suleiman 
238236caa7cSMaen Suleiman 	spin_unlock_irqrestore(&host->lock, flags);
239236caa7cSMaen Suleiman }
240236caa7cSMaen Suleiman 
mvsd_finish_cmd(struct mvsd_host * host,struct mmc_command * cmd,u32 err_status)241236caa7cSMaen Suleiman static u32 mvsd_finish_cmd(struct mvsd_host *host, struct mmc_command *cmd,
242236caa7cSMaen Suleiman 			   u32 err_status)
243236caa7cSMaen Suleiman {
244236caa7cSMaen Suleiman 	void __iomem *iobase = host->base;
245236caa7cSMaen Suleiman 
246236caa7cSMaen Suleiman 	if (cmd->flags & MMC_RSP_136) {
247236caa7cSMaen Suleiman 		unsigned int response[8], i;
248236caa7cSMaen Suleiman 		for (i = 0; i < 8; i++)
249236caa7cSMaen Suleiman 			response[i] = mvsd_read(MVSD_RSP(i));
250236caa7cSMaen Suleiman 		cmd->resp[0] =		((response[0] & 0x03ff) << 22) |
251236caa7cSMaen Suleiman 					((response[1] & 0xffff) << 6) |
252236caa7cSMaen Suleiman 					((response[2] & 0xfc00) >> 10);
253236caa7cSMaen Suleiman 		cmd->resp[1] =		((response[2] & 0x03ff) << 22) |
254236caa7cSMaen Suleiman 					((response[3] & 0xffff) << 6) |
255236caa7cSMaen Suleiman 					((response[4] & 0xfc00) >> 10);
256236caa7cSMaen Suleiman 		cmd->resp[2] =		((response[4] & 0x03ff) << 22) |
257236caa7cSMaen Suleiman 					((response[5] & 0xffff) << 6) |
258236caa7cSMaen Suleiman 					((response[6] & 0xfc00) >> 10);
259236caa7cSMaen Suleiman 		cmd->resp[3] =		((response[6] & 0x03ff) << 22) |
260236caa7cSMaen Suleiman 					((response[7] & 0x3fff) << 8);
261236caa7cSMaen Suleiman 	} else if (cmd->flags & MMC_RSP_PRESENT) {
262236caa7cSMaen Suleiman 		unsigned int response[3], i;
263236caa7cSMaen Suleiman 		for (i = 0; i < 3; i++)
264236caa7cSMaen Suleiman 			response[i] = mvsd_read(MVSD_RSP(i));
265236caa7cSMaen Suleiman 		cmd->resp[0] =		((response[2] & 0x003f) << (8 - 8)) |
266236caa7cSMaen Suleiman 					((response[1] & 0xffff) << (14 - 8)) |
267236caa7cSMaen Suleiman 					((response[0] & 0x03ff) << (30 - 8));
268236caa7cSMaen Suleiman 		cmd->resp[1] =		((response[0] & 0xfc00) >> 10);
269236caa7cSMaen Suleiman 		cmd->resp[2] = 0;
270236caa7cSMaen Suleiman 		cmd->resp[3] = 0;
271236caa7cSMaen Suleiman 	}
272236caa7cSMaen Suleiman 
273236caa7cSMaen Suleiman 	if (err_status & MVSD_ERR_CMD_TIMEOUT) {
274236caa7cSMaen Suleiman 		cmd->error = -ETIMEDOUT;
275236caa7cSMaen Suleiman 	} else if (err_status & (MVSD_ERR_CMD_CRC | MVSD_ERR_CMD_ENDBIT |
276236caa7cSMaen Suleiman 				 MVSD_ERR_CMD_INDEX | MVSD_ERR_CMD_STARTBIT)) {
277236caa7cSMaen Suleiman 		cmd->error = -EILSEQ;
278236caa7cSMaen Suleiman 	}
279236caa7cSMaen Suleiman 	err_status &= ~(MVSD_ERR_CMD_TIMEOUT | MVSD_ERR_CMD_CRC |
280236caa7cSMaen Suleiman 			MVSD_ERR_CMD_ENDBIT | MVSD_ERR_CMD_INDEX |
281236caa7cSMaen Suleiman 			MVSD_ERR_CMD_STARTBIT);
282236caa7cSMaen Suleiman 
283236caa7cSMaen Suleiman 	return err_status;
284236caa7cSMaen Suleiman }
285236caa7cSMaen Suleiman 
mvsd_finish_data(struct mvsd_host * host,struct mmc_data * data,u32 err_status)286236caa7cSMaen Suleiman static u32 mvsd_finish_data(struct mvsd_host *host, struct mmc_data *data,
287236caa7cSMaen Suleiman 			    u32 err_status)
288236caa7cSMaen Suleiman {
289236caa7cSMaen Suleiman 	void __iomem *iobase = host->base;
290236caa7cSMaen Suleiman 
291236caa7cSMaen Suleiman 	if (host->pio_ptr) {
292236caa7cSMaen Suleiman 		host->pio_ptr = NULL;
293236caa7cSMaen Suleiman 		host->pio_size = 0;
294236caa7cSMaen Suleiman 	} else {
295236caa7cSMaen Suleiman 		dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_frags,
296feeef096SHeiner Kallweit 			     mmc_get_dma_dir(data));
297236caa7cSMaen Suleiman 	}
298236caa7cSMaen Suleiman 
299236caa7cSMaen Suleiman 	if (err_status & MVSD_ERR_DATA_TIMEOUT)
300236caa7cSMaen Suleiman 		data->error = -ETIMEDOUT;
301236caa7cSMaen Suleiman 	else if (err_status & (MVSD_ERR_DATA_CRC | MVSD_ERR_DATA_ENDBIT))
302236caa7cSMaen Suleiman 		data->error = -EILSEQ;
303236caa7cSMaen Suleiman 	else if (err_status & MVSD_ERR_XFER_SIZE)
304236caa7cSMaen Suleiman 		data->error = -EBADE;
305236caa7cSMaen Suleiman 	err_status &= ~(MVSD_ERR_DATA_TIMEOUT | MVSD_ERR_DATA_CRC |
306236caa7cSMaen Suleiman 			MVSD_ERR_DATA_ENDBIT | MVSD_ERR_XFER_SIZE);
307236caa7cSMaen Suleiman 
308236caa7cSMaen Suleiman 	dev_dbg(host->dev, "data done: blocks_left=%d, bytes_left=%d\n",
309236caa7cSMaen Suleiman 		mvsd_read(MVSD_CURR_BLK_LEFT), mvsd_read(MVSD_CURR_BYTE_LEFT));
310236caa7cSMaen Suleiman 	data->bytes_xfered =
311236caa7cSMaen Suleiman 		(data->blocks - mvsd_read(MVSD_CURR_BLK_LEFT)) * data->blksz;
312236caa7cSMaen Suleiman 	/* We can't be sure about the last block when errors are detected */
313236caa7cSMaen Suleiman 	if (data->bytes_xfered && data->error)
314236caa7cSMaen Suleiman 		data->bytes_xfered -= data->blksz;
315236caa7cSMaen Suleiman 
316236caa7cSMaen Suleiman 	/* Handle Auto cmd 12 response */
317236caa7cSMaen Suleiman 	if (data->stop) {
318236caa7cSMaen Suleiman 		unsigned int response[3], i;
319236caa7cSMaen Suleiman 		for (i = 0; i < 3; i++)
320236caa7cSMaen Suleiman 			response[i] = mvsd_read(MVSD_AUTO_RSP(i));
321236caa7cSMaen Suleiman 		data->stop->resp[0] =	((response[2] & 0x003f) << (8 - 8)) |
322236caa7cSMaen Suleiman 					((response[1] & 0xffff) << (14 - 8)) |
323236caa7cSMaen Suleiman 					((response[0] & 0x03ff) << (30 - 8));
324236caa7cSMaen Suleiman 		data->stop->resp[1] =	((response[0] & 0xfc00) >> 10);
325236caa7cSMaen Suleiman 		data->stop->resp[2] = 0;
326236caa7cSMaen Suleiman 		data->stop->resp[3] = 0;
327236caa7cSMaen Suleiman 
328236caa7cSMaen Suleiman 		if (err_status & MVSD_ERR_AUTOCMD12) {
329236caa7cSMaen Suleiman 			u32 err_cmd12 = mvsd_read(MVSD_AUTOCMD12_ERR_STATUS);
330236caa7cSMaen Suleiman 			dev_dbg(host->dev, "c12err 0x%04x\n", err_cmd12);
331236caa7cSMaen Suleiman 			if (err_cmd12 & MVSD_AUTOCMD12_ERR_NOTEXE)
332236caa7cSMaen Suleiman 				data->stop->error = -ENOEXEC;
333236caa7cSMaen Suleiman 			else if (err_cmd12 & MVSD_AUTOCMD12_ERR_TIMEOUT)
334236caa7cSMaen Suleiman 				data->stop->error = -ETIMEDOUT;
335236caa7cSMaen Suleiman 			else if (err_cmd12)
336236caa7cSMaen Suleiman 				data->stop->error = -EILSEQ;
337236caa7cSMaen Suleiman 			err_status &= ~MVSD_ERR_AUTOCMD12;
338236caa7cSMaen Suleiman 		}
339236caa7cSMaen Suleiman 	}
340236caa7cSMaen Suleiman 
341236caa7cSMaen Suleiman 	return err_status;
342236caa7cSMaen Suleiman }
343236caa7cSMaen Suleiman 
mvsd_irq(int irq,void * dev)344236caa7cSMaen Suleiman static irqreturn_t mvsd_irq(int irq, void *dev)
345236caa7cSMaen Suleiman {
346236caa7cSMaen Suleiman 	struct mvsd_host *host = dev;
347236caa7cSMaen Suleiman 	void __iomem *iobase = host->base;
348236caa7cSMaen Suleiman 	u32 intr_status, intr_done_mask;
349236caa7cSMaen Suleiman 	int irq_handled = 0;
350236caa7cSMaen Suleiman 
351236caa7cSMaen Suleiman 	intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
352236caa7cSMaen Suleiman 	dev_dbg(host->dev, "intr 0x%04x intr_en 0x%04x hw_state 0x%04x\n",
353236caa7cSMaen Suleiman 		intr_status, mvsd_read(MVSD_NOR_INTR_EN),
354236caa7cSMaen Suleiman 		mvsd_read(MVSD_HW_STATE));
355236caa7cSMaen Suleiman 
356b78871d0SSebastian Hesselbarth 	/*
357b78871d0SSebastian Hesselbarth 	 * It looks like, SDIO IP can issue one late, spurious irq
358b78871d0SSebastian Hesselbarth 	 * although all irqs should be disabled. To work around this,
359b78871d0SSebastian Hesselbarth 	 * bail out early, if we didn't expect any irqs to occur.
360b78871d0SSebastian Hesselbarth 	 */
361b78871d0SSebastian Hesselbarth 	if (!mvsd_read(MVSD_NOR_INTR_EN) && !mvsd_read(MVSD_ERR_INTR_EN)) {
362b78871d0SSebastian Hesselbarth 		dev_dbg(host->dev, "spurious irq detected intr 0x%04x intr_en 0x%04x erri 0x%04x erri_en 0x%04x\n",
363b78871d0SSebastian Hesselbarth 			mvsd_read(MVSD_NOR_INTR_STATUS),
364b78871d0SSebastian Hesselbarth 			mvsd_read(MVSD_NOR_INTR_EN),
365b78871d0SSebastian Hesselbarth 			mvsd_read(MVSD_ERR_INTR_STATUS),
366b78871d0SSebastian Hesselbarth 			mvsd_read(MVSD_ERR_INTR_EN));
367b78871d0SSebastian Hesselbarth 		return IRQ_HANDLED;
368b78871d0SSebastian Hesselbarth 	}
369b78871d0SSebastian Hesselbarth 
370236caa7cSMaen Suleiman 	spin_lock(&host->lock);
371236caa7cSMaen Suleiman 
372236caa7cSMaen Suleiman 	/* PIO handling, if needed. Messy business... */
373236caa7cSMaen Suleiman 	if (host->pio_size &&
374236caa7cSMaen Suleiman 	    (intr_status & host->intr_en &
375236caa7cSMaen Suleiman 	     (MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W))) {
376236caa7cSMaen Suleiman 		u16 *p = host->pio_ptr;
377236caa7cSMaen Suleiman 		int s = host->pio_size;
378236caa7cSMaen Suleiman 		while (s >= 32 && (intr_status & MVSD_NOR_RX_FIFO_8W)) {
379236caa7cSMaen Suleiman 			readsw(iobase + MVSD_FIFO, p, 16);
380236caa7cSMaen Suleiman 			p += 16;
381236caa7cSMaen Suleiman 			s -= 32;
382236caa7cSMaen Suleiman 			intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
383236caa7cSMaen Suleiman 		}
384236caa7cSMaen Suleiman 		/*
385236caa7cSMaen Suleiman 		 * Normally we'd use < 32 here, but the RX_FIFO_8W bit
386236caa7cSMaen Suleiman 		 * doesn't appear to assert when there is exactly 32 bytes
387236caa7cSMaen Suleiman 		 * (8 words) left to fetch in a transfer.
388236caa7cSMaen Suleiman 		 */
389236caa7cSMaen Suleiman 		if (s <= 32) {
390236caa7cSMaen Suleiman 			while (s >= 4 && (intr_status & MVSD_NOR_RX_READY)) {
391236caa7cSMaen Suleiman 				put_unaligned(mvsd_read(MVSD_FIFO), p++);
392236caa7cSMaen Suleiman 				put_unaligned(mvsd_read(MVSD_FIFO), p++);
393236caa7cSMaen Suleiman 				s -= 4;
394236caa7cSMaen Suleiman 				intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
395236caa7cSMaen Suleiman 			}
396236caa7cSMaen Suleiman 			if (s && s < 4 && (intr_status & MVSD_NOR_RX_READY)) {
397236caa7cSMaen Suleiman 				u16 val[2] = {0, 0};
398236caa7cSMaen Suleiman 				val[0] = mvsd_read(MVSD_FIFO);
399236caa7cSMaen Suleiman 				val[1] = mvsd_read(MVSD_FIFO);
4006cdbf734SNicolas Pitre 				memcpy(p, ((void *)&val) + 4 - s, s);
401236caa7cSMaen Suleiman 				s = 0;
402236caa7cSMaen Suleiman 				intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
403236caa7cSMaen Suleiman 			}
404236caa7cSMaen Suleiman 			if (s == 0) {
405236caa7cSMaen Suleiman 				host->intr_en &=
406236caa7cSMaen Suleiman 				     ~(MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W);
407236caa7cSMaen Suleiman 				mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
408236caa7cSMaen Suleiman 			} else if (host->intr_en & MVSD_NOR_RX_FIFO_8W) {
409236caa7cSMaen Suleiman 				host->intr_en &= ~MVSD_NOR_RX_FIFO_8W;
410236caa7cSMaen Suleiman 				host->intr_en |= MVSD_NOR_RX_READY;
411236caa7cSMaen Suleiman 				mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
412236caa7cSMaen Suleiman 			}
413236caa7cSMaen Suleiman 		}
414236caa7cSMaen Suleiman 		dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n",
415236caa7cSMaen Suleiman 			s, intr_status, mvsd_read(MVSD_HW_STATE));
416236caa7cSMaen Suleiman 		host->pio_ptr = p;
417236caa7cSMaen Suleiman 		host->pio_size = s;
418236caa7cSMaen Suleiman 		irq_handled = 1;
419236caa7cSMaen Suleiman 	} else if (host->pio_size &&
420236caa7cSMaen Suleiman 		   (intr_status & host->intr_en &
421236caa7cSMaen Suleiman 		    (MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W))) {
422236caa7cSMaen Suleiman 		u16 *p = host->pio_ptr;
423236caa7cSMaen Suleiman 		int s = host->pio_size;
424236caa7cSMaen Suleiman 		/*
425236caa7cSMaen Suleiman 		 * The TX_FIFO_8W bit is unreliable. When set, bursting
426236caa7cSMaen Suleiman 		 * 16 halfwords all at once in the FIFO drops data. Actually
427236caa7cSMaen Suleiman 		 * TX_AVAIL does go off after only one word is pushed even if
428236caa7cSMaen Suleiman 		 * TX_FIFO_8W remains set.
429236caa7cSMaen Suleiman 		 */
430236caa7cSMaen Suleiman 		while (s >= 4 && (intr_status & MVSD_NOR_TX_AVAIL)) {
431236caa7cSMaen Suleiman 			mvsd_write(MVSD_FIFO, get_unaligned(p++));
432236caa7cSMaen Suleiman 			mvsd_write(MVSD_FIFO, get_unaligned(p++));
433236caa7cSMaen Suleiman 			s -= 4;
434236caa7cSMaen Suleiman 			intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
435236caa7cSMaen Suleiman 		}
436236caa7cSMaen Suleiman 		if (s < 4) {
437236caa7cSMaen Suleiman 			if (s && (intr_status & MVSD_NOR_TX_AVAIL)) {
438236caa7cSMaen Suleiman 				u16 val[2] = {0, 0};
4396cdbf734SNicolas Pitre 				memcpy(((void *)&val) + 4 - s, p, s);
440236caa7cSMaen Suleiman 				mvsd_write(MVSD_FIFO, val[0]);
441236caa7cSMaen Suleiman 				mvsd_write(MVSD_FIFO, val[1]);
442236caa7cSMaen Suleiman 				s = 0;
443236caa7cSMaen Suleiman 				intr_status = mvsd_read(MVSD_NOR_INTR_STATUS);
444236caa7cSMaen Suleiman 			}
445236caa7cSMaen Suleiman 			if (s == 0) {
446236caa7cSMaen Suleiman 				host->intr_en &=
447236caa7cSMaen Suleiman 				     ~(MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W);
448236caa7cSMaen Suleiman 				mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
449236caa7cSMaen Suleiman 			}
450236caa7cSMaen Suleiman 		}
451236caa7cSMaen Suleiman 		dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n",
452236caa7cSMaen Suleiman 			s, intr_status, mvsd_read(MVSD_HW_STATE));
453236caa7cSMaen Suleiman 		host->pio_ptr = p;
454236caa7cSMaen Suleiman 		host->pio_size = s;
455236caa7cSMaen Suleiman 		irq_handled = 1;
456236caa7cSMaen Suleiman 	}
457236caa7cSMaen Suleiman 
458236caa7cSMaen Suleiman 	mvsd_write(MVSD_NOR_INTR_STATUS, intr_status);
459236caa7cSMaen Suleiman 
460236caa7cSMaen Suleiman 	intr_done_mask = MVSD_NOR_CARD_INT | MVSD_NOR_RX_READY |
461236caa7cSMaen Suleiman 			 MVSD_NOR_RX_FIFO_8W | MVSD_NOR_TX_FIFO_8W;
462236caa7cSMaen Suleiman 	if (intr_status & host->intr_en & ~intr_done_mask) {
463236caa7cSMaen Suleiman 		struct mmc_request *mrq = host->mrq;
464236caa7cSMaen Suleiman 		struct mmc_command *cmd = mrq->cmd;
465236caa7cSMaen Suleiman 		u32 err_status = 0;
466236caa7cSMaen Suleiman 
467236caa7cSMaen Suleiman 		del_timer(&host->timer);
468236caa7cSMaen Suleiman 		host->mrq = NULL;
469236caa7cSMaen Suleiman 
470236caa7cSMaen Suleiman 		host->intr_en &= MVSD_NOR_CARD_INT;
471236caa7cSMaen Suleiman 		mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
472236caa7cSMaen Suleiman 		mvsd_write(MVSD_ERR_INTR_EN, 0);
473236caa7cSMaen Suleiman 
474236caa7cSMaen Suleiman 		spin_unlock(&host->lock);
475236caa7cSMaen Suleiman 
476236caa7cSMaen Suleiman 		if (intr_status & MVSD_NOR_UNEXP_RSP) {
477236caa7cSMaen Suleiman 			cmd->error = -EPROTO;
478236caa7cSMaen Suleiman 		} else if (intr_status & MVSD_NOR_ERROR) {
479236caa7cSMaen Suleiman 			err_status = mvsd_read(MVSD_ERR_INTR_STATUS);
480236caa7cSMaen Suleiman 			dev_dbg(host->dev, "err 0x%04x\n", err_status);
481236caa7cSMaen Suleiman 		}
482236caa7cSMaen Suleiman 
483236caa7cSMaen Suleiman 		err_status = mvsd_finish_cmd(host, cmd, err_status);
484236caa7cSMaen Suleiman 		if (mrq->data)
485236caa7cSMaen Suleiman 			err_status = mvsd_finish_data(host, mrq->data, err_status);
486236caa7cSMaen Suleiman 		if (err_status) {
487e573d698SThomas Petazzoni 			dev_err(host->dev, "unhandled error status %#04x\n",
488e573d698SThomas Petazzoni 				err_status);
489236caa7cSMaen Suleiman 			cmd->error = -ENOMSG;
490236caa7cSMaen Suleiman 		}
491236caa7cSMaen Suleiman 
492236caa7cSMaen Suleiman 		mmc_request_done(host->mmc, mrq);
493236caa7cSMaen Suleiman 		irq_handled = 1;
494236caa7cSMaen Suleiman 	} else
495236caa7cSMaen Suleiman 		spin_unlock(&host->lock);
496236caa7cSMaen Suleiman 
497236caa7cSMaen Suleiman 	if (intr_status & MVSD_NOR_CARD_INT) {
498236caa7cSMaen Suleiman 		mmc_signal_sdio_irq(host->mmc);
499236caa7cSMaen Suleiman 		irq_handled = 1;
500236caa7cSMaen Suleiman 	}
501236caa7cSMaen Suleiman 
502236caa7cSMaen Suleiman 	if (irq_handled)
503236caa7cSMaen Suleiman 		return IRQ_HANDLED;
504236caa7cSMaen Suleiman 
505e573d698SThomas Petazzoni 	dev_err(host->dev, "unhandled interrupt status=0x%04x en=0x%04x pio=%d\n",
506e573d698SThomas Petazzoni 		intr_status, host->intr_en, host->pio_size);
507236caa7cSMaen Suleiman 	return IRQ_NONE;
508236caa7cSMaen Suleiman }
509236caa7cSMaen Suleiman 
mvsd_timeout_timer(struct timer_list * t)5102ee4f620SKees Cook static void mvsd_timeout_timer(struct timer_list *t)
511236caa7cSMaen Suleiman {
5122ee4f620SKees Cook 	struct mvsd_host *host = from_timer(host, t, timer);
513236caa7cSMaen Suleiman 	void __iomem *iobase = host->base;
514236caa7cSMaen Suleiman 	struct mmc_request *mrq;
515236caa7cSMaen Suleiman 	unsigned long flags;
516236caa7cSMaen Suleiman 
517236caa7cSMaen Suleiman 	spin_lock_irqsave(&host->lock, flags);
518236caa7cSMaen Suleiman 	mrq = host->mrq;
519236caa7cSMaen Suleiman 	if (mrq) {
520e573d698SThomas Petazzoni 		dev_err(host->dev, "Timeout waiting for hardware interrupt.\n");
521e573d698SThomas Petazzoni 		dev_err(host->dev, "hw_state=0x%04x, intr_status=0x%04x intr_en=0x%04x\n",
522236caa7cSMaen Suleiman 			mvsd_read(MVSD_HW_STATE),
523236caa7cSMaen Suleiman 			mvsd_read(MVSD_NOR_INTR_STATUS),
524236caa7cSMaen Suleiman 			mvsd_read(MVSD_NOR_INTR_EN));
525236caa7cSMaen Suleiman 
526236caa7cSMaen Suleiman 		host->mrq = NULL;
527236caa7cSMaen Suleiman 
528236caa7cSMaen Suleiman 		mvsd_write(MVSD_SW_RESET, MVSD_SW_RESET_NOW);
529236caa7cSMaen Suleiman 
530236caa7cSMaen Suleiman 		host->xfer_mode &= MVSD_XFER_MODE_INT_CHK_EN;
531236caa7cSMaen Suleiman 		mvsd_write(MVSD_XFER_MODE, host->xfer_mode);
532236caa7cSMaen Suleiman 
533236caa7cSMaen Suleiman 		host->intr_en &= MVSD_NOR_CARD_INT;
534236caa7cSMaen Suleiman 		mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
535236caa7cSMaen Suleiman 		mvsd_write(MVSD_ERR_INTR_EN, 0);
536236caa7cSMaen Suleiman 		mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff);
537236caa7cSMaen Suleiman 
538236caa7cSMaen Suleiman 		mrq->cmd->error = -ETIMEDOUT;
539236caa7cSMaen Suleiman 		mvsd_finish_cmd(host, mrq->cmd, 0);
540236caa7cSMaen Suleiman 		if (mrq->data) {
541236caa7cSMaen Suleiman 			mrq->data->error = -ETIMEDOUT;
542236caa7cSMaen Suleiman 			mvsd_finish_data(host, mrq->data, 0);
543236caa7cSMaen Suleiman 		}
544236caa7cSMaen Suleiman 	}
545236caa7cSMaen Suleiman 	spin_unlock_irqrestore(&host->lock, flags);
546236caa7cSMaen Suleiman 
547236caa7cSMaen Suleiman 	if (mrq)
548236caa7cSMaen Suleiman 		mmc_request_done(host->mmc, mrq);
549236caa7cSMaen Suleiman }
550236caa7cSMaen Suleiman 
mvsd_enable_sdio_irq(struct mmc_host * mmc,int enable)551236caa7cSMaen Suleiman static void mvsd_enable_sdio_irq(struct mmc_host *mmc, int enable)
552236caa7cSMaen Suleiman {
553236caa7cSMaen Suleiman 	struct mvsd_host *host = mmc_priv(mmc);
554236caa7cSMaen Suleiman 	void __iomem *iobase = host->base;
555236caa7cSMaen Suleiman 	unsigned long flags;
556236caa7cSMaen Suleiman 
557236caa7cSMaen Suleiman 	spin_lock_irqsave(&host->lock, flags);
558236caa7cSMaen Suleiman 	if (enable) {
559236caa7cSMaen Suleiman 		host->xfer_mode |= MVSD_XFER_MODE_INT_CHK_EN;
560236caa7cSMaen Suleiman 		host->intr_en |= MVSD_NOR_CARD_INT;
561236caa7cSMaen Suleiman 	} else {
562236caa7cSMaen Suleiman 		host->xfer_mode &= ~MVSD_XFER_MODE_INT_CHK_EN;
563236caa7cSMaen Suleiman 		host->intr_en &= ~MVSD_NOR_CARD_INT;
564236caa7cSMaen Suleiman 	}
565236caa7cSMaen Suleiman 	mvsd_write(MVSD_XFER_MODE, host->xfer_mode);
566236caa7cSMaen Suleiman 	mvsd_write(MVSD_NOR_INTR_EN, host->intr_en);
567236caa7cSMaen Suleiman 	spin_unlock_irqrestore(&host->lock, flags);
568236caa7cSMaen Suleiman }
569236caa7cSMaen Suleiman 
mvsd_power_up(struct mvsd_host * host)570236caa7cSMaen Suleiman static void mvsd_power_up(struct mvsd_host *host)
571236caa7cSMaen Suleiman {
572236caa7cSMaen Suleiman 	void __iomem *iobase = host->base;
573236caa7cSMaen Suleiman 	dev_dbg(host->dev, "power up\n");
574236caa7cSMaen Suleiman 	mvsd_write(MVSD_NOR_INTR_EN, 0);
575236caa7cSMaen Suleiman 	mvsd_write(MVSD_ERR_INTR_EN, 0);
576236caa7cSMaen Suleiman 	mvsd_write(MVSD_SW_RESET, MVSD_SW_RESET_NOW);
577236caa7cSMaen Suleiman 	mvsd_write(MVSD_XFER_MODE, 0);
578236caa7cSMaen Suleiman 	mvsd_write(MVSD_NOR_STATUS_EN, 0xffff);
579236caa7cSMaen Suleiman 	mvsd_write(MVSD_ERR_STATUS_EN, 0xffff);
580236caa7cSMaen Suleiman 	mvsd_write(MVSD_NOR_INTR_STATUS, 0xffff);
581236caa7cSMaen Suleiman 	mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff);
582236caa7cSMaen Suleiman }
583236caa7cSMaen Suleiman 
mvsd_power_down(struct mvsd_host * host)584236caa7cSMaen Suleiman static void mvsd_power_down(struct mvsd_host *host)
585236caa7cSMaen Suleiman {
586236caa7cSMaen Suleiman 	void __iomem *iobase = host->base;
587236caa7cSMaen Suleiman 	dev_dbg(host->dev, "power down\n");
588236caa7cSMaen Suleiman 	mvsd_write(MVSD_NOR_INTR_EN, 0);
589236caa7cSMaen Suleiman 	mvsd_write(MVSD_ERR_INTR_EN, 0);
590236caa7cSMaen Suleiman 	mvsd_write(MVSD_SW_RESET, MVSD_SW_RESET_NOW);
591236caa7cSMaen Suleiman 	mvsd_write(MVSD_XFER_MODE, MVSD_XFER_MODE_STOP_CLK);
592236caa7cSMaen Suleiman 	mvsd_write(MVSD_NOR_STATUS_EN, 0);
593236caa7cSMaen Suleiman 	mvsd_write(MVSD_ERR_STATUS_EN, 0);
594236caa7cSMaen Suleiman 	mvsd_write(MVSD_NOR_INTR_STATUS, 0xffff);
595236caa7cSMaen Suleiman 	mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff);
596236caa7cSMaen Suleiman }
597236caa7cSMaen Suleiman 
mvsd_set_ios(struct mmc_host * mmc,struct mmc_ios * ios)598236caa7cSMaen Suleiman static void mvsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
599236caa7cSMaen Suleiman {
600236caa7cSMaen Suleiman 	struct mvsd_host *host = mmc_priv(mmc);
601236caa7cSMaen Suleiman 	void __iomem *iobase = host->base;
602236caa7cSMaen Suleiman 	u32 ctrl_reg = 0;
603236caa7cSMaen Suleiman 
604236caa7cSMaen Suleiman 	if (ios->power_mode == MMC_POWER_UP)
605236caa7cSMaen Suleiman 		mvsd_power_up(host);
606236caa7cSMaen Suleiman 
607236caa7cSMaen Suleiman 	if (ios->clock == 0) {
608236caa7cSMaen Suleiman 		mvsd_write(MVSD_XFER_MODE, MVSD_XFER_MODE_STOP_CLK);
609236caa7cSMaen Suleiman 		mvsd_write(MVSD_CLK_DIV, MVSD_BASE_DIV_MAX);
610236caa7cSMaen Suleiman 		host->clock = 0;
611236caa7cSMaen Suleiman 		dev_dbg(host->dev, "clock off\n");
612236caa7cSMaen Suleiman 	} else if (ios->clock != host->clock) {
613236caa7cSMaen Suleiman 		u32 m = DIV_ROUND_UP(host->base_clock, ios->clock) - 1;
614236caa7cSMaen Suleiman 		if (m > MVSD_BASE_DIV_MAX)
615236caa7cSMaen Suleiman 			m = MVSD_BASE_DIV_MAX;
616236caa7cSMaen Suleiman 		mvsd_write(MVSD_CLK_DIV, m);
617236caa7cSMaen Suleiman 		host->clock = ios->clock;
618236caa7cSMaen Suleiman 		host->ns_per_clk = 1000000000 / (host->base_clock / (m+1));
619236caa7cSMaen Suleiman 		dev_dbg(host->dev, "clock=%d (%d), div=0x%04x\n",
620236caa7cSMaen Suleiman 			ios->clock, host->base_clock / (m+1), m);
621236caa7cSMaen Suleiman 	}
622236caa7cSMaen Suleiman 
623236caa7cSMaen Suleiman 	/* default transfer mode */
624236caa7cSMaen Suleiman 	ctrl_reg |= MVSD_HOST_CTRL_BIG_ENDIAN;
625236caa7cSMaen Suleiman 	ctrl_reg &= ~MVSD_HOST_CTRL_LSB_FIRST;
626236caa7cSMaen Suleiman 
627236caa7cSMaen Suleiman 	/* default to maximum timeout */
628236caa7cSMaen Suleiman 	ctrl_reg |= MVSD_HOST_CTRL_TMOUT_MASK;
629236caa7cSMaen Suleiman 	ctrl_reg |= MVSD_HOST_CTRL_TMOUT_EN;
630236caa7cSMaen Suleiman 
631236caa7cSMaen Suleiman 	if (ios->bus_mode == MMC_BUSMODE_PUSHPULL)
632236caa7cSMaen Suleiman 		ctrl_reg |= MVSD_HOST_CTRL_PUSH_PULL_EN;
633236caa7cSMaen Suleiman 
634236caa7cSMaen Suleiman 	if (ios->bus_width == MMC_BUS_WIDTH_4)
635236caa7cSMaen Suleiman 		ctrl_reg |= MVSD_HOST_CTRL_DATA_WIDTH_4_BITS;
636236caa7cSMaen Suleiman 
6379ca6944cSNicolas Pitre 	/*
6389ca6944cSNicolas Pitre 	 * The HI_SPEED_EN bit is causing trouble with many (but not all)
6399ca6944cSNicolas Pitre 	 * high speed SD, SDHC and SDIO cards.  Not enabling that bit
6409ca6944cSNicolas Pitre 	 * makes all cards work.  So let's just ignore that bit for now
6419ca6944cSNicolas Pitre 	 * and revisit this issue if problems for not enabling this bit
6429ca6944cSNicolas Pitre 	 * are ever reported.
6439ca6944cSNicolas Pitre 	 */
6449ca6944cSNicolas Pitre #if 0
645236caa7cSMaen Suleiman 	if (ios->timing == MMC_TIMING_MMC_HS ||
646236caa7cSMaen Suleiman 	    ios->timing == MMC_TIMING_SD_HS)
647236caa7cSMaen Suleiman 		ctrl_reg |= MVSD_HOST_CTRL_HI_SPEED_EN;
6489ca6944cSNicolas Pitre #endif
649236caa7cSMaen Suleiman 
650236caa7cSMaen Suleiman 	host->ctrl = ctrl_reg;
651236caa7cSMaen Suleiman 	mvsd_write(MVSD_HOST_CTRL, ctrl_reg);
652236caa7cSMaen Suleiman 	dev_dbg(host->dev, "ctrl 0x%04x: %s %s %s\n", ctrl_reg,
653236caa7cSMaen Suleiman 		(ctrl_reg & MVSD_HOST_CTRL_PUSH_PULL_EN) ?
654236caa7cSMaen Suleiman 			"push-pull" : "open-drain",
655236caa7cSMaen Suleiman 		(ctrl_reg & MVSD_HOST_CTRL_DATA_WIDTH_4_BITS) ?
656236caa7cSMaen Suleiman 			"4bit-width" : "1bit-width",
657236caa7cSMaen Suleiman 		(ctrl_reg & MVSD_HOST_CTRL_HI_SPEED_EN) ?
658236caa7cSMaen Suleiman 			"high-speed" : "");
659236caa7cSMaen Suleiman 
660236caa7cSMaen Suleiman 	if (ios->power_mode == MMC_POWER_OFF)
661236caa7cSMaen Suleiman 		mvsd_power_down(host);
662236caa7cSMaen Suleiman }
663236caa7cSMaen Suleiman 
664236caa7cSMaen Suleiman static const struct mmc_host_ops mvsd_ops = {
665236caa7cSMaen Suleiman 	.request		= mvsd_request,
6663724482dSThomas Petazzoni 	.get_ro			= mmc_gpio_get_ro,
667236caa7cSMaen Suleiman 	.set_ios		= mvsd_set_ios,
668236caa7cSMaen Suleiman 	.enable_sdio_irq	= mvsd_enable_sdio_irq,
669236caa7cSMaen Suleiman };
670236caa7cSMaen Suleiman 
6711867adeeSJohan Hovold static void
mv_conf_mbus_windows(struct mvsd_host * host,const struct mbus_dram_target_info * dram)67263a9332bSAndrew Lunn mv_conf_mbus_windows(struct mvsd_host *host,
67363a9332bSAndrew Lunn 		     const struct mbus_dram_target_info *dram)
674236caa7cSMaen Suleiman {
675236caa7cSMaen Suleiman 	void __iomem *iobase = host->base;
676236caa7cSMaen Suleiman 	int i;
677236caa7cSMaen Suleiman 
678236caa7cSMaen Suleiman 	for (i = 0; i < 4; i++) {
679236caa7cSMaen Suleiman 		writel(0, iobase + MVSD_WINDOW_CTRL(i));
680236caa7cSMaen Suleiman 		writel(0, iobase + MVSD_WINDOW_BASE(i));
681236caa7cSMaen Suleiman 	}
682236caa7cSMaen Suleiman 
683236caa7cSMaen Suleiman 	for (i = 0; i < dram->num_cs; i++) {
68463a9332bSAndrew Lunn 		const struct mbus_dram_window *cs = dram->cs + i;
685236caa7cSMaen Suleiman 		writel(((cs->size - 1) & 0xffff0000) |
686236caa7cSMaen Suleiman 		       (cs->mbus_attr << 8) |
687236caa7cSMaen Suleiman 		       (dram->mbus_dram_target_id << 4) | 1,
688236caa7cSMaen Suleiman 		       iobase + MVSD_WINDOW_CTRL(i));
689236caa7cSMaen Suleiman 		writel(cs->base, iobase + MVSD_WINDOW_BASE(i));
690236caa7cSMaen Suleiman 	}
691236caa7cSMaen Suleiman }
692236caa7cSMaen Suleiman 
mvsd_probe(struct platform_device * pdev)6931867adeeSJohan Hovold static int mvsd_probe(struct platform_device *pdev)
694236caa7cSMaen Suleiman {
695111936ffSThomas Petazzoni 	struct device_node *np = pdev->dev.of_node;
696236caa7cSMaen Suleiman 	struct mmc_host *mmc = NULL;
697236caa7cSMaen Suleiman 	struct mvsd_host *host = NULL;
69863a9332bSAndrew Lunn 	const struct mbus_dram_target_info *dram;
699236caa7cSMaen Suleiman 	int ret, irq;
700236caa7cSMaen Suleiman 
70117da6783SLinus Walleij 	if (!np) {
70217da6783SLinus Walleij 		dev_err(&pdev->dev, "no DT node\n");
70317da6783SLinus Walleij 		return -ENODEV;
70417da6783SLinus Walleij 	}
705236caa7cSMaen Suleiman 	irq = platform_get_irq(pdev, 0);
7060a337eb1SYangtao Li 	if (irq < 0)
7078d84064dSSergey Shtylyov 		return irq;
708236caa7cSMaen Suleiman 
709236caa7cSMaen Suleiman 	mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev);
710236caa7cSMaen Suleiman 	if (!mmc) {
711236caa7cSMaen Suleiman 		ret = -ENOMEM;
712236caa7cSMaen Suleiman 		goto out;
713236caa7cSMaen Suleiman 	}
714236caa7cSMaen Suleiman 
715236caa7cSMaen Suleiman 	host = mmc_priv(mmc);
716236caa7cSMaen Suleiman 	host->mmc = mmc;
717236caa7cSMaen Suleiman 	host->dev = &pdev->dev;
718111936ffSThomas Petazzoni 
719111936ffSThomas Petazzoni 	/*
720111936ffSThomas Petazzoni 	 * Some non-DT platforms do not pass a clock, and the clock
721111936ffSThomas Petazzoni 	 * frequency is passed through platform_data. On DT platforms,
722111936ffSThomas Petazzoni 	 * a clock must always be passed, even if there is no gatable
723111936ffSThomas Petazzoni 	 * clock associated to the SDIO interface (it can simply be a
724111936ffSThomas Petazzoni 	 * fixed rate clock).
725111936ffSThomas Petazzoni 	 */
726111936ffSThomas Petazzoni 	host->clk = devm_clk_get(&pdev->dev, NULL);
72717da6783SLinus Walleij 	if (IS_ERR(host->clk)) {
72817da6783SLinus Walleij 		dev_err(&pdev->dev, "no clock associated\n");
72917da6783SLinus Walleij 		ret = -EINVAL;
73017da6783SLinus Walleij 		goto out;
73117da6783SLinus Walleij 	}
732111936ffSThomas Petazzoni 	clk_prepare_enable(host->clk);
733111936ffSThomas Petazzoni 
7342cd17224SSimon Baatz 	mmc->ops = &mvsd_ops;
7352cd17224SSimon Baatz 
7362cd17224SSimon Baatz 	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
7372cd17224SSimon Baatz 
7382cd17224SSimon Baatz 	mmc->f_min = DIV_ROUND_UP(host->base_clock, MVSD_BASE_DIV_MAX);
7392cd17224SSimon Baatz 	mmc->f_max = MVSD_CLOCKRATE_MAX;
7402cd17224SSimon Baatz 
7412cd17224SSimon Baatz 	mmc->max_blk_size = 2048;
7422cd17224SSimon Baatz 	mmc->max_blk_count = 65535;
7432cd17224SSimon Baatz 
7442cd17224SSimon Baatz 	mmc->max_segs = 1;
7452cd17224SSimon Baatz 	mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count;
7462cd17224SSimon Baatz 	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
7472cd17224SSimon Baatz 
748111936ffSThomas Petazzoni 	host->base_clock = clk_get_rate(host->clk) / 2;
7492cd17224SSimon Baatz 	ret = mmc_of_parse(mmc);
7502cd17224SSimon Baatz 	if (ret < 0)
7512cd17224SSimon Baatz 		goto out;
7522cd17224SSimon Baatz 	if (maxfreq)
753236caa7cSMaen Suleiman 		mmc->f_max = maxfreq;
754236caa7cSMaen Suleiman 
755236caa7cSMaen Suleiman 	spin_lock_init(&host->lock);
756236caa7cSMaen Suleiman 
7570a337eb1SYangtao Li 	host->base = devm_platform_ioremap_resource(pdev, 0);
758e02d2930SSachin Kamat 	if (IS_ERR(host->base)) {
759e02d2930SSachin Kamat 		ret = PTR_ERR(host->base);
760236caa7cSMaen Suleiman 		goto out;
761236caa7cSMaen Suleiman 	}
762236caa7cSMaen Suleiman 
763236caa7cSMaen Suleiman 	/* (Re-)program MBUS remapping windows if we are asked to. */
76463a9332bSAndrew Lunn 	dram = mv_mbus_dram_info();
76563a9332bSAndrew Lunn 	if (dram)
76663a9332bSAndrew Lunn 		mv_conf_mbus_windows(host, dram);
767236caa7cSMaen Suleiman 
768236caa7cSMaen Suleiman 	mvsd_power_down(host);
769236caa7cSMaen Suleiman 
770f42abc72SAndrew Lunn 	ret = devm_request_irq(&pdev->dev, irq, mvsd_irq, 0, DRIVER_NAME, host);
771236caa7cSMaen Suleiman 	if (ret) {
772e573d698SThomas Petazzoni 		dev_err(&pdev->dev, "cannot assign irq %d\n", irq);
773236caa7cSMaen Suleiman 		goto out;
774f42abc72SAndrew Lunn 	}
775236caa7cSMaen Suleiman 
7762ee4f620SKees Cook 	timer_setup(&host->timer, mvsd_timeout_timer, 0);
777236caa7cSMaen Suleiman 	platform_set_drvdata(pdev, mmc);
778236caa7cSMaen Suleiman 	ret = mmc_add_host(mmc);
779236caa7cSMaen Suleiman 	if (ret)
780236caa7cSMaen Suleiman 		goto out;
781236caa7cSMaen Suleiman 
78207728b77SThomas Petazzoni 	if (!(mmc->caps & MMC_CAP_NEEDS_POLL))
783fcf902beSSebastian Hesselbarth 		dev_dbg(&pdev->dev, "using GPIO for card detection\n");
784236caa7cSMaen Suleiman 	else
785fcf902beSSebastian Hesselbarth 		dev_dbg(&pdev->dev, "lacking card detect (fall back to polling)\n");
786fcf902beSSebastian Hesselbarth 
787236caa7cSMaen Suleiman 	return 0;
788236caa7cSMaen Suleiman 
789236caa7cSMaen Suleiman out:
790f42abc72SAndrew Lunn 	if (mmc) {
791f42abc72SAndrew Lunn 		if (!IS_ERR(host->clk))
792baffab28SSimon Baatz 			clk_disable_unprepare(host->clk);
793236caa7cSMaen Suleiman 		mmc_free_host(mmc);
794f42abc72SAndrew Lunn 	}
795236caa7cSMaen Suleiman 
796236caa7cSMaen Suleiman 	return ret;
797236caa7cSMaen Suleiman }
798236caa7cSMaen Suleiman 
mvsd_remove(struct platform_device * pdev)799*0fbfbfbaSYangtao Li static void mvsd_remove(struct platform_device *pdev)
800236caa7cSMaen Suleiman {
801236caa7cSMaen Suleiman 	struct mmc_host *mmc = platform_get_drvdata(pdev);
802236caa7cSMaen Suleiman 
803236caa7cSMaen Suleiman 	struct mvsd_host *host = mmc_priv(mmc);
804236caa7cSMaen Suleiman 
805236caa7cSMaen Suleiman 	mmc_remove_host(mmc);
806236caa7cSMaen Suleiman 	del_timer_sync(&host->timer);
807236caa7cSMaen Suleiman 	mvsd_power_down(host);
808f4f7561eSAndrew Lunn 
809f42abc72SAndrew Lunn 	if (!IS_ERR(host->clk))
810f4f7561eSAndrew Lunn 		clk_disable_unprepare(host->clk);
811236caa7cSMaen Suleiman 	mmc_free_host(mmc);
812236caa7cSMaen Suleiman }
813236caa7cSMaen Suleiman 
814111936ffSThomas Petazzoni static const struct of_device_id mvsdio_dt_ids[] = {
815111936ffSThomas Petazzoni 	{ .compatible = "marvell,orion-sdio" },
816111936ffSThomas Petazzoni 	{ /* sentinel */ }
817111936ffSThomas Petazzoni };
818111936ffSThomas Petazzoni MODULE_DEVICE_TABLE(of, mvsdio_dt_ids);
819111936ffSThomas Petazzoni 
820236caa7cSMaen Suleiman static struct platform_driver mvsd_driver = {
8211867adeeSJohan Hovold 	.probe		= mvsd_probe,
822*0fbfbfbaSYangtao Li 	.remove_new	= mvsd_remove,
823236caa7cSMaen Suleiman 	.driver		= {
824236caa7cSMaen Suleiman 		.name	= DRIVER_NAME,
82521b2cec6SDouglas Anderson 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
826111936ffSThomas Petazzoni 		.of_match_table = mvsdio_dt_ids,
827236caa7cSMaen Suleiman 	},
828236caa7cSMaen Suleiman };
829236caa7cSMaen Suleiman 
8301867adeeSJohan Hovold module_platform_driver(mvsd_driver);
831236caa7cSMaen Suleiman 
832236caa7cSMaen Suleiman /* maximum card clock frequency (default 50MHz) */
833236caa7cSMaen Suleiman module_param(maxfreq, int, 0);
834236caa7cSMaen Suleiman 
835236caa7cSMaen Suleiman /* force PIO transfers all the time */
836236caa7cSMaen Suleiman module_param(nodma, int, 0);
837236caa7cSMaen Suleiman 
838236caa7cSMaen Suleiman MODULE_AUTHOR("Maen Suleiman, Nicolas Pitre");
839236caa7cSMaen Suleiman MODULE_DESCRIPTION("Marvell MMC,SD,SDIO Host Controller driver");
840236caa7cSMaen Suleiman MODULE_LICENSE("GPL");
841703aacedSNicolas Pitre MODULE_ALIAS("platform:mvsdio");
842