#include <linux/device.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
-#include <linux/blkdev.h>
#include <linux/dma-mapping.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <asm/irq.h>
+#include <asm/scatterlist.h>
#include <asm/sizes.h>
#include <asm/arch/pxa-regs.h>
#define DBG(x...) do { } while (0)
#endif
+#define DRIVER_NAME "pxa2xx-mci"
+
+#define NR_SG 1
+
struct pxamci_host {
struct mmc_host *mmc;
spinlock_t lock;
struct resource *res;
- void *base;
+ void __iomem *base;
int irq;
int dma;
unsigned int clkrt;
dma_addr_t sg_dma;
struct pxa_dma_desc *sg_cpu;
+ unsigned int dma_len;
- dma_addr_t dma_buf;
- unsigned int dma_size;
unsigned int dma_dir;
};
-/*
- * The base MMC clock rate
- */
-#define CLOCKRATE 20000000
-
static inline unsigned int ns_to_clocks(unsigned int ns)
{
return (ns * (CLOCKRATE / 1000000) + 999) / 1000;
static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
{
unsigned int nob = data->blocks;
- unsigned int timeout, size;
- dma_addr_t dma;
+ unsigned int timeout;
u32 dcmd;
int i;
dcmd |= DCMD_BURST32 | DCMD_WIDTH1;
- host->dma_size = data->blocks << data->blksz_bits;
- host->dma_buf = dma_map_single(mmc_dev(host->mmc), data->req->buffer,
- host->dma_size, host->dma_dir);
-
- for (i = 0, size = host->dma_size, dma = host->dma_buf; size; i++) {
- u32 len = size;
-
- if (len > DCMD_LENGTH)
- len = 0x1000;
+ host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ host->dma_dir);
+ for (i = 0; i < host->dma_len; i++) {
if (data->flags & MMC_DATA_READ) {
host->sg_cpu[i].dsadr = host->res->start + MMC_RXFIFO;
- host->sg_cpu[i].dtadr = dma;
+ host->sg_cpu[i].dtadr = sg_dma_address(&data->sg[i]);
} else {
- host->sg_cpu[i].dsadr = dma;
+ host->sg_cpu[i].dsadr = sg_dma_address(&data->sg[i]);
host->sg_cpu[i].dtadr = host->res->start + MMC_TXFIFO;
}
- host->sg_cpu[i].dcmd = dcmd | len;
-
- dma += len;
- size -= len;
-
- if (size) {
- host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) *
- sizeof(struct pxa_dma_desc);
- } else {
- host->sg_cpu[i].ddadr = DDADR_STOP;
- }
+ host->sg_cpu[i].dcmd = dcmd | sg_dma_len(&data->sg[i]);
+ host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) *
+ sizeof(struct pxa_dma_desc);
}
+ host->sg_cpu[host->dma_len - 1].ddadr = DDADR_STOP;
wmb();
DDADR(host->dma) = host->sg_dma;
if (stat & STAT_TIME_OUT_RESPONSE) {
cmd->error = MMC_ERR_TIMEOUT;
} else if (stat & STAT_RES_CRC_ERR && cmd->flags & MMC_RSP_CRC) {
+#ifdef CONFIG_PXA27x
+ /*
+ * workaround for erratum #42:
+ * Intel PXA27x Family Processor Specification Update Rev 001
+ */
+ if (cmd->opcode == MMC_ALL_SEND_CID ||
+ cmd->opcode == MMC_SEND_CSD ||
+ cmd->opcode == MMC_SEND_CID) {
+ /* a bogus CRC error can appear if the msb of
+ the 15 byte response is a one */
+ if ((cmd->resp[0] & 0x80000000) == 0)
+ cmd->error = MMC_ERR_BADCRC;
+ } else {
+ DBG("ignoring CRC from command %d - *risky*\n",cmd->opcode);
+ }
+#else
cmd->error = MMC_ERR_BADCRC;
+#endif
}
pxamci_disable_irq(host, END_CMD_RES);
return 0;
DCSR(host->dma) = 0;
- dma_unmap_single(mmc_dev(host->mmc), host->dma_buf, host->dma_size,
- host->dma_dir);
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len,
+ host->dma_dir);
if (stat & STAT_READ_TIME_OUT)
data->error = MMC_ERR_TIMEOUT;
if (CLOCKRATE / clk > ios->clock)
clk <<= 1;
host->clkrt = fls(clk) - 1;
+ pxa_set_cken(CKEN12_MMC, 1);
/*
* we write clkrt on the next command
*/
- } else if (readl(host->base + MMC_STAT) & STAT_CLK_EN) {
- /*
- * Ensure that the clock is off.
- */
- writel(STOP_CLOCK, host->base + MMC_STRPCL);
+ } else {
+ pxamci_stop_clock(host);
+ pxa_set_cken(CKEN12_MMC, 0);
}
if (host->power_mode != ios->power_mode) {
if (!r || irq == NO_IRQ)
return -ENXIO;
- r = request_mem_region(r->start, SZ_4K, "PXAMCI");
+ r = request_mem_region(r->start, SZ_4K, DRIVER_NAME);
if (!r)
return -EBUSY;
}
mmc->ops = &pxamci_ops;
- mmc->f_min = 312500;
- mmc->f_max = 20000000;
+ mmc->f_min = CLOCKRATE_MIN;
+ mmc->f_max = CLOCKRATE_MAX;
+
+ /*
+ * We can do SG-DMA, but we don't because we never know how much
+ * data we successfully wrote to the card.
+ */
+ mmc->max_phys_segs = NR_SG;
+
+ /*
+ * Our hardware DMA can handle a maximum of one page per SG entry.
+ */
+ mmc->max_seg_size = PAGE_SIZE;
host = mmc_priv(mmc);
host->mmc = mmc;
spin_lock_init(&host->lock);
host->res = r;
host->irq = irq;
- host->imask = TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD|
- END_CMD_RES|PRG_DONE|DATA_TRAN_DONE;
+ host->imask = MMC_I_MASK_ALL;
host->base = ioremap(r->start, SZ_4K);
if (!host->base) {
writel(64, host->base + MMC_RESTO);
writel(host->imask, host->base + MMC_I_MASK);
- pxa_gpio_mode(GPIO6_MMCCLK_MD);
- pxa_gpio_mode(GPIO8_MMCCS0_MD);
- pxa_set_cken(CKEN12_MMC, 1);
-
- host->dma = pxa_request_dma("PXAMCI", DMA_PRIO_LOW, pxamci_dma_irq, host);
+ host->dma = pxa_request_dma(DRIVER_NAME, DMA_PRIO_LOW,
+ pxamci_dma_irq, host);
if (host->dma < 0) {
ret = -EBUSY;
goto out;
}
- ret = request_irq(host->irq, pxamci_irq, 0, "PXAMCI", host);
+ ret = request_irq(host->irq, pxamci_irq, 0, DRIVER_NAME, host);
if (ret)
goto out;
END_CMD_RES|PRG_DONE|DATA_TRAN_DONE,
host->base + MMC_I_MASK);
- pxa_set_cken(CKEN12_MMC, 0);
+ DRCMRRXMMC = 0;
+ DRCMRTXMMC = 0;
free_irq(host->irq, host);
pxa_free_dma(host->dma);
iounmap(host->base);
dma_free_coherent(dev, PAGE_SIZE, host->sg_cpu, host->sg_dma);
- pxa_set_cken(CKEN12_MMC, 0);
-
release_resource(host->res);
mmc_free_host(mmc);
#endif
static struct device_driver pxamci_driver = {
- .name = "pxa2xx-mci",
+ .name = DRIVER_NAME,
.bus = &platform_bus_type,
.probe = pxamci_probe,
.remove = pxamci_remove,