11c6a0718SPierre Ossman /* 270f10482SPierre Ossman * linux/drivers/mmc/host/pxa.c - PXA MMCI driver 31c6a0718SPierre Ossman * 41c6a0718SPierre Ossman * Copyright (C) 2003 Russell King, All Rights Reserved. 51c6a0718SPierre Ossman * 61c6a0718SPierre Ossman * This program is free software; you can redistribute it and/or modify 71c6a0718SPierre Ossman * it under the terms of the GNU General Public License version 2 as 81c6a0718SPierre Ossman * published by the Free Software Foundation. 91c6a0718SPierre Ossman * 101c6a0718SPierre Ossman * This hardware is really sick: 111c6a0718SPierre Ossman * - No way to clear interrupts. 121c6a0718SPierre Ossman * - Have to turn off the clock whenever we touch the device. 131c6a0718SPierre Ossman * - Doesn't tell you how many data blocks were transferred. 141c6a0718SPierre Ossman * Yuck! 151c6a0718SPierre Ossman * 161c6a0718SPierre Ossman * 1 and 3 byte data transfers not supported 171c6a0718SPierre Ossman * max block length up to 1023 181c6a0718SPierre Ossman */ 191c6a0718SPierre Ossman #include <linux/module.h> 201c6a0718SPierre Ossman #include <linux/init.h> 211c6a0718SPierre Ossman #include <linux/ioport.h> 221c6a0718SPierre Ossman #include <linux/platform_device.h> 231c6a0718SPierre Ossman #include <linux/delay.h> 241c6a0718SPierre Ossman #include <linux/interrupt.h> 251c6a0718SPierre Ossman #include <linux/dma-mapping.h> 261c6a0718SPierre Ossman #include <linux/mmc/host.h> 271c6a0718SPierre Ossman 281c6a0718SPierre Ossman #include <asm/dma.h> 291c6a0718SPierre Ossman #include <asm/io.h> 301c6a0718SPierre Ossman #include <asm/scatterlist.h> 311c6a0718SPierre Ossman #include <asm/sizes.h> 321c6a0718SPierre Ossman 331c6a0718SPierre Ossman #include <asm/arch/pxa-regs.h> 341c6a0718SPierre Ossman #include <asm/arch/mmc.h> 351c6a0718SPierre Ossman 361c6a0718SPierre Ossman #include "pxamci.h" 371c6a0718SPierre Ossman 381c6a0718SPierre Ossman #define DRIVER_NAME "pxa2xx-mci" 391c6a0718SPierre Ossman 401c6a0718SPierre Ossman #define NR_SG 1 411c6a0718SPierre Ossman 421c6a0718SPierre Ossman struct pxamci_host { 431c6a0718SPierre Ossman struct mmc_host *mmc; 441c6a0718SPierre Ossman spinlock_t lock; 451c6a0718SPierre Ossman struct resource *res; 461c6a0718SPierre Ossman void __iomem *base; 471c6a0718SPierre Ossman int irq; 481c6a0718SPierre Ossman int dma; 491c6a0718SPierre Ossman unsigned int clkrt; 501c6a0718SPierre Ossman unsigned int cmdat; 511c6a0718SPierre Ossman unsigned int imask; 521c6a0718SPierre Ossman unsigned int power_mode; 531c6a0718SPierre Ossman struct pxamci_platform_data *pdata; 541c6a0718SPierre Ossman 551c6a0718SPierre Ossman struct mmc_request *mrq; 561c6a0718SPierre Ossman struct mmc_command *cmd; 571c6a0718SPierre Ossman struct mmc_data *data; 581c6a0718SPierre Ossman 591c6a0718SPierre Ossman dma_addr_t sg_dma; 601c6a0718SPierre Ossman struct pxa_dma_desc *sg_cpu; 611c6a0718SPierre Ossman unsigned int dma_len; 621c6a0718SPierre Ossman 631c6a0718SPierre Ossman unsigned int dma_dir; 641c6a0718SPierre Ossman }; 651c6a0718SPierre Ossman 661c6a0718SPierre Ossman static void pxamci_stop_clock(struct pxamci_host *host) 671c6a0718SPierre Ossman { 681c6a0718SPierre Ossman if (readl(host->base + MMC_STAT) & STAT_CLK_EN) { 691c6a0718SPierre Ossman unsigned long timeout = 10000; 701c6a0718SPierre Ossman unsigned int v; 711c6a0718SPierre Ossman 721c6a0718SPierre Ossman writel(STOP_CLOCK, host->base + MMC_STRPCL); 731c6a0718SPierre Ossman 741c6a0718SPierre Ossman do { 751c6a0718SPierre Ossman v = readl(host->base + MMC_STAT); 761c6a0718SPierre Ossman if (!(v & STAT_CLK_EN)) 771c6a0718SPierre Ossman break; 781c6a0718SPierre Ossman udelay(1); 791c6a0718SPierre Ossman } while (timeout--); 801c6a0718SPierre Ossman 811c6a0718SPierre Ossman if (v & STAT_CLK_EN) 821c6a0718SPierre Ossman dev_err(mmc_dev(host->mmc), "unable to stop clock\n"); 831c6a0718SPierre Ossman } 841c6a0718SPierre Ossman } 851c6a0718SPierre Ossman 861c6a0718SPierre Ossman static void pxamci_enable_irq(struct pxamci_host *host, unsigned int mask) 871c6a0718SPierre Ossman { 881c6a0718SPierre Ossman unsigned long flags; 891c6a0718SPierre Ossman 901c6a0718SPierre Ossman spin_lock_irqsave(&host->lock, flags); 911c6a0718SPierre Ossman host->imask &= ~mask; 921c6a0718SPierre Ossman writel(host->imask, host->base + MMC_I_MASK); 931c6a0718SPierre Ossman spin_unlock_irqrestore(&host->lock, flags); 941c6a0718SPierre Ossman } 951c6a0718SPierre Ossman 961c6a0718SPierre Ossman static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask) 971c6a0718SPierre Ossman { 981c6a0718SPierre Ossman unsigned long flags; 991c6a0718SPierre Ossman 1001c6a0718SPierre Ossman spin_lock_irqsave(&host->lock, flags); 1011c6a0718SPierre Ossman host->imask |= mask; 1021c6a0718SPierre Ossman writel(host->imask, host->base + MMC_I_MASK); 1031c6a0718SPierre Ossman spin_unlock_irqrestore(&host->lock, flags); 1041c6a0718SPierre Ossman } 1051c6a0718SPierre Ossman 1061c6a0718SPierre Ossman static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) 1071c6a0718SPierre Ossman { 1081c6a0718SPierre Ossman unsigned int nob = data->blocks; 1091c6a0718SPierre Ossman unsigned long long clks; 1101c6a0718SPierre Ossman unsigned int timeout; 1111c6a0718SPierre Ossman u32 dcmd; 1121c6a0718SPierre Ossman int i; 1131c6a0718SPierre Ossman 1141c6a0718SPierre Ossman host->data = data; 1151c6a0718SPierre Ossman 1161c6a0718SPierre Ossman if (data->flags & MMC_DATA_STREAM) 1171c6a0718SPierre Ossman nob = 0xffff; 1181c6a0718SPierre Ossman 1191c6a0718SPierre Ossman writel(nob, host->base + MMC_NOB); 1201c6a0718SPierre Ossman writel(data->blksz, host->base + MMC_BLKLEN); 1211c6a0718SPierre Ossman 1221c6a0718SPierre Ossman clks = (unsigned long long)data->timeout_ns * CLOCKRATE; 1231c6a0718SPierre Ossman do_div(clks, 1000000000UL); 1241c6a0718SPierre Ossman timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt); 1251c6a0718SPierre Ossman writel((timeout + 255) / 256, host->base + MMC_RDTO); 1261c6a0718SPierre Ossman 1271c6a0718SPierre Ossman if (data->flags & MMC_DATA_READ) { 1281c6a0718SPierre Ossman host->dma_dir = DMA_FROM_DEVICE; 1291c6a0718SPierre Ossman dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG; 1301c6a0718SPierre Ossman DRCMRTXMMC = 0; 1311c6a0718SPierre Ossman DRCMRRXMMC = host->dma | DRCMR_MAPVLD; 1321c6a0718SPierre Ossman } else { 1331c6a0718SPierre Ossman host->dma_dir = DMA_TO_DEVICE; 1341c6a0718SPierre Ossman dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC; 1351c6a0718SPierre Ossman DRCMRRXMMC = 0; 1361c6a0718SPierre Ossman DRCMRTXMMC = host->dma | DRCMR_MAPVLD; 1371c6a0718SPierre Ossman } 1381c6a0718SPierre Ossman 1391c6a0718SPierre Ossman dcmd |= DCMD_BURST32 | DCMD_WIDTH1; 1401c6a0718SPierre Ossman 1411c6a0718SPierre Ossman host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, 1421c6a0718SPierre Ossman host->dma_dir); 1431c6a0718SPierre Ossman 1441c6a0718SPierre Ossman for (i = 0; i < host->dma_len; i++) { 1451c6a0718SPierre Ossman if (data->flags & MMC_DATA_READ) { 1461c6a0718SPierre Ossman host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO; 1471c6a0718SPierre Ossman host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]); 1481c6a0718SPierre Ossman } else { 1491c6a0718SPierre Ossman host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]); 1501c6a0718SPierre Ossman host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO; 1511c6a0718SPierre Ossman } 1521c6a0718SPierre Ossman host->sg_cpu[i].dcmd = dcmd | sg_dma_len(&data->sg[i]); 1531c6a0718SPierre Ossman host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) * 1541c6a0718SPierre Ossman sizeof(struct pxa_dma_desc); 1551c6a0718SPierre Ossman } 1561c6a0718SPierre Ossman host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP; 1571c6a0718SPierre Ossman wmb(); 1581c6a0718SPierre Ossman 1591c6a0718SPierre Ossman DDADR(host->dma) = host->sg_dma; 1601c6a0718SPierre Ossman DCSR(host->dma) = DCSR_RUN; 1611c6a0718SPierre Ossman } 1621c6a0718SPierre Ossman 1631c6a0718SPierre Ossman static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat) 1641c6a0718SPierre Ossman { 1651c6a0718SPierre Ossman WARN_ON(host->cmd != NULL); 1661c6a0718SPierre Ossman host->cmd = cmd; 1671c6a0718SPierre Ossman 1681c6a0718SPierre Ossman if (cmd->flags & MMC_RSP_BUSY) 1691c6a0718SPierre Ossman cmdat |= CMDAT_BUSY; 1701c6a0718SPierre Ossman 1711c6a0718SPierre Ossman #define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE)) 1721c6a0718SPierre Ossman switch (RSP_TYPE(mmc_resp_type(cmd))) { 1731c6a0718SPierre Ossman case RSP_TYPE(MMC_RSP_R1): /* r1, r1b, r6, r7 */ 1741c6a0718SPierre Ossman cmdat |= CMDAT_RESP_SHORT; 1751c6a0718SPierre Ossman break; 1761c6a0718SPierre Ossman case RSP_TYPE(MMC_RSP_R3): 1771c6a0718SPierre Ossman cmdat |= CMDAT_RESP_R3; 1781c6a0718SPierre Ossman break; 1791c6a0718SPierre Ossman case RSP_TYPE(MMC_RSP_R2): 1801c6a0718SPierre Ossman cmdat |= CMDAT_RESP_R2; 1811c6a0718SPierre Ossman break; 1821c6a0718SPierre Ossman default: 1831c6a0718SPierre Ossman break; 1841c6a0718SPierre Ossman } 1851c6a0718SPierre Ossman 1861c6a0718SPierre Ossman writel(cmd->opcode, host->base + MMC_CMD); 1871c6a0718SPierre Ossman writel(cmd->arg >> 16, host->base + MMC_ARGH); 1881c6a0718SPierre Ossman writel(cmd->arg & 0xffff, host->base + MMC_ARGL); 1891c6a0718SPierre Ossman writel(cmdat, host->base + MMC_CMDAT); 1901c6a0718SPierre Ossman writel(host->clkrt, host->base + MMC_CLKRT); 1911c6a0718SPierre Ossman 1921c6a0718SPierre Ossman writel(START_CLOCK, host->base + MMC_STRPCL); 1931c6a0718SPierre Ossman 1941c6a0718SPierre Ossman pxamci_enable_irq(host, END_CMD_RES); 1951c6a0718SPierre Ossman } 1961c6a0718SPierre Ossman 1971c6a0718SPierre Ossman static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq) 1981c6a0718SPierre Ossman { 1991c6a0718SPierre Ossman host->mrq = NULL; 2001c6a0718SPierre Ossman host->cmd = NULL; 2011c6a0718SPierre Ossman host->data = NULL; 2021c6a0718SPierre Ossman mmc_request_done(host->mmc, mrq); 2031c6a0718SPierre Ossman } 2041c6a0718SPierre Ossman 2051c6a0718SPierre Ossman static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat) 2061c6a0718SPierre Ossman { 2071c6a0718SPierre Ossman struct mmc_command *cmd = host->cmd; 2081c6a0718SPierre Ossman int i; 2091c6a0718SPierre Ossman u32 v; 2101c6a0718SPierre Ossman 2111c6a0718SPierre Ossman if (!cmd) 2121c6a0718SPierre Ossman return 0; 2131c6a0718SPierre Ossman 2141c6a0718SPierre Ossman host->cmd = NULL; 2151c6a0718SPierre Ossman 2161c6a0718SPierre Ossman /* 2171c6a0718SPierre Ossman * Did I mention this is Sick. We always need to 2181c6a0718SPierre Ossman * discard the upper 8 bits of the first 16-bit word. 2191c6a0718SPierre Ossman */ 2201c6a0718SPierre Ossman v = readl(host->base + MMC_RES) & 0xffff; 2211c6a0718SPierre Ossman for (i = 0; i < 4; i++) { 2221c6a0718SPierre Ossman u32 w1 = readl(host->base + MMC_RES) & 0xffff; 2231c6a0718SPierre Ossman u32 w2 = readl(host->base + MMC_RES) & 0xffff; 2241c6a0718SPierre Ossman cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8; 2251c6a0718SPierre Ossman v = w2; 2261c6a0718SPierre Ossman } 2271c6a0718SPierre Ossman 2281c6a0718SPierre Ossman if (stat & STAT_TIME_OUT_RESPONSE) { 2291c6a0718SPierre Ossman cmd->error = MMC_ERR_TIMEOUT; 2301c6a0718SPierre Ossman } else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) { 2311c6a0718SPierre Ossman #ifdef CONFIG_PXA27x 2321c6a0718SPierre Ossman /* 2331c6a0718SPierre Ossman * workaround for erratum #42: 2341c6a0718SPierre Ossman * Intel PXA27x Family Processor Specification Update Rev 001 23590e07d9fSNicolas Pitre * A bogus CRC error can appear if the msb of a 136 bit 23690e07d9fSNicolas Pitre * response is a one. 2371c6a0718SPierre Ossman */ 23890e07d9fSNicolas Pitre if (cmd->flags & MMC_RSP_136 && cmd->resp[0] & 0x80000000) { 2391c6a0718SPierre Ossman pr_debug("ignoring CRC from command %d - *risky*\n", cmd->opcode); 24090e07d9fSNicolas Pitre } else 2411c6a0718SPierre Ossman #endif 24290e07d9fSNicolas Pitre cmd->error = MMC_ERR_BADCRC; 2431c6a0718SPierre Ossman } 2441c6a0718SPierre Ossman 2451c6a0718SPierre Ossman pxamci_disable_irq(host, END_CMD_RES); 2461c6a0718SPierre Ossman if (host->data && cmd->error == MMC_ERR_NONE) { 2471c6a0718SPierre Ossman pxamci_enable_irq(host, DATA_TRAN_DONE); 2481c6a0718SPierre Ossman } else { 2491c6a0718SPierre Ossman pxamci_finish_request(host, host->mrq); 2501c6a0718SPierre Ossman } 2511c6a0718SPierre Ossman 2521c6a0718SPierre Ossman return 1; 2531c6a0718SPierre Ossman } 2541c6a0718SPierre Ossman 2551c6a0718SPierre Ossman static int pxamci_data_done(struct pxamci_host *host, unsigned int stat) 2561c6a0718SPierre Ossman { 2571c6a0718SPierre Ossman struct mmc_data *data = host->data; 2581c6a0718SPierre Ossman 2591c6a0718SPierre Ossman if (!data) 2601c6a0718SPierre Ossman return 0; 2611c6a0718SPierre Ossman 2621c6a0718SPierre Ossman DCSR(host->dma) = 0; 2631c6a0718SPierre Ossman dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, 2641c6a0718SPierre Ossman host->dma_dir); 2651c6a0718SPierre Ossman 2661c6a0718SPierre Ossman if (stat & STAT_READ_TIME_OUT) 2671c6a0718SPierre Ossman data->error = MMC_ERR_TIMEOUT; 2681c6a0718SPierre Ossman else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR)) 2691c6a0718SPierre Ossman data->error = MMC_ERR_BADCRC; 2701c6a0718SPierre Ossman 2711c6a0718SPierre Ossman /* 2721c6a0718SPierre Ossman * There appears to be a hardware design bug here. There seems to 2731c6a0718SPierre Ossman * be no way to find out how much data was transferred to the card. 2741c6a0718SPierre Ossman * This means that if there was an error on any block, we mark all 2751c6a0718SPierre Ossman * data blocks as being in error. 2761c6a0718SPierre Ossman */ 2771c6a0718SPierre Ossman if (data->error == MMC_ERR_NONE) 2781c6a0718SPierre Ossman data->bytes_xfered = data->blocks * data->blksz; 2791c6a0718SPierre Ossman else 2801c6a0718SPierre Ossman data->bytes_xfered = 0; 2811c6a0718SPierre Ossman 2821c6a0718SPierre Ossman pxamci_disable_irq(host, DATA_TRAN_DONE); 2831c6a0718SPierre Ossman 2841c6a0718SPierre Ossman host->data = NULL; 2851c6a0718SPierre Ossman if (host->mrq->stop) { 2861c6a0718SPierre Ossman pxamci_stop_clock(host); 2871c6a0718SPierre Ossman pxamci_start_cmd(host, host->mrq->stop, 0); 2881c6a0718SPierre Ossman } else { 2891c6a0718SPierre Ossman pxamci_finish_request(host, host->mrq); 2901c6a0718SPierre Ossman } 2911c6a0718SPierre Ossman 2921c6a0718SPierre Ossman return 1; 2931c6a0718SPierre Ossman } 2941c6a0718SPierre Ossman 2951c6a0718SPierre Ossman static irqreturn_t pxamci_irq(int irq, void *devid) 2961c6a0718SPierre Ossman { 2971c6a0718SPierre Ossman struct pxamci_host *host = devid; 2981c6a0718SPierre Ossman unsigned int ireg; 2991c6a0718SPierre Ossman int handled = 0; 3001c6a0718SPierre Ossman 3011c6a0718SPierre Ossman ireg = readl(host->base + MMC_I_REG); 3021c6a0718SPierre Ossman 3031c6a0718SPierre Ossman if (ireg) { 3041c6a0718SPierre Ossman unsigned stat = readl(host->base + MMC_STAT); 3051c6a0718SPierre Ossman 3061c6a0718SPierre Ossman pr_debug("PXAMCI: irq %08x stat %08x\n", ireg, stat); 3071c6a0718SPierre Ossman 3081c6a0718SPierre Ossman if (ireg & END_CMD_RES) 3091c6a0718SPierre Ossman handled |= pxamci_cmd_done(host, stat); 3101c6a0718SPierre Ossman if (ireg & DATA_TRAN_DONE) 3111c6a0718SPierre Ossman handled |= pxamci_data_done(host, stat); 3121c6a0718SPierre Ossman } 3131c6a0718SPierre Ossman 3141c6a0718SPierre Ossman return IRQ_RETVAL(handled); 3151c6a0718SPierre Ossman } 3161c6a0718SPierre Ossman 3171c6a0718SPierre Ossman static void pxamci_request(struct mmc_host *mmc, struct mmc_request *mrq) 3181c6a0718SPierre Ossman { 3191c6a0718SPierre Ossman struct pxamci_host *host = mmc_priv(mmc); 3201c6a0718SPierre Ossman unsigned int cmdat; 3211c6a0718SPierre Ossman 3221c6a0718SPierre Ossman WARN_ON(host->mrq != NULL); 3231c6a0718SPierre Ossman 3241c6a0718SPierre Ossman host->mrq = mrq; 3251c6a0718SPierre Ossman 3261c6a0718SPierre Ossman pxamci_stop_clock(host); 3271c6a0718SPierre Ossman 3281c6a0718SPierre Ossman cmdat = host->cmdat; 3291c6a0718SPierre Ossman host->cmdat &= ~CMDAT_INIT; 3301c6a0718SPierre Ossman 3311c6a0718SPierre Ossman if (mrq->data) { 3321c6a0718SPierre Ossman pxamci_setup_data(host, mrq->data); 3331c6a0718SPierre Ossman 3341c6a0718SPierre Ossman cmdat &= ~CMDAT_BUSY; 3351c6a0718SPierre Ossman cmdat |= CMDAT_DATAEN | CMDAT_DMAEN; 3361c6a0718SPierre Ossman if (mrq->data->flags & MMC_DATA_WRITE) 3371c6a0718SPierre Ossman cmdat |= CMDAT_WRITE; 3381c6a0718SPierre Ossman 3391c6a0718SPierre Ossman if (mrq->data->flags & MMC_DATA_STREAM) 3401c6a0718SPierre Ossman cmdat |= CMDAT_STREAM; 3411c6a0718SPierre Ossman } 3421c6a0718SPierre Ossman 3431c6a0718SPierre Ossman pxamci_start_cmd(host, mrq->cmd, cmdat); 3441c6a0718SPierre Ossman } 3451c6a0718SPierre Ossman 3461c6a0718SPierre Ossman static int pxamci_get_ro(struct mmc_host *mmc) 3471c6a0718SPierre Ossman { 3481c6a0718SPierre Ossman struct pxamci_host *host = mmc_priv(mmc); 3491c6a0718SPierre Ossman 3501c6a0718SPierre Ossman if (host->pdata && host->pdata->get_ro) 3511c6a0718SPierre Ossman return host->pdata->get_ro(mmc_dev(mmc)); 3521c6a0718SPierre Ossman /* Host doesn't support read only detection so assume writeable */ 3531c6a0718SPierre Ossman return 0; 3541c6a0718SPierre Ossman } 3551c6a0718SPierre Ossman 3561c6a0718SPierre Ossman static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 3571c6a0718SPierre Ossman { 3581c6a0718SPierre Ossman struct pxamci_host *host = mmc_priv(mmc); 3591c6a0718SPierre Ossman 3601c6a0718SPierre Ossman if (ios->clock) { 3611c6a0718SPierre Ossman unsigned int clk = CLOCKRATE / ios->clock; 3621c6a0718SPierre Ossman if (CLOCKRATE / clk > ios->clock) 3631c6a0718SPierre Ossman clk <<= 1; 3641c6a0718SPierre Ossman host->clkrt = fls(clk) - 1; 365c6799adeSLinus Torvalds pxa_set_cken(CKEN_MMC, 1); 3661c6a0718SPierre Ossman 3671c6a0718SPierre Ossman /* 3681c6a0718SPierre Ossman * we write clkrt on the next command 3691c6a0718SPierre Ossman */ 3701c6a0718SPierre Ossman } else { 3711c6a0718SPierre Ossman pxamci_stop_clock(host); 372c6799adeSLinus Torvalds pxa_set_cken(CKEN_MMC, 0); 3731c6a0718SPierre Ossman } 3741c6a0718SPierre Ossman 3751c6a0718SPierre Ossman if (host->power_mode != ios->power_mode) { 3761c6a0718SPierre Ossman host->power_mode = ios->power_mode; 3771c6a0718SPierre Ossman 3781c6a0718SPierre Ossman if (host->pdata && host->pdata->setpower) 3791c6a0718SPierre Ossman host->pdata->setpower(mmc_dev(mmc), ios->vdd); 3801c6a0718SPierre Ossman 3811c6a0718SPierre Ossman if (ios->power_mode == MMC_POWER_ON) 3821c6a0718SPierre Ossman host->cmdat |= CMDAT_INIT; 3831c6a0718SPierre Ossman } 3841c6a0718SPierre Ossman 3851c6a0718SPierre Ossman pr_debug("PXAMCI: clkrt = %x cmdat = %x\n", 3861c6a0718SPierre Ossman host->clkrt, host->cmdat); 3871c6a0718SPierre Ossman } 3881c6a0718SPierre Ossman 3891c6a0718SPierre Ossman static const struct mmc_host_ops pxamci_ops = { 3901c6a0718SPierre Ossman .request = pxamci_request, 3911c6a0718SPierre Ossman .get_ro = pxamci_get_ro, 3921c6a0718SPierre Ossman .set_ios = pxamci_set_ios, 3931c6a0718SPierre Ossman }; 3941c6a0718SPierre Ossman 3951c6a0718SPierre Ossman static void pxamci_dma_irq(int dma, void *devid) 3961c6a0718SPierre Ossman { 3971c6a0718SPierre Ossman printk(KERN_ERR "DMA%d: IRQ???\n", dma); 3981c6a0718SPierre Ossman DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; 3991c6a0718SPierre Ossman } 4001c6a0718SPierre Ossman 4011c6a0718SPierre Ossman static irqreturn_t pxamci_detect_irq(int irq, void *devid) 4021c6a0718SPierre Ossman { 4031c6a0718SPierre Ossman struct pxamci_host *host = mmc_priv(devid); 4041c6a0718SPierre Ossman 4051c6a0718SPierre Ossman mmc_detect_change(devid, host->pdata->detect_delay); 4061c6a0718SPierre Ossman return IRQ_HANDLED; 4071c6a0718SPierre Ossman } 4081c6a0718SPierre Ossman 4091c6a0718SPierre Ossman static int pxamci_probe(struct platform_device *pdev) 4101c6a0718SPierre Ossman { 4111c6a0718SPierre Ossman struct mmc_host *mmc; 4121c6a0718SPierre Ossman struct pxamci_host *host = NULL; 4131c6a0718SPierre Ossman struct resource *r; 4141c6a0718SPierre Ossman int ret, irq; 4151c6a0718SPierre Ossman 4161c6a0718SPierre Ossman r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4171c6a0718SPierre Ossman irq = platform_get_irq(pdev, 0); 4181c6a0718SPierre Ossman if (!r || irq < 0) 4191c6a0718SPierre Ossman return -ENXIO; 4201c6a0718SPierre Ossman 4211c6a0718SPierre Ossman r = request_mem_region(r->start, SZ_4K, DRIVER_NAME); 4221c6a0718SPierre Ossman if (!r) 4231c6a0718SPierre Ossman return -EBUSY; 4241c6a0718SPierre Ossman 4251c6a0718SPierre Ossman mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev); 4261c6a0718SPierre Ossman if (!mmc) { 4271c6a0718SPierre Ossman ret = -ENOMEM; 4281c6a0718SPierre Ossman goto out; 4291c6a0718SPierre Ossman } 4301c6a0718SPierre Ossman 4311c6a0718SPierre Ossman mmc->ops = &pxamci_ops; 4321c6a0718SPierre Ossman mmc->f_min = CLOCKRATE_MIN; 4331c6a0718SPierre Ossman mmc->f_max = CLOCKRATE_MAX; 4341c6a0718SPierre Ossman 4351c6a0718SPierre Ossman /* 4361c6a0718SPierre Ossman * We can do SG-DMA, but we don't because we never know how much 4371c6a0718SPierre Ossman * data we successfully wrote to the card. 4381c6a0718SPierre Ossman */ 4391c6a0718SPierre Ossman mmc->max_phys_segs = NR_SG; 4401c6a0718SPierre Ossman 4411c6a0718SPierre Ossman /* 4421c6a0718SPierre Ossman * Our hardware DMA can handle a maximum of one page per SG entry. 4431c6a0718SPierre Ossman */ 4441c6a0718SPierre Ossman mmc->max_seg_size = PAGE_SIZE; 4451c6a0718SPierre Ossman 4461c6a0718SPierre Ossman /* 4471c6a0718SPierre Ossman * Block length register is 10 bits. 4481c6a0718SPierre Ossman */ 4491c6a0718SPierre Ossman mmc->max_blk_size = 1023; 4501c6a0718SPierre Ossman 4511c6a0718SPierre Ossman /* 4521c6a0718SPierre Ossman * Block count register is 16 bits. 4531c6a0718SPierre Ossman */ 4541c6a0718SPierre Ossman mmc->max_blk_count = 65535; 4551c6a0718SPierre Ossman 4561c6a0718SPierre Ossman host = mmc_priv(mmc); 4571c6a0718SPierre Ossman host->mmc = mmc; 4581c6a0718SPierre Ossman host->dma = -1; 4591c6a0718SPierre Ossman host->pdata = pdev->dev.platform_data; 4601c6a0718SPierre Ossman mmc->ocr_avail = host->pdata ? 4611c6a0718SPierre Ossman host->pdata->ocr_mask : 4621c6a0718SPierre Ossman MMC_VDD_32_33|MMC_VDD_33_34; 4631c6a0718SPierre Ossman 4641c6a0718SPierre Ossman host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); 4651c6a0718SPierre Ossman if (!host->sg_cpu) { 4661c6a0718SPierre Ossman ret = -ENOMEM; 4671c6a0718SPierre Ossman goto out; 4681c6a0718SPierre Ossman } 4691c6a0718SPierre Ossman 4701c6a0718SPierre Ossman spin_lock_init(&host->lock); 4711c6a0718SPierre Ossman host->res = r; 4721c6a0718SPierre Ossman host->irq = irq; 4731c6a0718SPierre Ossman host->imask = MMC_I_MASK_ALL; 4741c6a0718SPierre Ossman 4751c6a0718SPierre Ossman host->base = ioremap(r->start, SZ_4K); 4761c6a0718SPierre Ossman if (!host->base) { 4771c6a0718SPierre Ossman ret = -ENOMEM; 4781c6a0718SPierre Ossman goto out; 4791c6a0718SPierre Ossman } 4801c6a0718SPierre Ossman 4811c6a0718SPierre Ossman /* 4821c6a0718SPierre Ossman * Ensure that the host controller is shut down, and setup 4831c6a0718SPierre Ossman * with our defaults. 4841c6a0718SPierre Ossman */ 4851c6a0718SPierre Ossman pxamci_stop_clock(host); 4861c6a0718SPierre Ossman writel(0, host->base + MMC_SPI); 4871c6a0718SPierre Ossman writel(64, host->base + MMC_RESTO); 4881c6a0718SPierre Ossman writel(host->imask, host->base + MMC_I_MASK); 4891c6a0718SPierre Ossman 4901c6a0718SPierre Ossman host->dma = pxa_request_dma(DRIVER_NAME, DMA_PRIO_LOW, 4911c6a0718SPierre Ossman pxamci_dma_irq, host); 4921c6a0718SPierre Ossman if (host->dma < 0) { 4931c6a0718SPierre Ossman ret = -EBUSY; 4941c6a0718SPierre Ossman goto out; 4951c6a0718SPierre Ossman } 4961c6a0718SPierre Ossman 4971c6a0718SPierre Ossman ret = request_irq(host->irq, pxamci_irq, 0, DRIVER_NAME, host); 4981c6a0718SPierre Ossman if (ret) 4991c6a0718SPierre Ossman goto out; 5001c6a0718SPierre Ossman 5011c6a0718SPierre Ossman platform_set_drvdata(pdev, mmc); 5021c6a0718SPierre Ossman 5031c6a0718SPierre Ossman if (host->pdata && host->pdata->init) 5041c6a0718SPierre Ossman host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc); 5051c6a0718SPierre Ossman 5061c6a0718SPierre Ossman mmc_add_host(mmc); 5071c6a0718SPierre Ossman 5081c6a0718SPierre Ossman return 0; 5091c6a0718SPierre Ossman 5101c6a0718SPierre Ossman out: 5111c6a0718SPierre Ossman if (host) { 5121c6a0718SPierre Ossman if (host->dma >= 0) 5131c6a0718SPierre Ossman pxa_free_dma(host->dma); 5141c6a0718SPierre Ossman if (host->base) 5151c6a0718SPierre Ossman iounmap(host->base); 5161c6a0718SPierre Ossman if (host->sg_cpu) 5171c6a0718SPierre Ossman dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); 5181c6a0718SPierre Ossman } 5191c6a0718SPierre Ossman if (mmc) 5201c6a0718SPierre Ossman mmc_free_host(mmc); 5211c6a0718SPierre Ossman release_resource(r); 5221c6a0718SPierre Ossman return ret; 5231c6a0718SPierre Ossman } 5241c6a0718SPierre Ossman 5251c6a0718SPierre Ossman static int pxamci_remove(struct platform_device *pdev) 5261c6a0718SPierre Ossman { 5271c6a0718SPierre Ossman struct mmc_host *mmc = platform_get_drvdata(pdev); 5281c6a0718SPierre Ossman 5291c6a0718SPierre Ossman platform_set_drvdata(pdev, NULL); 5301c6a0718SPierre Ossman 5311c6a0718SPierre Ossman if (mmc) { 5321c6a0718SPierre Ossman struct pxamci_host *host = mmc_priv(mmc); 5331c6a0718SPierre Ossman 5341c6a0718SPierre Ossman if (host->pdata && host->pdata->exit) 5351c6a0718SPierre Ossman host->pdata->exit(&pdev->dev, mmc); 5361c6a0718SPierre Ossman 5371c6a0718SPierre Ossman mmc_remove_host(mmc); 5381c6a0718SPierre Ossman 5391c6a0718SPierre Ossman pxamci_stop_clock(host); 5401c6a0718SPierre Ossman writel(TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD| 5411c6a0718SPierre Ossman END_CMD_RES|PRG_DONE|DATA_TRAN_DONE, 5421c6a0718SPierre Ossman host->base + MMC_I_MASK); 5431c6a0718SPierre Ossman 5441c6a0718SPierre Ossman DRCMRRXMMC = 0; 5451c6a0718SPierre Ossman DRCMRTXMMC = 0; 5461c6a0718SPierre Ossman 5471c6a0718SPierre Ossman free_irq(host->irq, host); 5481c6a0718SPierre Ossman pxa_free_dma(host->dma); 5491c6a0718SPierre Ossman iounmap(host->base); 5501c6a0718SPierre Ossman dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); 5511c6a0718SPierre Ossman 5521c6a0718SPierre Ossman release_resource(host->res); 5531c6a0718SPierre Ossman 5541c6a0718SPierre Ossman mmc_free_host(mmc); 5551c6a0718SPierre Ossman } 5561c6a0718SPierre Ossman return 0; 5571c6a0718SPierre Ossman } 5581c6a0718SPierre Ossman 5591c6a0718SPierre Ossman #ifdef CONFIG_PM 5601c6a0718SPierre Ossman static int pxamci_suspend(struct platform_device *dev, pm_message_t state) 5611c6a0718SPierre Ossman { 5621c6a0718SPierre Ossman struct mmc_host *mmc = platform_get_drvdata(dev); 5631c6a0718SPierre Ossman int ret = 0; 5641c6a0718SPierre Ossman 5651c6a0718SPierre Ossman if (mmc) 5661c6a0718SPierre Ossman ret = mmc_suspend_host(mmc, state); 5671c6a0718SPierre Ossman 5681c6a0718SPierre Ossman return ret; 5691c6a0718SPierre Ossman } 5701c6a0718SPierre Ossman 5711c6a0718SPierre Ossman static int pxamci_resume(struct platform_device *dev) 5721c6a0718SPierre Ossman { 5731c6a0718SPierre Ossman struct mmc_host *mmc = platform_get_drvdata(dev); 5741c6a0718SPierre Ossman int ret = 0; 5751c6a0718SPierre Ossman 5761c6a0718SPierre Ossman if (mmc) 5771c6a0718SPierre Ossman ret = mmc_resume_host(mmc); 5781c6a0718SPierre Ossman 5791c6a0718SPierre Ossman return ret; 5801c6a0718SPierre Ossman } 5811c6a0718SPierre Ossman #else 5821c6a0718SPierre Ossman #define pxamci_suspend NULL 5831c6a0718SPierre Ossman #define pxamci_resume NULL 5841c6a0718SPierre Ossman #endif 5851c6a0718SPierre Ossman 5861c6a0718SPierre Ossman static struct platform_driver pxamci_driver = { 5871c6a0718SPierre Ossman .probe = pxamci_probe, 5881c6a0718SPierre Ossman .remove = pxamci_remove, 5891c6a0718SPierre Ossman .suspend = pxamci_suspend, 5901c6a0718SPierre Ossman .resume = pxamci_resume, 5911c6a0718SPierre Ossman .driver = { 5921c6a0718SPierre Ossman .name = DRIVER_NAME, 5931c6a0718SPierre Ossman }, 5941c6a0718SPierre Ossman }; 5951c6a0718SPierre Ossman 5961c6a0718SPierre Ossman static int __init pxamci_init(void) 5971c6a0718SPierre Ossman { 5981c6a0718SPierre Ossman return platform_driver_register(&pxamci_driver); 5991c6a0718SPierre Ossman } 6001c6a0718SPierre Ossman 6011c6a0718SPierre Ossman static void __exit pxamci_exit(void) 6021c6a0718SPierre Ossman { 6031c6a0718SPierre Ossman platform_driver_unregister(&pxamci_driver); 6041c6a0718SPierre Ossman } 6051c6a0718SPierre Ossman 6061c6a0718SPierre Ossman module_init(pxamci_init); 6071c6a0718SPierre Ossman module_exit(pxamci_exit); 6081c6a0718SPierre Ossman 6091c6a0718SPierre Ossman MODULE_DESCRIPTION("PXA Multimedia Card Interface Driver"); 6101c6a0718SPierre Ossman MODULE_LICENSE("GPL"); 611