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