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 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 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 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 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 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 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 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 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 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 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 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 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 struct resource *r; 700236caa7cSMaen Suleiman int ret, irq; 701236caa7cSMaen Suleiman 70217da6783SLinus Walleij if (!np) { 70317da6783SLinus Walleij dev_err(&pdev->dev, "no DT node\n"); 70417da6783SLinus Walleij return -ENODEV; 70517da6783SLinus Walleij } 706236caa7cSMaen Suleiman r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 707236caa7cSMaen Suleiman irq = platform_get_irq(pdev, 0); 708111936ffSThomas Petazzoni if (!r || irq < 0) 709236caa7cSMaen Suleiman return -ENXIO; 710236caa7cSMaen Suleiman 711236caa7cSMaen Suleiman mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev); 712236caa7cSMaen Suleiman if (!mmc) { 713236caa7cSMaen Suleiman ret = -ENOMEM; 714236caa7cSMaen Suleiman goto out; 715236caa7cSMaen Suleiman } 716236caa7cSMaen Suleiman 717236caa7cSMaen Suleiman host = mmc_priv(mmc); 718236caa7cSMaen Suleiman host->mmc = mmc; 719236caa7cSMaen Suleiman host->dev = &pdev->dev; 720111936ffSThomas Petazzoni 721111936ffSThomas Petazzoni /* 722111936ffSThomas Petazzoni * Some non-DT platforms do not pass a clock, and the clock 723111936ffSThomas Petazzoni * frequency is passed through platform_data. On DT platforms, 724111936ffSThomas Petazzoni * a clock must always be passed, even if there is no gatable 725111936ffSThomas Petazzoni * clock associated to the SDIO interface (it can simply be a 726111936ffSThomas Petazzoni * fixed rate clock). 727111936ffSThomas Petazzoni */ 728111936ffSThomas Petazzoni host->clk = devm_clk_get(&pdev->dev, NULL); 72917da6783SLinus Walleij if (IS_ERR(host->clk)) { 73017da6783SLinus Walleij dev_err(&pdev->dev, "no clock associated\n"); 73117da6783SLinus Walleij ret = -EINVAL; 73217da6783SLinus Walleij goto out; 73317da6783SLinus Walleij } 734111936ffSThomas Petazzoni clk_prepare_enable(host->clk); 735111936ffSThomas Petazzoni 7362cd17224SSimon Baatz mmc->ops = &mvsd_ops; 7372cd17224SSimon Baatz 7382cd17224SSimon Baatz mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; 7392cd17224SSimon Baatz 7402cd17224SSimon Baatz mmc->f_min = DIV_ROUND_UP(host->base_clock, MVSD_BASE_DIV_MAX); 7412cd17224SSimon Baatz mmc->f_max = MVSD_CLOCKRATE_MAX; 7422cd17224SSimon Baatz 7432cd17224SSimon Baatz mmc->max_blk_size = 2048; 7442cd17224SSimon Baatz mmc->max_blk_count = 65535; 7452cd17224SSimon Baatz 7462cd17224SSimon Baatz mmc->max_segs = 1; 7472cd17224SSimon Baatz mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count; 7482cd17224SSimon Baatz mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; 7492cd17224SSimon Baatz 750111936ffSThomas Petazzoni host->base_clock = clk_get_rate(host->clk) / 2; 7512cd17224SSimon Baatz ret = mmc_of_parse(mmc); 7522cd17224SSimon Baatz if (ret < 0) 7532cd17224SSimon Baatz goto out; 7542cd17224SSimon Baatz if (maxfreq) 755236caa7cSMaen Suleiman mmc->f_max = maxfreq; 756236caa7cSMaen Suleiman 75777252da7SUlf Hansson mmc->caps |= MMC_CAP_ERASE; 75877252da7SUlf Hansson 759236caa7cSMaen Suleiman spin_lock_init(&host->lock); 760236caa7cSMaen Suleiman 761e02d2930SSachin Kamat host->base = devm_ioremap_resource(&pdev->dev, r); 762e02d2930SSachin Kamat if (IS_ERR(host->base)) { 763e02d2930SSachin Kamat ret = PTR_ERR(host->base); 764236caa7cSMaen Suleiman goto out; 765236caa7cSMaen Suleiman } 766236caa7cSMaen Suleiman 767236caa7cSMaen Suleiman /* (Re-)program MBUS remapping windows if we are asked to. */ 76863a9332bSAndrew Lunn dram = mv_mbus_dram_info(); 76963a9332bSAndrew Lunn if (dram) 77063a9332bSAndrew Lunn mv_conf_mbus_windows(host, dram); 771236caa7cSMaen Suleiman 772236caa7cSMaen Suleiman mvsd_power_down(host); 773236caa7cSMaen Suleiman 774f42abc72SAndrew Lunn ret = devm_request_irq(&pdev->dev, irq, mvsd_irq, 0, DRIVER_NAME, host); 775236caa7cSMaen Suleiman if (ret) { 776e573d698SThomas Petazzoni dev_err(&pdev->dev, "cannot assign irq %d\n", irq); 777236caa7cSMaen Suleiman goto out; 778f42abc72SAndrew Lunn } 779236caa7cSMaen Suleiman 7802ee4f620SKees Cook timer_setup(&host->timer, mvsd_timeout_timer, 0); 781236caa7cSMaen Suleiman platform_set_drvdata(pdev, mmc); 782236caa7cSMaen Suleiman ret = mmc_add_host(mmc); 783236caa7cSMaen Suleiman if (ret) 784236caa7cSMaen Suleiman goto out; 785236caa7cSMaen Suleiman 78607728b77SThomas Petazzoni if (!(mmc->caps & MMC_CAP_NEEDS_POLL)) 787fcf902beSSebastian Hesselbarth dev_dbg(&pdev->dev, "using GPIO for card detection\n"); 788236caa7cSMaen Suleiman else 789fcf902beSSebastian Hesselbarth dev_dbg(&pdev->dev, "lacking card detect (fall back to polling)\n"); 790fcf902beSSebastian Hesselbarth 791236caa7cSMaen Suleiman return 0; 792236caa7cSMaen Suleiman 793236caa7cSMaen Suleiman out: 794f42abc72SAndrew Lunn if (mmc) { 795f42abc72SAndrew Lunn if (!IS_ERR(host->clk)) 796baffab28SSimon Baatz clk_disable_unprepare(host->clk); 797236caa7cSMaen Suleiman mmc_free_host(mmc); 798f42abc72SAndrew Lunn } 799236caa7cSMaen Suleiman 800236caa7cSMaen Suleiman return ret; 801236caa7cSMaen Suleiman } 802236caa7cSMaen Suleiman 8031867adeeSJohan Hovold static int mvsd_remove(struct platform_device *pdev) 804236caa7cSMaen Suleiman { 805236caa7cSMaen Suleiman struct mmc_host *mmc = platform_get_drvdata(pdev); 806236caa7cSMaen Suleiman 807236caa7cSMaen Suleiman struct mvsd_host *host = mmc_priv(mmc); 808236caa7cSMaen Suleiman 809236caa7cSMaen Suleiman mmc_remove_host(mmc); 810236caa7cSMaen Suleiman del_timer_sync(&host->timer); 811236caa7cSMaen Suleiman mvsd_power_down(host); 812f4f7561eSAndrew Lunn 813f42abc72SAndrew Lunn if (!IS_ERR(host->clk)) 814f4f7561eSAndrew Lunn clk_disable_unprepare(host->clk); 815236caa7cSMaen Suleiman mmc_free_host(mmc); 816f42abc72SAndrew Lunn 817236caa7cSMaen Suleiman return 0; 818236caa7cSMaen Suleiman } 819236caa7cSMaen Suleiman 820111936ffSThomas Petazzoni static const struct of_device_id mvsdio_dt_ids[] = { 821111936ffSThomas Petazzoni { .compatible = "marvell,orion-sdio" }, 822111936ffSThomas Petazzoni { /* sentinel */ } 823111936ffSThomas Petazzoni }; 824111936ffSThomas Petazzoni MODULE_DEVICE_TABLE(of, mvsdio_dt_ids); 825111936ffSThomas Petazzoni 826236caa7cSMaen Suleiman static struct platform_driver mvsd_driver = { 8271867adeeSJohan Hovold .probe = mvsd_probe, 8281867adeeSJohan Hovold .remove = mvsd_remove, 829236caa7cSMaen Suleiman .driver = { 830236caa7cSMaen Suleiman .name = DRIVER_NAME, 831111936ffSThomas Petazzoni .of_match_table = mvsdio_dt_ids, 832236caa7cSMaen Suleiman }, 833236caa7cSMaen Suleiman }; 834236caa7cSMaen Suleiman 8351867adeeSJohan Hovold module_platform_driver(mvsd_driver); 836236caa7cSMaen Suleiman 837236caa7cSMaen Suleiman /* maximum card clock frequency (default 50MHz) */ 838236caa7cSMaen Suleiman module_param(maxfreq, int, 0); 839236caa7cSMaen Suleiman 840236caa7cSMaen Suleiman /* force PIO transfers all the time */ 841236caa7cSMaen Suleiman module_param(nodma, int, 0); 842236caa7cSMaen Suleiman 843236caa7cSMaen Suleiman MODULE_AUTHOR("Maen Suleiman, Nicolas Pitre"); 844236caa7cSMaen Suleiman MODULE_DESCRIPTION("Marvell MMC,SD,SDIO Host Controller driver"); 845236caa7cSMaen Suleiman MODULE_LICENSE("GPL"); 846703aacedSNicolas Pitre MODULE_ALIAS("platform:mvsdio"); 847