From 14d836e7499c53a1f6a65086c3d11600e871a971 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Fri, 13 Apr 2007 19:04:38 +0200 Subject: mmc: cull sg list to match mmc request size mmc layer may introduce additional (compared to block layer) limits on request size. Culling of the sg list to match adjusted request size simplifies the handling of such cases in the low level driver, allowing it to skip block count checks while processing sg entries. (fixes for wbsd and sdhci by Pierre Ossman) Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/mmc/mmc_block.c | 16 +++++- drivers/mmc/sdhci.c | 24 +++------ drivers/mmc/sdhci.h | 4 +- drivers/mmc/wbsd.c | 131 ++++++++++-------------------------------------- drivers/mmc/wbsd.h | 4 +- 5 files changed, 50 insertions(+), 129 deletions(-) diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c index 86439a0bb27..95b0da6abe8 100644 --- a/drivers/mmc/mmc_block.c +++ b/drivers/mmc/mmc_block.c @@ -223,7 +223,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) struct mmc_blk_data *md = mq->data; struct mmc_card *card = md->queue.card; struct mmc_blk_request brq; - int ret = 1; + int ret = 1, sg_pos, data_size; if (mmc_card_claim_host(card)) goto flush_queue; @@ -283,6 +283,20 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) brq.data.sg = mq->sg; brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg); + if (brq.data.blocks != + (req->nr_sectors >> (md->block_bits - 9))) { + data_size = brq.data.blocks * brq.data.blksz; + for (sg_pos = 0; sg_pos < brq.data.sg_len; sg_pos++) { + data_size -= mq->sg[sg_pos].length; + if (data_size <= 0) { + mq->sg[sg_pos].length += data_size; + sg_pos++; + break; + } + } + brq.data.sg_len = sg_pos; + } + mmc_wait_for_req(card->host, &brq.mrq); if (brq.cmd.error) { printk(KERN_ERR "%s: error %d sending read/write command\n", diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index d749f08601b..587dccf95f8 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -1,7 +1,7 @@ /* * linux/drivers/mmc/sdhci.c - Secure Digital Host Controller Interface driver * - * Copyright (C) 2005-2006 Pierre Ossman, All Rights Reserved. + * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -247,14 +247,13 @@ static void sdhci_read_block_pio(struct sdhci_host *host) chunk_remain = min(blksize, 4); } - size = min(host->size, host->remain); - size = min(size, chunk_remain); + size = min(host->remain, chunk_remain); chunk_remain -= size; blksize -= size; host->offset += size; host->remain -= size; - host->size -= size; + while (size) { *buffer = data & 0xFF; buffer++; @@ -289,14 +288,13 @@ static void sdhci_write_block_pio(struct sdhci_host *host) buffer = sdhci_sg_to_buffer(host) + host->offset; while (blksize) { - size = min(host->size, host->remain); - size = min(size, chunk_remain); + size = min(host->remain, chunk_remain); chunk_remain -= size; blksize -= size; host->offset += size; host->remain -= size; - host->size -= size; + while (size) { data >>= 8; data |= (u32)*buffer << 24; @@ -325,7 +323,7 @@ static void sdhci_transfer_pio(struct sdhci_host *host) BUG_ON(!host->data); - if (host->size == 0) + if (host->num_sg == 0) return; if (host->data->flags & MMC_DATA_READ) @@ -339,10 +337,8 @@ static void sdhci_transfer_pio(struct sdhci_host *host) else sdhci_write_block_pio(host); - if (host->size == 0) + if (host->num_sg == 0) break; - - BUG_ON(host->num_sg == 0); } DBG("PIO transfer complete.\n"); @@ -408,8 +404,6 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) writel(sg_dma_address(data->sg), host->ioaddr + SDHCI_DMA_ADDRESS); } else { - host->size = data->blksz * data->blocks; - host->cur_sg = data->sg; host->num_sg = data->sg_len; @@ -473,10 +467,6 @@ static void sdhci_finish_data(struct sdhci_host *host) "though there were blocks left.\n", mmc_hostname(host->mmc)); data->error = MMC_ERR_FAILED; - } else if (host->size != 0) { - printk(KERN_ERR "%s: %d bytes were left untransferred.\n", - mmc_hostname(host->mmc), host->size); - data->error = MMC_ERR_FAILED; } DBG("Ending data transfer (%d bytes)\n", data->bytes_xfered); diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h index e324f0a623d..7400f4bc114 100644 --- a/drivers/mmc/sdhci.h +++ b/drivers/mmc/sdhci.h @@ -1,7 +1,7 @@ /* * linux/drivers/mmc/sdhci.h - Secure Digital Host Controller Interface driver * - * Copyright (C) 2005 Pierre Ossman, All Rights Reserved. + * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -187,8 +187,6 @@ struct sdhci_host { int offset; /* Offset into current sg */ int remain; /* Bytes left in current */ - int size; /* Remaining bytes in transfer */ - char slot_descr[20]; /* Name for reservations */ int irq; /* Device IRQ */ diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c index 05ccfc43168..7a3e32ec46b 100644 --- a/drivers/mmc/wbsd.c +++ b/drivers/mmc/wbsd.c @@ -1,7 +1,7 @@ /* * linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver * - * Copyright (C) 2004-2006 Pierre Ossman, All Rights Reserved. + * Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -278,90 +278,36 @@ static inline char *wbsd_sg_to_buffer(struct wbsd_host *host) static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data) { - unsigned int len, i, size; + unsigned int len, i; struct scatterlist *sg; char *dmabuf = host->dma_buffer; char *sgbuf; - size = host->size; - sg = data->sg; len = data->sg_len; - /* - * Just loop through all entries. Size might not - * be the entire list though so make sure that - * we do not transfer too much. - */ for (i = 0; i < len; i++) { sgbuf = page_address(sg[i].page) + sg[i].offset; - if (size < sg[i].length) - memcpy(dmabuf, sgbuf, size); - else - memcpy(dmabuf, sgbuf, sg[i].length); + memcpy(dmabuf, sgbuf, sg[i].length); dmabuf += sg[i].length; - - if (size < sg[i].length) - size = 0; - else - size -= sg[i].length; - - if (size == 0) - break; } - - /* - * Check that we didn't get a request to transfer - * more data than can fit into the SG list. - */ - - BUG_ON(size != 0); - - host->size -= size; } static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data) { - unsigned int len, i, size; + unsigned int len, i; struct scatterlist *sg; char *dmabuf = host->dma_buffer; char *sgbuf; - size = host->size; - sg = data->sg; len = data->sg_len; - /* - * Just loop through all entries. Size might not - * be the entire list though so make sure that - * we do not transfer too much. - */ for (i = 0; i < len; i++) { sgbuf = page_address(sg[i].page) + sg[i].offset; - if (size < sg[i].length) - memcpy(sgbuf, dmabuf, size); - else - memcpy(sgbuf, dmabuf, sg[i].length); + memcpy(sgbuf, dmabuf, sg[i].length); dmabuf += sg[i].length; - - if (size < sg[i].length) - size = 0; - else - size -= sg[i].length; - - if (size == 0) - break; } - - /* - * Check that we didn't get a request to transfer - * more data than can fit into the SG list. - */ - - BUG_ON(size != 0); - - host->size -= size; } /* @@ -484,7 +430,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host) /* * Handle excessive data. */ - if (data->bytes_xfered == host->size) + if (host->num_sg == 0) return; buffer = wbsd_sg_to_buffer(host) + host->offset; @@ -513,12 +459,6 @@ static void wbsd_empty_fifo(struct wbsd_host *host) data->bytes_xfered++; - /* - * Transfer done? - */ - if (data->bytes_xfered == host->size) - return; - /* * End of scatter list entry? */ @@ -526,19 +466,8 @@ static void wbsd_empty_fifo(struct wbsd_host *host) /* * Get next entry. Check if last. */ - if (!wbsd_next_sg(host)) { - /* - * We should never reach this point. - * It means that we're trying to - * transfer more blocks than can fit - * into the scatter list. - */ - BUG_ON(1); - - host->size = data->bytes_xfered; - + if (!wbsd_next_sg(host)) return; - } buffer = wbsd_sg_to_buffer(host); } @@ -550,7 +479,7 @@ static void wbsd_empty_fifo(struct wbsd_host *host) * hardware problem. The chip doesn't trigger * FIFO threshold interrupts properly. */ - if ((host->size - data->bytes_xfered) < 16) + if ((data->blocks * data->blksz - data->bytes_xfered) < 16) tasklet_schedule(&host->fifo_tasklet); } @@ -564,7 +493,7 @@ static void wbsd_fill_fifo(struct wbsd_host *host) * Check that we aren't being called after the * entire buffer has been transfered. */ - if (data->bytes_xfered == host->size) + if (host->num_sg == 0) return; buffer = wbsd_sg_to_buffer(host) + host->offset; @@ -593,12 +522,6 @@ static void wbsd_fill_fifo(struct wbsd_host *host) data->bytes_xfered++; - /* - * Transfer done? - */ - if (data->bytes_xfered == host->size) - return; - /* * End of scatter list entry? */ @@ -606,19 +529,8 @@ static void wbsd_fill_fifo(struct wbsd_host *host) /* * Get next entry. Check if last. */ - if (!wbsd_next_sg(host)) { - /* - * We should never reach this point. - * It means that we're trying to - * transfer more blocks than can fit - * into the scatter list. - */ - BUG_ON(1); - - host->size = data->bytes_xfered; - + if (!wbsd_next_sg(host)) return; - } buffer = wbsd_sg_to_buffer(host); } @@ -638,6 +550,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) u16 blksize; u8 setup; unsigned long dmaflags; + unsigned int size; DBGF("blksz %04x blks %04x flags %08x\n", data->blksz, data->blocks, data->flags); @@ -647,7 +560,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) /* * Calculate size. */ - host->size = data->blocks * data->blksz; + size = data->blocks * data->blksz; /* * Check timeout values for overflow. @@ -705,8 +618,8 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) /* * The buffer for DMA is only 64 kB. */ - BUG_ON(host->size > 0x10000); - if (host->size > 0x10000) { + BUG_ON(size > 0x10000); + if (size > 0x10000) { data->error = MMC_ERR_INVALID; return; } @@ -729,7 +642,7 @@ static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) else set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40); set_dma_addr(host->dma, host->dma_addr); - set_dma_count(host->dma, host->size); + set_dma_count(host->dma, size); enable_dma(host->dma); release_dma_lock(dmaflags); @@ -812,6 +725,10 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data) count = get_dma_residue(host->dma); release_dma_lock(dmaflags); + data->bytes_xfered = host->mrq->data->blocks * + host->mrq->data->blksz - count; + data->bytes_xfered -= data->bytes_xfered % data->blksz; + /* * Any leftover data? */ @@ -820,7 +737,8 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data) "%d bytes left.\n", mmc_hostname(host->mmc), count); - data->error = MMC_ERR_FAILED; + if (data->error == MMC_ERR_NONE) + data->error = MMC_ERR_FAILED; } else { /* * Transfer data from DMA buffer to @@ -828,8 +746,11 @@ static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data) */ if (data->flags & MMC_DATA_READ) wbsd_dma_to_sg(host, data); + } - data->bytes_xfered = host->size; + if (data->error != MMC_ERR_NONE) { + if (data->bytes_xfered) + data->bytes_xfered -= data->blksz; } } @@ -1167,7 +1088,7 @@ static void wbsd_tasklet_fifo(unsigned long param) /* * Done? */ - if (host->size == data->bytes_xfered) { + if (host->num_sg == 0) { wbsd_write_index(host, WBSD_IDX_FIFOEN, 0); tasklet_schedule(&host->finish_tasklet); } diff --git a/drivers/mmc/wbsd.h b/drivers/mmc/wbsd.h index d06718b0e2a..6fb4fa42321 100644 --- a/drivers/mmc/wbsd.h +++ b/drivers/mmc/wbsd.h @@ -1,7 +1,7 @@ /* * linux/drivers/mmc/wbsd.h - Winbond W83L51xD SD/MMC driver * - * Copyright (C) 2004-2005 Pierre Ossman, All Rights Reserved. + * Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -158,8 +158,6 @@ struct wbsd_host unsigned int offset; /* Offset into current entry */ unsigned int remain; /* Data left in curren entry */ - int size; /* Total size of transfer */ - char* dma_buffer; /* ISA DMA buffer */ dma_addr_t dma_addr; /* Physical address for same */ -- cgit v1.2.3 From 976d9276c826d6b35e4a2478fd4978dbd63bdd6f Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 13 Apr 2007 22:47:01 +0200 Subject: mmc: enforce correct sg list Now that we've fixed our only offender when it comes to strange sg list, add a check so that future users keep the sg list proper with regard to transfer size. Signed-off-by: Pierre Ossman --- drivers/mmc/mmc.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 4a73e8b2428..e8f896c61b3 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -99,6 +99,10 @@ EXPORT_SYMBOL(mmc_request_done); void mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) { +#ifdef CONFIG_MMC_DEBUG + unsigned int i, sz; +#endif + pr_debug("%s: starting CMD%u arg %08x flags %08x\n", mmc_hostname(host), mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags); @@ -113,6 +117,13 @@ mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) BUG_ON(mrq->data->blocks * mrq->data->blksz > host->max_req_size); +#ifdef CONFIG_MMC_DEBUG + sz = 0; + for (i = 0;i < mrq->data->sg_len;i++) + sz += mrq->data->sg[i].length; + BUG_ON(sz != mrq->data->blocks * mrq->data->blksz); +#endif + mrq->cmd->data = mrq->data; mrq->data->error = 0; mrq->data->mrq = mrq; -- cgit v1.2.3 From 5721dbf217b073b40e31936781379ab2d17ea2ae Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 13 Apr 2007 23:25:59 +0200 Subject: wbsd: remove block crc test Block completion interrupts occur faster than we can process them, so just ignore them competely. Commit also fixes up some incorrect register defines. Signed-off-by: Pierre Ossman --- drivers/mmc/wbsd.c | 32 +------------------------------- drivers/mmc/wbsd.h | 5 ++--- 2 files changed, 3 insertions(+), 34 deletions(-) diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c index 7a3e32ec46b..673c64661d3 100644 --- a/drivers/mmc/wbsd.c +++ b/drivers/mmc/wbsd.c @@ -178,9 +178,8 @@ static void wbsd_init_device(struct wbsd_host *host) ier = 0; ier |= WBSD_EINT_CARD; ier |= WBSD_EINT_FIFO_THRE; - ier |= WBSD_EINT_CCRC; - ier |= WBSD_EINT_TIMEOUT; ier |= WBSD_EINT_CRC; + ier |= WBSD_EINT_TIMEOUT; ier |= WBSD_EINT_TC; outb(ier, host->base + WBSD_EIR); @@ -1166,30 +1165,6 @@ end: spin_unlock(&host->lock); } -static void wbsd_tasklet_block(unsigned long param) -{ - struct wbsd_host *host = (struct wbsd_host *)param; - struct mmc_data *data; - - spin_lock(&host->lock); - - if ((wbsd_read_index(host, WBSD_IDX_CRCSTATUS) & WBSD_CRC_MASK) != - WBSD_CRC_OK) { - data = wbsd_get_data(host); - if (!data) - goto end; - - DBGF("CRC error\n"); - - data->error = MMC_ERR_BADCRC; - - tasklet_schedule(&host->finish_tasklet); - } - -end: - spin_unlock(&host->lock); -} - /* * Interrupt handling */ @@ -1220,8 +1195,6 @@ static irqreturn_t wbsd_irq(int irq, void *dev_id) tasklet_hi_schedule(&host->crc_tasklet); if (isr & WBSD_INT_TIMEOUT) tasklet_hi_schedule(&host->timeout_tasklet); - if (isr & WBSD_INT_BUSYEND) - tasklet_hi_schedule(&host->block_tasklet); if (isr & WBSD_INT_TC) tasklet_schedule(&host->finish_tasklet); @@ -1522,8 +1495,6 @@ static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq) (unsigned long)host); tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish, (unsigned long)host); - tasklet_init(&host->block_tasklet, wbsd_tasklet_block, - (unsigned long)host); return 0; } @@ -1542,7 +1513,6 @@ static void __devexit wbsd_release_irq(struct wbsd_host *host) tasklet_kill(&host->crc_tasklet); tasklet_kill(&host->timeout_tasklet); tasklet_kill(&host->finish_tasklet); - tasklet_kill(&host->block_tasklet); } /* diff --git a/drivers/mmc/wbsd.h b/drivers/mmc/wbsd.h index 6fb4fa42321..873bda1e59b 100644 --- a/drivers/mmc/wbsd.h +++ b/drivers/mmc/wbsd.h @@ -46,10 +46,10 @@ #define WBSD_EINT_CARD 0x40 #define WBSD_EINT_FIFO_THRE 0x20 -#define WBSD_EINT_CCRC 0x10 +#define WBSD_EINT_CRC 0x10 #define WBSD_EINT_TIMEOUT 0x08 #define WBSD_EINT_PROGEND 0x04 -#define WBSD_EINT_CRC 0x02 +#define WBSD_EINT_BUSYEND 0x02 #define WBSD_EINT_TC 0x01 #define WBSD_INT_PENDING 0x80 @@ -180,7 +180,6 @@ struct wbsd_host struct tasklet_struct crc_tasklet; struct tasklet_struct timeout_tasklet; struct tasklet_struct finish_tasklet; - struct tasklet_struct block_tasklet; struct timer_list ignore_timer; /* Ignore detection timer */ }; -- cgit v1.2.3 From 4552f0cbd45225f2c1cbadc224505f14f8749569 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:12 +1000 Subject: tifm: hide details of interrupt processing from socket drivers Instead of passing transformed value of adapter interrupt status to socket drivers, implement two separate callbacks - one for card events and another for dma events. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/misc/tifm_7xx1.c | 22 ++++++++++++++-------- drivers/misc/tifm_core.c | 11 ++++++----- drivers/mmc/tifm_sd.c | 43 ++++++++++++++++++++++++++++--------------- include/linux/tifm.h | 11 ++--------- 4 files changed, 50 insertions(+), 37 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index bc60e2fc3c2..d6652b3301d 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -14,7 +14,13 @@ #include #define DRIVER_NAME "tifm_7xx1" -#define DRIVER_VERSION "0.7" +#define DRIVER_VERSION "0.8" + +#define TIFM_IRQ_ENABLE 0x80000000 +#define TIFM_IRQ_SOCKMASK(x) (x) +#define TIFM_IRQ_CARDMASK(x) ((x) << 8) +#define TIFM_IRQ_FIFOMASK(x) ((x) << 16) +#define TIFM_IRQ_SETALL 0xffffffff static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock) { @@ -31,7 +37,7 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) struct tifm_adapter *fm = dev_id; struct tifm_dev *sock; unsigned int irq_status; - unsigned int sock_irq_status, cnt; + unsigned int cnt; spin_lock(&fm->lock); irq_status = readl(fm->addr + FM_INTERRUPT_STATUS); @@ -45,12 +51,12 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) for (cnt = 0; cnt < fm->num_sockets; cnt++) { sock = fm->sockets[cnt]; - sock_irq_status = (irq_status >> cnt) - & (TIFM_IRQ_FIFOMASK(1) - | TIFM_IRQ_CARDMASK(1)); - - if (sock && sock_irq_status) - sock->signal_irq(sock, sock_irq_status); + if (sock) { + if ((irq_status >> cnt) & TIFM_IRQ_FIFOMASK(1)) + sock->data_event(sock); + if ((irq_status >> cnt) & TIFM_IRQ_CARDMASK(1)) + sock->card_event(sock); + } } fm->socket_change_set |= irq_status diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index 6b10ebe9d93..6799b9cca05 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -14,7 +14,7 @@ #include #define DRIVER_NAME "tifm_core" -#define DRIVER_VERSION "0.7" +#define DRIVER_VERSION "0.8" static DEFINE_IDR(tifm_adapter_idr); static DEFINE_SPINLOCK(tifm_adapter_lock); @@ -175,8 +175,7 @@ void tifm_free_device(struct device *dev) } EXPORT_SYMBOL(tifm_free_device); -static void tifm_dummy_signal_irq(struct tifm_dev *sock, - unsigned int sock_irq_status) +static void tifm_dummy_event(struct tifm_dev *sock) { return; } @@ -191,7 +190,8 @@ struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm) dev->dev.parent = fm->dev; dev->dev.bus = &tifm_bus_type; dev->dev.release = tifm_free_device; - dev->signal_irq = tifm_dummy_signal_irq; + dev->card_event = tifm_dummy_event; + dev->data_event = tifm_dummy_event; } return dev; } @@ -249,7 +249,8 @@ static int tifm_device_remove(struct device *dev) struct tifm_driver *drv = fm_dev->drv; if (drv) { - fm_dev->signal_irq = tifm_dummy_signal_irq; + fm_dev->card_event = tifm_dummy_event; + fm_dev->data_event = tifm_dummy_event; if (drv->remove) drv->remove(fm_dev); fm_dev->drv = NULL; diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index 0581d09c58f..8905b129e4e 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -17,7 +17,7 @@ #include #define DRIVER_NAME "tifm_sd" -#define DRIVER_VERSION "0.7" +#define DRIVER_VERSION "0.8" static int no_dma = 0; static int fixed_timeout = 0; @@ -316,24 +316,38 @@ change_state: } /* Called from interrupt handler */ -static void tifm_sd_signal_irq(struct tifm_dev *sock, - unsigned int sock_irq_status) +static void tifm_sd_data_event(struct tifm_dev *sock) { struct tifm_sd *host; - unsigned int host_status = 0, fifo_status = 0; - int error_code = 0; + unsigned int fifo_status = 0; spin_lock(&sock->lock); host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); - if (sock_irq_status & FIFO_EVENT) { - fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS); - writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS); + fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS); + writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS); + + host->flags |= fifo_status & FIFO_RDY; + + if (host->req) + tifm_sd_process_cmd(sock, host, 0); + + dev_dbg(&sock->dev, "fifo_status %x\n", fifo_status); + spin_unlock(&sock->lock); + +} + +/* Called from interrupt handler */ +static void tifm_sd_card_event(struct tifm_dev *sock) +{ + struct tifm_sd *host; + unsigned int host_status = 0; + int error_code = 0; + + spin_lock(&sock->lock); + host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); - host->flags |= fifo_status & FIFO_RDY; - } - if (sock_irq_status & CARD_EVENT) { host_status = readl(sock->addr + SOCK_MMCSD_STATUS); writel(host_status, sock->addr + SOCK_MMCSD_STATUS); @@ -377,13 +391,11 @@ static void tifm_sd_signal_irq(struct tifm_dev *sock, host->written_blocks++; host->flags &= ~CARD_BUSY; } - } if (host->req) tifm_sd_process_cmd(sock, host, host_status); done: - dev_dbg(&sock->dev, "host_status %x, fifo_status %x\n", - host_status, fifo_status); + dev_dbg(&sock->dev, "host_status %x\n", host_status); spin_unlock(&sock->lock); } @@ -882,7 +894,8 @@ static int tifm_sd_probe(struct tifm_dev *sock) mmc->max_blk_size = 2048; mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_seg_size = mmc->max_req_size; - sock->signal_irq = tifm_sd_signal_irq; + sock->card_event = tifm_sd_card_event; + sock->data_event = tifm_sd_data_event; rc = tifm_sd_initialize_host(host); if (!rc) diff --git a/include/linux/tifm.h b/include/linux/tifm.h index 3deb0a6c137..4470961655c 100644 --- a/include/linux/tifm.h +++ b/include/linux/tifm.h @@ -60,13 +60,6 @@ enum { SOCK_MS_SYSTEM = 0x190, SOCK_FIFO_ACCESS = 0x200 }; - -#define TIFM_IRQ_ENABLE 0x80000000 -#define TIFM_IRQ_SOCKMASK(x) (x) -#define TIFM_IRQ_CARDMASK(x) ((x) << 8) -#define TIFM_IRQ_FIFOMASK(x) ((x) << 16) -#define TIFM_IRQ_SETALL 0xffffffff - #define TIFM_CTRL_LED 0x00000040 #define TIFM_CTRL_FAST_CLK 0x00000100 @@ -90,8 +83,8 @@ struct tifm_dev { tifm_media_id media_id; unsigned int socket_id; - void (*signal_irq)(struct tifm_dev *sock, - unsigned int sock_irq_status); + void (*card_event)(struct tifm_dev *sock); + void (*data_event)(struct tifm_dev *sock); struct tifm_driver *drv; struct device dev; -- cgit v1.2.3 From 8dc4a61eca31dd45a9d45f9bc9c67d959f0f6cbd Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:13 +1000 Subject: tifm: use bus methods to handle probe/remove instead of driver ones. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/misc/tifm_core.c | 102 ++++++++++++++++++++++------------------------- include/linux/tifm.h | 1 - 2 files changed, 47 insertions(+), 56 deletions(-) diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index 6799b9cca05..dcff45a19bc 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -60,14 +60,54 @@ static int tifm_uevent(struct device *dev, char **envp, int num_envp, return 0; } +static int tifm_device_probe(struct device *dev) +{ + struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); + struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver, + driver); + int rc = -ENODEV; + + get_device(dev); + if (dev->driver && drv->probe) { + rc = drv->probe(sock); + if (!rc) + return 0; + } + put_device(dev); + return rc; +} + +static void tifm_dummy_event(struct tifm_dev *sock) +{ + return; +} + +static int tifm_device_remove(struct device *dev) +{ + struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); + struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver, + driver); + + if (dev->driver && drv->remove) { + sock->card_event = tifm_dummy_event; + sock->data_event = tifm_dummy_event; + drv->remove(sock); + sock->dev.driver = NULL; + } + + put_device(dev); + return 0; +} + #ifdef CONFIG_PM static int tifm_device_suspend(struct device *dev, pm_message_t state) { struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); - struct tifm_driver *drv = fm_dev->drv; + struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver, + driver); - if (drv && drv->suspend) + if (dev->driver && drv->suspend) return drv->suspend(fm_dev, state); return 0; } @@ -75,9 +115,10 @@ static int tifm_device_suspend(struct device *dev, pm_message_t state) static int tifm_device_resume(struct device *dev) { struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); - struct tifm_driver *drv = fm_dev->drv; + struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver, + driver); - if (drv && drv->resume) + if (dev->driver && drv->resume) return drv->resume(fm_dev); return 0; } @@ -93,6 +134,8 @@ static struct bus_type tifm_bus_type = { .name = "tifm", .match = tifm_match, .uevent = tifm_uevent, + .probe = tifm_device_probe, + .remove = tifm_device_remove, .suspend = tifm_device_suspend, .resume = tifm_device_resume }; @@ -175,11 +218,6 @@ void tifm_free_device(struct device *dev) } EXPORT_SYMBOL(tifm_free_device); -static void tifm_dummy_event(struct tifm_dev *sock) -{ - return; -} - struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm) { struct tifm_dev *dev = kzalloc(sizeof(struct tifm_dev), GFP_KERNEL); @@ -218,55 +256,9 @@ void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, } EXPORT_SYMBOL(tifm_unmap_sg); -static int tifm_device_probe(struct device *dev) -{ - struct tifm_driver *drv; - struct tifm_dev *fm_dev; - int rc = 0; - const tifm_media_id *id; - - drv = container_of(dev->driver, struct tifm_driver, driver); - fm_dev = container_of(dev, struct tifm_dev, dev); - get_device(dev); - if (!fm_dev->drv && drv->probe && drv->id_table) { - rc = -ENODEV; - id = tifm_device_match(drv->id_table, fm_dev); - if (id) - rc = drv->probe(fm_dev); - if (rc >= 0) { - rc = 0; - fm_dev->drv = drv; - } - } - if (rc) - put_device(dev); - return rc; -} - -static int tifm_device_remove(struct device *dev) -{ - struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); - struct tifm_driver *drv = fm_dev->drv; - - if (drv) { - fm_dev->card_event = tifm_dummy_event; - fm_dev->data_event = tifm_dummy_event; - if (drv->remove) - drv->remove(fm_dev); - fm_dev->drv = NULL; - } - - put_device(dev); - return 0; -} - int tifm_register_driver(struct tifm_driver *drv) { drv->driver.bus = &tifm_bus_type; - drv->driver.probe = tifm_device_probe; - drv->driver.remove = tifm_device_remove; - drv->driver.suspend = tifm_device_suspend; - drv->driver.resume = tifm_device_resume; return driver_register(&drv->driver); } diff --git a/include/linux/tifm.h b/include/linux/tifm.h index 4470961655c..ee1056396b9 100644 --- a/include/linux/tifm.h +++ b/include/linux/tifm.h @@ -86,7 +86,6 @@ struct tifm_dev { void (*card_event)(struct tifm_dev *sock); void (*data_event)(struct tifm_dev *sock); - struct tifm_driver *drv; struct device dev; }; -- cgit v1.2.3 From e23f2b8a1a52c00f0150659eb0bfde3a73976ffe Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:14 +1000 Subject: tifm: simplify bus match and uevent handlers Remove code duplicating the kernel functionality and clean up data structures involved in driver matching. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/misc/tifm_7xx1.c | 14 ++++++------ drivers/misc/tifm_core.c | 57 +++++++++++++++++++++++++++++------------------- drivers/mmc/tifm_sd.c | 6 ++--- include/linux/tifm.h | 16 ++++++++------ 4 files changed, 53 insertions(+), 40 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index d6652b3301d..fd7b8dadc82 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -73,7 +73,7 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static tifm_media_id tifm_7xx1_toggle_sock_power(char __iomem *sock_addr, +static unsigned char tifm_7xx1_toggle_sock_power(char __iomem *sock_addr, int is_x2) { unsigned int s_state; @@ -90,7 +90,7 @@ static tifm_media_id tifm_7xx1_toggle_sock_power(char __iomem *sock_addr, s_state = readl(sock_addr + SOCK_PRESENT_STATE); if (!(TIFM_SOCK_STATE_OCCUPIED & s_state)) - return FM_NULL; + return 0; if (is_x2) { writel((s_state & 7) | 0x0c00, sock_addr + SOCK_CONTROL); @@ -129,7 +129,7 @@ static int tifm_7xx1_switch_media(void *data) { struct tifm_adapter *fm = data; unsigned long flags; - tifm_media_id media_id; + unsigned char media_id; char *card_name = "xx"; int cnt, rc; struct tifm_dev *sock; @@ -184,7 +184,7 @@ static int tifm_7xx1_switch_media(void *data) if (sock) { sock->addr = tifm_7xx1_sock_addr(fm->addr, cnt); - sock->media_id = media_id; + sock->type = media_id; sock->socket_id = cnt; switch (media_id) { case 1: @@ -266,7 +266,7 @@ static int tifm_7xx1_resume(struct pci_dev *dev) struct tifm_adapter *fm = pci_get_drvdata(dev); int cnt, rc; unsigned long flags; - tifm_media_id new_ids[fm->num_sockets]; + unsigned char new_ids[fm->num_sockets]; pci_set_power_state(dev, PCI_D0); pci_restore_state(dev); @@ -285,10 +285,10 @@ static int tifm_7xx1_resume(struct pci_dev *dev) fm->socket_change_set = 0; for (cnt = 0; cnt < fm->num_sockets; cnt++) { if (fm->sockets[cnt]) { - if (fm->sockets[cnt]->media_id == new_ids[cnt]) + if (fm->sockets[cnt]->type == new_ids[cnt]) fm->socket_change_set |= 1 << cnt; - fm->sockets[cnt]->media_id = new_ids[cnt]; + fm->sockets[cnt]->type = new_ids[cnt]; } } diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index dcff45a19bc..6b2c447dc8e 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -19,42 +19,53 @@ static DEFINE_IDR(tifm_adapter_idr); static DEFINE_SPINLOCK(tifm_adapter_lock); -static tifm_media_id *tifm_device_match(tifm_media_id *ids, - struct tifm_dev *dev) +static const char *tifm_media_type_name(unsigned char type, unsigned char nt) { - while (*ids) { - if (dev->media_id == *ids) - return ids; - ids++; - } - return NULL; + const char *card_type_name[3][3] = { + { "SmartMedia/xD", "MemoryStick", "MMC/SD" }, + { "XD", "MS", "SD"}, + { "xd", "ms", "sd"} + }; + + if (nt > 2 || type < 1 || type > 3) + return NULL; + return card_type_name[nt][type - 1]; } -static int tifm_match(struct device *dev, struct device_driver *drv) +static int tifm_dev_match(struct tifm_dev *sock, struct tifm_device_id *id) { - struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); - struct tifm_driver *fm_drv; - - fm_drv = container_of(drv, struct tifm_driver, driver); - if (!fm_drv->id_table) - return -EINVAL; - if (tifm_device_match(fm_drv->id_table, fm_dev)) + if (sock->type == id->type) return 1; - return -ENODEV; + return 0; +} + +static int tifm_bus_match(struct device *dev, struct device_driver *drv) +{ + struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); + struct tifm_driver *fm_drv = container_of(drv, struct tifm_driver, + driver); + struct tifm_device_id *ids = fm_drv->id_table; + + if (ids) { + while (ids->type) { + if (tifm_dev_match(sock, ids)) + return 1; + ++ids; + } + } + return 0; } static int tifm_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size) { - struct tifm_dev *fm_dev; + struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); int i = 0; int length = 0; - const char *card_type_name[] = {"INV", "SM", "MS", "SD"}; - if (!dev || !(fm_dev = container_of(dev, struct tifm_dev, dev))) - return -ENODEV; if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, - "TIFM_CARD_TYPE=%s", card_type_name[fm_dev->media_id])) + "TIFM_CARD_TYPE=%s", + tifm_media_type_name(sock->type, 1))) return -ENOMEM; return 0; @@ -132,7 +143,7 @@ static int tifm_device_resume(struct device *dev) static struct bus_type tifm_bus_type = { .name = "tifm", - .match = tifm_match, + .match = tifm_bus_match, .uevent = tifm_uevent, .probe = tifm_device_probe, .remove = tifm_device_remove, diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index 8905b129e4e..4388ee9062a 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -948,7 +948,7 @@ static int tifm_sd_resume(struct tifm_dev *sock) struct mmc_host *mmc = tifm_get_drvdata(sock); struct tifm_sd *host = mmc_priv(mmc); - if (sock->media_id != FM_SD + if (sock->type != TIFM_TYPE_SD || tifm_sd_initialize_host(host)) { tifm_eject(sock); return 0; @@ -964,8 +964,8 @@ static int tifm_sd_resume(struct tifm_dev *sock) #endif /* CONFIG_PM */ -static tifm_media_id tifm_sd_id_tbl[] = { - FM_SD, 0 +static struct tifm_device_id tifm_sd_id_tbl[] = { + { TIFM_TYPE_SD }, { } }; static struct tifm_driver tifm_sd_driver = { diff --git a/include/linux/tifm.h b/include/linux/tifm.h index ee1056396b9..57b2653494c 100644 --- a/include/linux/tifm.h +++ b/include/linux/tifm.h @@ -74,13 +74,19 @@ enum { #define TIFM_DMA_TX 0x00008000 /* Meaning of this constant is unverified */ #define TIFM_DMA_EN 0x00000001 /* Meaning of this constant is unverified */ -typedef enum {FM_NULL = 0, FM_XD = 0x01, FM_MS = 0x02, FM_SD = 0x03} tifm_media_id; +#define TIFM_TYPE_XD 1 +#define TIFM_TYPE_MS 2 +#define TIFM_TYPE_SD 3 + +struct tifm_device_id { + unsigned char type; +}; struct tifm_driver; struct tifm_dev { char __iomem *addr; spinlock_t lock; - tifm_media_id media_id; + unsigned char type; unsigned int socket_id; void (*card_event)(struct tifm_dev *sock); @@ -90,7 +96,7 @@ struct tifm_dev { }; struct tifm_driver { - tifm_media_id *id_table; + struct tifm_device_id *id_table; int (*probe)(struct tifm_dev *dev); void (*remove)(struct tifm_dev *dev); int (*suspend)(struct tifm_dev *dev, @@ -141,8 +147,4 @@ static inline void tifm_set_drvdata(struct tifm_dev *dev, void *data) dev_set_drvdata(&dev->dev, data); } -struct tifm_device_id { - tifm_media_id media_id; -}; - #endif -- cgit v1.2.3 From 3540af8ffddcdbc7573451ac0b5cd57a2eaf8af5 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:15 +1000 Subject: tifm: replace per-adapter kthread with freezeable workqueue Freezeable workqueue makes sure that adapter work items (device insertions and removals) would be handled after the system is fully resumed. Previously this was achieved by explicit freezing of the kthread. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/misc/tifm_7xx1.c | 89 ++++++++++++++++++------------------------------ drivers/misc/tifm_core.c | 51 ++++++++++++++++++--------- include/linux/tifm.h | 8 ++--- 3 files changed, 72 insertions(+), 76 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index fd7b8dadc82..e5655fef42d 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -28,7 +28,7 @@ static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock) spin_lock_irqsave(&fm->lock, flags); fm->socket_change_set |= 1 << sock->socket_id; - wake_up_all(&fm->change_set_notify); + tifm_queue_work(&fm->media_switcher); spin_unlock_irqrestore(&fm->lock, flags); } @@ -64,10 +64,12 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) } writel(irq_status, fm->addr + FM_INTERRUPT_STATUS); - if (!fm->socket_change_set) + if (fm->finish_me) + complete_all(fm->finish_me); + else if (!fm->socket_change_set) writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE); else - wake_up_all(&fm->change_set_notify); + tifm_queue_work(&fm->media_switcher); spin_unlock(&fm->lock); return IRQ_HANDLED; @@ -125,37 +127,29 @@ tifm_7xx1_sock_addr(char __iomem *base_addr, unsigned int sock_num) return base_addr + ((sock_num + 1) << 10); } -static int tifm_7xx1_switch_media(void *data) +static void tifm_7xx1_switch_media(struct work_struct *work) { - struct tifm_adapter *fm = data; + struct tifm_adapter *fm = container_of(work, struct tifm_adapter, + media_switcher); unsigned long flags; unsigned char media_id; char *card_name = "xx"; - int cnt, rc; + int cnt; struct tifm_dev *sock; unsigned int socket_change_set; - while (1) { - rc = wait_event_interruptible(fm->change_set_notify, - fm->socket_change_set); - if (rc == -ERESTARTSYS) - try_to_freeze(); - - spin_lock_irqsave(&fm->lock, flags); - socket_change_set = fm->socket_change_set; - fm->socket_change_set = 0; + spin_lock_irqsave(&fm->lock, flags); + socket_change_set = fm->socket_change_set; + fm->socket_change_set = 0; - dev_dbg(fm->dev, "checking media set %x\n", - socket_change_set); + dev_dbg(fm->dev, "checking media set %x\n", + socket_change_set); - if (kthread_should_stop()) - socket_change_set = (1 << fm->num_sockets) - 1; + if (!socket_change_set) { spin_unlock_irqrestore(&fm->lock, flags); + return; + } - if (!socket_change_set) - continue; - - spin_lock_irqsave(&fm->lock, flags); for (cnt = 0; cnt < fm->num_sockets; cnt++) { if (!(socket_change_set & (1 << cnt))) continue; @@ -172,8 +166,6 @@ static int tifm_7xx1_switch_media(void *data) tifm_7xx1_sock_addr(fm->addr, cnt) + SOCK_CONTROL); } - if (kthread_should_stop()) - continue; spin_unlock_irqrestore(&fm->lock, flags); media_id = tifm_7xx1_toggle_sock_power( @@ -222,30 +214,16 @@ static int tifm_7xx1_switch_media(void *data) } } - if (!kthread_should_stop()) { - writel(TIFM_IRQ_FIFOMASK(socket_change_set) - | TIFM_IRQ_CARDMASK(socket_change_set), - fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - writel(TIFM_IRQ_FIFOMASK(socket_change_set) - | TIFM_IRQ_CARDMASK(socket_change_set), - fm->addr + FM_SET_INTERRUPT_ENABLE); - writel(TIFM_IRQ_ENABLE, - fm->addr + FM_SET_INTERRUPT_ENABLE); - spin_unlock_irqrestore(&fm->lock, flags); - } else { - for (cnt = 0; cnt < fm->num_sockets; cnt++) { - if (fm->sockets[cnt]) - fm->socket_change_set |= 1 << cnt; - } - if (!fm->socket_change_set) { - spin_unlock_irqrestore(&fm->lock, flags); - return 0; - } else { - spin_unlock_irqrestore(&fm->lock, flags); - } - } - } - return 0; + writel(TIFM_IRQ_FIFOMASK(socket_change_set) + | TIFM_IRQ_CARDMASK(socket_change_set), + fm->addr + FM_CLEAR_INTERRUPT_ENABLE); + + writel(TIFM_IRQ_FIFOMASK(socket_change_set) + | TIFM_IRQ_CARDMASK(socket_change_set), + fm->addr + FM_SET_INTERRUPT_ENABLE); + + writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE); + spin_unlock_irqrestore(&fm->lock, flags); } #ifdef CONFIG_PM @@ -267,6 +245,7 @@ static int tifm_7xx1_resume(struct pci_dev *dev) int cnt, rc; unsigned long flags; unsigned char new_ids[fm->num_sockets]; + DECLARE_COMPLETION_ONSTACK(finish_resume); pci_set_power_state(dev, PCI_D0); pci_restore_state(dev); @@ -299,12 +278,14 @@ static int tifm_7xx1_resume(struct pci_dev *dev) return 0; } else { fm->socket_change_set = 0; + fm->finish_me = &finish_resume; spin_unlock_irqrestore(&fm->lock, flags); } - wait_event_timeout(fm->change_set_notify, fm->socket_change_set, HZ); + wait_for_completion_timeout(&finish_resume, HZ); spin_lock_irqsave(&fm->lock, flags); + fm->finish_me = NULL; writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set) | TIFM_IRQ_CARDMASK(fm->socket_change_set), fm->addr + FM_CLEAR_INTERRUPT_ENABLE); @@ -365,6 +346,7 @@ static int tifm_7xx1_probe(struct pci_dev *dev, if (!fm->sockets) goto err_out_free; + INIT_WORK(&fm->media_switcher, tifm_7xx1_switch_media); fm->eject = tifm_7xx1_eject; pci_set_drvdata(dev, fm); @@ -377,15 +359,14 @@ static int tifm_7xx1_probe(struct pci_dev *dev, if (rc) goto err_out_unmap; - init_waitqueue_head(&fm->change_set_notify); - rc = tifm_add_adapter(fm, tifm_7xx1_switch_media); + rc = tifm_add_adapter(fm); if (rc) goto err_out_irq; writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), fm->addr + FM_SET_INTERRUPT_ENABLE); - wake_up_process(fm->media_switcher); + return 0; err_out_irq: @@ -417,8 +398,6 @@ static void tifm_7xx1_remove(struct pci_dev *dev) fm->socket_change_set = (1 << fm->num_sockets) - 1; spin_unlock_irqrestore(&fm->lock, flags); - kthread_stop(fm->media_switcher); - tifm_remove_adapter(fm); pci_set_drvdata(dev, NULL); diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index 6b2c447dc8e..ef8a97b819d 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -16,6 +16,7 @@ #define DRIVER_NAME "tifm_core" #define DRIVER_VERSION "0.8" +static struct workqueue_struct *workqueue; static DEFINE_IDR(tifm_adapter_idr); static DEFINE_SPINLOCK(tifm_adapter_lock); @@ -184,8 +185,7 @@ void tifm_free_adapter(struct tifm_adapter *fm) } EXPORT_SYMBOL(tifm_free_adapter); -int tifm_add_adapter(struct tifm_adapter *fm, - int (*mediathreadfn)(void *data)) +int tifm_add_adapter(struct tifm_adapter *fm) { int rc; @@ -197,16 +197,13 @@ int tifm_add_adapter(struct tifm_adapter *fm, spin_unlock(&tifm_adapter_lock); if (!rc) { snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id); - fm->media_switcher = kthread_create(mediathreadfn, - fm, "tifm/%u", fm->id); + rc = class_device_add(&fm->cdev); - if (!IS_ERR(fm->media_switcher)) - return class_device_add(&fm->cdev); - - spin_lock(&tifm_adapter_lock); - idr_remove(&tifm_adapter_idr, fm->id); - spin_unlock(&tifm_adapter_lock); - rc = -ENOMEM; + if (rc) { + spin_lock(&tifm_adapter_lock); + idr_remove(&tifm_adapter_idr, fm->id); + spin_unlock(&tifm_adapter_lock); + } } return rc; } @@ -214,6 +211,7 @@ EXPORT_SYMBOL(tifm_add_adapter); void tifm_remove_adapter(struct tifm_adapter *fm) { + flush_workqueue(workqueue); class_device_del(&fm->cdev); spin_lock(&tifm_adapter_lock); @@ -267,6 +265,12 @@ void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, } EXPORT_SYMBOL(tifm_unmap_sg); +void tifm_queue_work(struct work_struct *work) +{ + queue_work(workqueue, work); +} +EXPORT_SYMBOL(tifm_queue_work); + int tifm_register_driver(struct tifm_driver *drv) { drv->driver.bus = &tifm_bus_type; @@ -283,13 +287,25 @@ EXPORT_SYMBOL(tifm_unregister_driver); static int __init tifm_init(void) { - int rc = bus_register(&tifm_bus_type); + int rc; - if (!rc) { - rc = class_register(&tifm_adapter_class); - if (rc) - bus_unregister(&tifm_bus_type); - } + workqueue = create_freezeable_workqueue("tifm"); + if (!workqueue) + return -ENOMEM; + + rc = bus_register(&tifm_bus_type); + + if (rc) + goto err_out_wq; + + rc = class_register(&tifm_adapter_class); + if (!rc) + return 0; + + bus_unregister(&tifm_bus_type); + +err_out_wq: + destroy_workqueue(workqueue); return rc; } @@ -298,6 +314,7 @@ static void __exit tifm_exit(void) { class_unregister(&tifm_adapter_class); bus_unregister(&tifm_bus_type); + destroy_workqueue(workqueue); } subsys_initcall(tifm_init); diff --git a/include/linux/tifm.h b/include/linux/tifm.h index 57b2653494c..d9de79275c2 100644 --- a/include/linux/tifm.h +++ b/include/linux/tifm.h @@ -111,11 +111,11 @@ struct tifm_adapter { spinlock_t lock; unsigned int irq_status; unsigned int socket_change_set; - wait_queue_head_t change_set_notify; unsigned int id; unsigned int num_sockets; + struct completion *finish_me; struct tifm_dev **sockets; - struct task_struct *media_switcher; + struct work_struct media_switcher; struct class_device cdev; struct device *dev; @@ -125,7 +125,7 @@ struct tifm_adapter { struct tifm_adapter *tifm_alloc_adapter(void); void tifm_free_device(struct device *dev); void tifm_free_adapter(struct tifm_adapter *fm); -int tifm_add_adapter(struct tifm_adapter *fm, int (*mediathreadfn)(void *data)); +int tifm_add_adapter(struct tifm_adapter *fm); void tifm_remove_adapter(struct tifm_adapter *fm); struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm); int tifm_register_driver(struct tifm_driver *drv); @@ -135,7 +135,7 @@ int tifm_map_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, int direction); void tifm_unmap_sg(struct tifm_dev *sock, struct scatterlist *sg, int nents, int direction); - +void tifm_queue_work(struct work_struct *work); static inline void *tifm_get_drvdata(struct tifm_dev *dev) { -- cgit v1.2.3 From 342c0ec4859446140c0dc5d7d903bb3b3f0577cd Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:16 +1000 Subject: tifm_7xx1: improve card detection routine Remove unneeded conditions and change a sleeping regime a little in the card type detection routine. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/misc/tifm_7xx1.c | 46 +++++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index e5655fef42d..34746798ba8 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -75,48 +75,46 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) return IRQ_HANDLED; } -static unsigned char tifm_7xx1_toggle_sock_power(char __iomem *sock_addr, - int is_x2) +static unsigned char tifm_7xx1_toggle_sock_power(char __iomem *sock_addr) { unsigned int s_state; int cnt; writel(0x0e00, sock_addr + SOCK_CONTROL); - for (cnt = 0; cnt < 100; cnt++) { + for (cnt = 16; cnt <= 256; cnt <<= 1) { if (!(TIFM_SOCK_STATE_POWERED & readl(sock_addr + SOCK_PRESENT_STATE))) break; - msleep(10); + + msleep(cnt); } s_state = readl(sock_addr + SOCK_PRESENT_STATE); if (!(TIFM_SOCK_STATE_OCCUPIED & s_state)) return 0; - if (is_x2) { - writel((s_state & 7) | 0x0c00, sock_addr + SOCK_CONTROL); - } else { - // SmartMedia cards need extra 40 msec - if (((readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7) == 1) - msleep(40); - writel(readl(sock_addr + SOCK_CONTROL) | TIFM_CTRL_LED, - sock_addr + SOCK_CONTROL); - msleep(10); - writel((s_state & 0x7) | 0x0c00 | TIFM_CTRL_LED, - sock_addr + SOCK_CONTROL); - } + writel(readl(sock_addr + SOCK_CONTROL) | TIFM_CTRL_LED, + sock_addr + SOCK_CONTROL); - for (cnt = 0; cnt < 100; cnt++) { + /* xd needs some extra time before power on */ + if (((readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7) + == TIFM_TYPE_XD) + msleep(40); + + writel((s_state & 7) | 0x0c00, sock_addr + SOCK_CONTROL); + /* wait for power to stabilize */ + msleep(20); + for (cnt = 16; cnt <= 256; cnt <<= 1) { if ((TIFM_SOCK_STATE_POWERED & readl(sock_addr + SOCK_PRESENT_STATE))) break; - msleep(10); + + msleep(cnt); } - if (!is_x2) - writel(readl(sock_addr + SOCK_CONTROL) & (~TIFM_CTRL_LED), - sock_addr + SOCK_CONTROL); + writel(readl(sock_addr + SOCK_CONTROL) & (~TIFM_CTRL_LED), + sock_addr + SOCK_CONTROL); return (readl(sock_addr + SOCK_PRESENT_STATE) >> 4) & 7; } @@ -169,8 +167,7 @@ static void tifm_7xx1_switch_media(struct work_struct *work) spin_unlock_irqrestore(&fm->lock, flags); media_id = tifm_7xx1_toggle_sock_power( - tifm_7xx1_sock_addr(fm->addr, cnt), - fm->num_sockets == 2); + tifm_7xx1_sock_addr(fm->addr, cnt)); if (media_id) { sock = tifm_alloc_device(fm); if (sock) { @@ -258,8 +255,7 @@ static int tifm_7xx1_resume(struct pci_dev *dev) for (cnt = 0; cnt < fm->num_sockets; cnt++) new_ids[cnt] = tifm_7xx1_toggle_sock_power( - tifm_7xx1_sock_addr(fm->addr, cnt), - fm->num_sockets == 2); + tifm_7xx1_sock_addr(fm->addr, cnt)); spin_lock_irqsave(&fm->lock, flags); fm->socket_change_set = 0; for (cnt = 0; cnt < fm->num_sockets; cnt++) { -- cgit v1.2.3 From 6113ed73e61a13db9da48831e1b35788b7f837cc Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:17 +1000 Subject: tifm: move common adapter management tasks from tifm_7xx1 to tifm_core Some details of the adapter management (create, add, remove) are really belong to the tifm_core, as they are not hardware specific. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/misc/tifm_7xx1.c | 28 ++++++++++---------------- drivers/misc/tifm_core.c | 52 +++++++++++++++++++++++++++++------------------- include/linux/tifm.h | 16 +++++++++------ 3 files changed, 51 insertions(+), 45 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 34746798ba8..e051f9da9c5 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -22,6 +22,11 @@ #define TIFM_IRQ_FIFOMASK(x) ((x) << 16) #define TIFM_IRQ_SETALL 0xffffffff +static void tifm_7xx1_dummy_eject(struct tifm_adapter *fm, + struct tifm_dev *sock) +{ +} + static void tifm_7xx1_eject(struct tifm_adapter *fm, struct tifm_dev *sock) { unsigned long flags; @@ -140,7 +145,7 @@ static void tifm_7xx1_switch_media(struct work_struct *work) socket_change_set = fm->socket_change_set; fm->socket_change_set = 0; - dev_dbg(fm->dev, "checking media set %x\n", + dev_dbg(fm->cdev.dev, "checking media set %x\n", socket_change_set); if (!socket_change_set) { @@ -328,20 +333,13 @@ static int tifm_7xx1_probe(struct pci_dev *dev, pci_intx(dev, 1); - fm = tifm_alloc_adapter(); + fm = tifm_alloc_adapter(dev->device == PCI_DEVICE_ID_TI_XX21_XX11_FM + ? 4 : 2, &dev->dev); if (!fm) { rc = -ENOMEM; goto err_out_int; } - fm->dev = &dev->dev; - fm->num_sockets = (dev->device == PCI_DEVICE_ID_TI_XX21_XX11_FM) - ? 4 : 2; - fm->sockets = kzalloc(sizeof(struct tifm_dev*) * fm->num_sockets, - GFP_KERNEL); - if (!fm->sockets) - goto err_out_free; - INIT_WORK(&fm->media_switcher, tifm_7xx1_switch_media); fm->eject = tifm_7xx1_eject; pci_set_drvdata(dev, fm); @@ -351,7 +349,7 @@ static int tifm_7xx1_probe(struct pci_dev *dev, if (!fm->addr) goto err_out_free; - rc = request_irq(dev->irq, tifm_7xx1_isr, IRQF_SHARED, DRIVER_NAME, fm); + rc = request_irq(dev->irq, tifm_7xx1_isr, SA_SHIRQ, DRIVER_NAME, fm); if (rc) goto err_out_unmap; @@ -359,10 +357,8 @@ static int tifm_7xx1_probe(struct pci_dev *dev, if (rc) goto err_out_irq; - writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), fm->addr + FM_SET_INTERRUPT_ENABLE); - return 0; err_out_irq: @@ -384,16 +380,12 @@ err_out: static void tifm_7xx1_remove(struct pci_dev *dev) { struct tifm_adapter *fm = pci_get_drvdata(dev); - unsigned long flags; + fm->eject = tifm_7xx1_dummy_eject; writel(TIFM_IRQ_SETALL, fm->addr + FM_CLEAR_INTERRUPT_ENABLE); mmiowb(); free_irq(dev->irq, fm); - spin_lock_irqsave(&fm->lock, flags); - fm->socket_change_set = (1 << fm->num_sockets) - 1; - spin_unlock_irqrestore(&fm->lock, flags); - tifm_remove_adapter(fm); pci_set_drvdata(dev, NULL); diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index ef8a97b819d..f0cce2a642d 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -156,7 +156,6 @@ static void tifm_free(struct class_device *cdev) { struct tifm_adapter *fm = container_of(cdev, struct tifm_adapter, cdev); - kfree(fm->sockets); kfree(fm); } @@ -165,26 +164,24 @@ static struct class tifm_adapter_class = { .release = tifm_free }; -struct tifm_adapter *tifm_alloc_adapter(void) +struct tifm_adapter *tifm_alloc_adapter(unsigned int num_sockets, + struct device *dev) { struct tifm_adapter *fm; - fm = kzalloc(sizeof(struct tifm_adapter), GFP_KERNEL); + fm = kzalloc(sizeof(struct tifm_adapter) + + sizeof(struct tifm_dev*) * num_sockets, GFP_KERNEL); if (fm) { fm->cdev.class = &tifm_adapter_class; - spin_lock_init(&fm->lock); + fm->cdev.dev = dev; class_device_initialize(&fm->cdev); + spin_lock_init(&fm->lock); + fm->num_sockets = num_sockets; } return fm; } EXPORT_SYMBOL(tifm_alloc_adapter); -void tifm_free_adapter(struct tifm_adapter *fm) -{ - class_device_put(&fm->cdev); -} -EXPORT_SYMBOL(tifm_free_adapter); - int tifm_add_adapter(struct tifm_adapter *fm) { int rc; @@ -195,31 +192,44 @@ int tifm_add_adapter(struct tifm_adapter *fm) spin_lock(&tifm_adapter_lock); rc = idr_get_new(&tifm_adapter_idr, fm, &fm->id); spin_unlock(&tifm_adapter_lock); - if (!rc) { - snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id); - rc = class_device_add(&fm->cdev); - - if (rc) { - spin_lock(&tifm_adapter_lock); - idr_remove(&tifm_adapter_idr, fm->id); - spin_unlock(&tifm_adapter_lock); - } + if (rc) + return rc; + + snprintf(fm->cdev.class_id, BUS_ID_SIZE, "tifm%u", fm->id); + rc = class_device_add(&fm->cdev); + if (rc) { + spin_lock(&tifm_adapter_lock); + idr_remove(&tifm_adapter_idr, fm->id); + spin_unlock(&tifm_adapter_lock); } + return rc; } EXPORT_SYMBOL(tifm_add_adapter); void tifm_remove_adapter(struct tifm_adapter *fm) { + unsigned int cnt; + flush_workqueue(workqueue); - class_device_del(&fm->cdev); + for (cnt = 0; cnt < fm->num_sockets; ++cnt) { + if (fm->sockets[cnt]) + device_unregister(&fm->sockets[cnt]->dev); + } spin_lock(&tifm_adapter_lock); idr_remove(&tifm_adapter_idr, fm->id); spin_unlock(&tifm_adapter_lock); + class_device_del(&fm->cdev); } EXPORT_SYMBOL(tifm_remove_adapter); +void tifm_free_adapter(struct tifm_adapter *fm) +{ + class_device_put(&fm->cdev); +} +EXPORT_SYMBOL(tifm_free_adapter); + void tifm_free_device(struct device *dev) { struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); @@ -234,7 +244,7 @@ struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm) if (dev) { spin_lock_init(&dev->lock); - dev->dev.parent = fm->dev; + dev->dev.parent = fm->cdev.dev; dev->dev.bus = &tifm_bus_type; dev->dev.release = tifm_free_device; dev->card_event = tifm_dummy_event; diff --git a/include/linux/tifm.h b/include/linux/tifm.h index d9de79275c2..a7bd654e2ee 100644 --- a/include/linux/tifm.h +++ b/include/linux/tifm.h @@ -114,19 +114,23 @@ struct tifm_adapter { unsigned int id; unsigned int num_sockets; struct completion *finish_me; - struct tifm_dev **sockets; + struct work_struct media_switcher; struct class_device cdev; - struct device *dev; - void (*eject)(struct tifm_adapter *fm, struct tifm_dev *sock); + void (*eject)(struct tifm_adapter *fm, + struct tifm_dev *sock); + + struct tifm_dev *sockets[0]; }; -struct tifm_adapter *tifm_alloc_adapter(void); -void tifm_free_device(struct device *dev); -void tifm_free_adapter(struct tifm_adapter *fm); +struct tifm_adapter *tifm_alloc_adapter(unsigned int num_sockets, + struct device *dev); int tifm_add_adapter(struct tifm_adapter *fm); void tifm_remove_adapter(struct tifm_adapter *fm); +void tifm_free_adapter(struct tifm_adapter *fm); + +void tifm_free_device(struct device *dev); struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm); int tifm_register_driver(struct tifm_driver *drv); void tifm_unregister_driver(struct tifm_driver *drv); -- cgit v1.2.3 From 2428a8fe2261e901e058d9ea8b6ed7e1b4268b79 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:18 +1000 Subject: tifm: move common device management tasks from tifm_7xx1 to tifm_core Some details of the device management (create, add, remove) are really belong to the tifm_core, as they are not hardware specific. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/misc/tifm_7xx1.c | 94 ++++++++++++++++++------------------------------ drivers/misc/tifm_core.c | 39 +++++++++++++------- include/linux/tifm.h | 4 ++- 3 files changed, 65 insertions(+), 72 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index e051f9da9c5..356386904a5 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -136,7 +136,6 @@ static void tifm_7xx1_switch_media(struct work_struct *work) media_switcher); unsigned long flags; unsigned char media_id; - char *card_name = "xx"; int cnt; struct tifm_dev *sock; unsigned int socket_change_set; @@ -153,68 +152,45 @@ static void tifm_7xx1_switch_media(struct work_struct *work) return; } - for (cnt = 0; cnt < fm->num_sockets; cnt++) { - if (!(socket_change_set & (1 << cnt))) - continue; - sock = fm->sockets[cnt]; - if (sock) { - printk(KERN_INFO DRIVER_NAME - ": demand removing card from socket %d\n", - cnt); - fm->sockets[cnt] = NULL; - spin_unlock_irqrestore(&fm->lock, flags); - device_unregister(&sock->dev); - spin_lock_irqsave(&fm->lock, flags); - writel(0x0e00, - tifm_7xx1_sock_addr(fm->addr, cnt) - + SOCK_CONTROL); - } - + for (cnt = 0; cnt < fm->num_sockets; cnt++) { + if (!(socket_change_set & (1 << cnt))) + continue; + sock = fm->sockets[cnt]; + if (sock) { + printk(KERN_INFO + "%s : demand removing card from socket %u:%u\n", + fm->cdev.class_id, fm->id, cnt); + fm->sockets[cnt] = NULL; spin_unlock_irqrestore(&fm->lock, flags); - media_id = tifm_7xx1_toggle_sock_power( - tifm_7xx1_sock_addr(fm->addr, cnt)); - if (media_id) { - sock = tifm_alloc_device(fm); - if (sock) { - sock->addr = tifm_7xx1_sock_addr(fm->addr, - cnt); - sock->type = media_id; - sock->socket_id = cnt; - switch (media_id) { - case 1: - card_name = "xd"; - break; - case 2: - card_name = "ms"; - break; - case 3: - card_name = "sd"; - break; - default: - tifm_free_device(&sock->dev); - spin_lock_irqsave(&fm->lock, flags); - continue; - } - snprintf(sock->dev.bus_id, BUS_ID_SIZE, - "tifm_%s%u:%u", card_name, - fm->id, cnt); - printk(KERN_INFO DRIVER_NAME - ": %s card detected in socket %d\n", - card_name, cnt); - if (!device_register(&sock->dev)) { - spin_lock_irqsave(&fm->lock, flags); - if (!fm->sockets[cnt]) { - fm->sockets[cnt] = sock; - sock = NULL; - } - spin_unlock_irqrestore(&fm->lock, flags); - } - if (sock) - tifm_free_device(&sock->dev); - } + device_unregister(&sock->dev); + spin_lock_irqsave(&fm->lock, flags); + writel(0x0e00, tifm_7xx1_sock_addr(fm->addr, cnt) + + SOCK_CONTROL); + } + + spin_unlock_irqrestore(&fm->lock, flags); + + media_id = tifm_7xx1_toggle_sock_power( + tifm_7xx1_sock_addr(fm->addr, cnt)); + + // tifm_alloc_device will check if media_id is valid + sock = tifm_alloc_device(fm, cnt, media_id); + if (sock) { + sock->addr = tifm_7xx1_sock_addr(fm->addr, cnt); + + if (!device_register(&sock->dev)) { spin_lock_irqsave(&fm->lock, flags); + if (!fm->sockets[cnt]) { + fm->sockets[cnt] = sock; + sock = NULL; + } + spin_unlock_irqrestore(&fm->lock, flags); } + if (sock) + tifm_free_device(&sock->dev); } + spin_lock_irqsave(&fm->lock, flags); + } writel(TIFM_IRQ_FIFOMASK(socket_change_set) | TIFM_IRQ_CARDMASK(socket_change_set), diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index f0cce2a642d..1e591989835 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -232,25 +232,40 @@ EXPORT_SYMBOL(tifm_free_adapter); void tifm_free_device(struct device *dev) { - struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); - kfree(fm_dev); + struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); + kfree(sock); } EXPORT_SYMBOL(tifm_free_device); -struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm) +struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id, + unsigned char type) { - struct tifm_dev *dev = kzalloc(sizeof(struct tifm_dev), GFP_KERNEL); + struct tifm_dev *sock = NULL; + + if (!tifm_media_type_name(type, 0)) + return sock; - if (dev) { - spin_lock_init(&dev->lock); + sock = kzalloc(sizeof(struct tifm_dev), GFP_KERNEL); + if (sock) { + spin_lock_init(&sock->lock); + sock->type = type; + sock->socket_id = id; + sock->card_event = tifm_dummy_event; + sock->data_event = tifm_dummy_event; - dev->dev.parent = fm->cdev.dev; - dev->dev.bus = &tifm_bus_type; - dev->dev.release = tifm_free_device; - dev->card_event = tifm_dummy_event; - dev->data_event = tifm_dummy_event; + sock->dev.parent = fm->cdev.dev; + sock->dev.bus = &tifm_bus_type; + sock->dev.dma_mask = fm->cdev.dev->dma_mask; + sock->dev.release = tifm_free_device; + + snprintf(sock->dev.bus_id, BUS_ID_SIZE, + "tifm_%s%u:%u", tifm_media_type_name(type, 2), + fm->id, id); + printk(KERN_INFO DRIVER_NAME + ": %s card detected in socket %u:%u\n", + tifm_media_type_name(type, 0), fm->id, id); } - return dev; + return sock; } EXPORT_SYMBOL(tifm_alloc_device); diff --git a/include/linux/tifm.h b/include/linux/tifm.h index a7bd654e2ee..82da028d8c0 100644 --- a/include/linux/tifm.h +++ b/include/linux/tifm.h @@ -131,7 +131,9 @@ void tifm_remove_adapter(struct tifm_adapter *fm); void tifm_free_adapter(struct tifm_adapter *fm); void tifm_free_device(struct device *dev); -struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm); +struct tifm_dev *tifm_alloc_device(struct tifm_adapter *fm, unsigned int id, + unsigned char type); + int tifm_register_driver(struct tifm_driver *drv); void tifm_unregister_driver(struct tifm_driver *drv); void tifm_eject(struct tifm_dev *sock); -- cgit v1.2.3 From 88de1b2fed2bbe9eb1b7310195be84cf143efb4f Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:19 +1000 Subject: tifm_7xx1: fix adapter resume function Fixes to the adapter resume function to correctly handle all possible cases: 1. Card is removed during suspend 2. Card is inserted during suspend into previously empty socket 3. Card is replaced during suspend by same or different media type card. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/misc/tifm_7xx1.c | 58 ++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 356386904a5..eafa5575f31 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -220,7 +220,8 @@ static int tifm_7xx1_suspend(struct pci_dev *dev, pm_message_t state) static int tifm_7xx1_resume(struct pci_dev *dev) { struct tifm_adapter *fm = pci_get_drvdata(dev); - int cnt, rc; + int rc; + unsigned int good_sockets = 0, bad_sockets = 0; unsigned long flags; unsigned char new_ids[fm->num_sockets]; DECLARE_COMPLETION_ONSTACK(finish_resume); @@ -234,46 +235,49 @@ static int tifm_7xx1_resume(struct pci_dev *dev) dev_dbg(&dev->dev, "resuming host\n"); - for (cnt = 0; cnt < fm->num_sockets; cnt++) - new_ids[cnt] = tifm_7xx1_toggle_sock_power( - tifm_7xx1_sock_addr(fm->addr, cnt)); + for (rc = 0; rc < fm->num_sockets; rc++) + new_ids[rc] = tifm_7xx1_toggle_sock_power( + tifm_7xx1_sock_addr(fm->addr, rc)); spin_lock_irqsave(&fm->lock, flags); - fm->socket_change_set = 0; - for (cnt = 0; cnt < fm->num_sockets; cnt++) { - if (fm->sockets[cnt]) { - if (fm->sockets[cnt]->type == new_ids[cnt]) - fm->socket_change_set |= 1 << cnt; - - fm->sockets[cnt]->type = new_ids[cnt]; + for (rc = 0; rc < fm->num_sockets; rc++) { + if (fm->sockets[rc]) { + if (fm->sockets[rc]->type == new_ids[rc]) + good_sockets |= 1 << rc; + else + bad_sockets |= 1 << rc; } } writel(TIFM_IRQ_ENABLE | TIFM_IRQ_SOCKMASK((1 << fm->num_sockets) - 1), fm->addr + FM_SET_INTERRUPT_ENABLE); - if (!fm->socket_change_set) { - spin_unlock_irqrestore(&fm->lock, flags); - return 0; - } else { - fm->socket_change_set = 0; + dev_dbg(&dev->dev, "change sets on resume: good %x, bad %x\n", + good_sockets, bad_sockets); + + fm->socket_change_set = 0; + if (good_sockets) { fm->finish_me = &finish_resume; spin_unlock_irqrestore(&fm->lock, flags); + rc = wait_for_completion_timeout(&finish_resume, HZ); + dev_dbg(&dev->dev, "wait returned %d\n", rc); + writel(TIFM_IRQ_FIFOMASK(good_sockets) + | TIFM_IRQ_CARDMASK(good_sockets), + fm->addr + FM_CLEAR_INTERRUPT_ENABLE); + writel(TIFM_IRQ_FIFOMASK(good_sockets) + | TIFM_IRQ_CARDMASK(good_sockets), + fm->addr + FM_SET_INTERRUPT_ENABLE); + spin_lock_irqsave(&fm->lock, flags); + fm->finish_me = NULL; + fm->socket_change_set ^= good_sockets & fm->socket_change_set; } - wait_for_completion_timeout(&finish_resume, HZ); + fm->socket_change_set |= bad_sockets; + if (fm->socket_change_set) + tifm_queue_work(&fm->media_switcher); - spin_lock_irqsave(&fm->lock, flags); - fm->finish_me = NULL; - writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set) - | TIFM_IRQ_CARDMASK(fm->socket_change_set), - fm->addr + FM_CLEAR_INTERRUPT_ENABLE); - writel(TIFM_IRQ_FIFOMASK(fm->socket_change_set) - | TIFM_IRQ_CARDMASK(fm->socket_change_set), - fm->addr + FM_SET_INTERRUPT_ENABLE); + spin_unlock_irqrestore(&fm->lock, flags); writel(TIFM_IRQ_ENABLE, fm->addr + FM_SET_INTERRUPT_ENABLE); - fm->socket_change_set = 0; - spin_unlock_irqrestore(&fm->lock, flags); return 0; } -- cgit v1.2.3 From 4e64f223857b138e3474bedc967d51db25c414b3 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:20 +1000 Subject: tifm: add sysfs attribute for tifm devices A sysfs attribute reflecting current media type is added. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/misc/tifm_core.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index 1e591989835..70220beb3e0 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -142,14 +142,27 @@ static int tifm_device_resume(struct device *dev) #endif /* CONFIG_PM */ +static ssize_t type_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); + return sprintf(buf, "%x", sock->type); +} + +static struct device_attribute tifm_dev_attrs[] = { + __ATTR(type, S_IRUGO, type_show, NULL), + __ATTR_NULL +}; + static struct bus_type tifm_bus_type = { - .name = "tifm", - .match = tifm_bus_match, - .uevent = tifm_uevent, - .probe = tifm_device_probe, - .remove = tifm_device_remove, - .suspend = tifm_device_suspend, - .resume = tifm_device_resume + .name = "tifm", + .dev_attrs = tifm_dev_attrs, + .match = tifm_bus_match, + .uevent = tifm_uevent, + .probe = tifm_device_probe, + .remove = tifm_device_remove, + .suspend = tifm_device_suspend, + .resume = tifm_device_resume }; static void tifm_free(struct class_device *cdev) -- cgit v1.2.3 From b039d4a187a4064c926159db063004377281b041 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:21 +1000 Subject: tifm_sd: remove tifm_sd_terminate function tifm_sd_terminate can only lawfully be called on device removal so it can be merged with tifm_sd_remove. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/mmc/tifm_sd.c | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index 4388ee9062a..fe236cb95e5 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -665,32 +665,13 @@ static void tifm_sd_end_cmd_nodma(unsigned long data) mmc_request_done(mmc, mrq); } -static void tifm_sd_terminate(struct tifm_sd *host) -{ - struct tifm_dev *sock = host->dev; - unsigned long flags; - - writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); - mmiowb(); - spin_lock_irqsave(&sock->lock, flags); - host->flags |= EJECT; - if (host->req) { - writel(TIFM_FIFO_INT_SETALL, - sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); - writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); - tasklet_schedule(&host->finish_tasklet); - } - spin_unlock_irqrestore(&sock->lock, flags); -} - static void tifm_sd_abort(unsigned long data) { struct tifm_sd *host = (struct tifm_sd*)data; printk(KERN_ERR DRIVER_NAME - ": card failed to respond for a long period of time"); + ": card failed to respond for a long period of time\n"); - tifm_sd_terminate(host); tifm_eject(host->dev); } @@ -913,9 +894,20 @@ static void tifm_sd_remove(struct tifm_dev *sock) { struct mmc_host *mmc = tifm_get_drvdata(sock); struct tifm_sd *host = mmc_priv(mmc); + unsigned long flags; del_timer_sync(&host->timer); - tifm_sd_terminate(host); + writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); + mmiowb(); + spin_lock_irqsave(&sock->lock, flags); + host->flags |= EJECT; + if (host->req) { + writel(TIFM_FIFO_INT_SETALL, + sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); + writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); + tasklet_schedule(&host->finish_tasklet); + } + spin_unlock_irqrestore(&sock->lock, flags); wait_event_timeout(host->notify, host->flags & EJECT_DONE, host->timeout_jiffies); tasklet_kill(&host->finish_tasklet); -- cgit v1.2.3 From 592d372ae89dd5b43117cf5113a910f67f6e6a7e Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:22 +1000 Subject: tifm_sd: remove wait for power off on remove This wait was needed because of the mmc layer failure to wait for completion of all outstanding commands before host removal. It should be fixed now in the mmc layer. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/mmc/tifm_sd.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index fe236cb95e5..bf00e8cf670 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -80,7 +80,6 @@ typedef enum { enum { FIFO_RDY = 0x0001, /* hardware dependent value */ EJECT = 0x0004, - EJECT_DONE = 0x0008, CARD_BUSY = 0x0010, OPENDRAIN = 0x0040, /* hardware dependent value */ CARD_EVENT = 0x0100, /* hardware dependent value */ @@ -99,7 +98,6 @@ struct tifm_sd { struct tasklet_struct finish_tasklet; struct timer_list timer; struct mmc_request *req; - wait_queue_head_t notify; size_t written_blocks; size_t buffer_size; @@ -738,12 +736,6 @@ static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) /* chip_select : maybe later */ //vdd //power is set before probe / after remove - //I believe, power_off when already marked for eject is sufficient to - // allow removal. - if ((host->flags & EJECT) && ios->power_mode == MMC_POWER_OFF) { - host->flags |= EJECT_DONE; - wake_up_all(&host->notify); - } spin_unlock_irqrestore(&sock->lock, flags); } @@ -854,7 +846,6 @@ static int tifm_sd_probe(struct tifm_dev *sock) host->dev = sock; host->timeout_jiffies = msecs_to_jiffies(1000); - init_waitqueue_head(&host->notify); tasklet_init(&host->finish_tasklet, no_dma ? tifm_sd_end_cmd_nodma : tifm_sd_end_cmd, (unsigned long)host); @@ -896,28 +887,32 @@ static void tifm_sd_remove(struct tifm_dev *sock) struct tifm_sd *host = mmc_priv(mmc); unsigned long flags; - del_timer_sync(&host->timer); + spin_lock_irqsave(&sock->lock, flags); + host->flags |= EJECT; writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); mmiowb(); + spin_unlock_irqrestore(&sock->lock, flags); + + tasklet_kill(&host->finish_tasklet); + spin_lock_irqsave(&sock->lock, flags); - host->flags |= EJECT; if (host->req) { writel(TIFM_FIFO_INT_SETALL, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); + host->req->cmd->error = MMC_ERR_TIMEOUT; + if (host->req->stop) + host->req->stop->error = MMC_ERR_TIMEOUT; tasklet_schedule(&host->finish_tasklet); } spin_unlock_irqrestore(&sock->lock, flags); - wait_event_timeout(host->notify, host->flags & EJECT_DONE, - host->timeout_jiffies); - tasklet_kill(&host->finish_tasklet); mmc_remove_host(mmc); + dev_dbg(&sock->dev, "after remove\n"); /* The meaning of the bit majority in this constant is unknown. */ writel(0xfff8 & readl(sock->addr + SOCK_CONTROL), sock->addr + SOCK_CONTROL); - tifm_set_drvdata(sock, NULL); mmc_free_host(mmc); } -- cgit v1.2.3 From 0007d4837ac94d672f313cfc462f879b5d06f221 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:23 +1000 Subject: tifm_sd: separate command flags, socket flags and register bit masks host->flags variable was hosting a collection of bits with different semantics. For clarity, hardware bit masks are now defined as macros, socket flags represented as bit fields and flags (now cmd_flags) only hosts command processing modifiers. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/mmc/tifm_sd.c | 72 +++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index bf00e8cf670..fe8cb1aa681 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -50,6 +50,9 @@ module_param(fixed_timeout, bool, 0644); #define TIFM_MMCSD_AE 0x0800 /* fifo almost empty */ #define TIFM_MMCSD_CERR 0x4000 /* card status error */ +#define TIFM_MMCSD_ODTO 0x0040 /* open drain / extended timeout */ +#define TIFM_MMCSD_CARD_RO 0x0200 /* card is read-only */ + #define TIFM_MMCSD_FIFO_SIZE 0x0020 #define TIFM_MMCSD_RSP_R0 0x0000 @@ -79,17 +82,16 @@ typedef enum { enum { FIFO_RDY = 0x0001, /* hardware dependent value */ - EJECT = 0x0004, - CARD_BUSY = 0x0010, - OPENDRAIN = 0x0040, /* hardware dependent value */ - CARD_EVENT = 0x0100, /* hardware dependent value */ - CARD_RO = 0x0200, /* hardware dependent value */ - FIFO_EVENT = 0x10000 }; /* hardware dependent value */ + CARD_BUSY = 0x0010 +}; struct tifm_sd { struct tifm_dev *dev; - unsigned int flags; + unsigned short eject:1, + open_drain:1, + no_dma:1; + unsigned short cmd_flags; card_state_t state; unsigned int clk_freq; unsigned int clk_div; @@ -119,7 +121,7 @@ static int tifm_sd_transfer_data(struct tifm_dev *sock, struct tifm_sd *host, if (host_status & TIFM_MMCSD_BRS) { /* in non-dma rx mode BRS fires when fifo is still not empty */ - if (no_dma && (cmd->data->flags & MMC_DATA_READ)) { + if (host->no_dma && (cmd->data->flags & MMC_DATA_READ)) { buffer = tifm_sd_data_buffer(host->req->data); while (host->buffer_size > host->buffer_pos) { t_val = readl(sock->addr + SOCK_MMCSD_DATA); @@ -129,7 +131,7 @@ static int tifm_sd_transfer_data(struct tifm_dev *sock, struct tifm_sd *host, } } return 1; - } else if (no_dma) { + } else if (host->no_dma) { buffer = tifm_sd_data_buffer(host->req->data); if ((cmd->data->flags & MMC_DATA_READ) && (host_status & TIFM_MMCSD_AF)) { @@ -204,8 +206,10 @@ static unsigned int tifm_sd_op_flags(struct mmc_command *cmd) static void tifm_sd_exec(struct tifm_sd *host, struct mmc_command *cmd) { struct tifm_dev *sock = host->dev; - unsigned int cmd_mask = tifm_sd_op_flags(cmd) | - (host->flags & OPENDRAIN); + unsigned int cmd_mask = tifm_sd_op_flags(cmd); + + if (host->open_drain) + cmd_mask |= TIFM_MMCSD_ODTO; if (cmd->data && (cmd->data->flags & MMC_DATA_READ)) cmd_mask |= TIFM_MMCSD_READ; @@ -255,7 +259,7 @@ change_state: if (cmd->data->flags & MMC_DATA_WRITE) { host->state = CARD; } else { - if (no_dma) { + if (host->no_dma) { if (host->req->stop) { tifm_sd_exec(host, host->req->stop); host->state = SCMD; @@ -279,9 +283,9 @@ change_state: case CARD: dev_dbg(&sock->dev, "waiting for CARD, have %zd blocks\n", host->written_blocks); - if (!(host->flags & CARD_BUSY) + if (!(host->cmd_flags & CARD_BUSY) && (host->written_blocks == cmd->data->blocks)) { - if (no_dma) { + if (host->no_dma) { if (host->req->stop) { tifm_sd_exec(host, host->req->stop); host->state = SCMD; @@ -295,8 +299,8 @@ change_state: } break; case FIFO: - if (host->flags & FIFO_RDY) { - host->flags &= ~FIFO_RDY; + if (host->cmd_flags & FIFO_RDY) { + host->cmd_flags &= ~FIFO_RDY; if (host->req->stop) { tifm_sd_exec(host, host->req->stop); host->state = SCMD; @@ -325,7 +329,7 @@ static void tifm_sd_data_event(struct tifm_dev *sock) fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS); writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS); - host->flags |= fifo_status & FIFO_RDY; + host->cmd_flags |= fifo_status & FIFO_RDY; if (host->req) tifm_sd_process_cmd(sock, host, 0); @@ -383,11 +387,11 @@ static void tifm_sd_card_event(struct tifm_dev *sock) } if (host_status & TIFM_MMCSD_CB) - host->flags |= CARD_BUSY; + host->cmd_flags |= CARD_BUSY; if ((host_status & TIFM_MMCSD_EOFB) - && (host->flags & CARD_BUSY)) { + && (host->cmd_flags & CARD_BUSY)) { host->written_blocks++; - host->flags &= ~CARD_BUSY; + host->cmd_flags &= ~CARD_BUSY; } if (host->req) @@ -466,7 +470,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) struct mmc_data *r_data = mrq->cmd->data; spin_lock_irqsave(&sock->lock, flags); - if (host->flags & EJECT) { + if (host->eject) { spin_unlock_irqrestore(&sock->lock, flags); goto err_out; } @@ -491,7 +495,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) } host->written_blocks = 0; - host->flags &= ~CARD_BUSY; + host->cmd_flags &= ~CARD_BUSY; tifm_sd_prepare_data(host, mrq->cmd); } @@ -568,7 +572,7 @@ static void tifm_sd_request_nodma(struct mmc_host *mmc, struct mmc_request *mrq) struct mmc_data *r_data = mrq->cmd->data; spin_lock_irqsave(&sock->lock, flags); - if (host->flags & EJECT) { + if (host->eject) { spin_unlock_irqrestore(&sock->lock, flags); goto err_out; } @@ -593,7 +597,7 @@ static void tifm_sd_request_nodma(struct mmc_host *mmc, struct mmc_request *mrq) sock->addr + SOCK_MMCSD_BUFFER_CONFIG); host->written_blocks = 0; - host->flags &= ~CARD_BUSY; + host->cmd_flags &= ~CARD_BUSY; host->buffer_pos = 0; writel(r_data->blocks - 1, sock->addr + SOCK_MMCSD_NUM_BLOCKS); writel(r_data->blksz - 1, sock->addr + SOCK_MMCSD_BLOCK_LEN); @@ -728,10 +732,7 @@ static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) & readl(sock->addr + SOCK_MMCSD_CONFIG)), sock->addr + SOCK_MMCSD_CONFIG); - if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) - host->flags |= OPENDRAIN; - else - host->flags &= ~OPENDRAIN; + host->open_drain = (ios->bus_mode == MMC_BUSMODE_OPENDRAIN); /* chip_select : maybe later */ //vdd @@ -742,16 +743,14 @@ static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) static int tifm_sd_ro(struct mmc_host *mmc) { - int rc; + int rc = 0; struct tifm_sd *host = mmc_priv(mmc); struct tifm_dev *sock = host->dev; unsigned long flags; spin_lock_irqsave(&sock->lock, flags); - - host->flags |= (CARD_RO & readl(sock->addr + SOCK_PRESENT_STATE)); - rc = (host->flags & CARD_RO) ? 1 : 0; - + if (TIFM_MMCSD_CARD_RO & readl(sock->addr + SOCK_PRESENT_STATE)) + rc = 1; spin_unlock_irqrestore(&sock->lock, flags); return rc; } @@ -842,16 +841,17 @@ static int tifm_sd_probe(struct tifm_dev *sock) return -ENOMEM; host = mmc_priv(mmc); + host->no_dma = no_dma; tifm_set_drvdata(sock, mmc); host->dev = sock; host->timeout_jiffies = msecs_to_jiffies(1000); tasklet_init(&host->finish_tasklet, - no_dma ? tifm_sd_end_cmd_nodma : tifm_sd_end_cmd, + host->no_dma ? tifm_sd_end_cmd_nodma : tifm_sd_end_cmd, (unsigned long)host); setup_timer(&host->timer, tifm_sd_abort, (unsigned long)host); - tifm_sd_ops.request = no_dma ? tifm_sd_request_nodma : tifm_sd_request; + tifm_sd_ops.request = host->no_dma ? tifm_sd_request_nodma : tifm_sd_request; mmc->ops = &tifm_sd_ops; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE; @@ -888,7 +888,7 @@ static void tifm_sd_remove(struct tifm_dev *sock) unsigned long flags; spin_lock_irqsave(&sock->lock, flags); - host->flags |= EJECT; + host->eject = 1; writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); mmiowb(); spin_unlock_irqrestore(&sock->lock, flags); -- cgit v1.2.3 From dfef26d9aad4f983da232b259ee7f7faec479b2d Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:24 +1000 Subject: tifm_sd: merge dma and pio request processing paths To allow for switching of trasfer mode (dma/pio) on a per-request basis, pio and dma request issue and completion function are now merged. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/mmc/tifm_sd.c | 198 +++++++++++++++++--------------------------------- 1 file changed, 68 insertions(+), 130 deletions(-) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index fe8cb1aa681..52499548abe 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -484,19 +484,40 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) if (r_data) { tifm_sd_set_data_timeout(host, r_data); - sg_count = tifm_map_sg(sock, r_data->sg, r_data->sg_len, - mrq->cmd->flags & MMC_DATA_WRITE - ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - if (sg_count != 1) { - printk(KERN_ERR DRIVER_NAME - ": scatterlist map failed\n"); - spin_unlock_irqrestore(&sock->lock, flags); - goto err_out; - } + if (host->no_dma) { + host->buffer_size = mrq->cmd->data->blocks + * mrq->cmd->data->blksz; + + writel(TIFM_MMCSD_BUFINT + | readl(sock->addr + SOCK_MMCSD_INT_ENABLE), + sock->addr + SOCK_MMCSD_INT_ENABLE); + writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8) + | (TIFM_MMCSD_FIFO_SIZE - 1), + sock->addr + SOCK_MMCSD_BUFFER_CONFIG); + + host->written_blocks = 0; + host->cmd_flags &= ~CARD_BUSY; + host->buffer_pos = 0; + writel(r_data->blocks - 1, + sock->addr + SOCK_MMCSD_NUM_BLOCKS); + writel(r_data->blksz - 1, + sock->addr + SOCK_MMCSD_BLOCK_LEN); + } else { + sg_count = tifm_map_sg(sock, r_data->sg, r_data->sg_len, + mrq->cmd->flags & MMC_DATA_WRITE + ? PCI_DMA_TODEVICE + : PCI_DMA_FROMDEVICE); + if (sg_count != 1) { + printk(KERN_ERR DRIVER_NAME + ": scatterlist map failed\n"); + spin_unlock_irqrestore(&sock->lock, flags); + goto err_out; + } - host->written_blocks = 0; - host->cmd_flags &= ~CARD_BUSY; - tifm_sd_prepare_data(host, mrq->cmd); + host->written_blocks = 0; + host->cmd_flags &= ~CARD_BUSY; + tifm_sd_prepare_data(host, mrq->cmd); + } } host->req = mrq; @@ -542,128 +563,47 @@ static void tifm_sd_end_cmd(unsigned long data) r_data = mrq->cmd->data; if (r_data) { - if (r_data->flags & MMC_DATA_WRITE) { - r_data->bytes_xfered = host->written_blocks - * r_data->blksz; - } else { - r_data->bytes_xfered = r_data->blocks - - readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; - r_data->bytes_xfered *= r_data->blksz; - r_data->bytes_xfered += r_data->blksz - - readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1; - } - tifm_unmap_sg(sock, r_data->sg, r_data->sg_len, - (r_data->flags & MMC_DATA_WRITE) - ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - } - - writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); - - spin_unlock_irqrestore(&sock->lock, flags); - mmc_request_done(mmc, mrq); -} - -static void tifm_sd_request_nodma(struct mmc_host *mmc, struct mmc_request *mrq) -{ - struct tifm_sd *host = mmc_priv(mmc); - struct tifm_dev *sock = host->dev; - unsigned long flags; - struct mmc_data *r_data = mrq->cmd->data; - - spin_lock_irqsave(&sock->lock, flags); - if (host->eject) { - spin_unlock_irqrestore(&sock->lock, flags); - goto err_out; - } - - if (host->req) { - printk(KERN_ERR DRIVER_NAME ": unfinished request detected\n"); - spin_unlock_irqrestore(&sock->lock, flags); - goto err_out; - } - - if (r_data) { - tifm_sd_set_data_timeout(host, r_data); - - host->buffer_size = mrq->cmd->data->blocks - * mrq->cmd->data->blksz; - - writel(TIFM_MMCSD_BUFINT - | readl(sock->addr + SOCK_MMCSD_INT_ENABLE), - sock->addr + SOCK_MMCSD_INT_ENABLE); - writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8) - | (TIFM_MMCSD_FIFO_SIZE - 1), - sock->addr + SOCK_MMCSD_BUFFER_CONFIG); - - host->written_blocks = 0; - host->cmd_flags &= ~CARD_BUSY; - host->buffer_pos = 0; - writel(r_data->blocks - 1, sock->addr + SOCK_MMCSD_NUM_BLOCKS); - writel(r_data->blksz - 1, sock->addr + SOCK_MMCSD_BLOCK_LEN); - } - - host->req = mrq; - mod_timer(&host->timer, jiffies + host->timeout_jiffies); - host->state = CMD; - writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); - tifm_sd_exec(host, mrq->cmd); - spin_unlock_irqrestore(&sock->lock, flags); - return; - -err_out: - mrq->cmd->error = MMC_ERR_TIMEOUT; - mmc_request_done(mmc, mrq); -} - -static void tifm_sd_end_cmd_nodma(unsigned long data) -{ - struct tifm_sd *host = (struct tifm_sd*)data; - struct tifm_dev *sock = host->dev; - struct mmc_host *mmc = tifm_get_drvdata(sock); - struct mmc_request *mrq; - struct mmc_data *r_data = NULL; - unsigned long flags; - - spin_lock_irqsave(&sock->lock, flags); - - del_timer(&host->timer); - mrq = host->req; - host->req = NULL; - host->state = IDLE; - - if (!mrq) { - printk(KERN_ERR DRIVER_NAME ": no request to complete?\n"); - spin_unlock_irqrestore(&sock->lock, flags); - return; - } - - r_data = mrq->cmd->data; - if (r_data) { - writel((~TIFM_MMCSD_BUFINT) & - readl(sock->addr + SOCK_MMCSD_INT_ENABLE), - sock->addr + SOCK_MMCSD_INT_ENABLE); - - if (r_data->flags & MMC_DATA_WRITE) { - r_data->bytes_xfered = host->written_blocks - * r_data->blksz; + if (host->no_dma) { + writel((~TIFM_MMCSD_BUFINT) & + readl(sock->addr + SOCK_MMCSD_INT_ENABLE), + sock->addr + SOCK_MMCSD_INT_ENABLE); + + if (r_data->flags & MMC_DATA_WRITE) { + r_data->bytes_xfered = host->written_blocks + * r_data->blksz; + } else { + r_data->bytes_xfered = r_data->blocks - + readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) + - 1; + r_data->bytes_xfered *= r_data->blksz; + r_data->bytes_xfered += r_data->blksz + - readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + + 1; + } + host->buffer_pos = 0; + host->buffer_size = 0; } else { - r_data->bytes_xfered = r_data->blocks - - readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; - r_data->bytes_xfered *= r_data->blksz; - r_data->bytes_xfered += r_data->blksz - - readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1; + if (r_data->flags & MMC_DATA_WRITE) { + r_data->bytes_xfered = host->written_blocks + * r_data->blksz; + } else { + r_data->bytes_xfered = r_data->blocks - + readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; + r_data->bytes_xfered *= r_data->blksz; + r_data->bytes_xfered += r_data->blksz - + readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + + 1; + } + tifm_unmap_sg(sock, r_data->sg, r_data->sg_len, + (r_data->flags & MMC_DATA_WRITE) + ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); } - host->buffer_pos = 0; - host->buffer_size = 0; } writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), sock->addr + SOCK_CONTROL); spin_unlock_irqrestore(&sock->lock, flags); - mmc_request_done(mmc, mrq); } @@ -755,7 +695,7 @@ static int tifm_sd_ro(struct mmc_host *mmc) return rc; } -static struct mmc_host_ops tifm_sd_ops = { +static const struct mmc_host_ops tifm_sd_ops = { .request = tifm_sd_request, .set_ios = tifm_sd_ios, .get_ro = tifm_sd_ro @@ -846,12 +786,10 @@ static int tifm_sd_probe(struct tifm_dev *sock) host->dev = sock; host->timeout_jiffies = msecs_to_jiffies(1000); - tasklet_init(&host->finish_tasklet, - host->no_dma ? tifm_sd_end_cmd_nodma : tifm_sd_end_cmd, + tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd, (unsigned long)host); setup_timer(&host->timer, tifm_sd_abort, (unsigned long)host); - tifm_sd_ops.request = host->no_dma ? tifm_sd_request_nodma : tifm_sd_request; mmc->ops = &tifm_sd_ops; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE; -- cgit v1.2.3 From 72dc9d9619dd4682f4197e7a7f19af22fd6516a7 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 17:05:23 +1000 Subject: tifm_sd: replace command completion state machine with full checking State machine used to to track mmc command state was found to be fragile and unreliable, making many cards unusable. The safer solution is to perform all needed checks at every card event. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/mmc/tifm_sd.c | 259 ++++++++++++++++++++++++++------------------------ include/linux/tifm.h | 1 + 2 files changed, 138 insertions(+), 122 deletions(-) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index 52499548abe..103060f490a 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -70,19 +70,14 @@ module_param(fixed_timeout, bool, 0644); #define TIFM_MMCSD_CMD_AC 0x2000 #define TIFM_MMCSD_CMD_ADTC 0x3000 -typedef enum { - IDLE = 0, - CMD, /* main command ended */ - BRS, /* block transfer finished */ - SCMD, /* stop command ended */ - CARD, /* card left busy state */ - FIFO, /* FIFO operation completed (uncertain) */ - READY -} card_state_t; - enum { - FIFO_RDY = 0x0001, /* hardware dependent value */ - CARD_BUSY = 0x0010 + CMD_READY = 0x0001, + FIFO_READY = 0x0002, + BRS_READY = 0x0004, + SCMD_ACTIVE = 0x0008, + SCMD_READY = 0x0010, + CARD_BUSY = 0x0020, + DATA_CARRY = 0x0040 }; struct tifm_sd { @@ -92,7 +87,7 @@ struct tifm_sd { open_drain:1, no_dma:1; unsigned short cmd_flags; - card_state_t state; + unsigned int clk_freq; unsigned int clk_div; unsigned long timeout_jiffies; @@ -234,87 +229,76 @@ static void tifm_sd_fetch_resp(struct mmc_command *cmd, struct tifm_dev *sock) | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x00); } -static void tifm_sd_process_cmd(struct tifm_dev *sock, struct tifm_sd *host, - unsigned int host_status) +static void tifm_sd_check_status(struct tifm_sd *host) { + struct tifm_dev *sock = host->dev; struct mmc_command *cmd = host->req->cmd; -change_state: - switch (host->state) { - case IDLE: + if (cmd->error != MMC_ERR_NONE) + goto finish_request; + + if (!(host->cmd_flags & CMD_READY)) return; - case CMD: - if (host_status & (TIFM_MMCSD_EOC | TIFM_MMCSD_CERR)) { - tifm_sd_fetch_resp(cmd, sock); - if (cmd->data) { - host->state = BRS; - } else { - host->state = READY; - } - goto change_state; - } - break; - case BRS: - if (tifm_sd_transfer_data(sock, host, host_status)) { - if (cmd->data->flags & MMC_DATA_WRITE) { - host->state = CARD; - } else { - if (host->no_dma) { - if (host->req->stop) { - tifm_sd_exec(host, host->req->stop); - host->state = SCMD; - } else { - host->state = READY; - } - } else { - host->state = FIFO; - } - } - goto change_state; - } - break; - case SCMD: - if (host_status & TIFM_MMCSD_EOC) { - tifm_sd_fetch_resp(host->req->stop, sock); - host->state = READY; - goto change_state; + + if (cmd->data) { + if (cmd->data->error != MMC_ERR_NONE) { + if ((host->cmd_flags & SCMD_ACTIVE) + && !(host->cmd_flags & SCMD_READY)) + return; + + goto finish_request; } - break; - case CARD: - dev_dbg(&sock->dev, "waiting for CARD, have %zd blocks\n", - host->written_blocks); - if (!(host->cmd_flags & CARD_BUSY) - && (host->written_blocks == cmd->data->blocks)) { - if (host->no_dma) { - if (host->req->stop) { + + if (!(host->cmd_flags & BRS_READY)) + return; + + if (!(host->no_dma || (host->cmd_flags & FIFO_READY))) + return; + + if (cmd->data->flags & MMC_DATA_WRITE) { + if (host->req->stop) { + if (!(host->cmd_flags & SCMD_ACTIVE)) { + host->cmd_flags |= SCMD_ACTIVE; + writel(TIFM_MMCSD_EOFB + | readl(sock->addr + + SOCK_MMCSD_INT_ENABLE), + sock->addr + + SOCK_MMCSD_INT_ENABLE); tifm_sd_exec(host, host->req->stop); - host->state = SCMD; + return; } else { - host->state = READY; + if (!(host->cmd_flags & SCMD_READY) + || (host->cmd_flags & CARD_BUSY)) + return; + writel((~TIFM_MMCSD_EOFB) + & readl(sock->addr + + SOCK_MMCSD_INT_ENABLE), + sock->addr + + SOCK_MMCSD_INT_ENABLE); } } else { - host->state = FIFO; + if (host->cmd_flags & CARD_BUSY) + return; + writel((~TIFM_MMCSD_EOFB) + & readl(sock->addr + + SOCK_MMCSD_INT_ENABLE), + sock->addr + SOCK_MMCSD_INT_ENABLE); } - goto change_state; - } - break; - case FIFO: - if (host->cmd_flags & FIFO_RDY) { - host->cmd_flags &= ~FIFO_RDY; + } else { if (host->req->stop) { - tifm_sd_exec(host, host->req->stop); - host->state = SCMD; - } else { - host->state = READY; + if (!(host->cmd_flags & SCMD_ACTIVE)) { + host->cmd_flags |= SCMD_ACTIVE; + tifm_sd_exec(host, host->req->stop); + return; + } else { + if (!(host->cmd_flags & SCMD_READY)) + return; + } } - goto change_state; } - break; - case READY: - tasklet_schedule(&host->finish_tasklet); - return; } - +finish_request: + tasklet_schedule(&host->finish_tasklet); } /* Called from interrupt handler */ @@ -322,21 +306,25 @@ static void tifm_sd_data_event(struct tifm_dev *sock) { struct tifm_sd *host; unsigned int fifo_status = 0; + struct mmc_data *r_data = NULL; spin_lock(&sock->lock); host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); - fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS); - writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS); + dev_dbg(&sock->dev, "data event: fifo_status %x, flags %x\n", + fifo_status, host->cmd_flags); - host->cmd_flags |= fifo_status & FIFO_RDY; + if (host->req) { + r_data = host->req->cmd->data; - if (host->req) - tifm_sd_process_cmd(sock, host, 0); + if (r_data && (fifo_status & TIFM_FIFO_READY)) { + host->cmd_flags |= FIFO_READY; + tifm_sd_check_status(host); + } + } - dev_dbg(&sock->dev, "fifo_status %x\n", fifo_status); + writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS); spin_unlock(&sock->lock); - } /* Called from interrupt handler */ @@ -344,60 +332,88 @@ static void tifm_sd_card_event(struct tifm_dev *sock) { struct tifm_sd *host; unsigned int host_status = 0; - int error_code = 0; + int cmd_error = MMC_ERR_NONE; + struct mmc_command *cmd = NULL; + unsigned long flags; spin_lock(&sock->lock); host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); + host_status = readl(sock->addr + SOCK_MMCSD_STATUS); + dev_dbg(&sock->dev, "host event: host_status %x, flags %x\n", + host_status, host->cmd_flags); - - host_status = readl(sock->addr + SOCK_MMCSD_STATUS); - writel(host_status, sock->addr + SOCK_MMCSD_STATUS); - - if (!host->req) - goto done; + if (host->req) { + cmd = host->req->cmd; if (host_status & TIFM_MMCSD_ERRMASK) { - if (host_status & (TIFM_MMCSD_CTO | TIFM_MMCSD_DTO)) - error_code = MMC_ERR_TIMEOUT; - else if (host_status - & (TIFM_MMCSD_CCRC | TIFM_MMCSD_DCRC)) - error_code = MMC_ERR_BADCRC; + writel(host_status & TIFM_MMCSD_ERRMASK, + sock->addr + SOCK_MMCSD_STATUS); + if (host_status & TIFM_MMCSD_CTO) + cmd_error = MMC_ERR_TIMEOUT; + else if (host_status & TIFM_MMCSD_CCRC) + cmd_error = MMC_ERR_BADCRC; + + if (cmd->data) { + if (host_status & TIFM_MMCSD_DTO) + cmd->data->error = MMC_ERR_TIMEOUT; + else if (host_status & TIFM_MMCSD_DCRC) + cmd->data->error = MMC_ERR_BADCRC; + } writel(TIFM_FIFO_INT_SETALL, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL); if (host->req->stop) { - if (host->state == SCMD) { - host->req->stop->error = error_code; - } else if (host->state == BRS - || host->state == CARD - || host->state == FIFO) { - host->req->cmd->error = error_code; + if (host->cmd_flags & SCMD_ACTIVE) { + host->req->stop->error = cmd_error; + host->cmd_flags |= SCMD_READY; + } else { + cmd->error = cmd_error; + host->cmd_flags |= SCMD_ACTIVE; tifm_sd_exec(host, host->req->stop); - host->state = SCMD; goto done; - } else { - host->req->cmd->error = error_code; } - } else { - host->req->cmd->error = error_code; + } else + cmd->error = cmd_error; + } else { + if (host_status & (TIFM_MMCSD_EOC | TIFM_MMCSD_CERR)) { + if (!(host->cmd_flags & CMD_READY)) { + host->cmd_flags |= CMD_READY; + tifm_sd_fetch_resp(cmd, sock); + } else if (host->cmd_flags & SCMD_ACTIVE) { + host->cmd_flags |= SCMD_READY; + tifm_sd_fetch_resp(host->req->stop, + sock); + } } - host->state = READY; + if (host_status & TIFM_MMCSD_BRS) + host->cmd_flags |= BRS_READY; } - if (host_status & TIFM_MMCSD_CB) - host->cmd_flags |= CARD_BUSY; - if ((host_status & TIFM_MMCSD_EOFB) - && (host->cmd_flags & CARD_BUSY)) { - host->written_blocks++; - host->cmd_flags &= ~CARD_BUSY; + if (host->no_dma && cmd->data) { + if (host_status & TIFM_MMCSD_AE) + writel(host_status & TIFM_MMCSD_AE, + sock->addr + SOCK_MMCSD_STATUS); + + if (host_status & (TIFM_MMCSD_AE | TIFM_MMCSD_AF + | TIFM_MMCSD_BRS)) { + local_irq_save(flags); + tifm_sd_transfer_data(sock, host, host_status); + local_irq_restore(flags); + host_status &= ~TIFM_MMCSD_AE; + } } - if (host->req) - tifm_sd_process_cmd(sock, host, host_status); + if (host_status & TIFM_MMCSD_EOFB) + host->cmd_flags &= ~CARD_BUSY; + else if (host_status & TIFM_MMCSD_CB) + host->cmd_flags |= CARD_BUSY; + + tifm_sd_check_status(host); + } done: - dev_dbg(&sock->dev, "host_status %x\n", host_status); + writel(host_status, sock->addr + SOCK_MMCSD_STATUS); spin_unlock(&sock->lock); } @@ -522,7 +538,7 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) host->req = mrq; mod_timer(&host->timer, jiffies + host->timeout_jiffies); - host->state = CMD; + host->cmd_flags = 0; writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), sock->addr + SOCK_CONTROL); tifm_sd_exec(host, mrq->cmd); @@ -553,7 +569,6 @@ static void tifm_sd_end_cmd(unsigned long data) del_timer(&host->timer); mrq = host->req; host->req = NULL; - host->state = IDLE; if (!mrq) { printk(KERN_ERR DRIVER_NAME ": no request to complete?\n"); diff --git a/include/linux/tifm.h b/include/linux/tifm.h index 82da028d8c0..c8449fcea0c 100644 --- a/include/linux/tifm.h +++ b/include/linux/tifm.h @@ -67,6 +67,7 @@ enum { #define TIFM_SOCK_STATE_POWERED 0x00000080 #define TIFM_FIFO_ENABLE 0x00000001 /* Meaning of this constant is unverified */ +#define TIFM_FIFO_READY 0x00000001 /* Meaning of this constant is unverified */ #define TIFM_FIFO_INT_SETALL 0x0000ffff #define TIFM_FIFO_INTMASK 0x00000005 /* Meaning of this constant is unverified */ -- cgit v1.2.3 From 5897d657b58efb244b1f82a912ee93e5141ed14c Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 17:05:24 +1000 Subject: tifm_sd: fix resume handler Resume should not explicitly check for media type. Instead, it may relay on success of socket initialization. Small changes are introduced to tifm_sd_initialize to make it more robust. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/mmc/tifm_sd.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index 103060f490a..d20ccfcd911 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -36,7 +36,6 @@ module_param(fixed_timeout, bool, 0644); #define TIFM_MMCSD_INAB 0x0080 /* abort / initialize command */ #define TIFM_MMCSD_READ 0x8000 -#define TIFM_MMCSD_DATAMASK 0x401d /* set bits: CERR, EOFB, BRS, CB, EOC */ #define TIFM_MMCSD_ERRMASK 0x01e0 /* set bits: CCRC, CTO, DCRC, DTO */ #define TIFM_MMCSD_EOC 0x0001 /* end of command phase */ #define TIFM_MMCSD_CB 0x0004 /* card enter busy state */ @@ -731,7 +730,7 @@ static int tifm_sd_initialize_host(struct tifm_sd *host) sock->addr + SOCK_MMCSD_CONFIG); /* wait up to 0.51 sec for reset */ - for (rc = 2; rc <= 256; rc <<= 1) { + for (rc = 32; rc <= 256; rc <<= 1) { if (1 & readl(sock->addr + SOCK_MMCSD_SYSTEM_STATUS)) { rc = 0; break; @@ -740,8 +739,8 @@ static int tifm_sd_initialize_host(struct tifm_sd *host) } if (rc) { - printk(KERN_ERR DRIVER_NAME - ": controller failed to reset\n"); + printk(KERN_ERR "%s : controller failed to reset\n", + sock->dev.bus_id); return -ENODEV; } @@ -754,8 +753,7 @@ static int tifm_sd_initialize_host(struct tifm_sd *host) writel(64, sock->addr + SOCK_MMCSD_COMMAND_TO); writel(TIFM_MMCSD_INAB, sock->addr + SOCK_MMCSD_COMMAND); - /* INAB should take much less than reset */ - for (rc = 1; rc <= 16; rc <<= 1) { + for (rc = 16; rc <= 64; rc <<= 1) { host_status = readl(sock->addr + SOCK_MMCSD_STATUS); writel(host_status, sock->addr + SOCK_MMCSD_STATUS); if (!(host_status & TIFM_MMCSD_ERRMASK) @@ -767,12 +765,14 @@ static int tifm_sd_initialize_host(struct tifm_sd *host) } if (rc) { - printk(KERN_ERR DRIVER_NAME - ": card not ready - probe failed on initialization\n"); + printk(KERN_ERR + "%s : card not ready - probe failed on initialization\n", + sock->dev.bus_id); return -ENODEV; } - writel(TIFM_MMCSD_DATAMASK | TIFM_MMCSD_ERRMASK, + writel(TIFM_MMCSD_CERR | TIFM_MMCSD_BRS | TIFM_MMCSD_EOC + | TIFM_MMCSD_ERRMASK, sock->addr + SOCK_MMCSD_INT_ENABLE); mmiowb(); @@ -887,14 +887,17 @@ static int tifm_sd_resume(struct tifm_dev *sock) { struct mmc_host *mmc = tifm_get_drvdata(sock); struct tifm_sd *host = mmc_priv(mmc); + int rc; - if (sock->type != TIFM_TYPE_SD - || tifm_sd_initialize_host(host)) { - tifm_eject(sock); - return 0; - } else { - return mmc_resume_host(mmc); - } + rc = tifm_sd_initialize_host(host); + dev_dbg(&sock->dev, "resume initialize %d\n", rc); + + if (rc) + host->eject = 1; + else + rc = mmc_resume_host(mmc); + + return rc; } #else -- cgit v1.2.3 From 13cdf48ef15befbd36f8295091b9e0f9bd322963 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 17:05:25 +1000 Subject: tifm_sd: implement software scatter-gather It was found that delays associated with issue and completion of the commands severely limit performance of the new, fast SD cards. To alleviate this issue scatter-gather emulation in software is implemented for both dma and pio transfer modes. Non-block aligned and high memory sg entries are accounted for. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/mmc/tifm_sd.c | 443 +++++++++++++++++++++++++++++++++----------------- include/linux/tifm.h | 1 + 2 files changed, 297 insertions(+), 147 deletions(-) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index d20ccfcd911..8e69514e415 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #define DRIVER_NAME "tifm_sd" @@ -69,6 +70,8 @@ module_param(fixed_timeout, bool, 0644); #define TIFM_MMCSD_CMD_AC 0x2000 #define TIFM_MMCSD_CMD_ADTC 0x3000 +#define TIFM_MMCSD_MAX_BLOCK_SIZE 0x0800UL + enum { CMD_READY = 0x0001, FIFO_READY = 0x0002, @@ -95,63 +98,227 @@ struct tifm_sd { struct timer_list timer; struct mmc_request *req; - size_t written_blocks; - size_t buffer_size; - size_t buffer_pos; - + int sg_len; + int sg_pos; + unsigned int block_pos; + struct scatterlist bounce_buf; + unsigned char bounce_buf_data[TIFM_MMCSD_MAX_BLOCK_SIZE]; }; -static char* tifm_sd_data_buffer(struct mmc_data *data) +/* for some reason, host won't respond correctly to readw/writew */ +static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg, + unsigned int off, unsigned int cnt) { - return page_address(data->sg->page) + data->sg->offset; + struct tifm_dev *sock = host->dev; + unsigned char *buf; + unsigned int pos = 0, val; + + buf = kmap_atomic(pg, KM_BIO_DST_IRQ) + off; + if (host->cmd_flags & DATA_CARRY) { + buf[pos++] = host->bounce_buf_data[0]; + host->cmd_flags &= ~DATA_CARRY; + } + + while (pos < cnt) { + val = readl(sock->addr + SOCK_MMCSD_DATA); + buf[pos++] = val & 0xff; + if (pos == cnt) { + host->bounce_buf_data[0] = (val >> 8) & 0xff; + host->cmd_flags |= DATA_CARRY; + break; + } + buf[pos++] = (val >> 8) & 0xff; + } + kunmap_atomic(buf - off, KM_BIO_DST_IRQ); } -static int tifm_sd_transfer_data(struct tifm_dev *sock, struct tifm_sd *host, - unsigned int host_status) +static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg, + unsigned int off, unsigned int cnt) { - struct mmc_command *cmd = host->req->cmd; - unsigned int t_val = 0, cnt = 0; - char *buffer; - - if (host_status & TIFM_MMCSD_BRS) { - /* in non-dma rx mode BRS fires when fifo is still not empty */ - if (host->no_dma && (cmd->data->flags & MMC_DATA_READ)) { - buffer = tifm_sd_data_buffer(host->req->data); - while (host->buffer_size > host->buffer_pos) { - t_val = readl(sock->addr + SOCK_MMCSD_DATA); - buffer[host->buffer_pos++] = t_val & 0xff; - buffer[host->buffer_pos++] = - (t_val >> 8) & 0xff; - } + struct tifm_dev *sock = host->dev; + unsigned char *buf; + unsigned int pos = 0, val; + + buf = kmap_atomic(pg, KM_BIO_SRC_IRQ) + off; + if (host->cmd_flags & DATA_CARRY) { + val = host->bounce_buf_data[0] | ((buf[pos++] << 8) & 0xff00); + writel(val, sock->addr + SOCK_MMCSD_DATA); + host->cmd_flags &= ~DATA_CARRY; + } + + while (pos < cnt) { + val = buf[pos++]; + if (pos == cnt) { + host->bounce_buf_data[0] = val & 0xff; + host->cmd_flags |= DATA_CARRY; + break; } - return 1; - } else if (host->no_dma) { - buffer = tifm_sd_data_buffer(host->req->data); - if ((cmd->data->flags & MMC_DATA_READ) && - (host_status & TIFM_MMCSD_AF)) { - for (cnt = 0; cnt < TIFM_MMCSD_FIFO_SIZE; cnt++) { - t_val = readl(sock->addr + SOCK_MMCSD_DATA); - if (host->buffer_size > host->buffer_pos) { - buffer[host->buffer_pos++] = - t_val & 0xff; - buffer[host->buffer_pos++] = - (t_val >> 8) & 0xff; - } - } - } else if ((cmd->data->flags & MMC_DATA_WRITE) - && (host_status & TIFM_MMCSD_AE)) { - for (cnt = 0; cnt < TIFM_MMCSD_FIFO_SIZE; cnt++) { - if (host->buffer_size > host->buffer_pos) { - t_val = buffer[host->buffer_pos++] - & 0x00ff; - t_val |= ((buffer[host->buffer_pos++]) - << 8) & 0xff00; - writel(t_val, - sock->addr + SOCK_MMCSD_DATA); - } + val |= (buf[pos++] << 8) & 0xff00; + writel(val, sock->addr + SOCK_MMCSD_DATA); + } + kunmap_atomic(buf - off, KM_BIO_SRC_IRQ); +} + +static void tifm_sd_transfer_data(struct tifm_sd *host) +{ + struct mmc_data *r_data = host->req->cmd->data; + struct scatterlist *sg = r_data->sg; + unsigned int off, cnt, t_size = TIFM_MMCSD_FIFO_SIZE * 2; + unsigned int p_off, p_cnt; + struct page *pg; + + if (host->sg_pos == host->sg_len) + return; + while (t_size) { + cnt = sg[host->sg_pos].length - host->block_pos; + if (!cnt) { + host->block_pos = 0; + host->sg_pos++; + if (host->sg_pos == host->sg_len) { + if ((r_data->flags & MMC_DATA_WRITE) + && DATA_CARRY) + writel(host->bounce_buf_data[0], + host->dev->addr + + SOCK_MMCSD_DATA); + + return; } + cnt = sg[host->sg_pos].length; } + off = sg[host->sg_pos].offset + host->block_pos; + + pg = nth_page(sg[host->sg_pos].page, off >> PAGE_SHIFT); + p_off = offset_in_page(off); + p_cnt = PAGE_SIZE - p_off; + p_cnt = min(p_cnt, cnt); + p_cnt = min(p_cnt, t_size); + + if (r_data->flags & MMC_DATA_READ) + tifm_sd_read_fifo(host, pg, p_off, p_cnt); + else if (r_data->flags & MMC_DATA_WRITE) + tifm_sd_write_fifo(host, pg, p_off, p_cnt); + + t_size -= p_cnt; + host->block_pos += p_cnt; + } +} + +static void tifm_sd_copy_page(struct page *dst, unsigned int dst_off, + struct page *src, unsigned int src_off, + unsigned int count) +{ + unsigned char *src_buf = kmap_atomic(src, KM_BIO_SRC_IRQ) + src_off; + unsigned char *dst_buf = kmap_atomic(dst, KM_BIO_DST_IRQ) + dst_off; + + memcpy(dst_buf, src_buf, count); + + kunmap_atomic(dst_buf - dst_off, KM_BIO_DST_IRQ); + kunmap_atomic(src_buf - src_off, KM_BIO_SRC_IRQ); +} + +static void tifm_sd_bounce_block(struct tifm_sd *host, struct mmc_data *r_data) +{ + struct scatterlist *sg = r_data->sg; + unsigned int t_size = r_data->blksz; + unsigned int off, cnt; + unsigned int p_off, p_cnt; + struct page *pg; + + dev_dbg(&host->dev->dev, "bouncing block\n"); + while (t_size) { + cnt = sg[host->sg_pos].length - host->block_pos; + if (!cnt) { + host->block_pos = 0; + host->sg_pos++; + if (host->sg_pos == host->sg_len) + return; + cnt = sg[host->sg_pos].length; + } + off = sg[host->sg_pos].offset + host->block_pos; + + pg = nth_page(sg[host->sg_pos].page, off >> PAGE_SHIFT); + p_off = offset_in_page(off); + p_cnt = PAGE_SIZE - p_off; + p_cnt = min(p_cnt, cnt); + p_cnt = min(p_cnt, t_size); + + if (r_data->flags & MMC_DATA_WRITE) + tifm_sd_copy_page(host->bounce_buf.page, + r_data->blksz - t_size, + pg, p_off, p_cnt); + else if (r_data->flags & MMC_DATA_READ) + tifm_sd_copy_page(pg, p_off, host->bounce_buf.page, + r_data->blksz - t_size, p_cnt); + + t_size -= p_cnt; + host->block_pos += p_cnt; + } +} + +int tifm_sd_set_dma_data(struct tifm_sd *host, struct mmc_data *r_data) +{ + struct tifm_dev *sock = host->dev; + unsigned int t_size = TIFM_DMA_TSIZE * r_data->blksz; + unsigned int dma_len, dma_blk_cnt, dma_off; + struct scatterlist *sg = NULL; + unsigned long flags; + + if (host->sg_pos == host->sg_len) + return 1; + + if (host->cmd_flags & DATA_CARRY) { + host->cmd_flags &= ~DATA_CARRY; + local_irq_save(flags); + tifm_sd_bounce_block(host, r_data); + local_irq_restore(flags); + if (host->sg_pos == host->sg_len) + return 1; + } + + dma_len = sg_dma_len(&r_data->sg[host->sg_pos]) - host->block_pos; + if (!dma_len) { + host->block_pos = 0; + host->sg_pos++; + if (host->sg_pos == host->sg_len) + return 1; + dma_len = sg_dma_len(&r_data->sg[host->sg_pos]); + } + + if (dma_len < t_size) { + dma_blk_cnt = dma_len / r_data->blksz; + dma_off = host->block_pos; + host->block_pos += dma_blk_cnt * r_data->blksz; + } else { + dma_blk_cnt = TIFM_DMA_TSIZE; + dma_off = host->block_pos; + host->block_pos += t_size; } + + if (dma_blk_cnt) + sg = &r_data->sg[host->sg_pos]; + else if (dma_len) { + if (r_data->flags & MMC_DATA_WRITE) { + local_irq_save(flags); + tifm_sd_bounce_block(host, r_data); + local_irq_restore(flags); + } else + host->cmd_flags |= DATA_CARRY; + + sg = &host->bounce_buf; + dma_off = 0; + dma_blk_cnt = 1; + } else + return 1; + + dev_dbg(&sock->dev, "setting dma for %d blocks\n", dma_blk_cnt); + writel(sg_dma_address(sg) + dma_off, sock->addr + SOCK_DMA_ADDRESS); + if (r_data->flags & MMC_DATA_WRITE) + writel((dma_blk_cnt << 8) | TIFM_DMA_TX | TIFM_DMA_EN, + sock->addr + SOCK_DMA_CONTROL); + else + writel((dma_blk_cnt << 8) | TIFM_DMA_EN, + sock->addr + SOCK_DMA_CONTROL); + return 0; } @@ -317,8 +484,10 @@ static void tifm_sd_data_event(struct tifm_dev *sock) r_data = host->req->cmd->data; if (r_data && (fifo_status & TIFM_FIFO_READY)) { - host->cmd_flags |= FIFO_READY; - tifm_sd_check_status(host); + if (tifm_sd_set_dma_data(host, r_data)) { + host->cmd_flags |= FIFO_READY; + tifm_sd_check_status(host); + } } } @@ -398,7 +567,7 @@ static void tifm_sd_card_event(struct tifm_dev *sock) if (host_status & (TIFM_MMCSD_AE | TIFM_MMCSD_AF | TIFM_MMCSD_BRS)) { local_irq_save(flags); - tifm_sd_transfer_data(sock, host, host_status); + tifm_sd_transfer_data(host); local_irq_restore(flags); host_status &= ~TIFM_MMCSD_AE; } @@ -416,38 +585,6 @@ done: spin_unlock(&sock->lock); } -static void tifm_sd_prepare_data(struct tifm_sd *host, struct mmc_command *cmd) -{ - struct tifm_dev *sock = host->dev; - unsigned int dest_cnt; - - /* DMA style IO */ - dev_dbg(&sock->dev, "setting dma for %d blocks\n", - cmd->data->blocks); - writel(TIFM_FIFO_INT_SETALL, - sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); - writel(ilog2(cmd->data->blksz) - 2, - sock->addr + SOCK_FIFO_PAGE_SIZE); - writel(TIFM_FIFO_ENABLE, sock->addr + SOCK_FIFO_CONTROL); - writel(TIFM_FIFO_INTMASK, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); - - dest_cnt = (cmd->data->blocks) << 8; - - writel(sg_dma_address(cmd->data->sg), sock->addr + SOCK_DMA_ADDRESS); - - writel(cmd->data->blocks - 1, sock->addr + SOCK_MMCSD_NUM_BLOCKS); - writel(cmd->data->blksz - 1, sock->addr + SOCK_MMCSD_BLOCK_LEN); - - if (cmd->data->flags & MMC_DATA_WRITE) { - writel(TIFM_MMCSD_TXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); - writel(dest_cnt | TIFM_DMA_TX | TIFM_DMA_EN, - sock->addr + SOCK_DMA_CONTROL); - } else { - writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); - writel(dest_cnt | TIFM_DMA_EN, sock->addr + SOCK_DMA_CONTROL); - } -} - static void tifm_sd_set_data_timeout(struct tifm_sd *host, struct mmc_data *data) { @@ -481,7 +618,6 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) struct tifm_sd *host = mmc_priv(mmc); struct tifm_dev *sock = host->dev; unsigned long flags; - int sg_count = 0; struct mmc_data *r_data = mrq->cmd->data; spin_lock_irqsave(&sock->lock, flags); @@ -496,13 +632,19 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) goto err_out; } + host->cmd_flags = 0; + host->block_pos = 0; + host->sg_pos = 0; + if (r_data) { tifm_sd_set_data_timeout(host, r_data); - if (host->no_dma) { - host->buffer_size = mrq->cmd->data->blocks - * mrq->cmd->data->blksz; + if ((r_data->flags & MMC_DATA_WRITE) && !mrq->stop) + writel(TIFM_MMCSD_EOFB + | readl(sock->addr + SOCK_MMCSD_INT_ENABLE), + sock->addr + SOCK_MMCSD_INT_ENABLE); + if (host->no_dma) { writel(TIFM_MMCSD_BUFINT | readl(sock->addr + SOCK_MMCSD_INT_ENABLE), sock->addr + SOCK_MMCSD_INT_ENABLE); @@ -510,34 +652,64 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) | (TIFM_MMCSD_FIFO_SIZE - 1), sock->addr + SOCK_MMCSD_BUFFER_CONFIG); - host->written_blocks = 0; - host->cmd_flags &= ~CARD_BUSY; - host->buffer_pos = 0; - writel(r_data->blocks - 1, - sock->addr + SOCK_MMCSD_NUM_BLOCKS); - writel(r_data->blksz - 1, - sock->addr + SOCK_MMCSD_BLOCK_LEN); + host->sg_len = r_data->sg_len; } else { - sg_count = tifm_map_sg(sock, r_data->sg, r_data->sg_len, - mrq->cmd->flags & MMC_DATA_WRITE - ? PCI_DMA_TODEVICE - : PCI_DMA_FROMDEVICE); - if (sg_count != 1) { - printk(KERN_ERR DRIVER_NAME - ": scatterlist map failed\n"); + sg_init_one(&host->bounce_buf, host->bounce_buf_data, + r_data->blksz); + + if(1 != tifm_map_sg(sock, &host->bounce_buf, 1, + r_data->flags & MMC_DATA_WRITE + ? PCI_DMA_TODEVICE + : PCI_DMA_FROMDEVICE)) { + printk(KERN_ERR "%s : scatterlist map failed\n", + sock->dev.bus_id); + spin_unlock_irqrestore(&sock->lock, flags); + goto err_out; + } + host->sg_len = tifm_map_sg(sock, r_data->sg, + r_data->sg_len, + r_data->flags + & MMC_DATA_WRITE + ? PCI_DMA_TODEVICE + : PCI_DMA_FROMDEVICE); + if (host->sg_len < 1) { + printk(KERN_ERR "%s : scatterlist map failed\n", + sock->dev.bus_id); + tifm_unmap_sg(sock, &host->bounce_buf, 1, + r_data->flags & MMC_DATA_WRITE + ? PCI_DMA_TODEVICE + : PCI_DMA_FROMDEVICE); spin_unlock_irqrestore(&sock->lock, flags); goto err_out; } - host->written_blocks = 0; - host->cmd_flags &= ~CARD_BUSY; - tifm_sd_prepare_data(host, mrq->cmd); + writel(TIFM_FIFO_INT_SETALL, + sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); + writel(ilog2(r_data->blksz) - 2, + sock->addr + SOCK_FIFO_PAGE_SIZE); + writel(TIFM_FIFO_ENABLE, + sock->addr + SOCK_FIFO_CONTROL); + writel(TIFM_FIFO_INTMASK, + sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); + + if (r_data->flags & MMC_DATA_WRITE) + writel(TIFM_MMCSD_TXDE, + sock->addr + SOCK_MMCSD_BUFFER_CONFIG); + else + writel(TIFM_MMCSD_RXDE, + sock->addr + SOCK_MMCSD_BUFFER_CONFIG); + + tifm_sd_set_dma_data(host, r_data); } + + writel(r_data->blocks - 1, + sock->addr + SOCK_MMCSD_NUM_BLOCKS); + writel(r_data->blksz - 1, + sock->addr + SOCK_MMCSD_BLOCK_LEN); } host->req = mrq; mod_timer(&host->timer, jiffies + host->timeout_jiffies); - host->cmd_flags = 0; writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), sock->addr + SOCK_CONTROL); tifm_sd_exec(host, mrq->cmd); @@ -545,11 +717,6 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) return; err_out: - if (sg_count > 0) - tifm_unmap_sg(sock, r_data->sg, r_data->sg_len, - (r_data->flags & MMC_DATA_WRITE) - ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - mrq->cmd->error = MMC_ERR_TIMEOUT; mmc_request_done(mmc, mrq); } @@ -578,40 +745,23 @@ static void tifm_sd_end_cmd(unsigned long data) r_data = mrq->cmd->data; if (r_data) { if (host->no_dma) { - writel((~TIFM_MMCSD_BUFINT) & - readl(sock->addr + SOCK_MMCSD_INT_ENABLE), - sock->addr + SOCK_MMCSD_INT_ENABLE); - - if (r_data->flags & MMC_DATA_WRITE) { - r_data->bytes_xfered = host->written_blocks - * r_data->blksz; - } else { - r_data->bytes_xfered = r_data->blocks - - readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - - 1; - r_data->bytes_xfered *= r_data->blksz; - r_data->bytes_xfered += r_data->blksz - - readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) - + 1; - } - host->buffer_pos = 0; - host->buffer_size = 0; + writel((~TIFM_MMCSD_BUFINT) + & readl(sock->addr + SOCK_MMCSD_INT_ENABLE), + sock->addr + SOCK_MMCSD_INT_ENABLE); } else { - if (r_data->flags & MMC_DATA_WRITE) { - r_data->bytes_xfered = host->written_blocks - * r_data->blksz; - } else { - r_data->bytes_xfered = r_data->blocks - - readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; - r_data->bytes_xfered *= r_data->blksz; - r_data->bytes_xfered += r_data->blksz - - readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) - + 1; - } + tifm_unmap_sg(sock, &host->bounce_buf, 1, + (r_data->flags & MMC_DATA_WRITE) + ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); tifm_unmap_sg(sock, r_data->sg, r_data->sg_len, (r_data->flags & MMC_DATA_WRITE) ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); } + + r_data->bytes_xfered = r_data->blocks + - readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; + r_data->bytes_xfered *= r_data->blksz; + r_data->bytes_xfered += r_data->blksz + - readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1; } writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), @@ -810,15 +960,14 @@ static int tifm_sd_probe(struct tifm_dev *sock) mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE; mmc->f_min = 20000000 / 60; mmc->f_max = 24000000; - mmc->max_hw_segs = 1; - mmc->max_phys_segs = 1; - // limited by DMA counter - it's safer to stick with - // block counter has 11 bits though - mmc->max_blk_count = 256; - // 2k maximum hw block length - mmc->max_blk_size = 2048; - mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; - mmc->max_seg_size = mmc->max_req_size; + + mmc->max_blk_count = 2048; + mmc->max_hw_segs = mmc->max_blk_count; + mmc->max_blk_size = min(TIFM_MMCSD_MAX_BLOCK_SIZE, PAGE_SIZE); + mmc->max_seg_size = mmc->max_blk_count * mmc->max_blk_size; + mmc->max_req_size = mmc->max_seg_size; + mmc->max_phys_segs = mmc->max_hw_segs; + sock->card_event = tifm_sd_card_event; sock->data_event = tifm_sd_data_event; rc = tifm_sd_initialize_host(host); diff --git a/include/linux/tifm.h b/include/linux/tifm.h index c8449fcea0c..7ccad079546 100644 --- a/include/linux/tifm.h +++ b/include/linux/tifm.h @@ -74,6 +74,7 @@ enum { #define TIFM_DMA_RESET 0x00000002 /* Meaning of this constant is unverified */ #define TIFM_DMA_TX 0x00008000 /* Meaning of this constant is unverified */ #define TIFM_DMA_EN 0x00000001 /* Meaning of this constant is unverified */ +#define TIFM_DMA_TSIZE 0x0000007f #define TIFM_TYPE_XD 1 #define TIFM_TYPE_MS 2 -- cgit v1.2.3 From 91f8d0118a0e1f25f809f3fde5a7616a1eaabc2b Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 17:05:26 +1000 Subject: tifm: layout fixes, small changes to comments and printfs Cosmetic changes to the code. Signed-off-by: Alex Dubov Signed-off-by: Pierre Ossman --- drivers/misc/tifm_7xx1.c | 10 +++---- drivers/misc/tifm_core.c | 24 ++++++++--------- drivers/mmc/tifm_sd.c | 49 +++++++++++++++++++++------------- include/linux/tifm.h | 69 ++++++++++++++++++++++++------------------------ 4 files changed, 80 insertions(+), 72 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index eafa5575f31..9dcff14e752 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -10,8 +10,6 @@ */ #include -#include -#include #define DRIVER_NAME "tifm_7xx1" #define DRIVER_VERSION "0.8" @@ -41,8 +39,7 @@ static irqreturn_t tifm_7xx1_isr(int irq, void *dev_id) { struct tifm_adapter *fm = dev_id; struct tifm_dev *sock; - unsigned int irq_status; - unsigned int cnt; + unsigned int irq_status, cnt; spin_lock(&fm->lock); irq_status = readl(fm->addr + FM_INTERRUPT_STATUS); @@ -134,11 +131,10 @@ static void tifm_7xx1_switch_media(struct work_struct *work) { struct tifm_adapter *fm = container_of(work, struct tifm_adapter, media_switcher); + struct tifm_dev *sock; unsigned long flags; unsigned char media_id; - int cnt; - struct tifm_dev *sock; - unsigned int socket_change_set; + unsigned int socket_change_set, cnt; spin_lock_irqsave(&fm->lock, flags); socket_change_set = fm->socket_change_set; diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index 70220beb3e0..d195fb088f4 100644 --- a/drivers/misc/tifm_core.c +++ b/drivers/misc/tifm_core.c @@ -115,23 +115,23 @@ static int tifm_device_remove(struct device *dev) static int tifm_device_suspend(struct device *dev, pm_message_t state) { - struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); + struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver, driver); if (dev->driver && drv->suspend) - return drv->suspend(fm_dev, state); + return drv->suspend(sock, state); return 0; } static int tifm_device_resume(struct device *dev) { - struct tifm_dev *fm_dev = container_of(dev, struct tifm_dev, dev); + struct tifm_dev *sock = container_of(dev, struct tifm_dev, dev); struct tifm_driver *drv = container_of(dev->driver, struct tifm_driver, driver); if (dev->driver && drv->resume) - return drv->resume(fm_dev); + return drv->resume(sock); return 0; } @@ -155,14 +155,14 @@ static struct device_attribute tifm_dev_attrs[] = { }; static struct bus_type tifm_bus_type = { - .name = "tifm", - .dev_attrs = tifm_dev_attrs, - .match = tifm_bus_match, - .uevent = tifm_uevent, - .probe = tifm_device_probe, - .remove = tifm_device_remove, - .suspend = tifm_device_suspend, - .resume = tifm_device_resume + .name = "tifm", + .dev_attrs = tifm_dev_attrs, + .match = tifm_bus_match, + .uevent = tifm_uevent, + .probe = tifm_device_probe, + .remove = tifm_device_remove, + .suspend = tifm_device_suspend, + .resume = tifm_device_resume }; static void tifm_free(struct class_device *cdev) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index 8e69514e415..f692a2ec09c 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -7,6 +7,8 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * + * Special thanks to Brad Campbell for extensive testing of this driver. + * */ @@ -39,6 +41,7 @@ module_param(fixed_timeout, bool, 0644); #define TIFM_MMCSD_ERRMASK 0x01e0 /* set bits: CCRC, CTO, DCRC, DTO */ #define TIFM_MMCSD_EOC 0x0001 /* end of command phase */ +#define TIFM_MMCSD_CD 0x0002 /* card detect */ #define TIFM_MMCSD_CB 0x0004 /* card enter busy state */ #define TIFM_MMCSD_BRS 0x0008 /* block received/sent */ #define TIFM_MMCSD_EOFB 0x0010 /* card exit busy state */ @@ -48,6 +51,8 @@ module_param(fixed_timeout, bool, 0644); #define TIFM_MMCSD_CCRC 0x0100 /* command crc error */ #define TIFM_MMCSD_AF 0x0400 /* fifo almost full */ #define TIFM_MMCSD_AE 0x0800 /* fifo almost empty */ +#define TIFM_MMCSD_OCRB 0x1000 /* OCR busy */ +#define TIFM_MMCSD_CIRQ 0x2000 /* card irq (cmd40/sdio) */ #define TIFM_MMCSD_CERR 0x4000 /* card status error */ #define TIFM_MMCSD_ODTO 0x0040 /* open drain / extended timeout */ @@ -83,16 +88,16 @@ enum { }; struct tifm_sd { - struct tifm_dev *dev; + struct tifm_dev *dev; - unsigned short eject:1, - open_drain:1, - no_dma:1; - unsigned short cmd_flags; + unsigned short eject:1, + open_drain:1, + no_dma:1; + unsigned short cmd_flags; - unsigned int clk_freq; - unsigned int clk_div; - unsigned long timeout_jiffies; + unsigned int clk_freq; + unsigned int clk_div; + unsigned long timeout_jiffies; struct tasklet_struct finish_tasklet; struct timer_list timer; @@ -627,7 +632,8 @@ static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) } if (host->req) { - printk(KERN_ERR DRIVER_NAME ": unfinished request detected\n"); + printk(KERN_ERR "%s : unfinished request detected\n", + sock->dev.bus_id); spin_unlock_irqrestore(&sock->lock, flags); goto err_out; } @@ -737,7 +743,8 @@ static void tifm_sd_end_cmd(unsigned long data) host->req = NULL; if (!mrq) { - printk(KERN_ERR DRIVER_NAME ": no request to complete?\n"); + printk(KERN_ERR " %s : no request to complete?\n", + sock->dev.bus_id); spin_unlock_irqrestore(&sock->lock, flags); return; } @@ -775,8 +782,10 @@ static void tifm_sd_abort(unsigned long data) { struct tifm_sd *host = (struct tifm_sd*)data; - printk(KERN_ERR DRIVER_NAME - ": card failed to respond for a long period of time\n"); + printk(KERN_ERR + "%s : card failed to respond for a long period of time " + "(%x, %x)\n", + host->dev->dev.bus_id, host->req->cmd->opcode, host->cmd_flags); tifm_eject(host->dev); } @@ -790,8 +799,11 @@ static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) spin_lock_irqsave(&sock->lock, flags); - dev_dbg(&sock->dev, "Setting bus width %d, power %d\n", ios->bus_width, - ios->power_mode); + dev_dbg(&sock->dev, "ios: clock = %u, vdd = %x, bus_mode = %x, " + "chip_select = %x, power_mode = %x, bus_width = %x\n", + ios->clock, ios->vdd, ios->bus_mode, ios->chip_select, + ios->power_mode, ios->bus_width); + if (ios->bus_width == MMC_BUS_WIDTH_4) { writel(TIFM_MMCSD_4BBUS | readl(sock->addr + SOCK_MMCSD_CONFIG), sock->addr + SOCK_MMCSD_CONFIG); @@ -937,7 +949,8 @@ static int tifm_sd_probe(struct tifm_dev *sock) if (!(TIFM_SOCK_STATE_OCCUPIED & readl(sock->addr + SOCK_PRESENT_STATE))) { - printk(KERN_WARNING DRIVER_NAME ": card gone, unexpectedly\n"); + printk(KERN_WARNING "%s : card gone, unexpectedly\n", + sock->dev.bus_id); return rc; } @@ -974,11 +987,9 @@ static int tifm_sd_probe(struct tifm_dev *sock) if (!rc) rc = mmc_add_host(mmc); - if (rc) - goto out_free_mmc; + if (!rc) + return 0; - return 0; -out_free_mmc: mmc_free_host(mmc); return rc; } diff --git a/include/linux/tifm.h b/include/linux/tifm.h index 7ccad079546..2a196982601 100644 --- a/include/linux/tifm.h +++ b/include/linux/tifm.h @@ -14,16 +14,16 @@ #include #include -#include #include #include -#include +#include /* Host registers (relative to pci base address): */ enum { FM_SET_INTERRUPT_ENABLE = 0x008, FM_CLEAR_INTERRUPT_ENABLE = 0x00c, - FM_INTERRUPT_STATUS = 0x014 }; + FM_INTERRUPT_STATUS = 0x014 +}; /* Socket registers (relative to socket base address): */ enum { @@ -58,7 +58,8 @@ enum { SOCK_MS_DATA = 0x188, SOCK_MS_STATUS = 0x18c, SOCK_MS_SYSTEM = 0x190, - SOCK_FIFO_ACCESS = 0x200 }; + SOCK_FIFO_ACCESS = 0x200 +}; #define TIFM_CTRL_LED 0x00000040 #define TIFM_CTRL_FAST_CLK 0x00000100 @@ -66,14 +67,14 @@ enum { #define TIFM_SOCK_STATE_OCCUPIED 0x00000008 #define TIFM_SOCK_STATE_POWERED 0x00000080 -#define TIFM_FIFO_ENABLE 0x00000001 /* Meaning of this constant is unverified */ -#define TIFM_FIFO_READY 0x00000001 /* Meaning of this constant is unverified */ +#define TIFM_FIFO_ENABLE 0x00000001 +#define TIFM_FIFO_READY 0x00000001 #define TIFM_FIFO_INT_SETALL 0x0000ffff -#define TIFM_FIFO_INTMASK 0x00000005 /* Meaning of this constant is unverified */ +#define TIFM_FIFO_INTMASK 0x00000005 -#define TIFM_DMA_RESET 0x00000002 /* Meaning of this constant is unverified */ -#define TIFM_DMA_TX 0x00008000 /* Meaning of this constant is unverified */ -#define TIFM_DMA_EN 0x00000001 /* Meaning of this constant is unverified */ +#define TIFM_DMA_RESET 0x00000002 +#define TIFM_DMA_TX 0x00008000 +#define TIFM_DMA_EN 0x00000001 #define TIFM_DMA_TSIZE 0x0000007f #define TIFM_TYPE_XD 1 @@ -86,44 +87,44 @@ struct tifm_device_id { struct tifm_driver; struct tifm_dev { - char __iomem *addr; - spinlock_t lock; - unsigned char type; - unsigned int socket_id; + char __iomem *addr; + spinlock_t lock; + unsigned char type; + unsigned int socket_id; void (*card_event)(struct tifm_dev *sock); void (*data_event)(struct tifm_dev *sock); - struct device dev; + struct device dev; }; struct tifm_driver { struct tifm_device_id *id_table; - int (*probe)(struct tifm_dev *dev); - void (*remove)(struct tifm_dev *dev); - int (*suspend)(struct tifm_dev *dev, - pm_message_t state); - int (*resume)(struct tifm_dev *dev); + int (*probe)(struct tifm_dev *dev); + void (*remove)(struct tifm_dev *dev); + int (*suspend)(struct tifm_dev *dev, + pm_message_t state); + int (*resume)(struct tifm_dev *dev); - struct device_driver driver; + struct device_driver driver; }; struct tifm_adapter { - char __iomem *addr; - spinlock_t lock; - unsigned int irq_status; - unsigned int socket_change_set; - unsigned int id; - unsigned int num_sockets; - struct completion *finish_me; + char __iomem *addr; + spinlock_t lock; + unsigned int irq_status; + unsigned int socket_change_set; + unsigned int id; + unsigned int num_sockets; + struct completion *finish_me; - struct work_struct media_switcher; - struct class_device cdev; + struct work_struct media_switcher; + struct class_device cdev; - void (*eject)(struct tifm_adapter *fm, - struct tifm_dev *sock); + void (*eject)(struct tifm_adapter *fm, + struct tifm_dev *sock); - struct tifm_dev *sockets[0]; + struct tifm_dev *sockets[0]; }; struct tifm_adapter *tifm_alloc_adapter(unsigned int num_sockets, @@ -147,7 +148,7 @@ void tifm_queue_work(struct work_struct *work); static inline void *tifm_get_drvdata(struct tifm_dev *dev) { - return dev_get_drvdata(&dev->dev); + return dev_get_drvdata(&dev->dev); } static inline void tifm_set_drvdata(struct tifm_dev *dev, void *data) -- cgit v1.2.3 From c4030698029bb30d220fb0342a34280c629cee01 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 28 Apr 2007 14:21:10 +0200 Subject: tifm: add missing include for DMA_32BIT_MASK sparc64: drivers/misc/tifm_7xx1.c: In function `tifm_7xx1_probe': drivers/misc/tifm_7xx1.c:294: error: `DMA_32BIT_MASK' undeclared Signed-off-by: Andrew Morton Signed-off-by: Pierre Ossman --- drivers/misc/tifm_7xx1.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 9dcff14e752..1ba6c085419 100644 --- a/drivers/misc/tifm_7xx1.c +++ b/drivers/misc/tifm_7xx1.c @@ -10,6 +10,7 @@ */ #include +#include #define DRIVER_NAME "tifm_7xx1" #define DRIVER_VERSION "0.8" -- cgit v1.2.3 From de85989511f3a0e15b04d18582b23d428d6ddbbd Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 28 Apr 2007 14:59:35 +0200 Subject: mmc: use right timing mode constant Fix copy-n-paste error. Signed-off-by: Pierre Ossman --- drivers/mmc/mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index e8f896c61b3..2ba46273496 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1145,7 +1145,7 @@ static void mmc_process_ext_csds(struct mmc_host *host) mmc_card_set_highspeed(card); - host->ios.timing = MMC_TIMING_SD_HS; + host->ios.timing = MMC_TIMING_MMC_HS; mmc_set_ios(host); } -- cgit v1.2.3 From 85a18ad93ec66888d85758630019b10a84257f3c Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 17 Feb 2007 22:15:27 +0100 Subject: mmc: MMC sector based cards Support for MMC 4.2 sector based cards. This tweaks the init a bit and reads a new field out of the EXT_CSD. Signed-off-by: Pierre Ossman --- drivers/mmc/mmc.c | 25 ++++++++++++++++++++++--- drivers/mmc/mmc_block.c | 19 ++++++++++++++----- include/linux/mmc/card.h | 1 + include/linux/mmc/protocol.h | 1 + 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 2ba46273496..f772df93a39 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1106,11 +1106,29 @@ static void mmc_process_ext_csds(struct mmc_host *host) mmc_wait_for_req(host, &mrq); if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { - printk("%s: unable to read EXT_CSD, performance " - "might suffer.\n", mmc_hostname(card->host)); + if (card->csd.capacity == (4096 * 512)) { + printk(KERN_ERR "%s: unable to read EXT_CSD " + "on a possible high capacity card. " + "Card will be ignored.\n", + mmc_hostname(card->host)); + mmc_card_set_dead(card); + } else { + printk(KERN_WARNING "%s: unable to read " + "EXT_CSD, performance might " + "suffer.\n", + mmc_hostname(card->host)); + } continue; } + card->ext_csd.sectors = + ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | + ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | + ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | + ext_csd[EXT_CSD_SEC_CNT + 3] << 24; + if (card->ext_csd.sectors) + mmc_card_set_blockaddr(card); + switch (ext_csd[EXT_CSD_CARD_TYPE]) { case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26: card->ext_csd.hs_max_dtr = 52000000; @@ -1499,7 +1517,8 @@ static void mmc_setup(struct mmc_host *host) mmc_send_app_op_cond(host, host->ocr | (sd2 << 30), NULL); } } else { - mmc_send_op_cond(host, host->ocr, NULL); + /* The extra bit indicates that we support high capacity */ + mmc_send_op_cond(host, host->ocr | (1 << 30), NULL); } mmc_discover_cards(host); diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c index 95b0da6abe8..63fbde8756a 100644 --- a/drivers/mmc/mmc_block.c +++ b/drivers/mmc/mmc_block.c @@ -491,11 +491,20 @@ static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits); - /* - * The CSD capacity field is in units of read_blkbits. - * set_capacity takes units of 512 bytes. - */ - set_capacity(md->disk, card->csd.capacity << (card->csd.read_blkbits - 9)); + if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) { + /* + * The EXT_CSD sector count is in number or 512 byte + * sectors. + */ + set_capacity(md->disk, card->ext_csd.sectors); + } else { + /* + * The CSD capacity field is in units of read_blkbits. + * set_capacity takes units of 512 bytes. + */ + set_capacity(md->disk, + card->csd.capacity << (card->csd.read_blkbits - 9)); + } return md; err_putdisk: diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index e45712acfac..5d9896c260a 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -41,6 +41,7 @@ struct mmc_csd { struct mmc_ext_csd { unsigned int hs_max_dtr; + unsigned int sectors; }; struct sd_scr { diff --git a/include/linux/mmc/protocol.h b/include/linux/mmc/protocol.h index c90b6768329..d740ab94fa2 100644 --- a/include/linux/mmc/protocol.h +++ b/include/linux/mmc/protocol.h @@ -284,6 +284,7 @@ struct _mmc_csd { #define EXT_CSD_BUS_WIDTH 183 /* R/W */ #define EXT_CSD_HS_TIMING 185 /* R/W */ #define EXT_CSD_CARD_TYPE 196 /* RO */ +#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ /* * EXT_CSD field definitions -- cgit v1.2.3 From 9c2c0af950345e63ef86f28eca44333a1e1e709b Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 26 Dec 2006 15:25:58 +0100 Subject: mmc: add type field to cards Split out the type of card into its own field as it hardly qualifies as a state. Signed-off-by: Pierre Ossman --- drivers/mmc/mmc.c | 3 ++- include/linux/mmc/card.h | 15 +++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index f772df93a39..ccd3037da24 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -980,7 +980,7 @@ static void mmc_discover_cards(struct mmc_host *host) card->state &= ~MMC_STATE_DEAD; if (host->mode == MMC_MODE_SD) { - mmc_card_set_sd(card); + card->type = MMC_TYPE_SD; cmd.opcode = SD_SEND_RELATIVE_ADDR; cmd.arg = 0; @@ -1003,6 +1003,7 @@ static void mmc_discover_cards(struct mmc_host *host) } } } else { + card->type = MMC_TYPE_MMC; cmd.opcode = MMC_SET_RELATIVE_ADDR; cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 5d9896c260a..1ca50542ce1 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -65,14 +65,16 @@ struct mmc_card { struct mmc_host *host; /* the host this device belongs to */ struct device dev; /* the device */ unsigned int rca; /* relative card address of device */ + unsigned int type; /* card type */ +#define MMC_TYPE_MMC 0 /* MMC card */ +#define MMC_TYPE_SD 1 /* SD card */ unsigned int state; /* (our) card state */ #define MMC_STATE_PRESENT (1<<0) /* present in sysfs */ #define MMC_STATE_DEAD (1<<1) /* device no longer in stack */ #define MMC_STATE_BAD (1<<2) /* unrecognised device */ -#define MMC_STATE_SDCARD (1<<3) /* is an SD card */ -#define MMC_STATE_READONLY (1<<4) /* card is read-only */ -#define MMC_STATE_HIGHSPEED (1<<5) /* card is in high speed mode */ -#define MMC_STATE_BLOCKADDR (1<<6) /* card uses block-addressing */ +#define MMC_STATE_READONLY (1<<3) /* card is read-only */ +#define MMC_STATE_HIGHSPEED (1<<4) /* card is in high speed mode */ +#define MMC_STATE_BLOCKADDR (1<<5) /* card uses block-addressing */ u32 raw_cid[4]; /* raw card CID */ u32 raw_csd[4]; /* raw card CSD */ u32 raw_scr[2]; /* raw card SCR */ @@ -83,10 +85,12 @@ struct mmc_card { struct sd_switch_caps sw_caps; /* switch (CMD6) caps */ }; +#define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC) +#define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD) + #define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT) #define mmc_card_dead(c) ((c)->state & MMC_STATE_DEAD) #define mmc_card_bad(c) ((c)->state & MMC_STATE_BAD) -#define mmc_card_sd(c) ((c)->state & MMC_STATE_SDCARD) #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY) #define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED) #define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR) @@ -94,7 +98,6 @@ struct mmc_card { #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) #define mmc_card_set_dead(c) ((c)->state |= MMC_STATE_DEAD) #define mmc_card_set_bad(c) ((c)->state |= MMC_STATE_BAD) -#define mmc_card_set_sd(c) ((c)->state |= MMC_STATE_SDCARD) #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) #define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED) #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR) -- cgit v1.2.3 From f74d132cec60b686bce1f284822c1a496700bd3c Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 9 Feb 2007 22:49:31 +0100 Subject: mmc: Move OCR bit defines All host drivers were #include:ing mmc/protocol.h just to get access to the OCR bit defines. Move these to host.h instead. Signed-off-by: Pierre Ossman --- drivers/mmc/at91_mci.c | 1 - drivers/mmc/au1xmmc.c | 1 - drivers/mmc/imxmmc.c | 1 - drivers/mmc/mmci.c | 1 - drivers/mmc/omap.c | 1 - drivers/mmc/pxamci.c | 1 - drivers/mmc/sdhci.c | 1 - drivers/mmc/tifm_sd.c | 1 - drivers/mmc/wbsd.c | 1 - include/asm-arm/arch-imx/mmc.h | 2 +- include/asm-arm/arch-pxa/mmc.h | 2 +- include/asm-arm/mach/mmc.h | 2 +- include/linux/mmc/host.h | 25 +++++++++++++++++++++++++ include/linux/mmc/protocol.h | 27 +++------------------------ 14 files changed, 31 insertions(+), 36 deletions(-) diff --git a/drivers/mmc/at91_mci.c b/drivers/mmc/at91_mci.c index 459f4b4fede..e37943c314c 100644 --- a/drivers/mmc/at91_mci.c +++ b/drivers/mmc/at91_mci.c @@ -67,7 +67,6 @@ #include #include -#include #include #include diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c index b834be261ab..b7156a4555b 100644 --- a/drivers/mmc/au1xmmc.c +++ b/drivers/mmc/au1xmmc.c @@ -42,7 +42,6 @@ #include #include -#include #include #include #include diff --git a/drivers/mmc/imxmmc.c b/drivers/mmc/imxmmc.c index 0de5c9e94e7..7ee2045acbe 100644 --- a/drivers/mmc/imxmmc.c +++ b/drivers/mmc/imxmmc.c @@ -41,7 +41,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c index 5941dd951e8..d11c2d23cee 100644 --- a/drivers/mmc/mmci.c +++ b/drivers/mmc/mmci.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mmc/omap.c b/drivers/mmc/omap.c index 1e96a2f6502..e851384e51f 100644 --- a/drivers/mmc/omap.c +++ b/drivers/mmc/omap.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mmc/pxamci.c b/drivers/mmc/pxamci.c index 9774fc68b61..a98ff98fa56 100644 --- a/drivers/mmc/pxamci.c +++ b/drivers/mmc/pxamci.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 587dccf95f8..579142a7904 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -15,7 +15,6 @@ #include #include -#include #include diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index f692a2ec09c..b0d77d29841 100644 --- a/drivers/mmc/tifm_sd.c +++ b/drivers/mmc/tifm_sd.c @@ -13,7 +13,6 @@ #include -#include #include #include #include diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c index 673c64661d3..9f7518b37c3 100644 --- a/drivers/mmc/wbsd.c +++ b/drivers/mmc/wbsd.c @@ -33,7 +33,6 @@ #include #include #include -#include #include #include diff --git a/include/asm-arm/arch-imx/mmc.h b/include/asm-arm/arch-imx/mmc.h index 1937151665c..84c726934ac 100644 --- a/include/asm-arm/arch-imx/mmc.h +++ b/include/asm-arm/arch-imx/mmc.h @@ -1,7 +1,7 @@ #ifndef ASMARM_ARCH_MMC_H #define ASMARM_ARCH_MMC_H -#include +#include struct imxmmc_platform_data { int (*card_present)(void); diff --git a/include/asm-arm/arch-pxa/mmc.h b/include/asm-arm/arch-pxa/mmc.h index a38a28c4bbd..ef4f570381d 100644 --- a/include/asm-arm/arch-pxa/mmc.h +++ b/include/asm-arm/arch-pxa/mmc.h @@ -1,7 +1,7 @@ #ifndef ASMARM_ARCH_MMC_H #define ASMARM_ARCH_MMC_H -#include +#include #include struct device; diff --git a/include/asm-arm/mach/mmc.h b/include/asm-arm/mach/mmc.h index 1b3555d4b41..eb91145c00c 100644 --- a/include/asm-arm/mach/mmc.h +++ b/include/asm-arm/mach/mmc.h @@ -4,7 +4,7 @@ #ifndef ASMARM_MACH_MMC_H #define ASMARM_MACH_MMC_H -#include +#include struct mmc_platform_data { unsigned int ocr_mask; /* available voltages */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index bfcef8a1ad8..c89f4109130 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -88,6 +88,31 @@ struct mmc_host { unsigned int f_max; u32 ocr_avail; +#define MMC_VDD_145_150 0x00000001 /* VDD voltage 1.45 - 1.50 */ +#define MMC_VDD_150_155 0x00000002 /* VDD voltage 1.50 - 1.55 */ +#define MMC_VDD_155_160 0x00000004 /* VDD voltage 1.55 - 1.60 */ +#define MMC_VDD_160_165 0x00000008 /* VDD voltage 1.60 - 1.65 */ +#define MMC_VDD_165_170 0x00000010 /* VDD voltage 1.65 - 1.70 */ +#define MMC_VDD_17_18 0x00000020 /* VDD voltage 1.7 - 1.8 */ +#define MMC_VDD_18_19 0x00000040 /* VDD voltage 1.8 - 1.9 */ +#define MMC_VDD_19_20 0x00000080 /* VDD voltage 1.9 - 2.0 */ +#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */ +#define MMC_VDD_21_22 0x00000200 /* VDD voltage 2.1 ~ 2.2 */ +#define MMC_VDD_22_23 0x00000400 /* VDD voltage 2.2 ~ 2.3 */ +#define MMC_VDD_23_24 0x00000800 /* VDD voltage 2.3 ~ 2.4 */ +#define MMC_VDD_24_25 0x00001000 /* VDD voltage 2.4 ~ 2.5 */ +#define MMC_VDD_25_26 0x00002000 /* VDD voltage 2.5 ~ 2.6 */ +#define MMC_VDD_26_27 0x00004000 /* VDD voltage 2.6 ~ 2.7 */ +#define MMC_VDD_27_28 0x00008000 /* VDD voltage 2.7 ~ 2.8 */ +#define MMC_VDD_28_29 0x00010000 /* VDD voltage 2.8 ~ 2.9 */ +#define MMC_VDD_29_30 0x00020000 /* VDD voltage 2.9 ~ 3.0 */ +#define MMC_VDD_30_31 0x00040000 /* VDD voltage 3.0 ~ 3.1 */ +#define MMC_VDD_31_32 0x00080000 /* VDD voltage 3.1 ~ 3.2 */ +#define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */ +#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */ +#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */ +#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */ + unsigned long caps; /* Host capabilities */ #define MMC_CAP_4_BIT_DATA (1 << 0) /* Can the host do 4 bit transfers */ diff --git a/include/linux/mmc/protocol.h b/include/linux/mmc/protocol.h index d740ab94fa2..3ca91a6fc23 100644 --- a/include/linux/mmc/protocol.h +++ b/include/linux/mmc/protocol.h @@ -208,30 +208,9 @@ struct _mmc_csd { u8 ecc; }; -#define MMC_VDD_145_150 0x00000001 /* VDD voltage 1.45 - 1.50 */ -#define MMC_VDD_150_155 0x00000002 /* VDD voltage 1.50 - 1.55 */ -#define MMC_VDD_155_160 0x00000004 /* VDD voltage 1.55 - 1.60 */ -#define MMC_VDD_160_165 0x00000008 /* VDD voltage 1.60 - 1.65 */ -#define MMC_VDD_165_170 0x00000010 /* VDD voltage 1.65 - 1.70 */ -#define MMC_VDD_17_18 0x00000020 /* VDD voltage 1.7 - 1.8 */ -#define MMC_VDD_18_19 0x00000040 /* VDD voltage 1.8 - 1.9 */ -#define MMC_VDD_19_20 0x00000080 /* VDD voltage 1.9 - 2.0 */ -#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */ -#define MMC_VDD_21_22 0x00000200 /* VDD voltage 2.1 ~ 2.2 */ -#define MMC_VDD_22_23 0x00000400 /* VDD voltage 2.2 ~ 2.3 */ -#define MMC_VDD_23_24 0x00000800 /* VDD voltage 2.3 ~ 2.4 */ -#define MMC_VDD_24_25 0x00001000 /* VDD voltage 2.4 ~ 2.5 */ -#define MMC_VDD_25_26 0x00002000 /* VDD voltage 2.5 ~ 2.6 */ -#define MMC_VDD_26_27 0x00004000 /* VDD voltage 2.6 ~ 2.7 */ -#define MMC_VDD_27_28 0x00008000 /* VDD voltage 2.7 ~ 2.8 */ -#define MMC_VDD_28_29 0x00010000 /* VDD voltage 2.8 ~ 2.9 */ -#define MMC_VDD_29_30 0x00020000 /* VDD voltage 2.9 ~ 3.0 */ -#define MMC_VDD_30_31 0x00040000 /* VDD voltage 3.0 ~ 3.1 */ -#define MMC_VDD_31_32 0x00080000 /* VDD voltage 3.1 ~ 3.2 */ -#define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */ -#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */ -#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */ -#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */ +/* + * OCR bits are mostly in host.h + */ #define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */ /* -- cgit v1.2.3 From 29041dbe199b0dff392bf1b9d634357da0b3208f Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 10 Feb 2007 15:52:23 +0100 Subject: mmc: Move "present" marking The "present" state indicates that the card is a registered device, so it is more clear to put it together with the actual registration. Signed-off-by: Pierre Ossman --- drivers/mmc/mmc.c | 2 -- drivers/mmc/mmc_sysfs.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index ccd3037da24..9ffeeb2cba4 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1604,8 +1604,6 @@ static void mmc_rescan(struct work_struct *work) if (!mmc_card_present(card) && !mmc_card_dead(card)) { if (mmc_register_card(card)) mmc_card_set_dead(card); - else - mmc_card_set_present(card); } /* diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c index e0e82d849d5..06f264b2f79 100644 --- a/drivers/mmc/mmc_sysfs.c +++ b/drivers/mmc/mmc_sysfs.c @@ -217,6 +217,8 @@ int mmc_register_card(struct mmc_card *card) device_del(&card->dev); } } + if (ret == 0) + mmc_card_set_present(card); return ret; } -- cgit v1.2.3 From 98ac2162699f7e9880683cb954891817f20b607c Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 23 Dec 2006 20:03:02 +0100 Subject: mmc: Move queue functions to mmc_block The mmc block queue functions are tailored for the mmc_block driver, so move those functions into that module. Signed-off-by: Pierre Ossman --- drivers/mmc/Makefile | 2 +- drivers/mmc/block.c | 668 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/mmc_block.c | 667 ----------------------------------------------- drivers/mmc/mmc_queue.c | 250 ------------------ drivers/mmc/mmc_queue.h | 32 --- drivers/mmc/queue.c | 249 ++++++++++++++++++ drivers/mmc/queue.h | 32 +++ 7 files changed, 950 insertions(+), 950 deletions(-) create mode 100644 drivers/mmc/block.c delete mode 100644 drivers/mmc/mmc_block.c delete mode 100644 drivers/mmc/mmc_queue.c delete mode 100644 drivers/mmc/mmc_queue.h create mode 100644 drivers/mmc/queue.c create mode 100644 drivers/mmc/queue.h diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 83ffb9326a5..9ef010a5160 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_MMC) += mmc_core.o # Media drivers # obj-$(CONFIG_MMC_BLOCK) += mmc_block.o +mmc_block-objs := block.o queue.o # # Host drivers @@ -26,7 +27,6 @@ obj-$(CONFIG_MMC_AT91) += at91_mci.o obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o mmc_core-y := mmc.o mmc_sysfs.o -mmc_core-$(CONFIG_BLOCK) += mmc_queue.o ifeq ($(CONFIG_MMC_DEBUG),y) EXTRA_CFLAGS += -DDEBUG diff --git a/drivers/mmc/block.c b/drivers/mmc/block.c new file mode 100644 index 00000000000..8eba037a18e --- /dev/null +++ b/drivers/mmc/block.c @@ -0,0 +1,668 @@ +/* + * Block driver for media (i.e., flash cards) + * + * Copyright 2002 Hewlett-Packard Company + * Copyright 2005-2007 Pierre Ossman + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, + * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS + * FITNESS FOR ANY PARTICULAR PURPOSE. + * + * Many thanks to Alessandro Rubini and Jonathan Corbet! + * + * Author: Andrew Christian + * 28 May 2002 + */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "queue.h" + +/* + * max 8 partitions per card + */ +#define MMC_SHIFT 3 + +static int major; + +/* + * There is one mmc_blk_data per slot. + */ +struct mmc_blk_data { + spinlock_t lock; + struct gendisk *disk; + struct mmc_queue queue; + + unsigned int usage; + unsigned int block_bits; + unsigned int read_only; +}; + +static DEFINE_MUTEX(open_lock); + +static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) +{ + struct mmc_blk_data *md; + + mutex_lock(&open_lock); + md = disk->private_data; + if (md && md->usage == 0) + md = NULL; + if (md) + md->usage++; + mutex_unlock(&open_lock); + + return md; +} + +static void mmc_blk_put(struct mmc_blk_data *md) +{ + mutex_lock(&open_lock); + md->usage--; + if (md->usage == 0) { + put_disk(md->disk); + kfree(md); + } + mutex_unlock(&open_lock); +} + +static int mmc_blk_open(struct inode *inode, struct file *filp) +{ + struct mmc_blk_data *md; + int ret = -ENXIO; + + md = mmc_blk_get(inode->i_bdev->bd_disk); + if (md) { + if (md->usage == 2) + check_disk_change(inode->i_bdev); + ret = 0; + + if ((filp->f_mode & FMODE_WRITE) && md->read_only) + ret = -EROFS; + } + + return ret; +} + +static int mmc_blk_release(struct inode *inode, struct file *filp) +{ + struct mmc_blk_data *md = inode->i_bdev->bd_disk->private_data; + + mmc_blk_put(md); + return 0; +} + +static int +mmc_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo) +{ + geo->cylinders = get_capacity(bdev->bd_disk) / (4 * 16); + geo->heads = 4; + geo->sectors = 16; + return 0; +} + +static struct block_device_operations mmc_bdops = { + .open = mmc_blk_open, + .release = mmc_blk_release, + .getgeo = mmc_blk_getgeo, + .owner = THIS_MODULE, +}; + +struct mmc_blk_request { + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_command stop; + struct mmc_data data; +}; + +static int mmc_blk_prep_rq(struct mmc_queue *mq, struct request *req) +{ + struct mmc_blk_data *md = mq->data; + int stat = BLKPREP_OK; + + /* + * If we have no device, we haven't finished initialising. + */ + if (!md || !mq->card) { + printk(KERN_ERR "%s: killing request - no device/host\n", + req->rq_disk->disk_name); + stat = BLKPREP_KILL; + } + + return stat; +} + +static u32 mmc_sd_num_wr_blocks(struct mmc_card *card) +{ + int err; + u32 blocks; + + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + unsigned int timeout_us; + + struct scatterlist sg; + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_APP_CMD; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, 0); + if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) + return (u32)-1; + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_APP_SEND_NUM_WR_BLKS; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + memset(&data, 0, sizeof(struct mmc_data)); + + data.timeout_ns = card->csd.tacc_ns * 100; + data.timeout_clks = card->csd.tacc_clks * 100; + + timeout_us = data.timeout_ns / 1000; + timeout_us += data.timeout_clks * 1000 / + (card->host->ios.clock / 1000); + + if (timeout_us > 100000) { + data.timeout_ns = 100000000; + data.timeout_clks = 0; + } + + data.blksz = 4; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + memset(&mrq, 0, sizeof(struct mmc_request)); + + mrq.cmd = &cmd; + mrq.data = &data; + + sg_init_one(&sg, &blocks, 4); + + mmc_wait_for_req(card->host, &mrq); + + if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) + return (u32)-1; + + blocks = ntohl(blocks); + + return blocks; +} + +static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) +{ + struct mmc_blk_data *md = mq->data; + struct mmc_card *card = md->queue.card; + struct mmc_blk_request brq; + int ret = 1, sg_pos, data_size; + + if (mmc_card_claim_host(card)) + goto flush_queue; + + do { + struct mmc_command cmd; + u32 readcmd, writecmd; + + memset(&brq, 0, sizeof(struct mmc_blk_request)); + brq.mrq.cmd = &brq.cmd; + brq.mrq.data = &brq.data; + + brq.cmd.arg = req->sector; + if (!mmc_card_blockaddr(card)) + brq.cmd.arg <<= 9; + brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + brq.data.blksz = 1 << md->block_bits; + brq.stop.opcode = MMC_STOP_TRANSMISSION; + brq.stop.arg = 0; + brq.stop.flags = MMC_RSP_R1B | MMC_CMD_AC; + brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); + if (brq.data.blocks > card->host->max_blk_count) + brq.data.blocks = card->host->max_blk_count; + + mmc_set_data_timeout(&brq.data, card, rq_data_dir(req) != READ); + + /* + * If the host doesn't support multiple block writes, force + * block writes to single block. SD cards are excepted from + * this rule as they support querying the number of + * successfully written sectors. + */ + if (rq_data_dir(req) != READ && + !(card->host->caps & MMC_CAP_MULTIWRITE) && + !mmc_card_sd(card)) + brq.data.blocks = 1; + + if (brq.data.blocks > 1) { + brq.data.flags |= MMC_DATA_MULTI; + brq.mrq.stop = &brq.stop; + readcmd = MMC_READ_MULTIPLE_BLOCK; + writecmd = MMC_WRITE_MULTIPLE_BLOCK; + } else { + brq.mrq.stop = NULL; + readcmd = MMC_READ_SINGLE_BLOCK; + writecmd = MMC_WRITE_BLOCK; + } + + if (rq_data_dir(req) == READ) { + brq.cmd.opcode = readcmd; + brq.data.flags |= MMC_DATA_READ; + } else { + brq.cmd.opcode = writecmd; + brq.data.flags |= MMC_DATA_WRITE; + } + + brq.data.sg = mq->sg; + brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg); + + if (brq.data.blocks != + (req->nr_sectors >> (md->block_bits - 9))) { + data_size = brq.data.blocks * brq.data.blksz; + for (sg_pos = 0; sg_pos < brq.data.sg_len; sg_pos++) { + data_size -= mq->sg[sg_pos].length; + if (data_size <= 0) { + mq->sg[sg_pos].length += data_size; + sg_pos++; + break; + } + } + brq.data.sg_len = sg_pos; + } + + mmc_wait_for_req(card->host, &brq.mrq); + if (brq.cmd.error) { + printk(KERN_ERR "%s: error %d sending read/write command\n", + req->rq_disk->disk_name, brq.cmd.error); + goto cmd_err; + } + + if (brq.data.error) { + printk(KERN_ERR "%s: error %d transferring data\n", + req->rq_disk->disk_name, brq.data.error); + goto cmd_err; + } + + if (brq.stop.error) { + printk(KERN_ERR "%s: error %d sending stop command\n", + req->rq_disk->disk_name, brq.stop.error); + goto cmd_err; + } + + if (rq_data_dir(req) != READ) { + do { + int err; + + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + err = mmc_wait_for_cmd(card->host, &cmd, 5); + if (err) { + printk(KERN_ERR "%s: error %d requesting status\n", + req->rq_disk->disk_name, err); + goto cmd_err; + } + } while (!(cmd.resp[0] & R1_READY_FOR_DATA)); + +#if 0 + if (cmd.resp[0] & ~0x00000900) + printk(KERN_ERR "%s: status = %08x\n", + req->rq_disk->disk_name, cmd.resp[0]); + if (mmc_decode_status(cmd.resp)) + goto cmd_err; +#endif + } + + /* + * A block was successfully transferred. + */ + spin_lock_irq(&md->lock); + ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered); + if (!ret) { + /* + * The whole request completed successfully. + */ + add_disk_randomness(req->rq_disk); + blkdev_dequeue_request(req); + end_that_request_last(req, 1); + } + spin_unlock_irq(&md->lock); + } while (ret); + + mmc_card_release_host(card); + + return 1; + + cmd_err: + /* + * If this is an SD card and we're writing, we can first + * mark the known good sectors as ok. + * + * If the card is not SD, we can still ok written sectors + * if the controller can do proper error reporting. + * + * For reads we just fail the entire chunk as that should + * be safe in all cases. + */ + if (rq_data_dir(req) != READ && mmc_card_sd(card)) { + u32 blocks; + unsigned int bytes; + + blocks = mmc_sd_num_wr_blocks(card); + if (blocks != (u32)-1) { + if (card->csd.write_partial) + bytes = blocks << md->block_bits; + else + bytes = blocks << 9; + spin_lock_irq(&md->lock); + ret = end_that_request_chunk(req, 1, bytes); + spin_unlock_irq(&md->lock); + } + } else if (rq_data_dir(req) != READ && + (card->host->caps & MMC_CAP_MULTIWRITE)) { + spin_lock_irq(&md->lock); + ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered); + spin_unlock_irq(&md->lock); + } + +flush_queue: + + mmc_card_release_host(card); + + spin_lock_irq(&md->lock); + while (ret) { + ret = end_that_request_chunk(req, 0, + req->current_nr_sectors << 9); + } + + add_disk_randomness(req->rq_disk); + blkdev_dequeue_request(req); + end_that_request_last(req, 0); + spin_unlock_irq(&md->lock); + + return 0; +} + +#define MMC_NUM_MINORS (256 >> MMC_SHIFT) + +static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))]; + +static inline int mmc_blk_readonly(struct mmc_card *card) +{ + return mmc_card_readonly(card) || + !(card->csd.cmdclass & CCC_BLOCK_WRITE); +} + +static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) +{ + struct mmc_blk_data *md; + int devidx, ret; + + devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS); + if (devidx >= MMC_NUM_MINORS) + return ERR_PTR(-ENOSPC); + __set_bit(devidx, dev_use); + + md = kmalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); + if (!md) { + ret = -ENOMEM; + goto out; + } + + memset(md, 0, sizeof(struct mmc_blk_data)); + + /* + * Set the read-only status based on the supported commands + * and the write protect switch. + */ + md->read_only = mmc_blk_readonly(card); + + /* + * Both SD and MMC specifications state (although a bit + * unclearly in the MMC case) that a block size of 512 + * bytes must always be supported by the card. + */ + md->block_bits = 9; + + md->disk = alloc_disk(1 << MMC_SHIFT); + if (md->disk == NULL) { + ret = -ENOMEM; + goto err_kfree; + } + + spin_lock_init(&md->lock); + md->usage = 1; + + ret = mmc_init_queue(&md->queue, card, &md->lock); + if (ret) + goto err_putdisk; + + md->queue.prep_fn = mmc_blk_prep_rq; + md->queue.issue_fn = mmc_blk_issue_rq; + md->queue.data = md; + + md->disk->major = major; + md->disk->first_minor = devidx << MMC_SHIFT; + md->disk->fops = &mmc_bdops; + md->disk->private_data = md; + md->disk->queue = md->queue.queue; + md->disk->driverfs_dev = &card->dev; + + /* + * As discussed on lkml, GENHD_FL_REMOVABLE should: + * + * - be set for removable media with permanent block devices + * - be unset for removable block devices with permanent media + * + * Since MMC block devices clearly fall under the second + * case, we do not set GENHD_FL_REMOVABLE. Userspace + * should use the block device creation/destruction hotplug + * messages to tell when the card is present. + */ + + sprintf(md->disk->disk_name, "mmcblk%d", devidx); + + blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits); + + if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) { + /* + * The EXT_CSD sector count is in number or 512 byte + * sectors. + */ + set_capacity(md->disk, card->ext_csd.sectors); + } else { + /* + * The CSD capacity field is in units of read_blkbits. + * set_capacity takes units of 512 bytes. + */ + set_capacity(md->disk, + card->csd.capacity << (card->csd.read_blkbits - 9)); + } + return md; + + err_putdisk: + put_disk(md->disk); + err_kfree: + kfree(md); + out: + return ERR_PTR(ret); +} + +static int +mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) +{ + struct mmc_command cmd; + int err; + + /* Block-addressed cards ignore MMC_SET_BLOCKLEN. */ + if (mmc_card_blockaddr(card)) + return 0; + + mmc_card_claim_host(card); + cmd.opcode = MMC_SET_BLOCKLEN; + cmd.arg = 1 << md->block_bits; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + err = mmc_wait_for_cmd(card->host, &cmd, 5); + mmc_card_release_host(card); + + if (err) { + printk(KERN_ERR "%s: unable to set block size to %d: %d\n", + md->disk->disk_name, cmd.arg, err); + return -EINVAL; + } + + return 0; +} + +static int mmc_blk_probe(struct mmc_card *card) +{ + struct mmc_blk_data *md; + int err; + + /* + * Check that the card supports the command class(es) we need. + */ + if (!(card->csd.cmdclass & CCC_BLOCK_READ)) + return -ENODEV; + + md = mmc_blk_alloc(card); + if (IS_ERR(md)) + return PTR_ERR(md); + + err = mmc_blk_set_blksize(md, card); + if (err) + goto out; + + printk(KERN_INFO "%s: %s %s %lluKiB %s\n", + md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), + (unsigned long long)(get_capacity(md->disk) >> 1), + md->read_only ? "(ro)" : ""); + + mmc_set_drvdata(card, md); + add_disk(md->disk); + return 0; + + out: + mmc_blk_put(md); + + return err; +} + +static void mmc_blk_remove(struct mmc_card *card) +{ + struct mmc_blk_data *md = mmc_get_drvdata(card); + + if (md) { + int devidx; + + /* Stop new requests from getting into the queue */ + del_gendisk(md->disk); + + /* Then flush out any already in there */ + mmc_cleanup_queue(&md->queue); + + devidx = md->disk->first_minor >> MMC_SHIFT; + __clear_bit(devidx, dev_use); + + mmc_blk_put(md); + } + mmc_set_drvdata(card, NULL); +} + +#ifdef CONFIG_PM +static int mmc_blk_suspend(struct mmc_card *card, pm_message_t state) +{ + struct mmc_blk_data *md = mmc_get_drvdata(card); + + if (md) { + mmc_queue_suspend(&md->queue); + } + return 0; +} + +static int mmc_blk_resume(struct mmc_card *card) +{ + struct mmc_blk_data *md = mmc_get_drvdata(card); + + if (md) { + mmc_blk_set_blksize(md, card); + mmc_queue_resume(&md->queue); + } + return 0; +} +#else +#define mmc_blk_suspend NULL +#define mmc_blk_resume NULL +#endif + +static struct mmc_driver mmc_driver = { + .drv = { + .name = "mmcblk", + }, + .probe = mmc_blk_probe, + .remove = mmc_blk_remove, + .suspend = mmc_blk_suspend, + .resume = mmc_blk_resume, +}; + +static int __init mmc_blk_init(void) +{ + int res = -ENOMEM; + + res = register_blkdev(major, "mmc"); + if (res < 0) { + printk(KERN_WARNING "Unable to get major %d for MMC media: %d\n", + major, res); + goto out; + } + if (major == 0) + major = res; + + return mmc_register_driver(&mmc_driver); + + out: + return res; +} + +static void __exit mmc_blk_exit(void) +{ + mmc_unregister_driver(&mmc_driver); + unregister_blkdev(major, "mmc"); +} + +module_init(mmc_blk_init); +module_exit(mmc_blk_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver"); + +module_param(major, int, 0444); +MODULE_PARM_DESC(major, "specify the major device number for MMC block driver"); diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c deleted file mode 100644 index 63fbde8756a..00000000000 --- a/drivers/mmc/mmc_block.c +++ /dev/null @@ -1,667 +0,0 @@ -/* - * Block driver for media (i.e., flash cards) - * - * Copyright 2002 Hewlett-Packard Company - * - * Use consistent with the GNU GPL is permitted, - * provided that this copyright notice is - * preserved in its entirety in all copies and derived works. - * - * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, - * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS - * FITNESS FOR ANY PARTICULAR PURPOSE. - * - * Many thanks to Alessandro Rubini and Jonathan Corbet! - * - * Author: Andrew Christian - * 28 May 2002 - */ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include "mmc_queue.h" - -/* - * max 8 partitions per card - */ -#define MMC_SHIFT 3 - -static int major; - -/* - * There is one mmc_blk_data per slot. - */ -struct mmc_blk_data { - spinlock_t lock; - struct gendisk *disk; - struct mmc_queue queue; - - unsigned int usage; - unsigned int block_bits; - unsigned int read_only; -}; - -static DEFINE_MUTEX(open_lock); - -static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) -{ - struct mmc_blk_data *md; - - mutex_lock(&open_lock); - md = disk->private_data; - if (md && md->usage == 0) - md = NULL; - if (md) - md->usage++; - mutex_unlock(&open_lock); - - return md; -} - -static void mmc_blk_put(struct mmc_blk_data *md) -{ - mutex_lock(&open_lock); - md->usage--; - if (md->usage == 0) { - put_disk(md->disk); - kfree(md); - } - mutex_unlock(&open_lock); -} - -static int mmc_blk_open(struct inode *inode, struct file *filp) -{ - struct mmc_blk_data *md; - int ret = -ENXIO; - - md = mmc_blk_get(inode->i_bdev->bd_disk); - if (md) { - if (md->usage == 2) - check_disk_change(inode->i_bdev); - ret = 0; - - if ((filp->f_mode & FMODE_WRITE) && md->read_only) - ret = -EROFS; - } - - return ret; -} - -static int mmc_blk_release(struct inode *inode, struct file *filp) -{ - struct mmc_blk_data *md = inode->i_bdev->bd_disk->private_data; - - mmc_blk_put(md); - return 0; -} - -static int -mmc_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo) -{ - geo->cylinders = get_capacity(bdev->bd_disk) / (4 * 16); - geo->heads = 4; - geo->sectors = 16; - return 0; -} - -static struct block_device_operations mmc_bdops = { - .open = mmc_blk_open, - .release = mmc_blk_release, - .getgeo = mmc_blk_getgeo, - .owner = THIS_MODULE, -}; - -struct mmc_blk_request { - struct mmc_request mrq; - struct mmc_command cmd; - struct mmc_command stop; - struct mmc_data data; -}; - -static int mmc_blk_prep_rq(struct mmc_queue *mq, struct request *req) -{ - struct mmc_blk_data *md = mq->data; - int stat = BLKPREP_OK; - - /* - * If we have no device, we haven't finished initialising. - */ - if (!md || !mq->card) { - printk(KERN_ERR "%s: killing request - no device/host\n", - req->rq_disk->disk_name); - stat = BLKPREP_KILL; - } - - return stat; -} - -static u32 mmc_sd_num_wr_blocks(struct mmc_card *card) -{ - int err; - u32 blocks; - - struct mmc_request mrq; - struct mmc_command cmd; - struct mmc_data data; - unsigned int timeout_us; - - struct scatterlist sg; - - memset(&cmd, 0, sizeof(struct mmc_command)); - - cmd.opcode = MMC_APP_CMD; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - - err = mmc_wait_for_cmd(card->host, &cmd, 0); - if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) - return (u32)-1; - - memset(&cmd, 0, sizeof(struct mmc_command)); - - cmd.opcode = SD_APP_SEND_NUM_WR_BLKS; - cmd.arg = 0; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - - memset(&data, 0, sizeof(struct mmc_data)); - - data.timeout_ns = card->csd.tacc_ns * 100; - data.timeout_clks = card->csd.tacc_clks * 100; - - timeout_us = data.timeout_ns / 1000; - timeout_us += data.timeout_clks * 1000 / - (card->host->ios.clock / 1000); - - if (timeout_us > 100000) { - data.timeout_ns = 100000000; - data.timeout_clks = 0; - } - - data.blksz = 4; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = &sg; - data.sg_len = 1; - - memset(&mrq, 0, sizeof(struct mmc_request)); - - mrq.cmd = &cmd; - mrq.data = &data; - - sg_init_one(&sg, &blocks, 4); - - mmc_wait_for_req(card->host, &mrq); - - if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) - return (u32)-1; - - blocks = ntohl(blocks); - - return blocks; -} - -static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) -{ - struct mmc_blk_data *md = mq->data; - struct mmc_card *card = md->queue.card; - struct mmc_blk_request brq; - int ret = 1, sg_pos, data_size; - - if (mmc_card_claim_host(card)) - goto flush_queue; - - do { - struct mmc_command cmd; - u32 readcmd, writecmd; - - memset(&brq, 0, sizeof(struct mmc_blk_request)); - brq.mrq.cmd = &brq.cmd; - brq.mrq.data = &brq.data; - - brq.cmd.arg = req->sector; - if (!mmc_card_blockaddr(card)) - brq.cmd.arg <<= 9; - brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - brq.data.blksz = 1 << md->block_bits; - brq.stop.opcode = MMC_STOP_TRANSMISSION; - brq.stop.arg = 0; - brq.stop.flags = MMC_RSP_R1B | MMC_CMD_AC; - brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); - if (brq.data.blocks > card->host->max_blk_count) - brq.data.blocks = card->host->max_blk_count; - - mmc_set_data_timeout(&brq.data, card, rq_data_dir(req) != READ); - - /* - * If the host doesn't support multiple block writes, force - * block writes to single block. SD cards are excepted from - * this rule as they support querying the number of - * successfully written sectors. - */ - if (rq_data_dir(req) != READ && - !(card->host->caps & MMC_CAP_MULTIWRITE) && - !mmc_card_sd(card)) - brq.data.blocks = 1; - - if (brq.data.blocks > 1) { - brq.data.flags |= MMC_DATA_MULTI; - brq.mrq.stop = &brq.stop; - readcmd = MMC_READ_MULTIPLE_BLOCK; - writecmd = MMC_WRITE_MULTIPLE_BLOCK; - } else { - brq.mrq.stop = NULL; - readcmd = MMC_READ_SINGLE_BLOCK; - writecmd = MMC_WRITE_BLOCK; - } - - if (rq_data_dir(req) == READ) { - brq.cmd.opcode = readcmd; - brq.data.flags |= MMC_DATA_READ; - } else { - brq.cmd.opcode = writecmd; - brq.data.flags |= MMC_DATA_WRITE; - } - - brq.data.sg = mq->sg; - brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg); - - if (brq.data.blocks != - (req->nr_sectors >> (md->block_bits - 9))) { - data_size = brq.data.blocks * brq.data.blksz; - for (sg_pos = 0; sg_pos < brq.data.sg_len; sg_pos++) { - data_size -= mq->sg[sg_pos].length; - if (data_size <= 0) { - mq->sg[sg_pos].length += data_size; - sg_pos++; - break; - } - } - brq.data.sg_len = sg_pos; - } - - mmc_wait_for_req(card->host, &brq.mrq); - if (brq.cmd.error) { - printk(KERN_ERR "%s: error %d sending read/write command\n", - req->rq_disk->disk_name, brq.cmd.error); - goto cmd_err; - } - - if (brq.data.error) { - printk(KERN_ERR "%s: error %d transferring data\n", - req->rq_disk->disk_name, brq.data.error); - goto cmd_err; - } - - if (brq.stop.error) { - printk(KERN_ERR "%s: error %d sending stop command\n", - req->rq_disk->disk_name, brq.stop.error); - goto cmd_err; - } - - if (rq_data_dir(req) != READ) { - do { - int err; - - cmd.opcode = MMC_SEND_STATUS; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - err = mmc_wait_for_cmd(card->host, &cmd, 5); - if (err) { - printk(KERN_ERR "%s: error %d requesting status\n", - req->rq_disk->disk_name, err); - goto cmd_err; - } - } while (!(cmd.resp[0] & R1_READY_FOR_DATA)); - -#if 0 - if (cmd.resp[0] & ~0x00000900) - printk(KERN_ERR "%s: status = %08x\n", - req->rq_disk->disk_name, cmd.resp[0]); - if (mmc_decode_status(cmd.resp)) - goto cmd_err; -#endif - } - - /* - * A block was successfully transferred. - */ - spin_lock_irq(&md->lock); - ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered); - if (!ret) { - /* - * The whole request completed successfully. - */ - add_disk_randomness(req->rq_disk); - blkdev_dequeue_request(req); - end_that_request_last(req, 1); - } - spin_unlock_irq(&md->lock); - } while (ret); - - mmc_card_release_host(card); - - return 1; - - cmd_err: - /* - * If this is an SD card and we're writing, we can first - * mark the known good sectors as ok. - * - * If the card is not SD, we can still ok written sectors - * if the controller can do proper error reporting. - * - * For reads we just fail the entire chunk as that should - * be safe in all cases. - */ - if (rq_data_dir(req) != READ && mmc_card_sd(card)) { - u32 blocks; - unsigned int bytes; - - blocks = mmc_sd_num_wr_blocks(card); - if (blocks != (u32)-1) { - if (card->csd.write_partial) - bytes = blocks << md->block_bits; - else - bytes = blocks << 9; - spin_lock_irq(&md->lock); - ret = end_that_request_chunk(req, 1, bytes); - spin_unlock_irq(&md->lock); - } - } else if (rq_data_dir(req) != READ && - (card->host->caps & MMC_CAP_MULTIWRITE)) { - spin_lock_irq(&md->lock); - ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered); - spin_unlock_irq(&md->lock); - } - -flush_queue: - - mmc_card_release_host(card); - - spin_lock_irq(&md->lock); - while (ret) { - ret = end_that_request_chunk(req, 0, - req->current_nr_sectors << 9); - } - - add_disk_randomness(req->rq_disk); - blkdev_dequeue_request(req); - end_that_request_last(req, 0); - spin_unlock_irq(&md->lock); - - return 0; -} - -#define MMC_NUM_MINORS (256 >> MMC_SHIFT) - -static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))]; - -static inline int mmc_blk_readonly(struct mmc_card *card) -{ - return mmc_card_readonly(card) || - !(card->csd.cmdclass & CCC_BLOCK_WRITE); -} - -static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) -{ - struct mmc_blk_data *md; - int devidx, ret; - - devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS); - if (devidx >= MMC_NUM_MINORS) - return ERR_PTR(-ENOSPC); - __set_bit(devidx, dev_use); - - md = kmalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); - if (!md) { - ret = -ENOMEM; - goto out; - } - - memset(md, 0, sizeof(struct mmc_blk_data)); - - /* - * Set the read-only status based on the supported commands - * and the write protect switch. - */ - md->read_only = mmc_blk_readonly(card); - - /* - * Both SD and MMC specifications state (although a bit - * unclearly in the MMC case) that a block size of 512 - * bytes must always be supported by the card. - */ - md->block_bits = 9; - - md->disk = alloc_disk(1 << MMC_SHIFT); - if (md->disk == NULL) { - ret = -ENOMEM; - goto err_kfree; - } - - spin_lock_init(&md->lock); - md->usage = 1; - - ret = mmc_init_queue(&md->queue, card, &md->lock); - if (ret) - goto err_putdisk; - - md->queue.prep_fn = mmc_blk_prep_rq; - md->queue.issue_fn = mmc_blk_issue_rq; - md->queue.data = md; - - md->disk->major = major; - md->disk->first_minor = devidx << MMC_SHIFT; - md->disk->fops = &mmc_bdops; - md->disk->private_data = md; - md->disk->queue = md->queue.queue; - md->disk->driverfs_dev = &card->dev; - - /* - * As discussed on lkml, GENHD_FL_REMOVABLE should: - * - * - be set for removable media with permanent block devices - * - be unset for removable block devices with permanent media - * - * Since MMC block devices clearly fall under the second - * case, we do not set GENHD_FL_REMOVABLE. Userspace - * should use the block device creation/destruction hotplug - * messages to tell when the card is present. - */ - - sprintf(md->disk->disk_name, "mmcblk%d", devidx); - - blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits); - - if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) { - /* - * The EXT_CSD sector count is in number or 512 byte - * sectors. - */ - set_capacity(md->disk, card->ext_csd.sectors); - } else { - /* - * The CSD capacity field is in units of read_blkbits. - * set_capacity takes units of 512 bytes. - */ - set_capacity(md->disk, - card->csd.capacity << (card->csd.read_blkbits - 9)); - } - return md; - - err_putdisk: - put_disk(md->disk); - err_kfree: - kfree(md); - out: - return ERR_PTR(ret); -} - -static int -mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) -{ - struct mmc_command cmd; - int err; - - /* Block-addressed cards ignore MMC_SET_BLOCKLEN. */ - if (mmc_card_blockaddr(card)) - return 0; - - mmc_card_claim_host(card); - cmd.opcode = MMC_SET_BLOCKLEN; - cmd.arg = 1 << md->block_bits; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - err = mmc_wait_for_cmd(card->host, &cmd, 5); - mmc_card_release_host(card); - - if (err) { - printk(KERN_ERR "%s: unable to set block size to %d: %d\n", - md->disk->disk_name, cmd.arg, err); - return -EINVAL; - } - - return 0; -} - -static int mmc_blk_probe(struct mmc_card *card) -{ - struct mmc_blk_data *md; - int err; - - /* - * Check that the card supports the command class(es) we need. - */ - if (!(card->csd.cmdclass & CCC_BLOCK_READ)) - return -ENODEV; - - md = mmc_blk_alloc(card); - if (IS_ERR(md)) - return PTR_ERR(md); - - err = mmc_blk_set_blksize(md, card); - if (err) - goto out; - - printk(KERN_INFO "%s: %s %s %lluKiB %s\n", - md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), - (unsigned long long)(get_capacity(md->disk) >> 1), - md->read_only ? "(ro)" : ""); - - mmc_set_drvdata(card, md); - add_disk(md->disk); - return 0; - - out: - mmc_blk_put(md); - - return err; -} - -static void mmc_blk_remove(struct mmc_card *card) -{ - struct mmc_blk_data *md = mmc_get_drvdata(card); - - if (md) { - int devidx; - - /* Stop new requests from getting into the queue */ - del_gendisk(md->disk); - - /* Then flush out any already in there */ - mmc_cleanup_queue(&md->queue); - - devidx = md->disk->first_minor >> MMC_SHIFT; - __clear_bit(devidx, dev_use); - - mmc_blk_put(md); - } - mmc_set_drvdata(card, NULL); -} - -#ifdef CONFIG_PM -static int mmc_blk_suspend(struct mmc_card *card, pm_message_t state) -{ - struct mmc_blk_data *md = mmc_get_drvdata(card); - - if (md) { - mmc_queue_suspend(&md->queue); - } - return 0; -} - -static int mmc_blk_resume(struct mmc_card *card) -{ - struct mmc_blk_data *md = mmc_get_drvdata(card); - - if (md) { - mmc_blk_set_blksize(md, card); - mmc_queue_resume(&md->queue); - } - return 0; -} -#else -#define mmc_blk_suspend NULL -#define mmc_blk_resume NULL -#endif - -static struct mmc_driver mmc_driver = { - .drv = { - .name = "mmcblk", - }, - .probe = mmc_blk_probe, - .remove = mmc_blk_remove, - .suspend = mmc_blk_suspend, - .resume = mmc_blk_resume, -}; - -static int __init mmc_blk_init(void) -{ - int res = -ENOMEM; - - res = register_blkdev(major, "mmc"); - if (res < 0) { - printk(KERN_WARNING "Unable to get major %d for MMC media: %d\n", - major, res); - goto out; - } - if (major == 0) - major = res; - - return mmc_register_driver(&mmc_driver); - - out: - return res; -} - -static void __exit mmc_blk_exit(void) -{ - mmc_unregister_driver(&mmc_driver); - unregister_blkdev(major, "mmc"); -} - -module_init(mmc_blk_init); -module_exit(mmc_blk_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver"); - -module_param(major, int, 0444); -MODULE_PARM_DESC(major, "specify the major device number for MMC block driver"); diff --git a/drivers/mmc/mmc_queue.c b/drivers/mmc/mmc_queue.c deleted file mode 100644 index c27e42645cd..00000000000 --- a/drivers/mmc/mmc_queue.c +++ /dev/null @@ -1,250 +0,0 @@ -/* - * linux/drivers/mmc/mmc_queue.c - * - * Copyright (C) 2003 Russell King, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ -#include -#include -#include - -#include -#include -#include "mmc_queue.h" - -#define MMC_QUEUE_SUSPENDED (1 << 0) - -/* - * Prepare a MMC request. Essentially, this means passing the - * preparation off to the media driver. The media driver will - * create a mmc_io_request in req->special. - */ -static int mmc_prep_request(struct request_queue *q, struct request *req) -{ - struct mmc_queue *mq = q->queuedata; - int ret = BLKPREP_KILL; - - if (blk_special_request(req)) { - /* - * Special commands already have the command - * blocks already setup in req->special. - */ - BUG_ON(!req->special); - - ret = BLKPREP_OK; - } else if (blk_fs_request(req) || blk_pc_request(req)) { - /* - * Block I/O requests need translating according - * to the protocol. - */ - ret = mq->prep_fn(mq, req); - } else { - /* - * Everything else is invalid. - */ - blk_dump_rq_flags(req, "MMC bad request"); - } - - if (ret == BLKPREP_OK) - req->cmd_flags |= REQ_DONTPREP; - - return ret; -} - -static int mmc_queue_thread(void *d) -{ - struct mmc_queue *mq = d; - struct request_queue *q = mq->queue; - - /* - * Set iothread to ensure that we aren't put to sleep by - * the process freezing. We handle suspension ourselves. - */ - current->flags |= PF_MEMALLOC|PF_NOFREEZE; - - down(&mq->thread_sem); - do { - struct request *req = NULL; - - spin_lock_irq(q->queue_lock); - set_current_state(TASK_INTERRUPTIBLE); - if (!blk_queue_plugged(q)) - req = elv_next_request(q); - mq->req = req; - spin_unlock_irq(q->queue_lock); - - if (!req) { - if (kthread_should_stop()) { - set_current_state(TASK_RUNNING); - break; - } - up(&mq->thread_sem); - schedule(); - down(&mq->thread_sem); - continue; - } - set_current_state(TASK_RUNNING); - - mq->issue_fn(mq, req); - } while (1); - up(&mq->thread_sem); - - return 0; -} - -/* - * Generic MMC request handler. This is called for any queue on a - * particular host. When the host is not busy, we look for a request - * on any queue on this host, and attempt to issue it. This may - * not be the queue we were asked to process. - */ -static void mmc_request(request_queue_t *q) -{ - struct mmc_queue *mq = q->queuedata; - struct request *req; - int ret; - - if (!mq) { - printk(KERN_ERR "MMC: killing requests for dead queue\n"); - while ((req = elv_next_request(q)) != NULL) { - do { - ret = end_that_request_chunk(req, 0, - req->current_nr_sectors << 9); - } while (ret); - } - return; - } - - if (!mq->req) - wake_up_process(mq->thread); -} - -/** - * mmc_init_queue - initialise a queue structure. - * @mq: mmc queue - * @card: mmc card to attach this queue - * @lock: queue lock - * - * Initialise a MMC card request queue. - */ -int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock) -{ - struct mmc_host *host = card->host; - u64 limit = BLK_BOUNCE_HIGH; - int ret; - - if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask) - limit = *mmc_dev(host)->dma_mask; - - mq->card = card; - mq->queue = blk_init_queue(mmc_request, lock); - if (!mq->queue) - return -ENOMEM; - - blk_queue_prep_rq(mq->queue, mmc_prep_request); - blk_queue_bounce_limit(mq->queue, limit); - blk_queue_max_sectors(mq->queue, host->max_req_size / 512); - blk_queue_max_phys_segments(mq->queue, host->max_phys_segs); - blk_queue_max_hw_segments(mq->queue, host->max_hw_segs); - blk_queue_max_segment_size(mq->queue, host->max_seg_size); - - mq->queue->queuedata = mq; - mq->req = NULL; - - mq->sg = kmalloc(sizeof(struct scatterlist) * host->max_phys_segs, - GFP_KERNEL); - if (!mq->sg) { - ret = -ENOMEM; - goto cleanup_queue; - } - - init_MUTEX(&mq->thread_sem); - - mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd"); - if (IS_ERR(mq->thread)) { - ret = PTR_ERR(mq->thread); - goto free_sg; - } - - return 0; - - free_sg: - kfree(mq->sg); - mq->sg = NULL; - cleanup_queue: - blk_cleanup_queue(mq->queue); - return ret; -} -EXPORT_SYMBOL(mmc_init_queue); - -void mmc_cleanup_queue(struct mmc_queue *mq) -{ - request_queue_t *q = mq->queue; - unsigned long flags; - - /* Mark that we should start throwing out stragglers */ - spin_lock_irqsave(q->queue_lock, flags); - q->queuedata = NULL; - spin_unlock_irqrestore(q->queue_lock, flags); - - /* Then terminate our worker thread */ - kthread_stop(mq->thread); - - kfree(mq->sg); - mq->sg = NULL; - - blk_cleanup_queue(mq->queue); - - mq->card = NULL; -} -EXPORT_SYMBOL(mmc_cleanup_queue); - -/** - * mmc_queue_suspend - suspend a MMC request queue - * @mq: MMC queue to suspend - * - * Stop the block request queue, and wait for our thread to - * complete any outstanding requests. This ensures that we - * won't suspend while a request is being processed. - */ -void mmc_queue_suspend(struct mmc_queue *mq) -{ - request_queue_t *q = mq->queue; - unsigned long flags; - - if (!(mq->flags & MMC_QUEUE_SUSPENDED)) { - mq->flags |= MMC_QUEUE_SUSPENDED; - - spin_lock_irqsave(q->queue_lock, flags); - blk_stop_queue(q); - spin_unlock_irqrestore(q->queue_lock, flags); - - down(&mq->thread_sem); - } -} -EXPORT_SYMBOL(mmc_queue_suspend); - -/** - * mmc_queue_resume - resume a previously suspended MMC request queue - * @mq: MMC queue to resume - */ -void mmc_queue_resume(struct mmc_queue *mq) -{ - request_queue_t *q = mq->queue; - unsigned long flags; - - if (mq->flags & MMC_QUEUE_SUSPENDED) { - mq->flags &= ~MMC_QUEUE_SUSPENDED; - - up(&mq->thread_sem); - - spin_lock_irqsave(q->queue_lock, flags); - blk_start_queue(q); - spin_unlock_irqrestore(q->queue_lock, flags); - } -} -EXPORT_SYMBOL(mmc_queue_resume); diff --git a/drivers/mmc/mmc_queue.h b/drivers/mmc/mmc_queue.h deleted file mode 100644 index c9f139e764f..00000000000 --- a/drivers/mmc/mmc_queue.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef MMC_QUEUE_H -#define MMC_QUEUE_H - -struct request; -struct task_struct; - -struct mmc_queue { - struct mmc_card *card; - struct task_struct *thread; - struct semaphore thread_sem; - unsigned int flags; - struct request *req; - int (*prep_fn)(struct mmc_queue *, struct request *); - int (*issue_fn)(struct mmc_queue *, struct request *); - void *data; - struct request_queue *queue; - struct scatterlist *sg; -}; - -struct mmc_io_request { - struct request *rq; - int num; - struct mmc_command selcmd; /* mmc_queue private */ - struct mmc_command cmd[4]; /* max 4 commands */ -}; - -extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *); -extern void mmc_cleanup_queue(struct mmc_queue *); -extern void mmc_queue_suspend(struct mmc_queue *); -extern void mmc_queue_resume(struct mmc_queue *); - -#endif diff --git a/drivers/mmc/queue.c b/drivers/mmc/queue.c new file mode 100644 index 00000000000..aa75ac11a19 --- /dev/null +++ b/drivers/mmc/queue.c @@ -0,0 +1,249 @@ +/* + * linux/drivers/mmc/queue.c + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * Copyright 2006-2007 Pierre Ossman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include + +#include +#include +#include "queue.h" + +#define MMC_QUEUE_SUSPENDED (1 << 0) + +/* + * Prepare a MMC request. Essentially, this means passing the + * preparation off to the media driver. The media driver will + * create a mmc_io_request in req->special. + */ +static int mmc_prep_request(struct request_queue *q, struct request *req) +{ + struct mmc_queue *mq = q->queuedata; + int ret = BLKPREP_KILL; + + if (blk_special_request(req)) { + /* + * Special commands already have the command + * blocks already setup in req->special. + */ + BUG_ON(!req->special); + + ret = BLKPREP_OK; + } else if (blk_fs_request(req) || blk_pc_request(req)) { + /* + * Block I/O requests need translating according + * to the protocol. + */ + ret = mq->prep_fn(mq, req); + } else { + /* + * Everything else is invalid. + */ + blk_dump_rq_flags(req, "MMC bad request"); + } + + if (ret == BLKPREP_OK) + req->cmd_flags |= REQ_DONTPREP; + + return ret; +} + +static int mmc_queue_thread(void *d) +{ + struct mmc_queue *mq = d; + struct request_queue *q = mq->queue; + + /* + * Set iothread to ensure that we aren't put to sleep by + * the process freezing. We handle suspension ourselves. + */ + current->flags |= PF_MEMALLOC|PF_NOFREEZE; + + down(&mq->thread_sem); + do { + struct request *req = NULL; + + spin_lock_irq(q->queue_lock); + set_current_state(TASK_INTERRUPTIBLE); + if (!blk_queue_plugged(q)) + req = elv_next_request(q); + mq->req = req; + spin_unlock_irq(q->queue_lock); + + if (!req) { + if (kthread_should_stop()) { + set_current_state(TASK_RUNNING); + break; + } + up(&mq->thread_sem); + schedule(); + down(&mq->thread_sem); + continue; + } + set_current_state(TASK_RUNNING); + + mq->issue_fn(mq, req); + } while (1); + up(&mq->thread_sem); + + return 0; +} + +/* + * Generic MMC request handler. This is called for any queue on a + * particular host. When the host is not busy, we look for a request + * on any queue on this host, and attempt to issue it. This may + * not be the queue we were asked to process. + */ +static void mmc_request(request_queue_t *q) +{ + struct mmc_queue *mq = q->queuedata; + struct request *req; + int ret; + + if (!mq) { + printk(KERN_ERR "MMC: killing requests for dead queue\n"); + while ((req = elv_next_request(q)) != NULL) { + do { + ret = end_that_request_chunk(req, 0, + req->current_nr_sectors << 9); + } while (ret); + } + return; + } + + if (!mq->req) + wake_up_process(mq->thread); +} + +/** + * mmc_init_queue - initialise a queue structure. + * @mq: mmc queue + * @card: mmc card to attach this queue + * @lock: queue lock + * + * Initialise a MMC card request queue. + */ +int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock) +{ + struct mmc_host *host = card->host; + u64 limit = BLK_BOUNCE_HIGH; + int ret; + + if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask) + limit = *mmc_dev(host)->dma_mask; + + mq->card = card; + mq->queue = blk_init_queue(mmc_request, lock); + if (!mq->queue) + return -ENOMEM; + + blk_queue_prep_rq(mq->queue, mmc_prep_request); + blk_queue_bounce_limit(mq->queue, limit); + blk_queue_max_sectors(mq->queue, host->max_req_size / 512); + blk_queue_max_phys_segments(mq->queue, host->max_phys_segs); + blk_queue_max_hw_segments(mq->queue, host->max_hw_segs); + blk_queue_max_segment_size(mq->queue, host->max_seg_size); + + mq->queue->queuedata = mq; + mq->req = NULL; + + mq->sg = kmalloc(sizeof(struct scatterlist) * host->max_phys_segs, + GFP_KERNEL); + if (!mq->sg) { + ret = -ENOMEM; + goto cleanup_queue; + } + + init_MUTEX(&mq->thread_sem); + + mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd"); + if (IS_ERR(mq->thread)) { + ret = PTR_ERR(mq->thread); + goto free_sg; + } + + return 0; + + free_sg: + kfree(mq->sg); + mq->sg = NULL; + cleanup_queue: + blk_cleanup_queue(mq->queue); + return ret; +} + +void mmc_cleanup_queue(struct mmc_queue *mq) +{ + request_queue_t *q = mq->queue; + unsigned long flags; + + /* Mark that we should start throwing out stragglers */ + spin_lock_irqsave(q->queue_lock, flags); + q->queuedata = NULL; + spin_unlock_irqrestore(q->queue_lock, flags); + + /* Then terminate our worker thread */ + kthread_stop(mq->thread); + + kfree(mq->sg); + mq->sg = NULL; + + blk_cleanup_queue(mq->queue); + + mq->card = NULL; +} +EXPORT_SYMBOL(mmc_cleanup_queue); + +/** + * mmc_queue_suspend - suspend a MMC request queue + * @mq: MMC queue to suspend + * + * Stop the block request queue, and wait for our thread to + * complete any outstanding requests. This ensures that we + * won't suspend while a request is being processed. + */ +void mmc_queue_suspend(struct mmc_queue *mq) +{ + request_queue_t *q = mq->queue; + unsigned long flags; + + if (!(mq->flags & MMC_QUEUE_SUSPENDED)) { + mq->flags |= MMC_QUEUE_SUSPENDED; + + spin_lock_irqsave(q->queue_lock, flags); + blk_stop_queue(q); + spin_unlock_irqrestore(q->queue_lock, flags); + + down(&mq->thread_sem); + } +} + +/** + * mmc_queue_resume - resume a previously suspended MMC request queue + * @mq: MMC queue to resume + */ +void mmc_queue_resume(struct mmc_queue *mq) +{ + request_queue_t *q = mq->queue; + unsigned long flags; + + if (mq->flags & MMC_QUEUE_SUSPENDED) { + mq->flags &= ~MMC_QUEUE_SUSPENDED; + + up(&mq->thread_sem); + + spin_lock_irqsave(q->queue_lock, flags); + blk_start_queue(q); + spin_unlock_irqrestore(q->queue_lock, flags); + } +} + diff --git a/drivers/mmc/queue.h b/drivers/mmc/queue.h new file mode 100644 index 00000000000..c9f139e764f --- /dev/null +++ b/drivers/mmc/queue.h @@ -0,0 +1,32 @@ +#ifndef MMC_QUEUE_H +#define MMC_QUEUE_H + +struct request; +struct task_struct; + +struct mmc_queue { + struct mmc_card *card; + struct task_struct *thread; + struct semaphore thread_sem; + unsigned int flags; + struct request *req; + int (*prep_fn)(struct mmc_queue *, struct request *); + int (*issue_fn)(struct mmc_queue *, struct request *); + void *data; + struct request_queue *queue; + struct scatterlist *sg; +}; + +struct mmc_io_request { + struct request *rq; + int num; + struct mmc_command selcmd; /* mmc_queue private */ + struct mmc_command cmd[4]; /* max 4 commands */ +}; + +extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *); +extern void mmc_cleanup_queue(struct mmc_queue *); +extern void mmc_queue_suspend(struct mmc_queue *); +extern void mmc_queue_resume(struct mmc_queue *); + +#endif -- cgit v1.2.3 From 1c6a0718f0bfdab0d9b7da5f7b74f38a0058c03a Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 11 Feb 2007 19:57:36 +0100 Subject: mmc: Move host and card drivers to subdirs Clean up the drivers/mmc directory by moving card and host drivers into subdirectories. Signed-off-by: Pierre Ossman --- drivers/mmc/Kconfig | 106 +-- drivers/mmc/Makefile | 30 +- drivers/mmc/at91_mci.c | 1001 --------------------- drivers/mmc/au1xmmc.c | 1031 ---------------------- drivers/mmc/au1xmmc.h | 96 -- drivers/mmc/block.c | 668 -------------- drivers/mmc/card/Kconfig | 17 + drivers/mmc/card/Makefile | 11 + drivers/mmc/card/block.c | 668 ++++++++++++++ drivers/mmc/card/queue.c | 249 ++++++ drivers/mmc/card/queue.h | 32 + drivers/mmc/host/Kconfig | 103 +++ drivers/mmc/host/Makefile | 18 + drivers/mmc/host/at91_mci.c | 1001 +++++++++++++++++++++ drivers/mmc/host/au1xmmc.c | 1031 ++++++++++++++++++++++ drivers/mmc/host/au1xmmc.h | 96 ++ drivers/mmc/host/imxmmc.c | 1137 ++++++++++++++++++++++++ drivers/mmc/host/imxmmc.h | 67 ++ drivers/mmc/host/mmci.c | 702 +++++++++++++++ drivers/mmc/host/mmci.h | 179 ++++ drivers/mmc/host/omap.c | 1288 +++++++++++++++++++++++++++ drivers/mmc/host/pxamci.c | 616 +++++++++++++ drivers/mmc/host/pxamci.h | 124 +++ drivers/mmc/host/sdhci.c | 1539 ++++++++++++++++++++++++++++++++ drivers/mmc/host/sdhci.h | 210 +++++ drivers/mmc/host/tifm_sd.c | 1102 +++++++++++++++++++++++ drivers/mmc/host/wbsd.c | 2062 +++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/wbsd.h | 185 ++++ drivers/mmc/imxmmc.c | 1137 ------------------------ drivers/mmc/imxmmc.h | 67 -- drivers/mmc/mmci.c | 702 --------------- drivers/mmc/mmci.h | 179 ---- drivers/mmc/omap.c | 1288 --------------------------- drivers/mmc/pxamci.c | 616 ------------- drivers/mmc/pxamci.h | 124 --- drivers/mmc/queue.c | 249 ------ drivers/mmc/queue.h | 32 - drivers/mmc/sdhci.c | 1539 -------------------------------- drivers/mmc/sdhci.h | 210 ----- drivers/mmc/tifm_sd.c | 1102 ----------------------- drivers/mmc/wbsd.c | 2062 ------------------------------------------- drivers/mmc/wbsd.h | 185 ---- 42 files changed, 12446 insertions(+), 12415 deletions(-) delete mode 100644 drivers/mmc/at91_mci.c delete mode 100644 drivers/mmc/au1xmmc.c delete mode 100644 drivers/mmc/au1xmmc.h delete mode 100644 drivers/mmc/block.c create mode 100644 drivers/mmc/card/Kconfig create mode 100644 drivers/mmc/card/Makefile create mode 100644 drivers/mmc/card/block.c create mode 100644 drivers/mmc/card/queue.c create mode 100644 drivers/mmc/card/queue.h create mode 100644 drivers/mmc/host/Kconfig create mode 100644 drivers/mmc/host/Makefile create mode 100644 drivers/mmc/host/at91_mci.c create mode 100644 drivers/mmc/host/au1xmmc.c create mode 100644 drivers/mmc/host/au1xmmc.h create mode 100644 drivers/mmc/host/imxmmc.c create mode 100644 drivers/mmc/host/imxmmc.h create mode 100644 drivers/mmc/host/mmci.c create mode 100644 drivers/mmc/host/mmci.h create mode 100644 drivers/mmc/host/omap.c create mode 100644 drivers/mmc/host/pxamci.c create mode 100644 drivers/mmc/host/pxamci.h create mode 100644 drivers/mmc/host/sdhci.c create mode 100644 drivers/mmc/host/sdhci.h create mode 100644 drivers/mmc/host/tifm_sd.c create mode 100644 drivers/mmc/host/wbsd.c create mode 100644 drivers/mmc/host/wbsd.h delete mode 100644 drivers/mmc/imxmmc.c delete mode 100644 drivers/mmc/imxmmc.h delete mode 100644 drivers/mmc/mmci.c delete mode 100644 drivers/mmc/mmci.h delete mode 100644 drivers/mmc/omap.c delete mode 100644 drivers/mmc/pxamci.c delete mode 100644 drivers/mmc/pxamci.h delete mode 100644 drivers/mmc/queue.c delete mode 100644 drivers/mmc/queue.h delete mode 100644 drivers/mmc/sdhci.c delete mode 100644 drivers/mmc/sdhci.h delete mode 100644 drivers/mmc/tifm_sd.c delete mode 100644 drivers/mmc/wbsd.c delete mode 100644 drivers/mmc/wbsd.h diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 12af9c71876..c7d64c0187f 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -19,110 +19,8 @@ config MMC_DEBUG This is an option for use by developers; most people should say N here. This enables MMC core and driver debugging. -config MMC_BLOCK - tristate "MMC block device driver" - depends on MMC && BLOCK - default y - help - Say Y here to enable the MMC block device driver support. - This provides a block device driver, which you can use to - mount the filesystem. Almost everyone wishing MMC support - should say Y or M here. - -config MMC_ARMMMCI - tristate "ARM AMBA Multimedia Card Interface support" - depends on ARM_AMBA && MMC - help - This selects the ARM(R) AMBA(R) PrimeCell Multimedia Card - Interface (PL180 and PL181) support. If you have an ARM(R) - platform with a Multimedia Card slot, say Y or M here. - - If unsure, say N. - -config MMC_PXA - tristate "Intel PXA25x/26x/27x Multimedia Card Interface support" - depends on ARCH_PXA && MMC - help - This selects the Intel(R) PXA(R) Multimedia card Interface. - If you have a PXA(R) platform with a Multimedia Card slot, - say Y or M here. - - If unsure, say N. - -config MMC_SDHCI - tristate "Secure Digital Host Controller Interface support (EXPERIMENTAL)" - depends on PCI && MMC && EXPERIMENTAL - help - This select the generic Secure Digital Host Controller Interface. - It is used by manufacturers such as Texas Instruments(R), Ricoh(R) - and Toshiba(R). Most controllers found in laptops are of this type. - If you have a controller with this interface, say Y or M here. - - If unsure, say N. - -config MMC_OMAP - tristate "TI OMAP Multimedia Card Interface support" - depends on ARCH_OMAP && MMC - select TPS65010 if MACH_OMAP_H2 - help - This selects the TI OMAP Multimedia card Interface. - If you have an OMAP board with a Multimedia Card slot, - say Y or M here. - - If unsure, say N. - -config MMC_WBSD - tristate "Winbond W83L51xD SD/MMC Card Interface support" - depends on MMC && ISA_DMA_API - help - This selects the Winbond(R) W83L51xD Secure digital and - Multimedia card Interface. - If you have a machine with a integrated W83L518D or W83L519D - SD/MMC card reader, say Y or M here. - - If unsure, say N. - -config MMC_AU1X - tristate "Alchemy AU1XX0 MMC Card Interface support" - depends on MMC && SOC_AU1200 - help - This selects the AMD Alchemy(R) Multimedia card interface. - If you have a Alchemy platform with a MMC slot, say Y or M here. - - If unsure, say N. - -config MMC_AT91 - tristate "AT91 SD/MMC Card Interface support" - depends on ARCH_AT91 && MMC - help - This selects the AT91 MCI controller. - - If unsure, say N. - -config MMC_IMX - tristate "Motorola i.MX Multimedia Card Interface support" - depends on ARCH_IMX && MMC - help - This selects the Motorola i.MX Multimedia card Interface. - If you have a i.MX platform with a Multimedia Card slot, - say Y or M here. - - If unsure, say N. - -config MMC_TIFM_SD - tristate "TI Flash Media MMC/SD Interface support (EXPERIMENTAL)" - depends on MMC && EXPERIMENTAL && PCI - select TIFM_CORE - help - Say Y here if you want to be able to access MMC/SD cards with - the Texas Instruments(R) Flash Media card reader, found in many - laptops. - This option 'selects' (turns on, enables) 'TIFM_CORE', but you - probably also need appropriate card reader host adapter, such as - 'Misc devices: TI Flash Media PCI74xx/PCI76xx host adapter support - (TIFM_7XX1)'. +source "drivers/mmc/card/Kconfig" - To compile this driver as a module, choose M here: the - module will be called tifm_sd. +source "drivers/mmc/host/Kconfig" endmenu diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 9ef010a5160..4d2bdfeb8d7 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -2,32 +2,16 @@ # Makefile for the kernel mmc device drivers. # +ifeq ($(CONFIG_MMC_DEBUG),y) + EXTRA_CFLAGS += -DDEBUG +endif + # # Core # obj-$(CONFIG_MMC) += mmc_core.o +mmc_core-y := mmc.o mmc_sysfs.o -# -# Media drivers -# -obj-$(CONFIG_MMC_BLOCK) += mmc_block.o -mmc_block-objs := block.o queue.o +obj-$(CONFIG_MMC) += card/ +obj-$(CONFIG_MMC) += host/ -# -# Host drivers -# -obj-$(CONFIG_MMC_ARMMMCI) += mmci.o -obj-$(CONFIG_MMC_PXA) += pxamci.o -obj-$(CONFIG_MMC_IMX) += imxmmc.o -obj-$(CONFIG_MMC_SDHCI) += sdhci.o -obj-$(CONFIG_MMC_WBSD) += wbsd.o -obj-$(CONFIG_MMC_AU1X) += au1xmmc.o -obj-$(CONFIG_MMC_OMAP) += omap.o -obj-$(CONFIG_MMC_AT91) += at91_mci.o -obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o - -mmc_core-y := mmc.o mmc_sysfs.o - -ifeq ($(CONFIG_MMC_DEBUG),y) -EXTRA_CFLAGS += -DDEBUG -endif diff --git a/drivers/mmc/at91_mci.c b/drivers/mmc/at91_mci.c deleted file mode 100644 index e37943c314c..00000000000 --- a/drivers/mmc/at91_mci.c +++ /dev/null @@ -1,1001 +0,0 @@ -/* - * linux/drivers/mmc/at91_mci.c - ATMEL AT91 MCI Driver - * - * Copyright (C) 2005 Cougar Creek Computing Devices Ltd, All Rights Reserved - * - * Copyright (C) 2006 Malcolm Noyes - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* - This is the AT91 MCI driver that has been tested with both MMC cards - and SD-cards. Boards that support write protect are now supported. - The CCAT91SBC001 board does not support SD cards. - - The three entry points are at91_mci_request, at91_mci_set_ios - and at91_mci_get_ro. - - SET IOS - This configures the device to put it into the correct mode and clock speed - required. - - MCI REQUEST - MCI request processes the commands sent in the mmc_request structure. This - can consist of a processing command and a stop command in the case of - multiple block transfers. - - There are three main types of request, commands, reads and writes. - - Commands are straight forward. The command is submitted to the controller and - the request function returns. When the controller generates an interrupt to indicate - the command is finished, the response to the command are read and the mmc_request_done - function called to end the request. - - Reads and writes work in a similar manner to normal commands but involve the PDC (DMA) - controller to manage the transfers. - - A read is done from the controller directly to the scatterlist passed in from the request. - Due to a bug in the AT91RM9200 controller, when a read is completed, all the words are byte - swapped in the scatterlist buffers. AT91SAM926x are not affected by this bug. - - The sequence of read interrupts is: ENDRX, RXBUFF, CMDRDY - - A write is slightly different in that the bytes to write are read from the scatterlist - into a dma memory buffer (this is in case the source buffer should be read only). The - entire write buffer is then done from this single dma memory buffer. - - The sequence of write interrupts is: ENDTX, TXBUFE, NOTBUSY, CMDRDY - - GET RO - Gets the status of the write protect pin, if available. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#define DRIVER_NAME "at91_mci" - -#undef SUPPORT_4WIRE - -#define FL_SENT_COMMAND (1 << 0) -#define FL_SENT_STOP (1 << 1) - -#define AT91_MCI_ERRORS (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE \ - | AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE \ - | AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE) - -#define at91_mci_read(host, reg) __raw_readl((host)->baseaddr + (reg)) -#define at91_mci_write(host, reg, val) __raw_writel((val), (host)->baseaddr + (reg)) - - -/* - * Low level type for this driver - */ -struct at91mci_host -{ - struct mmc_host *mmc; - struct mmc_command *cmd; - struct mmc_request *request; - - void __iomem *baseaddr; - int irq; - - struct at91_mmc_data *board; - int present; - - struct clk *mci_clk; - - /* - * Flag indicating when the command has been sent. This is used to - * work out whether or not to send the stop - */ - unsigned int flags; - /* flag for current bus settings */ - u32 bus_mode; - - /* DMA buffer used for transmitting */ - unsigned int* buffer; - dma_addr_t physical_address; - unsigned int total_length; - - /* Latest in the scatterlist that has been enabled for transfer, but not freed */ - int in_use_index; - - /* Latest in the scatterlist that has been enabled for transfer */ - int transfer_index; -}; - -/* - * Copy from sg to a dma block - used for transfers - */ -static inline void at91mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data) -{ - unsigned int len, i, size; - unsigned *dmabuf = host->buffer; - - size = host->total_length; - len = data->sg_len; - - /* - * Just loop through all entries. Size might not - * be the entire list though so make sure that - * we do not transfer too much. - */ - for (i = 0; i < len; i++) { - struct scatterlist *sg; - int amount; - unsigned int *sgbuffer; - - sg = &data->sg[i]; - - sgbuffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset; - amount = min(size, sg->length); - size -= amount; - - if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ - int index; - - for (index = 0; index < (amount / 4); index++) - *dmabuf++ = swab32(sgbuffer[index]); - } - else - memcpy(dmabuf, sgbuffer, amount); - - kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ); - - if (size == 0) - break; - } - - /* - * Check that we didn't get a request to transfer - * more data than can fit into the SG list. - */ - BUG_ON(size != 0); -} - -/* - * Prepare a dma read - */ -static void at91mci_pre_dma_read(struct at91mci_host *host) -{ - int i; - struct scatterlist *sg; - struct mmc_command *cmd; - struct mmc_data *data; - - pr_debug("pre dma read\n"); - - cmd = host->cmd; - if (!cmd) { - pr_debug("no command\n"); - return; - } - - data = cmd->data; - if (!data) { - pr_debug("no data\n"); - return; - } - - for (i = 0; i < 2; i++) { - /* nothing left to transfer */ - if (host->transfer_index >= data->sg_len) { - pr_debug("Nothing left to transfer (index = %d)\n", host->transfer_index); - break; - } - - /* Check to see if this needs filling */ - if (i == 0) { - if (at91_mci_read(host, ATMEL_PDC_RCR) != 0) { - pr_debug("Transfer active in current\n"); - continue; - } - } - else { - if (at91_mci_read(host, ATMEL_PDC_RNCR) != 0) { - pr_debug("Transfer active in next\n"); - continue; - } - } - - /* Setup the next transfer */ - pr_debug("Using transfer index %d\n", host->transfer_index); - - sg = &data->sg[host->transfer_index++]; - pr_debug("sg = %p\n", sg); - - sg->dma_address = dma_map_page(NULL, sg->page, sg->offset, sg->length, DMA_FROM_DEVICE); - - pr_debug("dma address = %08X, length = %d\n", sg->dma_address, sg->length); - - if (i == 0) { - at91_mci_write(host, ATMEL_PDC_RPR, sg->dma_address); - at91_mci_write(host, ATMEL_PDC_RCR, sg->length / 4); - } - else { - at91_mci_write(host, ATMEL_PDC_RNPR, sg->dma_address); - at91_mci_write(host, ATMEL_PDC_RNCR, sg->length / 4); - } - } - - pr_debug("pre dma read done\n"); -} - -/* - * Handle after a dma read - */ -static void at91mci_post_dma_read(struct at91mci_host *host) -{ - struct mmc_command *cmd; - struct mmc_data *data; - - pr_debug("post dma read\n"); - - cmd = host->cmd; - if (!cmd) { - pr_debug("no command\n"); - return; - } - - data = cmd->data; - if (!data) { - pr_debug("no data\n"); - return; - } - - while (host->in_use_index < host->transfer_index) { - unsigned int *buffer; - - struct scatterlist *sg; - - pr_debug("finishing index %d\n", host->in_use_index); - - sg = &data->sg[host->in_use_index++]; - - pr_debug("Unmapping page %08X\n", sg->dma_address); - - dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE); - - /* Swap the contents of the buffer */ - buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset; - pr_debug("buffer = %p, length = %d\n", buffer, sg->length); - - data->bytes_xfered += sg->length; - - if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ - int index; - - for (index = 0; index < (sg->length / 4); index++) - buffer[index] = swab32(buffer[index]); - } - - kunmap_atomic(buffer, KM_BIO_SRC_IRQ); - flush_dcache_page(sg->page); - } - - /* Is there another transfer to trigger? */ - if (host->transfer_index < data->sg_len) - at91mci_pre_dma_read(host); - else { - at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); - at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); - } - - pr_debug("post dma read done\n"); -} - -/* - * Handle transmitted data - */ -static void at91_mci_handle_transmitted(struct at91mci_host *host) -{ - struct mmc_command *cmd; - struct mmc_data *data; - - pr_debug("Handling the transmit\n"); - - /* Disable the transfer */ - at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); - - /* Now wait for cmd ready */ - at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE); - at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); - - cmd = host->cmd; - if (!cmd) return; - - data = cmd->data; - if (!data) return; - - data->bytes_xfered = host->total_length; -} - -/* - * Enable the controller - */ -static void at91_mci_enable(struct at91mci_host *host) -{ - at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); - at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); - at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC); - at91_mci_write(host, AT91_MCI_MR, AT91_MCI_PDCMODE | 0x34a); - - /* use Slot A or B (only one at same time) */ - at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b); -} - -/* - * Disable the controller - */ -static void at91_mci_disable(struct at91mci_host *host) -{ - at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST); -} - -/* - * Send a command - * return the interrupts to enable - */ -static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd) -{ - unsigned int cmdr, mr; - unsigned int block_length; - struct mmc_data *data = cmd->data; - - unsigned int blocks; - unsigned int ier = 0; - - host->cmd = cmd; - - /* Not sure if this is needed */ -#if 0 - if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) { - pr_debug("Clearing timeout\n"); - at91_mci_write(host, AT91_MCI_ARGR, 0); - at91_mci_write(host, AT91_MCI_CMDR, AT91_MCI_OPDCMD); - while (!(at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_CMDRDY)) { - /* spin */ - pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR)); - } - } -#endif - cmdr = cmd->opcode; - - if (mmc_resp_type(cmd) == MMC_RSP_NONE) - cmdr |= AT91_MCI_RSPTYP_NONE; - else { - /* if a response is expected then allow maximum response latancy */ - cmdr |= AT91_MCI_MAXLAT; - /* set 136 bit response for R2, 48 bit response otherwise */ - if (mmc_resp_type(cmd) == MMC_RSP_R2) - cmdr |= AT91_MCI_RSPTYP_136; - else - cmdr |= AT91_MCI_RSPTYP_48; - } - - if (data) { - block_length = data->blksz; - blocks = data->blocks; - - /* always set data start - also set direction flag for read */ - if (data->flags & MMC_DATA_READ) - cmdr |= (AT91_MCI_TRDIR | AT91_MCI_TRCMD_START); - else if (data->flags & MMC_DATA_WRITE) - cmdr |= AT91_MCI_TRCMD_START; - - if (data->flags & MMC_DATA_STREAM) - cmdr |= AT91_MCI_TRTYP_STREAM; - if (data->flags & MMC_DATA_MULTI) - cmdr |= AT91_MCI_TRTYP_MULTIPLE; - } - else { - block_length = 0; - blocks = 0; - } - - if (cmd->opcode == MMC_STOP_TRANSMISSION) - cmdr |= AT91_MCI_TRCMD_STOP; - - if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) - cmdr |= AT91_MCI_OPDCMD; - - /* - * Set the arguments and send the command - */ - pr_debug("Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08X)\n", - cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(host, AT91_MCI_MR)); - - if (!data) { - at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS | ATMEL_PDC_RXTDIS); - at91_mci_write(host, ATMEL_PDC_RPR, 0); - at91_mci_write(host, ATMEL_PDC_RCR, 0); - at91_mci_write(host, ATMEL_PDC_RNPR, 0); - at91_mci_write(host, ATMEL_PDC_RNCR, 0); - at91_mci_write(host, ATMEL_PDC_TPR, 0); - at91_mci_write(host, ATMEL_PDC_TCR, 0); - at91_mci_write(host, ATMEL_PDC_TNPR, 0); - at91_mci_write(host, ATMEL_PDC_TNCR, 0); - - at91_mci_write(host, AT91_MCI_ARGR, cmd->arg); - at91_mci_write(host, AT91_MCI_CMDR, cmdr); - return AT91_MCI_CMDRDY; - } - - mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; /* zero block length and PDC mode */ - at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE); - - /* - * Disable the PDC controller - */ - at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); - - if (cmdr & AT91_MCI_TRCMD_START) { - data->bytes_xfered = 0; - host->transfer_index = 0; - host->in_use_index = 0; - if (cmdr & AT91_MCI_TRDIR) { - /* - * Handle a read - */ - host->buffer = NULL; - host->total_length = 0; - - at91mci_pre_dma_read(host); - ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */; - } - else { - /* - * Handle a write - */ - host->total_length = block_length * blocks; - host->buffer = dma_alloc_coherent(NULL, - host->total_length, - &host->physical_address, GFP_KERNEL); - - at91mci_sg_to_dma(host, data); - - pr_debug("Transmitting %d bytes\n", host->total_length); - - at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); - at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4); - ier = AT91_MCI_TXBUFE; - } - } - - /* - * Send the command and then enable the PDC - not the other way round as - * the data sheet says - */ - - at91_mci_write(host, AT91_MCI_ARGR, cmd->arg); - at91_mci_write(host, AT91_MCI_CMDR, cmdr); - - if (cmdr & AT91_MCI_TRCMD_START) { - if (cmdr & AT91_MCI_TRDIR) - at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN); - else - at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN); - } - return ier; -} - -/* - * Wait for a command to complete - */ -static void at91mci_process_command(struct at91mci_host *host, struct mmc_command *cmd) -{ - unsigned int ier; - - ier = at91_mci_send_command(host, cmd); - - pr_debug("setting ier to %08X\n", ier); - - /* Stop on errors or the required value */ - at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier); -} - -/* - * Process the next step in the request - */ -static void at91mci_process_next(struct at91mci_host *host) -{ - if (!(host->flags & FL_SENT_COMMAND)) { - host->flags |= FL_SENT_COMMAND; - at91mci_process_command(host, host->request->cmd); - } - else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) { - host->flags |= FL_SENT_STOP; - at91mci_process_command(host, host->request->stop); - } - else - mmc_request_done(host->mmc, host->request); -} - -/* - * Handle a command that has been completed - */ -static void at91mci_completed_command(struct at91mci_host *host) -{ - struct mmc_command *cmd = host->cmd; - unsigned int status; - - at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); - - cmd->resp[0] = at91_mci_read(host, AT91_MCI_RSPR(0)); - cmd->resp[1] = at91_mci_read(host, AT91_MCI_RSPR(1)); - cmd->resp[2] = at91_mci_read(host, AT91_MCI_RSPR(2)); - cmd->resp[3] = at91_mci_read(host, AT91_MCI_RSPR(3)); - - if (host->buffer) { - dma_free_coherent(NULL, host->total_length, host->buffer, host->physical_address); - host->buffer = NULL; - } - - status = at91_mci_read(host, AT91_MCI_SR); - - pr_debug("Status = %08X [%08X %08X %08X %08X]\n", - status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); - - if (status & (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE | - AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE | - AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)) { - if ((status & AT91_MCI_RCRCE) && - ((cmd->opcode == MMC_SEND_OP_COND) || (cmd->opcode == SD_APP_OP_COND))) { - cmd->error = MMC_ERR_NONE; - } - else { - if (status & (AT91_MCI_RTOE | AT91_MCI_DTOE)) - cmd->error = MMC_ERR_TIMEOUT; - else if (status & (AT91_MCI_RCRCE | AT91_MCI_DCRCE)) - cmd->error = MMC_ERR_BADCRC; - else if (status & (AT91_MCI_OVRE | AT91_MCI_UNRE)) - cmd->error = MMC_ERR_FIFO; - else - cmd->error = MMC_ERR_FAILED; - - pr_debug("Error detected and set to %d (cmd = %d, retries = %d)\n", - cmd->error, cmd->opcode, cmd->retries); - } - } - else - cmd->error = MMC_ERR_NONE; - - at91mci_process_next(host); -} - -/* - * Handle an MMC request - */ -static void at91_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) -{ - struct at91mci_host *host = mmc_priv(mmc); - host->request = mrq; - host->flags = 0; - - at91mci_process_next(host); -} - -/* - * Set the IOS - */ -static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -{ - int clkdiv; - struct at91mci_host *host = mmc_priv(mmc); - unsigned long at91_master_clock = clk_get_rate(host->mci_clk); - - host->bus_mode = ios->bus_mode; - - if (ios->clock == 0) { - /* Disable the MCI controller */ - at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS); - clkdiv = 0; - } - else { - /* Enable the MCI controller */ - at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); - - if ((at91_master_clock % (ios->clock * 2)) == 0) - clkdiv = ((at91_master_clock / ios->clock) / 2) - 1; - else - clkdiv = (at91_master_clock / ios->clock) / 2; - - pr_debug("clkdiv = %d. mcck = %ld\n", clkdiv, - at91_master_clock / (2 * (clkdiv + 1))); - } - if (ios->bus_width == MMC_BUS_WIDTH_4 && host->board->wire4) { - pr_debug("MMC: Setting controller bus width to 4\n"); - at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) | AT91_MCI_SDCBUS); - } - else { - pr_debug("MMC: Setting controller bus width to 1\n"); - at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); - } - - /* Set the clock divider */ - at91_mci_write(host, AT91_MCI_MR, (at91_mci_read(host, AT91_MCI_MR) & ~AT91_MCI_CLKDIV) | clkdiv); - - /* maybe switch power to the card */ - if (host->board->vcc_pin) { - switch (ios->power_mode) { - case MMC_POWER_OFF: - at91_set_gpio_value(host->board->vcc_pin, 0); - break; - case MMC_POWER_UP: - case MMC_POWER_ON: - at91_set_gpio_value(host->board->vcc_pin, 1); - break; - } - } -} - -/* - * Handle an interrupt - */ -static irqreturn_t at91_mci_irq(int irq, void *devid) -{ - struct at91mci_host *host = devid; - int completed = 0; - unsigned int int_status, int_mask; - - int_status = at91_mci_read(host, AT91_MCI_SR); - int_mask = at91_mci_read(host, AT91_MCI_IMR); - - pr_debug("MCI irq: status = %08X, %08X, %08X\n", int_status, int_mask, - int_status & int_mask); - - int_status = int_status & int_mask; - - if (int_status & AT91_MCI_ERRORS) { - completed = 1; - - if (int_status & AT91_MCI_UNRE) - pr_debug("MMC: Underrun error\n"); - if (int_status & AT91_MCI_OVRE) - pr_debug("MMC: Overrun error\n"); - if (int_status & AT91_MCI_DTOE) - pr_debug("MMC: Data timeout\n"); - if (int_status & AT91_MCI_DCRCE) - pr_debug("MMC: CRC error in data\n"); - if (int_status & AT91_MCI_RTOE) - pr_debug("MMC: Response timeout\n"); - if (int_status & AT91_MCI_RENDE) - pr_debug("MMC: Response end bit error\n"); - if (int_status & AT91_MCI_RCRCE) - pr_debug("MMC: Response CRC error\n"); - if (int_status & AT91_MCI_RDIRE) - pr_debug("MMC: Response direction error\n"); - if (int_status & AT91_MCI_RINDE) - pr_debug("MMC: Response index error\n"); - } else { - /* Only continue processing if no errors */ - - if (int_status & AT91_MCI_TXBUFE) { - pr_debug("TX buffer empty\n"); - at91_mci_handle_transmitted(host); - } - - if (int_status & AT91_MCI_RXBUFF) { - pr_debug("RX buffer full\n"); - at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY); - } - - if (int_status & AT91_MCI_ENDTX) - pr_debug("Transmit has ended\n"); - - if (int_status & AT91_MCI_ENDRX) { - pr_debug("Receive has ended\n"); - at91mci_post_dma_read(host); - } - - if (int_status & AT91_MCI_NOTBUSY) { - pr_debug("Card is ready\n"); - at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY); - } - - if (int_status & AT91_MCI_DTIP) - pr_debug("Data transfer in progress\n"); - - if (int_status & AT91_MCI_BLKE) - pr_debug("Block transfer has ended\n"); - - if (int_status & AT91_MCI_TXRDY) - pr_debug("Ready to transmit\n"); - - if (int_status & AT91_MCI_RXRDY) - pr_debug("Ready to receive\n"); - - if (int_status & AT91_MCI_CMDRDY) { - pr_debug("Command ready\n"); - completed = 1; - } - } - - if (completed) { - pr_debug("Completed command\n"); - at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); - at91mci_completed_command(host); - } else - at91_mci_write(host, AT91_MCI_IDR, int_status); - - return IRQ_HANDLED; -} - -static irqreturn_t at91_mmc_det_irq(int irq, void *_host) -{ - struct at91mci_host *host = _host; - int present = !at91_get_gpio_value(irq); - - /* - * we expect this irq on both insert and remove, - * and use a short delay to debounce. - */ - if (present != host->present) { - host->present = present; - pr_debug("%s: card %s\n", mmc_hostname(host->mmc), - present ? "insert" : "remove"); - if (!present) { - pr_debug("****** Resetting SD-card bus width ******\n"); - at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); - } - mmc_detect_change(host->mmc, msecs_to_jiffies(100)); - } - return IRQ_HANDLED; -} - -static int at91_mci_get_ro(struct mmc_host *mmc) -{ - int read_only = 0; - struct at91mci_host *host = mmc_priv(mmc); - - if (host->board->wp_pin) { - read_only = at91_get_gpio_value(host->board->wp_pin); - printk(KERN_WARNING "%s: card is %s\n", mmc_hostname(mmc), - (read_only ? "read-only" : "read-write") ); - } - else { - printk(KERN_WARNING "%s: host does not support reading read-only " - "switch. Assuming write-enable.\n", mmc_hostname(mmc)); - } - return read_only; -} - -static const struct mmc_host_ops at91_mci_ops = { - .request = at91_mci_request, - .set_ios = at91_mci_set_ios, - .get_ro = at91_mci_get_ro, -}; - -/* - * Probe for the device - */ -static int __init at91_mci_probe(struct platform_device *pdev) -{ - struct mmc_host *mmc; - struct at91mci_host *host; - struct resource *res; - int ret; - - pr_debug("Probe MCI devices\n"); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) - return -ENXIO; - - if (!request_mem_region(res->start, res->end - res->start + 1, DRIVER_NAME)) - return -EBUSY; - - mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev); - if (!mmc) { - pr_debug("Failed to allocate mmc host\n"); - release_mem_region(res->start, res->end - res->start + 1); - return -ENOMEM; - } - - mmc->ops = &at91_mci_ops; - mmc->f_min = 375000; - mmc->f_max = 25000000; - mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = MMC_CAP_BYTEBLOCK; - - mmc->max_blk_size = 4095; - mmc->max_blk_count = mmc->max_req_size; - - host = mmc_priv(mmc); - host->mmc = mmc; - host->buffer = NULL; - host->bus_mode = 0; - host->board = pdev->dev.platform_data; - if (host->board->wire4) { -#ifdef SUPPORT_4WIRE - mmc->caps |= MMC_CAP_4_BIT_DATA; -#else - printk("AT91 MMC: 4 wire bus mode not supported by this driver - using 1 wire\n"); -#endif - } - - /* - * Get Clock - */ - host->mci_clk = clk_get(&pdev->dev, "mci_clk"); - if (IS_ERR(host->mci_clk)) { - printk(KERN_ERR "AT91 MMC: no clock defined.\n"); - mmc_free_host(mmc); - release_mem_region(res->start, res->end - res->start + 1); - return -ENODEV; - } - - /* - * Map I/O region - */ - host->baseaddr = ioremap(res->start, res->end - res->start + 1); - if (!host->baseaddr) { - clk_put(host->mci_clk); - mmc_free_host(mmc); - release_mem_region(res->start, res->end - res->start + 1); - return -ENOMEM; - } - - /* - * Reset hardware - */ - clk_enable(host->mci_clk); /* Enable the peripheral clock */ - at91_mci_disable(host); - at91_mci_enable(host); - - /* - * Allocate the MCI interrupt - */ - host->irq = platform_get_irq(pdev, 0); - ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED, DRIVER_NAME, host); - if (ret) { - printk(KERN_ERR "AT91 MMC: Failed to request MCI interrupt\n"); - clk_disable(host->mci_clk); - clk_put(host->mci_clk); - mmc_free_host(mmc); - iounmap(host->baseaddr); - release_mem_region(res->start, res->end - res->start + 1); - return ret; - } - - platform_set_drvdata(pdev, mmc); - - /* - * Add host to MMC layer - */ - if (host->board->det_pin) - host->present = !at91_get_gpio_value(host->board->det_pin); - else - host->present = -1; - - mmc_add_host(mmc); - - /* - * monitor card insertion/removal if we can - */ - if (host->board->det_pin) { - ret = request_irq(host->board->det_pin, at91_mmc_det_irq, - 0, DRIVER_NAME, host); - if (ret) - printk(KERN_ERR "AT91 MMC: Couldn't allocate MMC detect irq\n"); - } - - pr_debug("Added MCI driver\n"); - - return 0; -} - -/* - * Remove a device - */ -static int __exit at91_mci_remove(struct platform_device *pdev) -{ - struct mmc_host *mmc = platform_get_drvdata(pdev); - struct at91mci_host *host; - struct resource *res; - - if (!mmc) - return -1; - - host = mmc_priv(mmc); - - if (host->present != -1) { - free_irq(host->board->det_pin, host); - cancel_delayed_work(&host->mmc->detect); - } - - at91_mci_disable(host); - mmc_remove_host(mmc); - free_irq(host->irq, host); - - clk_disable(host->mci_clk); /* Disable the peripheral clock */ - clk_put(host->mci_clk); - - iounmap(host->baseaddr); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, res->end - res->start + 1); - - mmc_free_host(mmc); - platform_set_drvdata(pdev, NULL); - pr_debug("MCI Removed\n"); - - return 0; -} - -#ifdef CONFIG_PM -static int at91_mci_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct mmc_host *mmc = platform_get_drvdata(pdev); - int ret = 0; - - if (mmc) - ret = mmc_suspend_host(mmc, state); - - return ret; -} - -static int at91_mci_resume(struct platform_device *pdev) -{ - struct mmc_host *mmc = platform_get_drvdata(pdev); - int ret = 0; - - if (mmc) - ret = mmc_resume_host(mmc); - - return ret; -} -#else -#define at91_mci_suspend NULL -#define at91_mci_resume NULL -#endif - -static struct platform_driver at91_mci_driver = { - .remove = __exit_p(at91_mci_remove), - .suspend = at91_mci_suspend, - .resume = at91_mci_resume, - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - }, -}; - -static int __init at91_mci_init(void) -{ - return platform_driver_probe(&at91_mci_driver, at91_mci_probe); -} - -static void __exit at91_mci_exit(void) -{ - platform_driver_unregister(&at91_mci_driver); -} - -module_init(at91_mci_init); -module_exit(at91_mci_exit); - -MODULE_DESCRIPTION("AT91 Multimedia Card Interface driver"); -MODULE_AUTHOR("Nick Randell"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c deleted file mode 100644 index b7156a4555b..00000000000 --- a/drivers/mmc/au1xmmc.c +++ /dev/null @@ -1,1031 +0,0 @@ -/* - * linux/drivers/mmc/au1xmmc.c - AU1XX0 MMC driver - * - * Copyright (c) 2005, Advanced Micro Devices, Inc. - * - * Developed with help from the 2.4.30 MMC AU1XXX controller including - * the following copyright notices: - * Copyright (c) 2003-2004 Embedded Edge, LLC. - * Portions Copyright (C) 2002 Embedix, Inc - * Copyright 2002 Hewlett-Packard Company - - * 2.6 version of this driver inspired by: - * (drivers/mmc/wbsd.c) Copyright (C) 2004-2005 Pierre Ossman, - * All Rights Reserved. - * (drivers/mmc/pxa.c) Copyright (C) 2003 Russell King, - * All Rights Reserved. - * - - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* Why is a timer used to detect insert events? - * - * From the AU1100 MMC application guide: - * If the Au1100-based design is intended to support both MultiMediaCards - * and 1- or 4-data bit SecureDigital cards, then the solution is to - * connect a weak (560KOhm) pull-up resistor to connector pin 1. - * In doing so, a MMC card never enters SPI-mode communications, - * but now the SecureDigital card-detect feature of CD/DAT3 is ineffective - * (the low to high transition will not occur). - * - * So we use the timer to check the status manually. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include "au1xmmc.h" - -#define DRIVER_NAME "au1xxx-mmc" - -/* Set this to enable special debugging macros */ - -#ifdef DEBUG -#define DBG(fmt, idx, args...) printk("au1xx(%d): DEBUG: " fmt, idx, ##args) -#else -#define DBG(fmt, idx, args...) -#endif - -const struct { - u32 iobase; - u32 tx_devid, rx_devid; - u16 bcsrpwr; - u16 bcsrstatus; - u16 wpstatus; -} au1xmmc_card_table[] = { - { SD0_BASE, DSCR_CMD0_SDMS_TX0, DSCR_CMD0_SDMS_RX0, - BCSR_BOARD_SD0PWR, BCSR_INT_SD0INSERT, BCSR_STATUS_SD0WP }, -#ifndef CONFIG_MIPS_DB1200 - { SD1_BASE, DSCR_CMD0_SDMS_TX1, DSCR_CMD0_SDMS_RX1, - BCSR_BOARD_DS1PWR, BCSR_INT_SD1INSERT, BCSR_STATUS_SD1WP } -#endif -}; - -#define AU1XMMC_CONTROLLER_COUNT \ - (sizeof(au1xmmc_card_table) / sizeof(au1xmmc_card_table[0])) - -/* This array stores pointers for the hosts (used by the IRQ handler) */ -struct au1xmmc_host *au1xmmc_hosts[AU1XMMC_CONTROLLER_COUNT]; -static int dma = 1; - -#ifdef MODULE -module_param(dma, bool, 0); -MODULE_PARM_DESC(dma, "Use DMA engine for data transfers (0 = disabled)"); -#endif - -static inline void IRQ_ON(struct au1xmmc_host *host, u32 mask) -{ - u32 val = au_readl(HOST_CONFIG(host)); - val |= mask; - au_writel(val, HOST_CONFIG(host)); - au_sync(); -} - -static inline void FLUSH_FIFO(struct au1xmmc_host *host) -{ - u32 val = au_readl(HOST_CONFIG2(host)); - - au_writel(val | SD_CONFIG2_FF, HOST_CONFIG2(host)); - au_sync_delay(1); - - /* SEND_STOP will turn off clock control - this re-enables it */ - val &= ~SD_CONFIG2_DF; - - au_writel(val, HOST_CONFIG2(host)); - au_sync(); -} - -static inline void IRQ_OFF(struct au1xmmc_host *host, u32 mask) -{ - u32 val = au_readl(HOST_CONFIG(host)); - val &= ~mask; - au_writel(val, HOST_CONFIG(host)); - au_sync(); -} - -static inline void SEND_STOP(struct au1xmmc_host *host) -{ - - /* We know the value of CONFIG2, so avoid a read we don't need */ - u32 mask = SD_CONFIG2_EN; - - WARN_ON(host->status != HOST_S_DATA); - host->status = HOST_S_STOP; - - au_writel(mask | SD_CONFIG2_DF, HOST_CONFIG2(host)); - au_sync(); - - /* Send the stop commmand */ - au_writel(STOP_CMD, HOST_CMD(host)); -} - -static void au1xmmc_set_power(struct au1xmmc_host *host, int state) -{ - - u32 val = au1xmmc_card_table[host->id].bcsrpwr; - - bcsr->board &= ~val; - if (state) bcsr->board |= val; - - au_sync_delay(1); -} - -static inline int au1xmmc_card_inserted(struct au1xmmc_host *host) -{ - return (bcsr->sig_status & au1xmmc_card_table[host->id].bcsrstatus) - ? 1 : 0; -} - -static int au1xmmc_card_readonly(struct mmc_host *mmc) -{ - struct au1xmmc_host *host = mmc_priv(mmc); - return (bcsr->status & au1xmmc_card_table[host->id].wpstatus) - ? 1 : 0; -} - -static void au1xmmc_finish_request(struct au1xmmc_host *host) -{ - - struct mmc_request *mrq = host->mrq; - - host->mrq = NULL; - host->flags &= HOST_F_ACTIVE; - - host->dma.len = 0; - host->dma.dir = 0; - - host->pio.index = 0; - host->pio.offset = 0; - host->pio.len = 0; - - host->status = HOST_S_IDLE; - - bcsr->disk_leds |= (1 << 8); - - mmc_request_done(host->mmc, mrq); -} - -static void au1xmmc_tasklet_finish(unsigned long param) -{ - struct au1xmmc_host *host = (struct au1xmmc_host *) param; - au1xmmc_finish_request(host); -} - -static int au1xmmc_send_command(struct au1xmmc_host *host, int wait, - struct mmc_command *cmd) -{ - - u32 mmccmd = (cmd->opcode << SD_CMD_CI_SHIFT); - - switch (mmc_resp_type(cmd)) { - case MMC_RSP_NONE: - break; - case MMC_RSP_R1: - mmccmd |= SD_CMD_RT_1; - break; - case MMC_RSP_R1B: - mmccmd |= SD_CMD_RT_1B; - break; - case MMC_RSP_R2: - mmccmd |= SD_CMD_RT_2; - break; - case MMC_RSP_R3: - mmccmd |= SD_CMD_RT_3; - break; - default: - printk(KERN_INFO "au1xmmc: unhandled response type %02x\n", - mmc_resp_type(cmd)); - return MMC_ERR_INVALID; - } - - switch(cmd->opcode) { - case MMC_READ_SINGLE_BLOCK: - case SD_APP_SEND_SCR: - mmccmd |= SD_CMD_CT_2; - break; - case MMC_READ_MULTIPLE_BLOCK: - mmccmd |= SD_CMD_CT_4; - break; - case MMC_WRITE_BLOCK: - mmccmd |= SD_CMD_CT_1; - break; - - case MMC_WRITE_MULTIPLE_BLOCK: - mmccmd |= SD_CMD_CT_3; - break; - case MMC_STOP_TRANSMISSION: - mmccmd |= SD_CMD_CT_7; - break; - } - - au_writel(cmd->arg, HOST_CMDARG(host)); - au_sync(); - - if (wait) - IRQ_OFF(host, SD_CONFIG_CR); - - au_writel((mmccmd | SD_CMD_GO), HOST_CMD(host)); - au_sync(); - - /* Wait for the command to go on the line */ - - while(1) { - if (!(au_readl(HOST_CMD(host)) & SD_CMD_GO)) - break; - } - - /* Wait for the command to come back */ - - if (wait) { - u32 status = au_readl(HOST_STATUS(host)); - - while(!(status & SD_STATUS_CR)) - status = au_readl(HOST_STATUS(host)); - - /* Clear the CR status */ - au_writel(SD_STATUS_CR, HOST_STATUS(host)); - - IRQ_ON(host, SD_CONFIG_CR); - } - - return MMC_ERR_NONE; -} - -static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status) -{ - - struct mmc_request *mrq = host->mrq; - struct mmc_data *data; - u32 crc; - - WARN_ON(host->status != HOST_S_DATA && host->status != HOST_S_STOP); - - if (host->mrq == NULL) - return; - - data = mrq->cmd->data; - - if (status == 0) - status = au_readl(HOST_STATUS(host)); - - /* The transaction is really over when the SD_STATUS_DB bit is clear */ - - while((host->flags & HOST_F_XMIT) && (status & SD_STATUS_DB)) - status = au_readl(HOST_STATUS(host)); - - data->error = MMC_ERR_NONE; - dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma.dir); - - /* Process any errors */ - - crc = (status & (SD_STATUS_WC | SD_STATUS_RC)); - if (host->flags & HOST_F_XMIT) - crc |= ((status & 0x07) == 0x02) ? 0 : 1; - - if (crc) - data->error = MMC_ERR_BADCRC; - - /* Clear the CRC bits */ - au_writel(SD_STATUS_WC | SD_STATUS_RC, HOST_STATUS(host)); - - data->bytes_xfered = 0; - - if (data->error == MMC_ERR_NONE) { - if (host->flags & HOST_F_DMA) { - u32 chan = DMA_CHANNEL(host); - - chan_tab_t *c = *((chan_tab_t **) chan); - au1x_dma_chan_t *cp = c->chan_ptr; - data->bytes_xfered = cp->ddma_bytecnt; - } - else - data->bytes_xfered = - (data->blocks * data->blksz) - - host->pio.len; - } - - au1xmmc_finish_request(host); -} - -static void au1xmmc_tasklet_data(unsigned long param) -{ - struct au1xmmc_host *host = (struct au1xmmc_host *) param; - - u32 status = au_readl(HOST_STATUS(host)); - au1xmmc_data_complete(host, status); -} - -#define AU1XMMC_MAX_TRANSFER 8 - -static void au1xmmc_send_pio(struct au1xmmc_host *host) -{ - - struct mmc_data *data = 0; - int sg_len, max, count = 0; - unsigned char *sg_ptr; - u32 status = 0; - struct scatterlist *sg; - - data = host->mrq->data; - - if (!(host->flags & HOST_F_XMIT)) - return; - - /* This is the pointer to the data buffer */ - sg = &data->sg[host->pio.index]; - sg_ptr = page_address(sg->page) + sg->offset + host->pio.offset; - - /* This is the space left inside the buffer */ - sg_len = data->sg[host->pio.index].length - host->pio.offset; - - /* Check to if we need less then the size of the sg_buffer */ - - max = (sg_len > host->pio.len) ? host->pio.len : sg_len; - if (max > AU1XMMC_MAX_TRANSFER) max = AU1XMMC_MAX_TRANSFER; - - for(count = 0; count < max; count++ ) { - unsigned char val; - - status = au_readl(HOST_STATUS(host)); - - if (!(status & SD_STATUS_TH)) - break; - - val = *sg_ptr++; - - au_writel((unsigned long) val, HOST_TXPORT(host)); - au_sync(); - } - - host->pio.len -= count; - host->pio.offset += count; - - if (count == sg_len) { - host->pio.index++; - host->pio.offset = 0; - } - - if (host->pio.len == 0) { - IRQ_OFF(host, SD_CONFIG_TH); - - if (host->flags & HOST_F_STOP) - SEND_STOP(host); - - tasklet_schedule(&host->data_task); - } -} - -static void au1xmmc_receive_pio(struct au1xmmc_host *host) -{ - - struct mmc_data *data = 0; - int sg_len = 0, max = 0, count = 0; - unsigned char *sg_ptr = 0; - u32 status = 0; - struct scatterlist *sg; - - data = host->mrq->data; - - if (!(host->flags & HOST_F_RECV)) - return; - - max = host->pio.len; - - if (host->pio.index < host->dma.len) { - sg = &data->sg[host->pio.index]; - sg_ptr = page_address(sg->page) + sg->offset + host->pio.offset; - - /* This is the space left inside the buffer */ - sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset; - - /* Check to if we need less then the size of the sg_buffer */ - if (sg_len < max) max = sg_len; - } - - if (max > AU1XMMC_MAX_TRANSFER) - max = AU1XMMC_MAX_TRANSFER; - - for(count = 0; count < max; count++ ) { - u32 val; - status = au_readl(HOST_STATUS(host)); - - if (!(status & SD_STATUS_NE)) - break; - - if (status & SD_STATUS_RC) { - DBG("RX CRC Error [%d + %d].\n", host->id, - host->pio.len, count); - break; - } - - if (status & SD_STATUS_RO) { - DBG("RX Overrun [%d + %d]\n", host->id, - host->pio.len, count); - break; - } - else if (status & SD_STATUS_RU) { - DBG("RX Underrun [%d + %d]\n", host->id, - host->pio.len, count); - break; - } - - val = au_readl(HOST_RXPORT(host)); - - if (sg_ptr) - *sg_ptr++ = (unsigned char) (val & 0xFF); - } - - host->pio.len -= count; - host->pio.offset += count; - - if (sg_len && count == sg_len) { - host->pio.index++; - host->pio.offset = 0; - } - - if (host->pio.len == 0) { - //IRQ_OFF(host, SD_CONFIG_RA | SD_CONFIG_RF); - IRQ_OFF(host, SD_CONFIG_NE); - - if (host->flags & HOST_F_STOP) - SEND_STOP(host); - - tasklet_schedule(&host->data_task); - } -} - -/* static void au1xmmc_cmd_complete - This is called when a command has been completed - grab the response - and check for errors. Then start the data transfer if it is indicated. -*/ - -static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status) -{ - - struct mmc_request *mrq = host->mrq; - struct mmc_command *cmd; - int trans; - - if (!host->mrq) - return; - - cmd = mrq->cmd; - cmd->error = MMC_ERR_NONE; - - if (cmd->flags & MMC_RSP_PRESENT) { - if (cmd->flags & MMC_RSP_136) { - u32 r[4]; - int i; - - r[0] = au_readl(host->iobase + SD_RESP3); - r[1] = au_readl(host->iobase + SD_RESP2); - r[2] = au_readl(host->iobase + SD_RESP1); - r[3] = au_readl(host->iobase + SD_RESP0); - - /* The CRC is omitted from the response, so really - * we only got 120 bytes, but the engine expects - * 128 bits, so we have to shift things up - */ - - for(i = 0; i < 4; i++) { - cmd->resp[i] = (r[i] & 0x00FFFFFF) << 8; - if (i != 3) - cmd->resp[i] |= (r[i + 1] & 0xFF000000) >> 24; - } - } else { - /* Techincally, we should be getting all 48 bits of - * the response (SD_RESP1 + SD_RESP2), but because - * our response omits the CRC, our data ends up - * being shifted 8 bits to the right. In this case, - * that means that the OSR data starts at bit 31, - * so we can just read RESP0 and return that - */ - cmd->resp[0] = au_readl(host->iobase + SD_RESP0); - } - } - - /* Figure out errors */ - - if (status & (SD_STATUS_SC | SD_STATUS_WC | SD_STATUS_RC)) - cmd->error = MMC_ERR_BADCRC; - - trans = host->flags & (HOST_F_XMIT | HOST_F_RECV); - - if (!trans || cmd->error != MMC_ERR_NONE) { - - IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA|SD_CONFIG_RF); - tasklet_schedule(&host->finish_task); - return; - } - - host->status = HOST_S_DATA; - - if (host->flags & HOST_F_DMA) { - u32 channel = DMA_CHANNEL(host); - - /* Start the DMA as soon as the buffer gets something in it */ - - if (host->flags & HOST_F_RECV) { - u32 mask = SD_STATUS_DB | SD_STATUS_NE; - - while((status & mask) != mask) - status = au_readl(HOST_STATUS(host)); - } - - au1xxx_dbdma_start(channel); - } -} - -static void au1xmmc_set_clock(struct au1xmmc_host *host, int rate) -{ - - unsigned int pbus = get_au1x00_speed(); - unsigned int divisor; - u32 config; - - /* From databook: - divisor = ((((cpuclock / sbus_divisor) / 2) / mmcclock) / 2) - 1 - */ - - pbus /= ((au_readl(SYS_POWERCTRL) & 0x3) + 2); - pbus /= 2; - - divisor = ((pbus / rate) / 2) - 1; - - config = au_readl(HOST_CONFIG(host)); - - config &= ~(SD_CONFIG_DIV); - config |= (divisor & SD_CONFIG_DIV) | SD_CONFIG_DE; - - au_writel(config, HOST_CONFIG(host)); - au_sync(); -} - -static int -au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data) -{ - - int datalen = data->blocks * data->blksz; - - if (dma != 0) - host->flags |= HOST_F_DMA; - - if (data->flags & MMC_DATA_READ) - host->flags |= HOST_F_RECV; - else - host->flags |= HOST_F_XMIT; - - if (host->mrq->stop) - host->flags |= HOST_F_STOP; - - host->dma.dir = DMA_BIDIRECTIONAL; - - host->dma.len = dma_map_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, host->dma.dir); - - if (host->dma.len == 0) - return MMC_ERR_TIMEOUT; - - au_writel(data->blksz - 1, HOST_BLKSIZE(host)); - - if (host->flags & HOST_F_DMA) { - int i; - u32 channel = DMA_CHANNEL(host); - - au1xxx_dbdma_stop(channel); - - for(i = 0; i < host->dma.len; i++) { - u32 ret = 0, flags = DDMA_FLAGS_NOIE; - struct scatterlist *sg = &data->sg[i]; - int sg_len = sg->length; - - int len = (datalen > sg_len) ? sg_len : datalen; - - if (i == host->dma.len - 1) - flags = DDMA_FLAGS_IE; - - if (host->flags & HOST_F_XMIT){ - ret = au1xxx_dbdma_put_source_flags(channel, - (void *) (page_address(sg->page) + - sg->offset), - len, flags); - } - else { - ret = au1xxx_dbdma_put_dest_flags(channel, - (void *) (page_address(sg->page) + - sg->offset), - len, flags); - } - - if (!ret) - goto dataerr; - - datalen -= len; - } - } - else { - host->pio.index = 0; - host->pio.offset = 0; - host->pio.len = datalen; - - if (host->flags & HOST_F_XMIT) - IRQ_ON(host, SD_CONFIG_TH); - else - IRQ_ON(host, SD_CONFIG_NE); - //IRQ_ON(host, SD_CONFIG_RA|SD_CONFIG_RF); - } - - return MMC_ERR_NONE; - - dataerr: - dma_unmap_sg(mmc_dev(host->mmc),data->sg,data->sg_len,host->dma.dir); - return MMC_ERR_TIMEOUT; -} - -/* static void au1xmmc_request - This actually starts a command or data transaction -*/ - -static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq) -{ - - struct au1xmmc_host *host = mmc_priv(mmc); - int ret = MMC_ERR_NONE; - - WARN_ON(irqs_disabled()); - WARN_ON(host->status != HOST_S_IDLE); - - host->mrq = mrq; - host->status = HOST_S_CMD; - - bcsr->disk_leds &= ~(1 << 8); - - if (mrq->data) { - FLUSH_FIFO(host); - ret = au1xmmc_prepare_data(host, mrq->data); - } - - if (ret == MMC_ERR_NONE) - ret = au1xmmc_send_command(host, 0, mrq->cmd); - - if (ret != MMC_ERR_NONE) { - mrq->cmd->error = ret; - au1xmmc_finish_request(host); - } -} - -static void au1xmmc_reset_controller(struct au1xmmc_host *host) -{ - - /* Apply the clock */ - au_writel(SD_ENABLE_CE, HOST_ENABLE(host)); - au_sync_delay(1); - - au_writel(SD_ENABLE_R | SD_ENABLE_CE, HOST_ENABLE(host)); - au_sync_delay(5); - - au_writel(~0, HOST_STATUS(host)); - au_sync(); - - au_writel(0, HOST_BLKSIZE(host)); - au_writel(0x001fffff, HOST_TIMEOUT(host)); - au_sync(); - - au_writel(SD_CONFIG2_EN, HOST_CONFIG2(host)); - au_sync(); - - au_writel(SD_CONFIG2_EN | SD_CONFIG2_FF, HOST_CONFIG2(host)); - au_sync_delay(1); - - au_writel(SD_CONFIG2_EN, HOST_CONFIG2(host)); - au_sync(); - - /* Configure interrupts */ - au_writel(AU1XMMC_INTERRUPTS, HOST_CONFIG(host)); - au_sync(); -} - - -static void au1xmmc_set_ios(struct mmc_host* mmc, struct mmc_ios* ios) -{ - struct au1xmmc_host *host = mmc_priv(mmc); - - if (ios->power_mode == MMC_POWER_OFF) - au1xmmc_set_power(host, 0); - else if (ios->power_mode == MMC_POWER_ON) { - au1xmmc_set_power(host, 1); - } - - if (ios->clock && ios->clock != host->clock) { - au1xmmc_set_clock(host, ios->clock); - host->clock = ios->clock; - } -} - -static void au1xmmc_dma_callback(int irq, void *dev_id) -{ - struct au1xmmc_host *host = (struct au1xmmc_host *) dev_id; - - /* Avoid spurious interrupts */ - - if (!host->mrq) - return; - - if (host->flags & HOST_F_STOP) - SEND_STOP(host); - - tasklet_schedule(&host->data_task); -} - -#define STATUS_TIMEOUT (SD_STATUS_RAT | SD_STATUS_DT) -#define STATUS_DATA_IN (SD_STATUS_NE) -#define STATUS_DATA_OUT (SD_STATUS_TH) - -static irqreturn_t au1xmmc_irq(int irq, void *dev_id) -{ - - u32 status; - int i, ret = 0; - - disable_irq(AU1100_SD_IRQ); - - for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) { - struct au1xmmc_host * host = au1xmmc_hosts[i]; - u32 handled = 1; - - status = au_readl(HOST_STATUS(host)); - - if (host->mrq && (status & STATUS_TIMEOUT)) { - if (status & SD_STATUS_RAT) - host->mrq->cmd->error = MMC_ERR_TIMEOUT; - - else if (status & SD_STATUS_DT) - host->mrq->data->error = MMC_ERR_TIMEOUT; - - /* In PIO mode, interrupts might still be enabled */ - IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH); - - //IRQ_OFF(host, SD_CONFIG_TH|SD_CONFIG_RA|SD_CONFIG_RF); - tasklet_schedule(&host->finish_task); - } -#if 0 - else if (status & SD_STATUS_DD) { - - /* Sometimes we get a DD before a NE in PIO mode */ - - if (!(host->flags & HOST_F_DMA) && - (status & SD_STATUS_NE)) - au1xmmc_receive_pio(host); - else { - au1xmmc_data_complete(host, status); - //tasklet_schedule(&host->data_task); - } - } -#endif - else if (status & (SD_STATUS_CR)) { - if (host->status == HOST_S_CMD) - au1xmmc_cmd_complete(host,status); - } - else if (!(host->flags & HOST_F_DMA)) { - if ((host->flags & HOST_F_XMIT) && - (status & STATUS_DATA_OUT)) - au1xmmc_send_pio(host); - else if ((host->flags & HOST_F_RECV) && - (status & STATUS_DATA_IN)) - au1xmmc_receive_pio(host); - } - else if (status & 0x203FBC70) { - DBG("Unhandled status %8.8x\n", host->id, status); - handled = 0; - } - - au_writel(status, HOST_STATUS(host)); - au_sync(); - - ret |= handled; - } - - enable_irq(AU1100_SD_IRQ); - return ret; -} - -static void au1xmmc_poll_event(unsigned long arg) -{ - struct au1xmmc_host *host = (struct au1xmmc_host *) arg; - - int card = au1xmmc_card_inserted(host); - int controller = (host->flags & HOST_F_ACTIVE) ? 1 : 0; - - if (card != controller) { - host->flags &= ~HOST_F_ACTIVE; - if (card) host->flags |= HOST_F_ACTIVE; - mmc_detect_change(host->mmc, 0); - } - - if (host->mrq != NULL) { - u32 status = au_readl(HOST_STATUS(host)); - DBG("PENDING - %8.8x\n", host->id, status); - } - - mod_timer(&host->timer, jiffies + AU1XMMC_DETECT_TIMEOUT); -} - -static dbdev_tab_t au1xmmc_mem_dbdev = -{ - DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 8, 0x00000000, 0, 0 -}; - -static void au1xmmc_init_dma(struct au1xmmc_host *host) -{ - - u32 rxchan, txchan; - - int txid = au1xmmc_card_table[host->id].tx_devid; - int rxid = au1xmmc_card_table[host->id].rx_devid; - - /* DSCR_CMD0_ALWAYS has a stride of 32 bits, we need a stride - of 8 bits. And since devices are shared, we need to create - our own to avoid freaking out other devices - */ - - int memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev); - - txchan = au1xxx_dbdma_chan_alloc(memid, txid, - au1xmmc_dma_callback, (void *) host); - - rxchan = au1xxx_dbdma_chan_alloc(rxid, memid, - au1xmmc_dma_callback, (void *) host); - - au1xxx_dbdma_set_devwidth(txchan, 8); - au1xxx_dbdma_set_devwidth(rxchan, 8); - - au1xxx_dbdma_ring_alloc(txchan, AU1XMMC_DESCRIPTOR_COUNT); - au1xxx_dbdma_ring_alloc(rxchan, AU1XMMC_DESCRIPTOR_COUNT); - - host->tx_chan = txchan; - host->rx_chan = rxchan; -} - -static const struct mmc_host_ops au1xmmc_ops = { - .request = au1xmmc_request, - .set_ios = au1xmmc_set_ios, - .get_ro = au1xmmc_card_readonly, -}; - -static int __devinit au1xmmc_probe(struct platform_device *pdev) -{ - - int i, ret = 0; - - /* THe interrupt is shared among all controllers */ - ret = request_irq(AU1100_SD_IRQ, au1xmmc_irq, IRQF_DISABLED, "MMC", 0); - - if (ret) { - printk(DRIVER_NAME "ERROR: Couldn't get int %d: %d\n", - AU1100_SD_IRQ, ret); - return -ENXIO; - } - - disable_irq(AU1100_SD_IRQ); - - for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) { - struct mmc_host *mmc = mmc_alloc_host(sizeof(struct au1xmmc_host), &pdev->dev); - struct au1xmmc_host *host = 0; - - if (!mmc) { - printk(DRIVER_NAME "ERROR: no mem for host %d\n", i); - au1xmmc_hosts[i] = 0; - continue; - } - - mmc->ops = &au1xmmc_ops; - - mmc->f_min = 450000; - mmc->f_max = 24000000; - - mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE; - mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT; - - mmc->max_blk_size = 2048; - mmc->max_blk_count = 512; - - mmc->ocr_avail = AU1XMMC_OCR; - - host = mmc_priv(mmc); - host->mmc = mmc; - - host->id = i; - host->iobase = au1xmmc_card_table[host->id].iobase; - host->clock = 0; - host->power_mode = MMC_POWER_OFF; - - host->flags = au1xmmc_card_inserted(host) ? HOST_F_ACTIVE : 0; - host->status = HOST_S_IDLE; - - init_timer(&host->timer); - - host->timer.function = au1xmmc_poll_event; - host->timer.data = (unsigned long) host; - host->timer.expires = jiffies + AU1XMMC_DETECT_TIMEOUT; - - tasklet_init(&host->data_task, au1xmmc_tasklet_data, - (unsigned long) host); - - tasklet_init(&host->finish_task, au1xmmc_tasklet_finish, - (unsigned long) host); - - spin_lock_init(&host->lock); - - if (dma != 0) - au1xmmc_init_dma(host); - - au1xmmc_reset_controller(host); - - mmc_add_host(mmc); - au1xmmc_hosts[i] = host; - - add_timer(&host->timer); - - printk(KERN_INFO DRIVER_NAME ": MMC Controller %d set up at %8.8X (mode=%s)\n", - host->id, host->iobase, dma ? "dma" : "pio"); - } - - enable_irq(AU1100_SD_IRQ); - - return 0; -} - -static int __devexit au1xmmc_remove(struct platform_device *pdev) -{ - - int i; - - disable_irq(AU1100_SD_IRQ); - - for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) { - struct au1xmmc_host *host = au1xmmc_hosts[i]; - if (!host) continue; - - tasklet_kill(&host->data_task); - tasklet_kill(&host->finish_task); - - del_timer_sync(&host->timer); - au1xmmc_set_power(host, 0); - - mmc_remove_host(host->mmc); - - au1xxx_dbdma_chan_free(host->tx_chan); - au1xxx_dbdma_chan_free(host->rx_chan); - - au_writel(0x0, HOST_ENABLE(host)); - au_sync(); - } - - free_irq(AU1100_SD_IRQ, 0); - return 0; -} - -static struct platform_driver au1xmmc_driver = { - .probe = au1xmmc_probe, - .remove = au1xmmc_remove, - .suspend = NULL, - .resume = NULL, - .driver = { - .name = DRIVER_NAME, - }, -}; - -static int __init au1xmmc_init(void) -{ - return platform_driver_register(&au1xmmc_driver); -} - -static void __exit au1xmmc_exit(void) -{ - platform_driver_unregister(&au1xmmc_driver); -} - -module_init(au1xmmc_init); -module_exit(au1xmmc_exit); - -#ifdef MODULE -MODULE_AUTHOR("Advanced Micro Devices, Inc"); -MODULE_DESCRIPTION("MMC/SD driver for the Alchemy Au1XXX"); -MODULE_LICENSE("GPL"); -#endif - diff --git a/drivers/mmc/au1xmmc.h b/drivers/mmc/au1xmmc.h deleted file mode 100644 index 341cbdf0bac..00000000000 --- a/drivers/mmc/au1xmmc.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef _AU1XMMC_H_ -#define _AU1XMMC_H_ - -/* Hardware definitions */ - -#define AU1XMMC_DESCRIPTOR_COUNT 1 -#define AU1XMMC_DESCRIPTOR_SIZE 2048 - -#define AU1XMMC_OCR ( MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 | \ - MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33 | \ - MMC_VDD_33_34 | MMC_VDD_34_35 | MMC_VDD_35_36) - -/* Easy access macros */ - -#define HOST_STATUS(h) ((h)->iobase + SD_STATUS) -#define HOST_CONFIG(h) ((h)->iobase + SD_CONFIG) -#define HOST_ENABLE(h) ((h)->iobase + SD_ENABLE) -#define HOST_TXPORT(h) ((h)->iobase + SD_TXPORT) -#define HOST_RXPORT(h) ((h)->iobase + SD_RXPORT) -#define HOST_CMDARG(h) ((h)->iobase + SD_CMDARG) -#define HOST_BLKSIZE(h) ((h)->iobase + SD_BLKSIZE) -#define HOST_CMD(h) ((h)->iobase + SD_CMD) -#define HOST_CONFIG2(h) ((h)->iobase + SD_CONFIG2) -#define HOST_TIMEOUT(h) ((h)->iobase + SD_TIMEOUT) -#define HOST_DEBUG(h) ((h)->iobase + SD_DEBUG) - -#define DMA_CHANNEL(h) \ - ( ((h)->flags & HOST_F_XMIT) ? (h)->tx_chan : (h)->rx_chan) - -/* This gives us a hard value for the stop command that we can write directly - * to the command register - */ - -#define STOP_CMD (SD_CMD_RT_1B|SD_CMD_CT_7|(0xC << SD_CMD_CI_SHIFT)|SD_CMD_GO) - -/* This is the set of interrupts that we configure by default */ - -#if 0 -#define AU1XMMC_INTERRUPTS (SD_CONFIG_SC | SD_CONFIG_DT | SD_CONFIG_DD | \ - SD_CONFIG_RAT | SD_CONFIG_CR | SD_CONFIG_I) -#endif - -#define AU1XMMC_INTERRUPTS (SD_CONFIG_SC | SD_CONFIG_DT | \ - SD_CONFIG_RAT | SD_CONFIG_CR | SD_CONFIG_I) -/* The poll event (looking for insert/remove events runs twice a second */ -#define AU1XMMC_DETECT_TIMEOUT (HZ/2) - -struct au1xmmc_host { - struct mmc_host *mmc; - struct mmc_request *mrq; - - u32 id; - - u32 flags; - u32 iobase; - u32 clock; - u32 bus_width; - u32 power_mode; - - int status; - - struct { - int len; - int dir; - } dma; - - struct { - int index; - int offset; - int len; - } pio; - - u32 tx_chan; - u32 rx_chan; - - struct timer_list timer; - struct tasklet_struct finish_task; - struct tasklet_struct data_task; - - spinlock_t lock; -}; - -/* Status flags used by the host structure */ - -#define HOST_F_XMIT 0x0001 -#define HOST_F_RECV 0x0002 -#define HOST_F_DMA 0x0010 -#define HOST_F_ACTIVE 0x0100 -#define HOST_F_STOP 0x1000 - -#define HOST_S_IDLE 0x0001 -#define HOST_S_CMD 0x0002 -#define HOST_S_DATA 0x0003 -#define HOST_S_STOP 0x0004 - -#endif diff --git a/drivers/mmc/block.c b/drivers/mmc/block.c deleted file mode 100644 index 8eba037a18e..00000000000 --- a/drivers/mmc/block.c +++ /dev/null @@ -1,668 +0,0 @@ -/* - * Block driver for media (i.e., flash cards) - * - * Copyright 2002 Hewlett-Packard Company - * Copyright 2005-2007 Pierre Ossman - * - * Use consistent with the GNU GPL is permitted, - * provided that this copyright notice is - * preserved in its entirety in all copies and derived works. - * - * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, - * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS - * FITNESS FOR ANY PARTICULAR PURPOSE. - * - * Many thanks to Alessandro Rubini and Jonathan Corbet! - * - * Author: Andrew Christian - * 28 May 2002 - */ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include "queue.h" - -/* - * max 8 partitions per card - */ -#define MMC_SHIFT 3 - -static int major; - -/* - * There is one mmc_blk_data per slot. - */ -struct mmc_blk_data { - spinlock_t lock; - struct gendisk *disk; - struct mmc_queue queue; - - unsigned int usage; - unsigned int block_bits; - unsigned int read_only; -}; - -static DEFINE_MUTEX(open_lock); - -static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) -{ - struct mmc_blk_data *md; - - mutex_lock(&open_lock); - md = disk->private_data; - if (md && md->usage == 0) - md = NULL; - if (md) - md->usage++; - mutex_unlock(&open_lock); - - return md; -} - -static void mmc_blk_put(struct mmc_blk_data *md) -{ - mutex_lock(&open_lock); - md->usage--; - if (md->usage == 0) { - put_disk(md->disk); - kfree(md); - } - mutex_unlock(&open_lock); -} - -static int mmc_blk_open(struct inode *inode, struct file *filp) -{ - struct mmc_blk_data *md; - int ret = -ENXIO; - - md = mmc_blk_get(inode->i_bdev->bd_disk); - if (md) { - if (md->usage == 2) - check_disk_change(inode->i_bdev); - ret = 0; - - if ((filp->f_mode & FMODE_WRITE) && md->read_only) - ret = -EROFS; - } - - return ret; -} - -static int mmc_blk_release(struct inode *inode, struct file *filp) -{ - struct mmc_blk_data *md = inode->i_bdev->bd_disk->private_data; - - mmc_blk_put(md); - return 0; -} - -static int -mmc_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo) -{ - geo->cylinders = get_capacity(bdev->bd_disk) / (4 * 16); - geo->heads = 4; - geo->sectors = 16; - return 0; -} - -static struct block_device_operations mmc_bdops = { - .open = mmc_blk_open, - .release = mmc_blk_release, - .getgeo = mmc_blk_getgeo, - .owner = THIS_MODULE, -}; - -struct mmc_blk_request { - struct mmc_request mrq; - struct mmc_command cmd; - struct mmc_command stop; - struct mmc_data data; -}; - -static int mmc_blk_prep_rq(struct mmc_queue *mq, struct request *req) -{ - struct mmc_blk_data *md = mq->data; - int stat = BLKPREP_OK; - - /* - * If we have no device, we haven't finished initialising. - */ - if (!md || !mq->card) { - printk(KERN_ERR "%s: killing request - no device/host\n", - req->rq_disk->disk_name); - stat = BLKPREP_KILL; - } - - return stat; -} - -static u32 mmc_sd_num_wr_blocks(struct mmc_card *card) -{ - int err; - u32 blocks; - - struct mmc_request mrq; - struct mmc_command cmd; - struct mmc_data data; - unsigned int timeout_us; - - struct scatterlist sg; - - memset(&cmd, 0, sizeof(struct mmc_command)); - - cmd.opcode = MMC_APP_CMD; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - - err = mmc_wait_for_cmd(card->host, &cmd, 0); - if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) - return (u32)-1; - - memset(&cmd, 0, sizeof(struct mmc_command)); - - cmd.opcode = SD_APP_SEND_NUM_WR_BLKS; - cmd.arg = 0; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - - memset(&data, 0, sizeof(struct mmc_data)); - - data.timeout_ns = card->csd.tacc_ns * 100; - data.timeout_clks = card->csd.tacc_clks * 100; - - timeout_us = data.timeout_ns / 1000; - timeout_us += data.timeout_clks * 1000 / - (card->host->ios.clock / 1000); - - if (timeout_us > 100000) { - data.timeout_ns = 100000000; - data.timeout_clks = 0; - } - - data.blksz = 4; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = &sg; - data.sg_len = 1; - - memset(&mrq, 0, sizeof(struct mmc_request)); - - mrq.cmd = &cmd; - mrq.data = &data; - - sg_init_one(&sg, &blocks, 4); - - mmc_wait_for_req(card->host, &mrq); - - if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) - return (u32)-1; - - blocks = ntohl(blocks); - - return blocks; -} - -static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) -{ - struct mmc_blk_data *md = mq->data; - struct mmc_card *card = md->queue.card; - struct mmc_blk_request brq; - int ret = 1, sg_pos, data_size; - - if (mmc_card_claim_host(card)) - goto flush_queue; - - do { - struct mmc_command cmd; - u32 readcmd, writecmd; - - memset(&brq, 0, sizeof(struct mmc_blk_request)); - brq.mrq.cmd = &brq.cmd; - brq.mrq.data = &brq.data; - - brq.cmd.arg = req->sector; - if (!mmc_card_blockaddr(card)) - brq.cmd.arg <<= 9; - brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - brq.data.blksz = 1 << md->block_bits; - brq.stop.opcode = MMC_STOP_TRANSMISSION; - brq.stop.arg = 0; - brq.stop.flags = MMC_RSP_R1B | MMC_CMD_AC; - brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); - if (brq.data.blocks > card->host->max_blk_count) - brq.data.blocks = card->host->max_blk_count; - - mmc_set_data_timeout(&brq.data, card, rq_data_dir(req) != READ); - - /* - * If the host doesn't support multiple block writes, force - * block writes to single block. SD cards are excepted from - * this rule as they support querying the number of - * successfully written sectors. - */ - if (rq_data_dir(req) != READ && - !(card->host->caps & MMC_CAP_MULTIWRITE) && - !mmc_card_sd(card)) - brq.data.blocks = 1; - - if (brq.data.blocks > 1) { - brq.data.flags |= MMC_DATA_MULTI; - brq.mrq.stop = &brq.stop; - readcmd = MMC_READ_MULTIPLE_BLOCK; - writecmd = MMC_WRITE_MULTIPLE_BLOCK; - } else { - brq.mrq.stop = NULL; - readcmd = MMC_READ_SINGLE_BLOCK; - writecmd = MMC_WRITE_BLOCK; - } - - if (rq_data_dir(req) == READ) { - brq.cmd.opcode = readcmd; - brq.data.flags |= MMC_DATA_READ; - } else { - brq.cmd.opcode = writecmd; - brq.data.flags |= MMC_DATA_WRITE; - } - - brq.data.sg = mq->sg; - brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg); - - if (brq.data.blocks != - (req->nr_sectors >> (md->block_bits - 9))) { - data_size = brq.data.blocks * brq.data.blksz; - for (sg_pos = 0; sg_pos < brq.data.sg_len; sg_pos++) { - data_size -= mq->sg[sg_pos].length; - if (data_size <= 0) { - mq->sg[sg_pos].length += data_size; - sg_pos++; - break; - } - } - brq.data.sg_len = sg_pos; - } - - mmc_wait_for_req(card->host, &brq.mrq); - if (brq.cmd.error) { - printk(KERN_ERR "%s: error %d sending read/write command\n", - req->rq_disk->disk_name, brq.cmd.error); - goto cmd_err; - } - - if (brq.data.error) { - printk(KERN_ERR "%s: error %d transferring data\n", - req->rq_disk->disk_name, brq.data.error); - goto cmd_err; - } - - if (brq.stop.error) { - printk(KERN_ERR "%s: error %d sending stop command\n", - req->rq_disk->disk_name, brq.stop.error); - goto cmd_err; - } - - if (rq_data_dir(req) != READ) { - do { - int err; - - cmd.opcode = MMC_SEND_STATUS; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - err = mmc_wait_for_cmd(card->host, &cmd, 5); - if (err) { - printk(KERN_ERR "%s: error %d requesting status\n", - req->rq_disk->disk_name, err); - goto cmd_err; - } - } while (!(cmd.resp[0] & R1_READY_FOR_DATA)); - -#if 0 - if (cmd.resp[0] & ~0x00000900) - printk(KERN_ERR "%s: status = %08x\n", - req->rq_disk->disk_name, cmd.resp[0]); - if (mmc_decode_status(cmd.resp)) - goto cmd_err; -#endif - } - - /* - * A block was successfully transferred. - */ - spin_lock_irq(&md->lock); - ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered); - if (!ret) { - /* - * The whole request completed successfully. - */ - add_disk_randomness(req->rq_disk); - blkdev_dequeue_request(req); - end_that_request_last(req, 1); - } - spin_unlock_irq(&md->lock); - } while (ret); - - mmc_card_release_host(card); - - return 1; - - cmd_err: - /* - * If this is an SD card and we're writing, we can first - * mark the known good sectors as ok. - * - * If the card is not SD, we can still ok written sectors - * if the controller can do proper error reporting. - * - * For reads we just fail the entire chunk as that should - * be safe in all cases. - */ - if (rq_data_dir(req) != READ && mmc_card_sd(card)) { - u32 blocks; - unsigned int bytes; - - blocks = mmc_sd_num_wr_blocks(card); - if (blocks != (u32)-1) { - if (card->csd.write_partial) - bytes = blocks << md->block_bits; - else - bytes = blocks << 9; - spin_lock_irq(&md->lock); - ret = end_that_request_chunk(req, 1, bytes); - spin_unlock_irq(&md->lock); - } - } else if (rq_data_dir(req) != READ && - (card->host->caps & MMC_CAP_MULTIWRITE)) { - spin_lock_irq(&md->lock); - ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered); - spin_unlock_irq(&md->lock); - } - -flush_queue: - - mmc_card_release_host(card); - - spin_lock_irq(&md->lock); - while (ret) { - ret = end_that_request_chunk(req, 0, - req->current_nr_sectors << 9); - } - - add_disk_randomness(req->rq_disk); - blkdev_dequeue_request(req); - end_that_request_last(req, 0); - spin_unlock_irq(&md->lock); - - return 0; -} - -#define MMC_NUM_MINORS (256 >> MMC_SHIFT) - -static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))]; - -static inline int mmc_blk_readonly(struct mmc_card *card) -{ - return mmc_card_readonly(card) || - !(card->csd.cmdclass & CCC_BLOCK_WRITE); -} - -static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) -{ - struct mmc_blk_data *md; - int devidx, ret; - - devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS); - if (devidx >= MMC_NUM_MINORS) - return ERR_PTR(-ENOSPC); - __set_bit(devidx, dev_use); - - md = kmalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); - if (!md) { - ret = -ENOMEM; - goto out; - } - - memset(md, 0, sizeof(struct mmc_blk_data)); - - /* - * Set the read-only status based on the supported commands - * and the write protect switch. - */ - md->read_only = mmc_blk_readonly(card); - - /* - * Both SD and MMC specifications state (although a bit - * unclearly in the MMC case) that a block size of 512 - * bytes must always be supported by the card. - */ - md->block_bits = 9; - - md->disk = alloc_disk(1 << MMC_SHIFT); - if (md->disk == NULL) { - ret = -ENOMEM; - goto err_kfree; - } - - spin_lock_init(&md->lock); - md->usage = 1; - - ret = mmc_init_queue(&md->queue, card, &md->lock); - if (ret) - goto err_putdisk; - - md->queue.prep_fn = mmc_blk_prep_rq; - md->queue.issue_fn = mmc_blk_issue_rq; - md->queue.data = md; - - md->disk->major = major; - md->disk->first_minor = devidx << MMC_SHIFT; - md->disk->fops = &mmc_bdops; - md->disk->private_data = md; - md->disk->queue = md->queue.queue; - md->disk->driverfs_dev = &card->dev; - - /* - * As discussed on lkml, GENHD_FL_REMOVABLE should: - * - * - be set for removable media with permanent block devices - * - be unset for removable block devices with permanent media - * - * Since MMC block devices clearly fall under the second - * case, we do not set GENHD_FL_REMOVABLE. Userspace - * should use the block device creation/destruction hotplug - * messages to tell when the card is present. - */ - - sprintf(md->disk->disk_name, "mmcblk%d", devidx); - - blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits); - - if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) { - /* - * The EXT_CSD sector count is in number or 512 byte - * sectors. - */ - set_capacity(md->disk, card->ext_csd.sectors); - } else { - /* - * The CSD capacity field is in units of read_blkbits. - * set_capacity takes units of 512 bytes. - */ - set_capacity(md->disk, - card->csd.capacity << (card->csd.read_blkbits - 9)); - } - return md; - - err_putdisk: - put_disk(md->disk); - err_kfree: - kfree(md); - out: - return ERR_PTR(ret); -} - -static int -mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) -{ - struct mmc_command cmd; - int err; - - /* Block-addressed cards ignore MMC_SET_BLOCKLEN. */ - if (mmc_card_blockaddr(card)) - return 0; - - mmc_card_claim_host(card); - cmd.opcode = MMC_SET_BLOCKLEN; - cmd.arg = 1 << md->block_bits; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - err = mmc_wait_for_cmd(card->host, &cmd, 5); - mmc_card_release_host(card); - - if (err) { - printk(KERN_ERR "%s: unable to set block size to %d: %d\n", - md->disk->disk_name, cmd.arg, err); - return -EINVAL; - } - - return 0; -} - -static int mmc_blk_probe(struct mmc_card *card) -{ - struct mmc_blk_data *md; - int err; - - /* - * Check that the card supports the command class(es) we need. - */ - if (!(card->csd.cmdclass & CCC_BLOCK_READ)) - return -ENODEV; - - md = mmc_blk_alloc(card); - if (IS_ERR(md)) - return PTR_ERR(md); - - err = mmc_blk_set_blksize(md, card); - if (err) - goto out; - - printk(KERN_INFO "%s: %s %s %lluKiB %s\n", - md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), - (unsigned long long)(get_capacity(md->disk) >> 1), - md->read_only ? "(ro)" : ""); - - mmc_set_drvdata(card, md); - add_disk(md->disk); - return 0; - - out: - mmc_blk_put(md); - - return err; -} - -static void mmc_blk_remove(struct mmc_card *card) -{ - struct mmc_blk_data *md = mmc_get_drvdata(card); - - if (md) { - int devidx; - - /* Stop new requests from getting into the queue */ - del_gendisk(md->disk); - - /* Then flush out any already in there */ - mmc_cleanup_queue(&md->queue); - - devidx = md->disk->first_minor >> MMC_SHIFT; - __clear_bit(devidx, dev_use); - - mmc_blk_put(md); - } - mmc_set_drvdata(card, NULL); -} - -#ifdef CONFIG_PM -static int mmc_blk_suspend(struct mmc_card *card, pm_message_t state) -{ - struct mmc_blk_data *md = mmc_get_drvdata(card); - - if (md) { - mmc_queue_suspend(&md->queue); - } - return 0; -} - -static int mmc_blk_resume(struct mmc_card *card) -{ - struct mmc_blk_data *md = mmc_get_drvdata(card); - - if (md) { - mmc_blk_set_blksize(md, card); - mmc_queue_resume(&md->queue); - } - return 0; -} -#else -#define mmc_blk_suspend NULL -#define mmc_blk_resume NULL -#endif - -static struct mmc_driver mmc_driver = { - .drv = { - .name = "mmcblk", - }, - .probe = mmc_blk_probe, - .remove = mmc_blk_remove, - .suspend = mmc_blk_suspend, - .resume = mmc_blk_resume, -}; - -static int __init mmc_blk_init(void) -{ - int res = -ENOMEM; - - res = register_blkdev(major, "mmc"); - if (res < 0) { - printk(KERN_WARNING "Unable to get major %d for MMC media: %d\n", - major, res); - goto out; - } - if (major == 0) - major = res; - - return mmc_register_driver(&mmc_driver); - - out: - return res; -} - -static void __exit mmc_blk_exit(void) -{ - mmc_unregister_driver(&mmc_driver); - unregister_blkdev(major, "mmc"); -} - -module_init(mmc_blk_init); -module_exit(mmc_blk_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver"); - -module_param(major, int, 0444); -MODULE_PARM_DESC(major, "specify the major device number for MMC block driver"); diff --git a/drivers/mmc/card/Kconfig b/drivers/mmc/card/Kconfig new file mode 100644 index 00000000000..01a9fd376a1 --- /dev/null +++ b/drivers/mmc/card/Kconfig @@ -0,0 +1,17 @@ +# +# MMC/SD card drivers +# + +comment "MMC/SD Card Drivers" + depends MMC + +config MMC_BLOCK + tristate "MMC block device driver" + depends on MMC && BLOCK + default y + help + Say Y here to enable the MMC block device driver support. + This provides a block device driver, which you can use to + mount the filesystem. Almost everyone wishing MMC support + should say Y or M here. + diff --git a/drivers/mmc/card/Makefile b/drivers/mmc/card/Makefile new file mode 100644 index 00000000000..cf8c939867f --- /dev/null +++ b/drivers/mmc/card/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for MMC/SD card drivers +# + +ifeq ($(CONFIG_MMC_DEBUG),y) + EXTRA_CFLAGS += -DDEBUG +endif + +obj-$(CONFIG_MMC_BLOCK) += mmc_block.o +mmc_block-objs := block.o queue.o + diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c new file mode 100644 index 00000000000..8eba037a18e --- /dev/null +++ b/drivers/mmc/card/block.c @@ -0,0 +1,668 @@ +/* + * Block driver for media (i.e., flash cards) + * + * Copyright 2002 Hewlett-Packard Company + * Copyright 2005-2007 Pierre Ossman + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, + * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS + * FITNESS FOR ANY PARTICULAR PURPOSE. + * + * Many thanks to Alessandro Rubini and Jonathan Corbet! + * + * Author: Andrew Christian + * 28 May 2002 + */ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "queue.h" + +/* + * max 8 partitions per card + */ +#define MMC_SHIFT 3 + +static int major; + +/* + * There is one mmc_blk_data per slot. + */ +struct mmc_blk_data { + spinlock_t lock; + struct gendisk *disk; + struct mmc_queue queue; + + unsigned int usage; + unsigned int block_bits; + unsigned int read_only; +}; + +static DEFINE_MUTEX(open_lock); + +static struct mmc_blk_data *mmc_blk_get(struct gendisk *disk) +{ + struct mmc_blk_data *md; + + mutex_lock(&open_lock); + md = disk->private_data; + if (md && md->usage == 0) + md = NULL; + if (md) + md->usage++; + mutex_unlock(&open_lock); + + return md; +} + +static void mmc_blk_put(struct mmc_blk_data *md) +{ + mutex_lock(&open_lock); + md->usage--; + if (md->usage == 0) { + put_disk(md->disk); + kfree(md); + } + mutex_unlock(&open_lock); +} + +static int mmc_blk_open(struct inode *inode, struct file *filp) +{ + struct mmc_blk_data *md; + int ret = -ENXIO; + + md = mmc_blk_get(inode->i_bdev->bd_disk); + if (md) { + if (md->usage == 2) + check_disk_change(inode->i_bdev); + ret = 0; + + if ((filp->f_mode & FMODE_WRITE) && md->read_only) + ret = -EROFS; + } + + return ret; +} + +static int mmc_blk_release(struct inode *inode, struct file *filp) +{ + struct mmc_blk_data *md = inode->i_bdev->bd_disk->private_data; + + mmc_blk_put(md); + return 0; +} + +static int +mmc_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo) +{ + geo->cylinders = get_capacity(bdev->bd_disk) / (4 * 16); + geo->heads = 4; + geo->sectors = 16; + return 0; +} + +static struct block_device_operations mmc_bdops = { + .open = mmc_blk_open, + .release = mmc_blk_release, + .getgeo = mmc_blk_getgeo, + .owner = THIS_MODULE, +}; + +struct mmc_blk_request { + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_command stop; + struct mmc_data data; +}; + +static int mmc_blk_prep_rq(struct mmc_queue *mq, struct request *req) +{ + struct mmc_blk_data *md = mq->data; + int stat = BLKPREP_OK; + + /* + * If we have no device, we haven't finished initialising. + */ + if (!md || !mq->card) { + printk(KERN_ERR "%s: killing request - no device/host\n", + req->rq_disk->disk_name); + stat = BLKPREP_KILL; + } + + return stat; +} + +static u32 mmc_sd_num_wr_blocks(struct mmc_card *card) +{ + int err; + u32 blocks; + + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + unsigned int timeout_us; + + struct scatterlist sg; + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_APP_CMD; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, 0); + if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) + return (u32)-1; + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_APP_SEND_NUM_WR_BLKS; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + memset(&data, 0, sizeof(struct mmc_data)); + + data.timeout_ns = card->csd.tacc_ns * 100; + data.timeout_clks = card->csd.tacc_clks * 100; + + timeout_us = data.timeout_ns / 1000; + timeout_us += data.timeout_clks * 1000 / + (card->host->ios.clock / 1000); + + if (timeout_us > 100000) { + data.timeout_ns = 100000000; + data.timeout_clks = 0; + } + + data.blksz = 4; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + memset(&mrq, 0, sizeof(struct mmc_request)); + + mrq.cmd = &cmd; + mrq.data = &data; + + sg_init_one(&sg, &blocks, 4); + + mmc_wait_for_req(card->host, &mrq); + + if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) + return (u32)-1; + + blocks = ntohl(blocks); + + return blocks; +} + +static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) +{ + struct mmc_blk_data *md = mq->data; + struct mmc_card *card = md->queue.card; + struct mmc_blk_request brq; + int ret = 1, sg_pos, data_size; + + if (mmc_card_claim_host(card)) + goto flush_queue; + + do { + struct mmc_command cmd; + u32 readcmd, writecmd; + + memset(&brq, 0, sizeof(struct mmc_blk_request)); + brq.mrq.cmd = &brq.cmd; + brq.mrq.data = &brq.data; + + brq.cmd.arg = req->sector; + if (!mmc_card_blockaddr(card)) + brq.cmd.arg <<= 9; + brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + brq.data.blksz = 1 << md->block_bits; + brq.stop.opcode = MMC_STOP_TRANSMISSION; + brq.stop.arg = 0; + brq.stop.flags = MMC_RSP_R1B | MMC_CMD_AC; + brq.data.blocks = req->nr_sectors >> (md->block_bits - 9); + if (brq.data.blocks > card->host->max_blk_count) + brq.data.blocks = card->host->max_blk_count; + + mmc_set_data_timeout(&brq.data, card, rq_data_dir(req) != READ); + + /* + * If the host doesn't support multiple block writes, force + * block writes to single block. SD cards are excepted from + * this rule as they support querying the number of + * successfully written sectors. + */ + if (rq_data_dir(req) != READ && + !(card->host->caps & MMC_CAP_MULTIWRITE) && + !mmc_card_sd(card)) + brq.data.blocks = 1; + + if (brq.data.blocks > 1) { + brq.data.flags |= MMC_DATA_MULTI; + brq.mrq.stop = &brq.stop; + readcmd = MMC_READ_MULTIPLE_BLOCK; + writecmd = MMC_WRITE_MULTIPLE_BLOCK; + } else { + brq.mrq.stop = NULL; + readcmd = MMC_READ_SINGLE_BLOCK; + writecmd = MMC_WRITE_BLOCK; + } + + if (rq_data_dir(req) == READ) { + brq.cmd.opcode = readcmd; + brq.data.flags |= MMC_DATA_READ; + } else { + brq.cmd.opcode = writecmd; + brq.data.flags |= MMC_DATA_WRITE; + } + + brq.data.sg = mq->sg; + brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg); + + if (brq.data.blocks != + (req->nr_sectors >> (md->block_bits - 9))) { + data_size = brq.data.blocks * brq.data.blksz; + for (sg_pos = 0; sg_pos < brq.data.sg_len; sg_pos++) { + data_size -= mq->sg[sg_pos].length; + if (data_size <= 0) { + mq->sg[sg_pos].length += data_size; + sg_pos++; + break; + } + } + brq.data.sg_len = sg_pos; + } + + mmc_wait_for_req(card->host, &brq.mrq); + if (brq.cmd.error) { + printk(KERN_ERR "%s: error %d sending read/write command\n", + req->rq_disk->disk_name, brq.cmd.error); + goto cmd_err; + } + + if (brq.data.error) { + printk(KERN_ERR "%s: error %d transferring data\n", + req->rq_disk->disk_name, brq.data.error); + goto cmd_err; + } + + if (brq.stop.error) { + printk(KERN_ERR "%s: error %d sending stop command\n", + req->rq_disk->disk_name, brq.stop.error); + goto cmd_err; + } + + if (rq_data_dir(req) != READ) { + do { + int err; + + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + err = mmc_wait_for_cmd(card->host, &cmd, 5); + if (err) { + printk(KERN_ERR "%s: error %d requesting status\n", + req->rq_disk->disk_name, err); + goto cmd_err; + } + } while (!(cmd.resp[0] & R1_READY_FOR_DATA)); + +#if 0 + if (cmd.resp[0] & ~0x00000900) + printk(KERN_ERR "%s: status = %08x\n", + req->rq_disk->disk_name, cmd.resp[0]); + if (mmc_decode_status(cmd.resp)) + goto cmd_err; +#endif + } + + /* + * A block was successfully transferred. + */ + spin_lock_irq(&md->lock); + ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered); + if (!ret) { + /* + * The whole request completed successfully. + */ + add_disk_randomness(req->rq_disk); + blkdev_dequeue_request(req); + end_that_request_last(req, 1); + } + spin_unlock_irq(&md->lock); + } while (ret); + + mmc_card_release_host(card); + + return 1; + + cmd_err: + /* + * If this is an SD card and we're writing, we can first + * mark the known good sectors as ok. + * + * If the card is not SD, we can still ok written sectors + * if the controller can do proper error reporting. + * + * For reads we just fail the entire chunk as that should + * be safe in all cases. + */ + if (rq_data_dir(req) != READ && mmc_card_sd(card)) { + u32 blocks; + unsigned int bytes; + + blocks = mmc_sd_num_wr_blocks(card); + if (blocks != (u32)-1) { + if (card->csd.write_partial) + bytes = blocks << md->block_bits; + else + bytes = blocks << 9; + spin_lock_irq(&md->lock); + ret = end_that_request_chunk(req, 1, bytes); + spin_unlock_irq(&md->lock); + } + } else if (rq_data_dir(req) != READ && + (card->host->caps & MMC_CAP_MULTIWRITE)) { + spin_lock_irq(&md->lock); + ret = end_that_request_chunk(req, 1, brq.data.bytes_xfered); + spin_unlock_irq(&md->lock); + } + +flush_queue: + + mmc_card_release_host(card); + + spin_lock_irq(&md->lock); + while (ret) { + ret = end_that_request_chunk(req, 0, + req->current_nr_sectors << 9); + } + + add_disk_randomness(req->rq_disk); + blkdev_dequeue_request(req); + end_that_request_last(req, 0); + spin_unlock_irq(&md->lock); + + return 0; +} + +#define MMC_NUM_MINORS (256 >> MMC_SHIFT) + +static unsigned long dev_use[MMC_NUM_MINORS/(8*sizeof(unsigned long))]; + +static inline int mmc_blk_readonly(struct mmc_card *card) +{ + return mmc_card_readonly(card) || + !(card->csd.cmdclass & CCC_BLOCK_WRITE); +} + +static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card) +{ + struct mmc_blk_data *md; + int devidx, ret; + + devidx = find_first_zero_bit(dev_use, MMC_NUM_MINORS); + if (devidx >= MMC_NUM_MINORS) + return ERR_PTR(-ENOSPC); + __set_bit(devidx, dev_use); + + md = kmalloc(sizeof(struct mmc_blk_data), GFP_KERNEL); + if (!md) { + ret = -ENOMEM; + goto out; + } + + memset(md, 0, sizeof(struct mmc_blk_data)); + + /* + * Set the read-only status based on the supported commands + * and the write protect switch. + */ + md->read_only = mmc_blk_readonly(card); + + /* + * Both SD and MMC specifications state (although a bit + * unclearly in the MMC case) that a block size of 512 + * bytes must always be supported by the card. + */ + md->block_bits = 9; + + md->disk = alloc_disk(1 << MMC_SHIFT); + if (md->disk == NULL) { + ret = -ENOMEM; + goto err_kfree; + } + + spin_lock_init(&md->lock); + md->usage = 1; + + ret = mmc_init_queue(&md->queue, card, &md->lock); + if (ret) + goto err_putdisk; + + md->queue.prep_fn = mmc_blk_prep_rq; + md->queue.issue_fn = mmc_blk_issue_rq; + md->queue.data = md; + + md->disk->major = major; + md->disk->first_minor = devidx << MMC_SHIFT; + md->disk->fops = &mmc_bdops; + md->disk->private_data = md; + md->disk->queue = md->queue.queue; + md->disk->driverfs_dev = &card->dev; + + /* + * As discussed on lkml, GENHD_FL_REMOVABLE should: + * + * - be set for removable media with permanent block devices + * - be unset for removable block devices with permanent media + * + * Since MMC block devices clearly fall under the second + * case, we do not set GENHD_FL_REMOVABLE. Userspace + * should use the block device creation/destruction hotplug + * messages to tell when the card is present. + */ + + sprintf(md->disk->disk_name, "mmcblk%d", devidx); + + blk_queue_hardsect_size(md->queue.queue, 1 << md->block_bits); + + if (!mmc_card_sd(card) && mmc_card_blockaddr(card)) { + /* + * The EXT_CSD sector count is in number or 512 byte + * sectors. + */ + set_capacity(md->disk, card->ext_csd.sectors); + } else { + /* + * The CSD capacity field is in units of read_blkbits. + * set_capacity takes units of 512 bytes. + */ + set_capacity(md->disk, + card->csd.capacity << (card->csd.read_blkbits - 9)); + } + return md; + + err_putdisk: + put_disk(md->disk); + err_kfree: + kfree(md); + out: + return ERR_PTR(ret); +} + +static int +mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) +{ + struct mmc_command cmd; + int err; + + /* Block-addressed cards ignore MMC_SET_BLOCKLEN. */ + if (mmc_card_blockaddr(card)) + return 0; + + mmc_card_claim_host(card); + cmd.opcode = MMC_SET_BLOCKLEN; + cmd.arg = 1 << md->block_bits; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + err = mmc_wait_for_cmd(card->host, &cmd, 5); + mmc_card_release_host(card); + + if (err) { + printk(KERN_ERR "%s: unable to set block size to %d: %d\n", + md->disk->disk_name, cmd.arg, err); + return -EINVAL; + } + + return 0; +} + +static int mmc_blk_probe(struct mmc_card *card) +{ + struct mmc_blk_data *md; + int err; + + /* + * Check that the card supports the command class(es) we need. + */ + if (!(card->csd.cmdclass & CCC_BLOCK_READ)) + return -ENODEV; + + md = mmc_blk_alloc(card); + if (IS_ERR(md)) + return PTR_ERR(md); + + err = mmc_blk_set_blksize(md, card); + if (err) + goto out; + + printk(KERN_INFO "%s: %s %s %lluKiB %s\n", + md->disk->disk_name, mmc_card_id(card), mmc_card_name(card), + (unsigned long long)(get_capacity(md->disk) >> 1), + md->read_only ? "(ro)" : ""); + + mmc_set_drvdata(card, md); + add_disk(md->disk); + return 0; + + out: + mmc_blk_put(md); + + return err; +} + +static void mmc_blk_remove(struct mmc_card *card) +{ + struct mmc_blk_data *md = mmc_get_drvdata(card); + + if (md) { + int devidx; + + /* Stop new requests from getting into the queue */ + del_gendisk(md->disk); + + /* Then flush out any already in there */ + mmc_cleanup_queue(&md->queue); + + devidx = md->disk->first_minor >> MMC_SHIFT; + __clear_bit(devidx, dev_use); + + mmc_blk_put(md); + } + mmc_set_drvdata(card, NULL); +} + +#ifdef CONFIG_PM +static int mmc_blk_suspend(struct mmc_card *card, pm_message_t state) +{ + struct mmc_blk_data *md = mmc_get_drvdata(card); + + if (md) { + mmc_queue_suspend(&md->queue); + } + return 0; +} + +static int mmc_blk_resume(struct mmc_card *card) +{ + struct mmc_blk_data *md = mmc_get_drvdata(card); + + if (md) { + mmc_blk_set_blksize(md, card); + mmc_queue_resume(&md->queue); + } + return 0; +} +#else +#define mmc_blk_suspend NULL +#define mmc_blk_resume NULL +#endif + +static struct mmc_driver mmc_driver = { + .drv = { + .name = "mmcblk", + }, + .probe = mmc_blk_probe, + .remove = mmc_blk_remove, + .suspend = mmc_blk_suspend, + .resume = mmc_blk_resume, +}; + +static int __init mmc_blk_init(void) +{ + int res = -ENOMEM; + + res = register_blkdev(major, "mmc"); + if (res < 0) { + printk(KERN_WARNING "Unable to get major %d for MMC media: %d\n", + major, res); + goto out; + } + if (major == 0) + major = res; + + return mmc_register_driver(&mmc_driver); + + out: + return res; +} + +static void __exit mmc_blk_exit(void) +{ + mmc_unregister_driver(&mmc_driver); + unregister_blkdev(major, "mmc"); +} + +module_init(mmc_blk_init); +module_exit(mmc_blk_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver"); + +module_param(major, int, 0444); +MODULE_PARM_DESC(major, "specify the major device number for MMC block driver"); diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c new file mode 100644 index 00000000000..aa75ac11a19 --- /dev/null +++ b/drivers/mmc/card/queue.c @@ -0,0 +1,249 @@ +/* + * linux/drivers/mmc/queue.c + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * Copyright 2006-2007 Pierre Ossman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include + +#include +#include +#include "queue.h" + +#define MMC_QUEUE_SUSPENDED (1 << 0) + +/* + * Prepare a MMC request. Essentially, this means passing the + * preparation off to the media driver. The media driver will + * create a mmc_io_request in req->special. + */ +static int mmc_prep_request(struct request_queue *q, struct request *req) +{ + struct mmc_queue *mq = q->queuedata; + int ret = BLKPREP_KILL; + + if (blk_special_request(req)) { + /* + * Special commands already have the command + * blocks already setup in req->special. + */ + BUG_ON(!req->special); + + ret = BLKPREP_OK; + } else if (blk_fs_request(req) || blk_pc_request(req)) { + /* + * Block I/O requests need translating according + * to the protocol. + */ + ret = mq->prep_fn(mq, req); + } else { + /* + * Everything else is invalid. + */ + blk_dump_rq_flags(req, "MMC bad request"); + } + + if (ret == BLKPREP_OK) + req->cmd_flags |= REQ_DONTPREP; + + return ret; +} + +static int mmc_queue_thread(void *d) +{ + struct mmc_queue *mq = d; + struct request_queue *q = mq->queue; + + /* + * Set iothread to ensure that we aren't put to sleep by + * the process freezing. We handle suspension ourselves. + */ + current->flags |= PF_MEMALLOC|PF_NOFREEZE; + + down(&mq->thread_sem); + do { + struct request *req = NULL; + + spin_lock_irq(q->queue_lock); + set_current_state(TASK_INTERRUPTIBLE); + if (!blk_queue_plugged(q)) + req = elv_next_request(q); + mq->req = req; + spin_unlock_irq(q->queue_lock); + + if (!req) { + if (kthread_should_stop()) { + set_current_state(TASK_RUNNING); + break; + } + up(&mq->thread_sem); + schedule(); + down(&mq->thread_sem); + continue; + } + set_current_state(TASK_RUNNING); + + mq->issue_fn(mq, req); + } while (1); + up(&mq->thread_sem); + + return 0; +} + +/* + * Generic MMC request handler. This is called for any queue on a + * particular host. When the host is not busy, we look for a request + * on any queue on this host, and attempt to issue it. This may + * not be the queue we were asked to process. + */ +static void mmc_request(request_queue_t *q) +{ + struct mmc_queue *mq = q->queuedata; + struct request *req; + int ret; + + if (!mq) { + printk(KERN_ERR "MMC: killing requests for dead queue\n"); + while ((req = elv_next_request(q)) != NULL) { + do { + ret = end_that_request_chunk(req, 0, + req->current_nr_sectors << 9); + } while (ret); + } + return; + } + + if (!mq->req) + wake_up_process(mq->thread); +} + +/** + * mmc_init_queue - initialise a queue structure. + * @mq: mmc queue + * @card: mmc card to attach this queue + * @lock: queue lock + * + * Initialise a MMC card request queue. + */ +int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock) +{ + struct mmc_host *host = card->host; + u64 limit = BLK_BOUNCE_HIGH; + int ret; + + if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask) + limit = *mmc_dev(host)->dma_mask; + + mq->card = card; + mq->queue = blk_init_queue(mmc_request, lock); + if (!mq->queue) + return -ENOMEM; + + blk_queue_prep_rq(mq->queue, mmc_prep_request); + blk_queue_bounce_limit(mq->queue, limit); + blk_queue_max_sectors(mq->queue, host->max_req_size / 512); + blk_queue_max_phys_segments(mq->queue, host->max_phys_segs); + blk_queue_max_hw_segments(mq->queue, host->max_hw_segs); + blk_queue_max_segment_size(mq->queue, host->max_seg_size); + + mq->queue->queuedata = mq; + mq->req = NULL; + + mq->sg = kmalloc(sizeof(struct scatterlist) * host->max_phys_segs, + GFP_KERNEL); + if (!mq->sg) { + ret = -ENOMEM; + goto cleanup_queue; + } + + init_MUTEX(&mq->thread_sem); + + mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd"); + if (IS_ERR(mq->thread)) { + ret = PTR_ERR(mq->thread); + goto free_sg; + } + + return 0; + + free_sg: + kfree(mq->sg); + mq->sg = NULL; + cleanup_queue: + blk_cleanup_queue(mq->queue); + return ret; +} + +void mmc_cleanup_queue(struct mmc_queue *mq) +{ + request_queue_t *q = mq->queue; + unsigned long flags; + + /* Mark that we should start throwing out stragglers */ + spin_lock_irqsave(q->queue_lock, flags); + q->queuedata = NULL; + spin_unlock_irqrestore(q->queue_lock, flags); + + /* Then terminate our worker thread */ + kthread_stop(mq->thread); + + kfree(mq->sg); + mq->sg = NULL; + + blk_cleanup_queue(mq->queue); + + mq->card = NULL; +} +EXPORT_SYMBOL(mmc_cleanup_queue); + +/** + * mmc_queue_suspend - suspend a MMC request queue + * @mq: MMC queue to suspend + * + * Stop the block request queue, and wait for our thread to + * complete any outstanding requests. This ensures that we + * won't suspend while a request is being processed. + */ +void mmc_queue_suspend(struct mmc_queue *mq) +{ + request_queue_t *q = mq->queue; + unsigned long flags; + + if (!(mq->flags & MMC_QUEUE_SUSPENDED)) { + mq->flags |= MMC_QUEUE_SUSPENDED; + + spin_lock_irqsave(q->queue_lock, flags); + blk_stop_queue(q); + spin_unlock_irqrestore(q->queue_lock, flags); + + down(&mq->thread_sem); + } +} + +/** + * mmc_queue_resume - resume a previously suspended MMC request queue + * @mq: MMC queue to resume + */ +void mmc_queue_resume(struct mmc_queue *mq) +{ + request_queue_t *q = mq->queue; + unsigned long flags; + + if (mq->flags & MMC_QUEUE_SUSPENDED) { + mq->flags &= ~MMC_QUEUE_SUSPENDED; + + up(&mq->thread_sem); + + spin_lock_irqsave(q->queue_lock, flags); + blk_start_queue(q); + spin_unlock_irqrestore(q->queue_lock, flags); + } +} + diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h new file mode 100644 index 00000000000..c9f139e764f --- /dev/null +++ b/drivers/mmc/card/queue.h @@ -0,0 +1,32 @@ +#ifndef MMC_QUEUE_H +#define MMC_QUEUE_H + +struct request; +struct task_struct; + +struct mmc_queue { + struct mmc_card *card; + struct task_struct *thread; + struct semaphore thread_sem; + unsigned int flags; + struct request *req; + int (*prep_fn)(struct mmc_queue *, struct request *); + int (*issue_fn)(struct mmc_queue *, struct request *); + void *data; + struct request_queue *queue; + struct scatterlist *sg; +}; + +struct mmc_io_request { + struct request *rq; + int num; + struct mmc_command selcmd; /* mmc_queue private */ + struct mmc_command cmd[4]; /* max 4 commands */ +}; + +extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *); +extern void mmc_cleanup_queue(struct mmc_queue *); +extern void mmc_queue_suspend(struct mmc_queue *); +extern void mmc_queue_resume(struct mmc_queue *); + +#endif diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig new file mode 100644 index 00000000000..ed4deab2203 --- /dev/null +++ b/drivers/mmc/host/Kconfig @@ -0,0 +1,103 @@ +# +# MMC/SD host controller drivers +# + +comment "MMC/SD Host Controller Drivers" + depends on MMC + +config MMC_ARMMMCI + tristate "ARM AMBA Multimedia Card Interface support" + depends on ARM_AMBA && MMC + help + This selects the ARM(R) AMBA(R) PrimeCell Multimedia Card + Interface (PL180 and PL181) support. If you have an ARM(R) + platform with a Multimedia Card slot, say Y or M here. + + If unsure, say N. + +config MMC_PXA + tristate "Intel PXA25x/26x/27x Multimedia Card Interface support" + depends on ARCH_PXA && MMC + help + This selects the Intel(R) PXA(R) Multimedia card Interface. + If you have a PXA(R) platform with a Multimedia Card slot, + say Y or M here. + + If unsure, say N. + +config MMC_SDHCI + tristate "Secure Digital Host Controller Interface support (EXPERIMENTAL)" + depends on PCI && MMC && EXPERIMENTAL + help + This select the generic Secure Digital Host Controller Interface. + It is used by manufacturers such as Texas Instruments(R), Ricoh(R) + and Toshiba(R). Most controllers found in laptops are of this type. + If you have a controller with this interface, say Y or M here. + + If unsure, say N. + +config MMC_OMAP + tristate "TI OMAP Multimedia Card Interface support" + depends on ARCH_OMAP && MMC + select TPS65010 if MACH_OMAP_H2 + help + This selects the TI OMAP Multimedia card Interface. + If you have an OMAP board with a Multimedia Card slot, + say Y or M here. + + If unsure, say N. + +config MMC_WBSD + tristate "Winbond W83L51xD SD/MMC Card Interface support" + depends on MMC && ISA_DMA_API + help + This selects the Winbond(R) W83L51xD Secure digital and + Multimedia card Interface. + If you have a machine with a integrated W83L518D or W83L519D + SD/MMC card reader, say Y or M here. + + If unsure, say N. + +config MMC_AU1X + tristate "Alchemy AU1XX0 MMC Card Interface support" + depends on MMC && SOC_AU1200 + help + This selects the AMD Alchemy(R) Multimedia card interface. + If you have a Alchemy platform with a MMC slot, say Y or M here. + + If unsure, say N. + +config MMC_AT91 + tristate "AT91 SD/MMC Card Interface support" + depends on ARCH_AT91 && MMC + help + This selects the AT91 MCI controller. + + If unsure, say N. + +config MMC_IMX + tristate "Motorola i.MX Multimedia Card Interface support" + depends on ARCH_IMX && MMC + help + This selects the Motorola i.MX Multimedia card Interface. + If you have a i.MX platform with a Multimedia Card slot, + say Y or M here. + + If unsure, say N. + +config MMC_TIFM_SD + tristate "TI Flash Media MMC/SD Interface support (EXPERIMENTAL)" + depends on MMC && EXPERIMENTAL && PCI + select TIFM_CORE + help + Say Y here if you want to be able to access MMC/SD cards with + the Texas Instruments(R) Flash Media card reader, found in many + laptops. + This option 'selects' (turns on, enables) 'TIFM_CORE', but you + probably also need appropriate card reader host adapter, such as + 'Misc devices: TI Flash Media PCI74xx/PCI76xx host adapter support + (TIFM_7XX1)'. + + To compile this driver as a module, choose M here: the + module will be called tifm_sd. + diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile new file mode 100644 index 00000000000..6685f64345b --- /dev/null +++ b/drivers/mmc/host/Makefile @@ -0,0 +1,18 @@ +# +# Makefile for MMC/SD host controller drivers +# + +ifeq ($(CONFIG_MMC_DEBUG),y) + EXTRA_CFLAGS += -DDEBUG +endif + +obj-$(CONFIG_MMC_ARMMMCI) += mmci.o +obj-$(CONFIG_MMC_PXA) += pxamci.o +obj-$(CONFIG_MMC_IMX) += imxmmc.o +obj-$(CONFIG_MMC_SDHCI) += sdhci.o +obj-$(CONFIG_MMC_WBSD) += wbsd.o +obj-$(CONFIG_MMC_AU1X) += au1xmmc.o +obj-$(CONFIG_MMC_OMAP) += omap.o +obj-$(CONFIG_MMC_AT91) += at91_mci.o +obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o + diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c new file mode 100644 index 00000000000..e37943c314c --- /dev/null +++ b/drivers/mmc/host/at91_mci.c @@ -0,0 +1,1001 @@ +/* + * linux/drivers/mmc/at91_mci.c - ATMEL AT91 MCI Driver + * + * Copyright (C) 2005 Cougar Creek Computing Devices Ltd, All Rights Reserved + * + * Copyright (C) 2006 Malcolm Noyes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + This is the AT91 MCI driver that has been tested with both MMC cards + and SD-cards. Boards that support write protect are now supported. + The CCAT91SBC001 board does not support SD cards. + + The three entry points are at91_mci_request, at91_mci_set_ios + and at91_mci_get_ro. + + SET IOS + This configures the device to put it into the correct mode and clock speed + required. + + MCI REQUEST + MCI request processes the commands sent in the mmc_request structure. This + can consist of a processing command and a stop command in the case of + multiple block transfers. + + There are three main types of request, commands, reads and writes. + + Commands are straight forward. The command is submitted to the controller and + the request function returns. When the controller generates an interrupt to indicate + the command is finished, the response to the command are read and the mmc_request_done + function called to end the request. + + Reads and writes work in a similar manner to normal commands but involve the PDC (DMA) + controller to manage the transfers. + + A read is done from the controller directly to the scatterlist passed in from the request. + Due to a bug in the AT91RM9200 controller, when a read is completed, all the words are byte + swapped in the scatterlist buffers. AT91SAM926x are not affected by this bug. + + The sequence of read interrupts is: ENDRX, RXBUFF, CMDRDY + + A write is slightly different in that the bytes to write are read from the scatterlist + into a dma memory buffer (this is in case the source buffer should be read only). The + entire write buffer is then done from this single dma memory buffer. + + The sequence of write interrupts is: ENDTX, TXBUFE, NOTBUSY, CMDRDY + + GET RO + Gets the status of the write protect pin, if available. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "at91_mci" + +#undef SUPPORT_4WIRE + +#define FL_SENT_COMMAND (1 << 0) +#define FL_SENT_STOP (1 << 1) + +#define AT91_MCI_ERRORS (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE \ + | AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE \ + | AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE) + +#define at91_mci_read(host, reg) __raw_readl((host)->baseaddr + (reg)) +#define at91_mci_write(host, reg, val) __raw_writel((val), (host)->baseaddr + (reg)) + + +/* + * Low level type for this driver + */ +struct at91mci_host +{ + struct mmc_host *mmc; + struct mmc_command *cmd; + struct mmc_request *request; + + void __iomem *baseaddr; + int irq; + + struct at91_mmc_data *board; + int present; + + struct clk *mci_clk; + + /* + * Flag indicating when the command has been sent. This is used to + * work out whether or not to send the stop + */ + unsigned int flags; + /* flag for current bus settings */ + u32 bus_mode; + + /* DMA buffer used for transmitting */ + unsigned int* buffer; + dma_addr_t physical_address; + unsigned int total_length; + + /* Latest in the scatterlist that has been enabled for transfer, but not freed */ + int in_use_index; + + /* Latest in the scatterlist that has been enabled for transfer */ + int transfer_index; +}; + +/* + * Copy from sg to a dma block - used for transfers + */ +static inline void at91mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data) +{ + unsigned int len, i, size; + unsigned *dmabuf = host->buffer; + + size = host->total_length; + len = data->sg_len; + + /* + * Just loop through all entries. Size might not + * be the entire list though so make sure that + * we do not transfer too much. + */ + for (i = 0; i < len; i++) { + struct scatterlist *sg; + int amount; + unsigned int *sgbuffer; + + sg = &data->sg[i]; + + sgbuffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset; + amount = min(size, sg->length); + size -= amount; + + if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ + int index; + + for (index = 0; index < (amount / 4); index++) + *dmabuf++ = swab32(sgbuffer[index]); + } + else + memcpy(dmabuf, sgbuffer, amount); + + kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ); + + if (size == 0) + break; + } + + /* + * Check that we didn't get a request to transfer + * more data than can fit into the SG list. + */ + BUG_ON(size != 0); +} + +/* + * Prepare a dma read + */ +static void at91mci_pre_dma_read(struct at91mci_host *host) +{ + int i; + struct scatterlist *sg; + struct mmc_command *cmd; + struct mmc_data *data; + + pr_debug("pre dma read\n"); + + cmd = host->cmd; + if (!cmd) { + pr_debug("no command\n"); + return; + } + + data = cmd->data; + if (!data) { + pr_debug("no data\n"); + return; + } + + for (i = 0; i < 2; i++) { + /* nothing left to transfer */ + if (host->transfer_index >= data->sg_len) { + pr_debug("Nothing left to transfer (index = %d)\n", host->transfer_index); + break; + } + + /* Check to see if this needs filling */ + if (i == 0) { + if (at91_mci_read(host, ATMEL_PDC_RCR) != 0) { + pr_debug("Transfer active in current\n"); + continue; + } + } + else { + if (at91_mci_read(host, ATMEL_PDC_RNCR) != 0) { + pr_debug("Transfer active in next\n"); + continue; + } + } + + /* Setup the next transfer */ + pr_debug("Using transfer index %d\n", host->transfer_index); + + sg = &data->sg[host->transfer_index++]; + pr_debug("sg = %p\n", sg); + + sg->dma_address = dma_map_page(NULL, sg->page, sg->offset, sg->length, DMA_FROM_DEVICE); + + pr_debug("dma address = %08X, length = %d\n", sg->dma_address, sg->length); + + if (i == 0) { + at91_mci_write(host, ATMEL_PDC_RPR, sg->dma_address); + at91_mci_write(host, ATMEL_PDC_RCR, sg->length / 4); + } + else { + at91_mci_write(host, ATMEL_PDC_RNPR, sg->dma_address); + at91_mci_write(host, ATMEL_PDC_RNCR, sg->length / 4); + } + } + + pr_debug("pre dma read done\n"); +} + +/* + * Handle after a dma read + */ +static void at91mci_post_dma_read(struct at91mci_host *host) +{ + struct mmc_command *cmd; + struct mmc_data *data; + + pr_debug("post dma read\n"); + + cmd = host->cmd; + if (!cmd) { + pr_debug("no command\n"); + return; + } + + data = cmd->data; + if (!data) { + pr_debug("no data\n"); + return; + } + + while (host->in_use_index < host->transfer_index) { + unsigned int *buffer; + + struct scatterlist *sg; + + pr_debug("finishing index %d\n", host->in_use_index); + + sg = &data->sg[host->in_use_index++]; + + pr_debug("Unmapping page %08X\n", sg->dma_address); + + dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE); + + /* Swap the contents of the buffer */ + buffer = kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset; + pr_debug("buffer = %p, length = %d\n", buffer, sg->length); + + data->bytes_xfered += sg->length; + + if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ + int index; + + for (index = 0; index < (sg->length / 4); index++) + buffer[index] = swab32(buffer[index]); + } + + kunmap_atomic(buffer, KM_BIO_SRC_IRQ); + flush_dcache_page(sg->page); + } + + /* Is there another transfer to trigger? */ + if (host->transfer_index < data->sg_len) + at91mci_pre_dma_read(host); + else { + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); + } + + pr_debug("post dma read done\n"); +} + +/* + * Handle transmitted data + */ +static void at91_mci_handle_transmitted(struct at91mci_host *host) +{ + struct mmc_command *cmd; + struct mmc_data *data; + + pr_debug("Handling the transmit\n"); + + /* Disable the transfer */ + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); + + /* Now wait for cmd ready */ + at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE); + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); + + cmd = host->cmd; + if (!cmd) return; + + data = cmd->data; + if (!data) return; + + data->bytes_xfered = host->total_length; +} + +/* + * Enable the controller + */ +static void at91_mci_enable(struct at91mci_host *host) +{ + at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); + at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); + at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC); + at91_mci_write(host, AT91_MCI_MR, AT91_MCI_PDCMODE | 0x34a); + + /* use Slot A or B (only one at same time) */ + at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b); +} + +/* + * Disable the controller + */ +static void at91_mci_disable(struct at91mci_host *host) +{ + at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS | AT91_MCI_SWRST); +} + +/* + * Send a command + * return the interrupts to enable + */ +static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd) +{ + unsigned int cmdr, mr; + unsigned int block_length; + struct mmc_data *data = cmd->data; + + unsigned int blocks; + unsigned int ier = 0; + + host->cmd = cmd; + + /* Not sure if this is needed */ +#if 0 + if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) { + pr_debug("Clearing timeout\n"); + at91_mci_write(host, AT91_MCI_ARGR, 0); + at91_mci_write(host, AT91_MCI_CMDR, AT91_MCI_OPDCMD); + while (!(at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_CMDRDY)) { + /* spin */ + pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR)); + } + } +#endif + cmdr = cmd->opcode; + + if (mmc_resp_type(cmd) == MMC_RSP_NONE) + cmdr |= AT91_MCI_RSPTYP_NONE; + else { + /* if a response is expected then allow maximum response latancy */ + cmdr |= AT91_MCI_MAXLAT; + /* set 136 bit response for R2, 48 bit response otherwise */ + if (mmc_resp_type(cmd) == MMC_RSP_R2) + cmdr |= AT91_MCI_RSPTYP_136; + else + cmdr |= AT91_MCI_RSPTYP_48; + } + + if (data) { + block_length = data->blksz; + blocks = data->blocks; + + /* always set data start - also set direction flag for read */ + if (data->flags & MMC_DATA_READ) + cmdr |= (AT91_MCI_TRDIR | AT91_MCI_TRCMD_START); + else if (data->flags & MMC_DATA_WRITE) + cmdr |= AT91_MCI_TRCMD_START; + + if (data->flags & MMC_DATA_STREAM) + cmdr |= AT91_MCI_TRTYP_STREAM; + if (data->flags & MMC_DATA_MULTI) + cmdr |= AT91_MCI_TRTYP_MULTIPLE; + } + else { + block_length = 0; + blocks = 0; + } + + if (cmd->opcode == MMC_STOP_TRANSMISSION) + cmdr |= AT91_MCI_TRCMD_STOP; + + if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) + cmdr |= AT91_MCI_OPDCMD; + + /* + * Set the arguments and send the command + */ + pr_debug("Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08X)\n", + cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(host, AT91_MCI_MR)); + + if (!data) { + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS | ATMEL_PDC_RXTDIS); + at91_mci_write(host, ATMEL_PDC_RPR, 0); + at91_mci_write(host, ATMEL_PDC_RCR, 0); + at91_mci_write(host, ATMEL_PDC_RNPR, 0); + at91_mci_write(host, ATMEL_PDC_RNCR, 0); + at91_mci_write(host, ATMEL_PDC_TPR, 0); + at91_mci_write(host, ATMEL_PDC_TCR, 0); + at91_mci_write(host, ATMEL_PDC_TNPR, 0); + at91_mci_write(host, ATMEL_PDC_TNCR, 0); + + at91_mci_write(host, AT91_MCI_ARGR, cmd->arg); + at91_mci_write(host, AT91_MCI_CMDR, cmdr); + return AT91_MCI_CMDRDY; + } + + mr = at91_mci_read(host, AT91_MCI_MR) & 0x7fff; /* zero block length and PDC mode */ + at91_mci_write(host, AT91_MCI_MR, mr | (block_length << 16) | AT91_MCI_PDCMODE); + + /* + * Disable the PDC controller + */ + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); + + if (cmdr & AT91_MCI_TRCMD_START) { + data->bytes_xfered = 0; + host->transfer_index = 0; + host->in_use_index = 0; + if (cmdr & AT91_MCI_TRDIR) { + /* + * Handle a read + */ + host->buffer = NULL; + host->total_length = 0; + + at91mci_pre_dma_read(host); + ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */; + } + else { + /* + * Handle a write + */ + host->total_length = block_length * blocks; + host->buffer = dma_alloc_coherent(NULL, + host->total_length, + &host->physical_address, GFP_KERNEL); + + at91mci_sg_to_dma(host, data); + + pr_debug("Transmitting %d bytes\n", host->total_length); + + at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); + at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4); + ier = AT91_MCI_TXBUFE; + } + } + + /* + * Send the command and then enable the PDC - not the other way round as + * the data sheet says + */ + + at91_mci_write(host, AT91_MCI_ARGR, cmd->arg); + at91_mci_write(host, AT91_MCI_CMDR, cmdr); + + if (cmdr & AT91_MCI_TRCMD_START) { + if (cmdr & AT91_MCI_TRDIR) + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN); + else + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN); + } + return ier; +} + +/* + * Wait for a command to complete + */ +static void at91mci_process_command(struct at91mci_host *host, struct mmc_command *cmd) +{ + unsigned int ier; + + ier = at91_mci_send_command(host, cmd); + + pr_debug("setting ier to %08X\n", ier); + + /* Stop on errors or the required value */ + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier); +} + +/* + * Process the next step in the request + */ +static void at91mci_process_next(struct at91mci_host *host) +{ + if (!(host->flags & FL_SENT_COMMAND)) { + host->flags |= FL_SENT_COMMAND; + at91mci_process_command(host, host->request->cmd); + } + else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) { + host->flags |= FL_SENT_STOP; + at91mci_process_command(host, host->request->stop); + } + else + mmc_request_done(host->mmc, host->request); +} + +/* + * Handle a command that has been completed + */ +static void at91mci_completed_command(struct at91mci_host *host) +{ + struct mmc_command *cmd = host->cmd; + unsigned int status; + + at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); + + cmd->resp[0] = at91_mci_read(host, AT91_MCI_RSPR(0)); + cmd->resp[1] = at91_mci_read(host, AT91_MCI_RSPR(1)); + cmd->resp[2] = at91_mci_read(host, AT91_MCI_RSPR(2)); + cmd->resp[3] = at91_mci_read(host, AT91_MCI_RSPR(3)); + + if (host->buffer) { + dma_free_coherent(NULL, host->total_length, host->buffer, host->physical_address); + host->buffer = NULL; + } + + status = at91_mci_read(host, AT91_MCI_SR); + + pr_debug("Status = %08X [%08X %08X %08X %08X]\n", + status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); + + if (status & (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE | + AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE | + AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)) { + if ((status & AT91_MCI_RCRCE) && + ((cmd->opcode == MMC_SEND_OP_COND) || (cmd->opcode == SD_APP_OP_COND))) { + cmd->error = MMC_ERR_NONE; + } + else { + if (status & (AT91_MCI_RTOE | AT91_MCI_DTOE)) + cmd->error = MMC_ERR_TIMEOUT; + else if (status & (AT91_MCI_RCRCE | AT91_MCI_DCRCE)) + cmd->error = MMC_ERR_BADCRC; + else if (status & (AT91_MCI_OVRE | AT91_MCI_UNRE)) + cmd->error = MMC_ERR_FIFO; + else + cmd->error = MMC_ERR_FAILED; + + pr_debug("Error detected and set to %d (cmd = %d, retries = %d)\n", + cmd->error, cmd->opcode, cmd->retries); + } + } + else + cmd->error = MMC_ERR_NONE; + + at91mci_process_next(host); +} + +/* + * Handle an MMC request + */ +static void at91_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct at91mci_host *host = mmc_priv(mmc); + host->request = mrq; + host->flags = 0; + + at91mci_process_next(host); +} + +/* + * Set the IOS + */ +static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + int clkdiv; + struct at91mci_host *host = mmc_priv(mmc); + unsigned long at91_master_clock = clk_get_rate(host->mci_clk); + + host->bus_mode = ios->bus_mode; + + if (ios->clock == 0) { + /* Disable the MCI controller */ + at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS); + clkdiv = 0; + } + else { + /* Enable the MCI controller */ + at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); + + if ((at91_master_clock % (ios->clock * 2)) == 0) + clkdiv = ((at91_master_clock / ios->clock) / 2) - 1; + else + clkdiv = (at91_master_clock / ios->clock) / 2; + + pr_debug("clkdiv = %d. mcck = %ld\n", clkdiv, + at91_master_clock / (2 * (clkdiv + 1))); + } + if (ios->bus_width == MMC_BUS_WIDTH_4 && host->board->wire4) { + pr_debug("MMC: Setting controller bus width to 4\n"); + at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) | AT91_MCI_SDCBUS); + } + else { + pr_debug("MMC: Setting controller bus width to 1\n"); + at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); + } + + /* Set the clock divider */ + at91_mci_write(host, AT91_MCI_MR, (at91_mci_read(host, AT91_MCI_MR) & ~AT91_MCI_CLKDIV) | clkdiv); + + /* maybe switch power to the card */ + if (host->board->vcc_pin) { + switch (ios->power_mode) { + case MMC_POWER_OFF: + at91_set_gpio_value(host->board->vcc_pin, 0); + break; + case MMC_POWER_UP: + case MMC_POWER_ON: + at91_set_gpio_value(host->board->vcc_pin, 1); + break; + } + } +} + +/* + * Handle an interrupt + */ +static irqreturn_t at91_mci_irq(int irq, void *devid) +{ + struct at91mci_host *host = devid; + int completed = 0; + unsigned int int_status, int_mask; + + int_status = at91_mci_read(host, AT91_MCI_SR); + int_mask = at91_mci_read(host, AT91_MCI_IMR); + + pr_debug("MCI irq: status = %08X, %08X, %08X\n", int_status, int_mask, + int_status & int_mask); + + int_status = int_status & int_mask; + + if (int_status & AT91_MCI_ERRORS) { + completed = 1; + + if (int_status & AT91_MCI_UNRE) + pr_debug("MMC: Underrun error\n"); + if (int_status & AT91_MCI_OVRE) + pr_debug("MMC: Overrun error\n"); + if (int_status & AT91_MCI_DTOE) + pr_debug("MMC: Data timeout\n"); + if (int_status & AT91_MCI_DCRCE) + pr_debug("MMC: CRC error in data\n"); + if (int_status & AT91_MCI_RTOE) + pr_debug("MMC: Response timeout\n"); + if (int_status & AT91_MCI_RENDE) + pr_debug("MMC: Response end bit error\n"); + if (int_status & AT91_MCI_RCRCE) + pr_debug("MMC: Response CRC error\n"); + if (int_status & AT91_MCI_RDIRE) + pr_debug("MMC: Response direction error\n"); + if (int_status & AT91_MCI_RINDE) + pr_debug("MMC: Response index error\n"); + } else { + /* Only continue processing if no errors */ + + if (int_status & AT91_MCI_TXBUFE) { + pr_debug("TX buffer empty\n"); + at91_mci_handle_transmitted(host); + } + + if (int_status & AT91_MCI_RXBUFF) { + pr_debug("RX buffer full\n"); + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY); + } + + if (int_status & AT91_MCI_ENDTX) + pr_debug("Transmit has ended\n"); + + if (int_status & AT91_MCI_ENDRX) { + pr_debug("Receive has ended\n"); + at91mci_post_dma_read(host); + } + + if (int_status & AT91_MCI_NOTBUSY) { + pr_debug("Card is ready\n"); + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY); + } + + if (int_status & AT91_MCI_DTIP) + pr_debug("Data transfer in progress\n"); + + if (int_status & AT91_MCI_BLKE) + pr_debug("Block transfer has ended\n"); + + if (int_status & AT91_MCI_TXRDY) + pr_debug("Ready to transmit\n"); + + if (int_status & AT91_MCI_RXRDY) + pr_debug("Ready to receive\n"); + + if (int_status & AT91_MCI_CMDRDY) { + pr_debug("Command ready\n"); + completed = 1; + } + } + + if (completed) { + pr_debug("Completed command\n"); + at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); + at91mci_completed_command(host); + } else + at91_mci_write(host, AT91_MCI_IDR, int_status); + + return IRQ_HANDLED; +} + +static irqreturn_t at91_mmc_det_irq(int irq, void *_host) +{ + struct at91mci_host *host = _host; + int present = !at91_get_gpio_value(irq); + + /* + * we expect this irq on both insert and remove, + * and use a short delay to debounce. + */ + if (present != host->present) { + host->present = present; + pr_debug("%s: card %s\n", mmc_hostname(host->mmc), + present ? "insert" : "remove"); + if (!present) { + pr_debug("****** Resetting SD-card bus width ******\n"); + at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); + } + mmc_detect_change(host->mmc, msecs_to_jiffies(100)); + } + return IRQ_HANDLED; +} + +static int at91_mci_get_ro(struct mmc_host *mmc) +{ + int read_only = 0; + struct at91mci_host *host = mmc_priv(mmc); + + if (host->board->wp_pin) { + read_only = at91_get_gpio_value(host->board->wp_pin); + printk(KERN_WARNING "%s: card is %s\n", mmc_hostname(mmc), + (read_only ? "read-only" : "read-write") ); + } + else { + printk(KERN_WARNING "%s: host does not support reading read-only " + "switch. Assuming write-enable.\n", mmc_hostname(mmc)); + } + return read_only; +} + +static const struct mmc_host_ops at91_mci_ops = { + .request = at91_mci_request, + .set_ios = at91_mci_set_ios, + .get_ro = at91_mci_get_ro, +}; + +/* + * Probe for the device + */ +static int __init at91_mci_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct at91mci_host *host; + struct resource *res; + int ret; + + pr_debug("Probe MCI devices\n"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + if (!request_mem_region(res->start, res->end - res->start + 1, DRIVER_NAME)) + return -EBUSY; + + mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev); + if (!mmc) { + pr_debug("Failed to allocate mmc host\n"); + release_mem_region(res->start, res->end - res->start + 1); + return -ENOMEM; + } + + mmc->ops = &at91_mci_ops; + mmc->f_min = 375000; + mmc->f_max = 25000000; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->caps = MMC_CAP_BYTEBLOCK; + + mmc->max_blk_size = 4095; + mmc->max_blk_count = mmc->max_req_size; + + host = mmc_priv(mmc); + host->mmc = mmc; + host->buffer = NULL; + host->bus_mode = 0; + host->board = pdev->dev.platform_data; + if (host->board->wire4) { +#ifdef SUPPORT_4WIRE + mmc->caps |= MMC_CAP_4_BIT_DATA; +#else + printk("AT91 MMC: 4 wire bus mode not supported by this driver - using 1 wire\n"); +#endif + } + + /* + * Get Clock + */ + host->mci_clk = clk_get(&pdev->dev, "mci_clk"); + if (IS_ERR(host->mci_clk)) { + printk(KERN_ERR "AT91 MMC: no clock defined.\n"); + mmc_free_host(mmc); + release_mem_region(res->start, res->end - res->start + 1); + return -ENODEV; + } + + /* + * Map I/O region + */ + host->baseaddr = ioremap(res->start, res->end - res->start + 1); + if (!host->baseaddr) { + clk_put(host->mci_clk); + mmc_free_host(mmc); + release_mem_region(res->start, res->end - res->start + 1); + return -ENOMEM; + } + + /* + * Reset hardware + */ + clk_enable(host->mci_clk); /* Enable the peripheral clock */ + at91_mci_disable(host); + at91_mci_enable(host); + + /* + * Allocate the MCI interrupt + */ + host->irq = platform_get_irq(pdev, 0); + ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED, DRIVER_NAME, host); + if (ret) { + printk(KERN_ERR "AT91 MMC: Failed to request MCI interrupt\n"); + clk_disable(host->mci_clk); + clk_put(host->mci_clk); + mmc_free_host(mmc); + iounmap(host->baseaddr); + release_mem_region(res->start, res->end - res->start + 1); + return ret; + } + + platform_set_drvdata(pdev, mmc); + + /* + * Add host to MMC layer + */ + if (host->board->det_pin) + host->present = !at91_get_gpio_value(host->board->det_pin); + else + host->present = -1; + + mmc_add_host(mmc); + + /* + * monitor card insertion/removal if we can + */ + if (host->board->det_pin) { + ret = request_irq(host->board->det_pin, at91_mmc_det_irq, + 0, DRIVER_NAME, host); + if (ret) + printk(KERN_ERR "AT91 MMC: Couldn't allocate MMC detect irq\n"); + } + + pr_debug("Added MCI driver\n"); + + return 0; +} + +/* + * Remove a device + */ +static int __exit at91_mci_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + struct at91mci_host *host; + struct resource *res; + + if (!mmc) + return -1; + + host = mmc_priv(mmc); + + if (host->present != -1) { + free_irq(host->board->det_pin, host); + cancel_delayed_work(&host->mmc->detect); + } + + at91_mci_disable(host); + mmc_remove_host(mmc); + free_irq(host->irq, host); + + clk_disable(host->mci_clk); /* Disable the peripheral clock */ + clk_put(host->mci_clk); + + iounmap(host->baseaddr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, res->end - res->start + 1); + + mmc_free_host(mmc); + platform_set_drvdata(pdev, NULL); + pr_debug("MCI Removed\n"); + + return 0; +} + +#ifdef CONFIG_PM +static int at91_mci_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + int ret = 0; + + if (mmc) + ret = mmc_suspend_host(mmc, state); + + return ret; +} + +static int at91_mci_resume(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + int ret = 0; + + if (mmc) + ret = mmc_resume_host(mmc); + + return ret; +} +#else +#define at91_mci_suspend NULL +#define at91_mci_resume NULL +#endif + +static struct platform_driver at91_mci_driver = { + .remove = __exit_p(at91_mci_remove), + .suspend = at91_mci_suspend, + .resume = at91_mci_resume, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init at91_mci_init(void) +{ + return platform_driver_probe(&at91_mci_driver, at91_mci_probe); +} + +static void __exit at91_mci_exit(void) +{ + platform_driver_unregister(&at91_mci_driver); +} + +module_init(at91_mci_init); +module_exit(at91_mci_exit); + +MODULE_DESCRIPTION("AT91 Multimedia Card Interface driver"); +MODULE_AUTHOR("Nick Randell"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/au1xmmc.c b/drivers/mmc/host/au1xmmc.c new file mode 100644 index 00000000000..b7156a4555b --- /dev/null +++ b/drivers/mmc/host/au1xmmc.c @@ -0,0 +1,1031 @@ +/* + * linux/drivers/mmc/au1xmmc.c - AU1XX0 MMC driver + * + * Copyright (c) 2005, Advanced Micro Devices, Inc. + * + * Developed with help from the 2.4.30 MMC AU1XXX controller including + * the following copyright notices: + * Copyright (c) 2003-2004 Embedded Edge, LLC. + * Portions Copyright (C) 2002 Embedix, Inc + * Copyright 2002 Hewlett-Packard Company + + * 2.6 version of this driver inspired by: + * (drivers/mmc/wbsd.c) Copyright (C) 2004-2005 Pierre Ossman, + * All Rights Reserved. + * (drivers/mmc/pxa.c) Copyright (C) 2003 Russell King, + * All Rights Reserved. + * + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Why is a timer used to detect insert events? + * + * From the AU1100 MMC application guide: + * If the Au1100-based design is intended to support both MultiMediaCards + * and 1- or 4-data bit SecureDigital cards, then the solution is to + * connect a weak (560KOhm) pull-up resistor to connector pin 1. + * In doing so, a MMC card never enters SPI-mode communications, + * but now the SecureDigital card-detect feature of CD/DAT3 is ineffective + * (the low to high transition will not occur). + * + * So we use the timer to check the status manually. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include "au1xmmc.h" + +#define DRIVER_NAME "au1xxx-mmc" + +/* Set this to enable special debugging macros */ + +#ifdef DEBUG +#define DBG(fmt, idx, args...) printk("au1xx(%d): DEBUG: " fmt, idx, ##args) +#else +#define DBG(fmt, idx, args...) +#endif + +const struct { + u32 iobase; + u32 tx_devid, rx_devid; + u16 bcsrpwr; + u16 bcsrstatus; + u16 wpstatus; +} au1xmmc_card_table[] = { + { SD0_BASE, DSCR_CMD0_SDMS_TX0, DSCR_CMD0_SDMS_RX0, + BCSR_BOARD_SD0PWR, BCSR_INT_SD0INSERT, BCSR_STATUS_SD0WP }, +#ifndef CONFIG_MIPS_DB1200 + { SD1_BASE, DSCR_CMD0_SDMS_TX1, DSCR_CMD0_SDMS_RX1, + BCSR_BOARD_DS1PWR, BCSR_INT_SD1INSERT, BCSR_STATUS_SD1WP } +#endif +}; + +#define AU1XMMC_CONTROLLER_COUNT \ + (sizeof(au1xmmc_card_table) / sizeof(au1xmmc_card_table[0])) + +/* This array stores pointers for the hosts (used by the IRQ handler) */ +struct au1xmmc_host *au1xmmc_hosts[AU1XMMC_CONTROLLER_COUNT]; +static int dma = 1; + +#ifdef MODULE +module_param(dma, bool, 0); +MODULE_PARM_DESC(dma, "Use DMA engine for data transfers (0 = disabled)"); +#endif + +static inline void IRQ_ON(struct au1xmmc_host *host, u32 mask) +{ + u32 val = au_readl(HOST_CONFIG(host)); + val |= mask; + au_writel(val, HOST_CONFIG(host)); + au_sync(); +} + +static inline void FLUSH_FIFO(struct au1xmmc_host *host) +{ + u32 val = au_readl(HOST_CONFIG2(host)); + + au_writel(val | SD_CONFIG2_FF, HOST_CONFIG2(host)); + au_sync_delay(1); + + /* SEND_STOP will turn off clock control - this re-enables it */ + val &= ~SD_CONFIG2_DF; + + au_writel(val, HOST_CONFIG2(host)); + au_sync(); +} + +static inline void IRQ_OFF(struct au1xmmc_host *host, u32 mask) +{ + u32 val = au_readl(HOST_CONFIG(host)); + val &= ~mask; + au_writel(val, HOST_CONFIG(host)); + au_sync(); +} + +static inline void SEND_STOP(struct au1xmmc_host *host) +{ + + /* We know the value of CONFIG2, so avoid a read we don't need */ + u32 mask = SD_CONFIG2_EN; + + WARN_ON(host->status != HOST_S_DATA); + host->status = HOST_S_STOP; + + au_writel(mask | SD_CONFIG2_DF, HOST_CONFIG2(host)); + au_sync(); + + /* Send the stop commmand */ + au_writel(STOP_CMD, HOST_CMD(host)); +} + +static void au1xmmc_set_power(struct au1xmmc_host *host, int state) +{ + + u32 val = au1xmmc_card_table[host->id].bcsrpwr; + + bcsr->board &= ~val; + if (state) bcsr->board |= val; + + au_sync_delay(1); +} + +static inline int au1xmmc_card_inserted(struct au1xmmc_host *host) +{ + return (bcsr->sig_status & au1xmmc_card_table[host->id].bcsrstatus) + ? 1 : 0; +} + +static int au1xmmc_card_readonly(struct mmc_host *mmc) +{ + struct au1xmmc_host *host = mmc_priv(mmc); + return (bcsr->status & au1xmmc_card_table[host->id].wpstatus) + ? 1 : 0; +} + +static void au1xmmc_finish_request(struct au1xmmc_host *host) +{ + + struct mmc_request *mrq = host->mrq; + + host->mrq = NULL; + host->flags &= HOST_F_ACTIVE; + + host->dma.len = 0; + host->dma.dir = 0; + + host->pio.index = 0; + host->pio.offset = 0; + host->pio.len = 0; + + host->status = HOST_S_IDLE; + + bcsr->disk_leds |= (1 << 8); + + mmc_request_done(host->mmc, mrq); +} + +static void au1xmmc_tasklet_finish(unsigned long param) +{ + struct au1xmmc_host *host = (struct au1xmmc_host *) param; + au1xmmc_finish_request(host); +} + +static int au1xmmc_send_command(struct au1xmmc_host *host, int wait, + struct mmc_command *cmd) +{ + + u32 mmccmd = (cmd->opcode << SD_CMD_CI_SHIFT); + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + break; + case MMC_RSP_R1: + mmccmd |= SD_CMD_RT_1; + break; + case MMC_RSP_R1B: + mmccmd |= SD_CMD_RT_1B; + break; + case MMC_RSP_R2: + mmccmd |= SD_CMD_RT_2; + break; + case MMC_RSP_R3: + mmccmd |= SD_CMD_RT_3; + break; + default: + printk(KERN_INFO "au1xmmc: unhandled response type %02x\n", + mmc_resp_type(cmd)); + return MMC_ERR_INVALID; + } + + switch(cmd->opcode) { + case MMC_READ_SINGLE_BLOCK: + case SD_APP_SEND_SCR: + mmccmd |= SD_CMD_CT_2; + break; + case MMC_READ_MULTIPLE_BLOCK: + mmccmd |= SD_CMD_CT_4; + break; + case MMC_WRITE_BLOCK: + mmccmd |= SD_CMD_CT_1; + break; + + case MMC_WRITE_MULTIPLE_BLOCK: + mmccmd |= SD_CMD_CT_3; + break; + case MMC_STOP_TRANSMISSION: + mmccmd |= SD_CMD_CT_7; + break; + } + + au_writel(cmd->arg, HOST_CMDARG(host)); + au_sync(); + + if (wait) + IRQ_OFF(host, SD_CONFIG_CR); + + au_writel((mmccmd | SD_CMD_GO), HOST_CMD(host)); + au_sync(); + + /* Wait for the command to go on the line */ + + while(1) { + if (!(au_readl(HOST_CMD(host)) & SD_CMD_GO)) + break; + } + + /* Wait for the command to come back */ + + if (wait) { + u32 status = au_readl(HOST_STATUS(host)); + + while(!(status & SD_STATUS_CR)) + status = au_readl(HOST_STATUS(host)); + + /* Clear the CR status */ + au_writel(SD_STATUS_CR, HOST_STATUS(host)); + + IRQ_ON(host, SD_CONFIG_CR); + } + + return MMC_ERR_NONE; +} + +static void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status) +{ + + struct mmc_request *mrq = host->mrq; + struct mmc_data *data; + u32 crc; + + WARN_ON(host->status != HOST_S_DATA && host->status != HOST_S_STOP); + + if (host->mrq == NULL) + return; + + data = mrq->cmd->data; + + if (status == 0) + status = au_readl(HOST_STATUS(host)); + + /* The transaction is really over when the SD_STATUS_DB bit is clear */ + + while((host->flags & HOST_F_XMIT) && (status & SD_STATUS_DB)) + status = au_readl(HOST_STATUS(host)); + + data->error = MMC_ERR_NONE; + dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma.dir); + + /* Process any errors */ + + crc = (status & (SD_STATUS_WC | SD_STATUS_RC)); + if (host->flags & HOST_F_XMIT) + crc |= ((status & 0x07) == 0x02) ? 0 : 1; + + if (crc) + data->error = MMC_ERR_BADCRC; + + /* Clear the CRC bits */ + au_writel(SD_STATUS_WC | SD_STATUS_RC, HOST_STATUS(host)); + + data->bytes_xfered = 0; + + if (data->error == MMC_ERR_NONE) { + if (host->flags & HOST_F_DMA) { + u32 chan = DMA_CHANNEL(host); + + chan_tab_t *c = *((chan_tab_t **) chan); + au1x_dma_chan_t *cp = c->chan_ptr; + data->bytes_xfered = cp->ddma_bytecnt; + } + else + data->bytes_xfered = + (data->blocks * data->blksz) - + host->pio.len; + } + + au1xmmc_finish_request(host); +} + +static void au1xmmc_tasklet_data(unsigned long param) +{ + struct au1xmmc_host *host = (struct au1xmmc_host *) param; + + u32 status = au_readl(HOST_STATUS(host)); + au1xmmc_data_complete(host, status); +} + +#define AU1XMMC_MAX_TRANSFER 8 + +static void au1xmmc_send_pio(struct au1xmmc_host *host) +{ + + struct mmc_data *data = 0; + int sg_len, max, count = 0; + unsigned char *sg_ptr; + u32 status = 0; + struct scatterlist *sg; + + data = host->mrq->data; + + if (!(host->flags & HOST_F_XMIT)) + return; + + /* This is the pointer to the data buffer */ + sg = &data->sg[host->pio.index]; + sg_ptr = page_address(sg->page) + sg->offset + host->pio.offset; + + /* This is the space left inside the buffer */ + sg_len = data->sg[host->pio.index].length - host->pio.offset; + + /* Check to if we need less then the size of the sg_buffer */ + + max = (sg_len > host->pio.len) ? host->pio.len : sg_len; + if (max > AU1XMMC_MAX_TRANSFER) max = AU1XMMC_MAX_TRANSFER; + + for(count = 0; count < max; count++ ) { + unsigned char val; + + status = au_readl(HOST_STATUS(host)); + + if (!(status & SD_STATUS_TH)) + break; + + val = *sg_ptr++; + + au_writel((unsigned long) val, HOST_TXPORT(host)); + au_sync(); + } + + host->pio.len -= count; + host->pio.offset += count; + + if (count == sg_len) { + host->pio.index++; + host->pio.offset = 0; + } + + if (host->pio.len == 0) { + IRQ_OFF(host, SD_CONFIG_TH); + + if (host->flags & HOST_F_STOP) + SEND_STOP(host); + + tasklet_schedule(&host->data_task); + } +} + +static void au1xmmc_receive_pio(struct au1xmmc_host *host) +{ + + struct mmc_data *data = 0; + int sg_len = 0, max = 0, count = 0; + unsigned char *sg_ptr = 0; + u32 status = 0; + struct scatterlist *sg; + + data = host->mrq->data; + + if (!(host->flags & HOST_F_RECV)) + return; + + max = host->pio.len; + + if (host->pio.index < host->dma.len) { + sg = &data->sg[host->pio.index]; + sg_ptr = page_address(sg->page) + sg->offset + host->pio.offset; + + /* This is the space left inside the buffer */ + sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset; + + /* Check to if we need less then the size of the sg_buffer */ + if (sg_len < max) max = sg_len; + } + + if (max > AU1XMMC_MAX_TRANSFER) + max = AU1XMMC_MAX_TRANSFER; + + for(count = 0; count < max; count++ ) { + u32 val; + status = au_readl(HOST_STATUS(host)); + + if (!(status & SD_STATUS_NE)) + break; + + if (status & SD_STATUS_RC) { + DBG("RX CRC Error [%d + %d].\n", host->id, + host->pio.len, count); + break; + } + + if (status & SD_STATUS_RO) { + DBG("RX Overrun [%d + %d]\n", host->id, + host->pio.len, count); + break; + } + else if (status & SD_STATUS_RU) { + DBG("RX Underrun [%d + %d]\n", host->id, + host->pio.len, count); + break; + } + + val = au_readl(HOST_RXPORT(host)); + + if (sg_ptr) + *sg_ptr++ = (unsigned char) (val & 0xFF); + } + + host->pio.len -= count; + host->pio.offset += count; + + if (sg_len && count == sg_len) { + host->pio.index++; + host->pio.offset = 0; + } + + if (host->pio.len == 0) { + //IRQ_OFF(host, SD_CONFIG_RA | SD_CONFIG_RF); + IRQ_OFF(host, SD_CONFIG_NE); + + if (host->flags & HOST_F_STOP) + SEND_STOP(host); + + tasklet_schedule(&host->data_task); + } +} + +/* static void au1xmmc_cmd_complete + This is called when a command has been completed - grab the response + and check for errors. Then start the data transfer if it is indicated. +*/ + +static void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status) +{ + + struct mmc_request *mrq = host->mrq; + struct mmc_command *cmd; + int trans; + + if (!host->mrq) + return; + + cmd = mrq->cmd; + cmd->error = MMC_ERR_NONE; + + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) { + u32 r[4]; + int i; + + r[0] = au_readl(host->iobase + SD_RESP3); + r[1] = au_readl(host->iobase + SD_RESP2); + r[2] = au_readl(host->iobase + SD_RESP1); + r[3] = au_readl(host->iobase + SD_RESP0); + + /* The CRC is omitted from the response, so really + * we only got 120 bytes, but the engine expects + * 128 bits, so we have to shift things up + */ + + for(i = 0; i < 4; i++) { + cmd->resp[i] = (r[i] & 0x00FFFFFF) << 8; + if (i != 3) + cmd->resp[i] |= (r[i + 1] & 0xFF000000) >> 24; + } + } else { + /* Techincally, we should be getting all 48 bits of + * the response (SD_RESP1 + SD_RESP2), but because + * our response omits the CRC, our data ends up + * being shifted 8 bits to the right. In this case, + * that means that the OSR data starts at bit 31, + * so we can just read RESP0 and return that + */ + cmd->resp[0] = au_readl(host->iobase + SD_RESP0); + } + } + + /* Figure out errors */ + + if (status & (SD_STATUS_SC | SD_STATUS_WC | SD_STATUS_RC)) + cmd->error = MMC_ERR_BADCRC; + + trans = host->flags & (HOST_F_XMIT | HOST_F_RECV); + + if (!trans || cmd->error != MMC_ERR_NONE) { + + IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA|SD_CONFIG_RF); + tasklet_schedule(&host->finish_task); + return; + } + + host->status = HOST_S_DATA; + + if (host->flags & HOST_F_DMA) { + u32 channel = DMA_CHANNEL(host); + + /* Start the DMA as soon as the buffer gets something in it */ + + if (host->flags & HOST_F_RECV) { + u32 mask = SD_STATUS_DB | SD_STATUS_NE; + + while((status & mask) != mask) + status = au_readl(HOST_STATUS(host)); + } + + au1xxx_dbdma_start(channel); + } +} + +static void au1xmmc_set_clock(struct au1xmmc_host *host, int rate) +{ + + unsigned int pbus = get_au1x00_speed(); + unsigned int divisor; + u32 config; + + /* From databook: + divisor = ((((cpuclock / sbus_divisor) / 2) / mmcclock) / 2) - 1 + */ + + pbus /= ((au_readl(SYS_POWERCTRL) & 0x3) + 2); + pbus /= 2; + + divisor = ((pbus / rate) / 2) - 1; + + config = au_readl(HOST_CONFIG(host)); + + config &= ~(SD_CONFIG_DIV); + config |= (divisor & SD_CONFIG_DIV) | SD_CONFIG_DE; + + au_writel(config, HOST_CONFIG(host)); + au_sync(); +} + +static int +au1xmmc_prepare_data(struct au1xmmc_host *host, struct mmc_data *data) +{ + + int datalen = data->blocks * data->blksz; + + if (dma != 0) + host->flags |= HOST_F_DMA; + + if (data->flags & MMC_DATA_READ) + host->flags |= HOST_F_RECV; + else + host->flags |= HOST_F_XMIT; + + if (host->mrq->stop) + host->flags |= HOST_F_STOP; + + host->dma.dir = DMA_BIDIRECTIONAL; + + host->dma.len = dma_map_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, host->dma.dir); + + if (host->dma.len == 0) + return MMC_ERR_TIMEOUT; + + au_writel(data->blksz - 1, HOST_BLKSIZE(host)); + + if (host->flags & HOST_F_DMA) { + int i; + u32 channel = DMA_CHANNEL(host); + + au1xxx_dbdma_stop(channel); + + for(i = 0; i < host->dma.len; i++) { + u32 ret = 0, flags = DDMA_FLAGS_NOIE; + struct scatterlist *sg = &data->sg[i]; + int sg_len = sg->length; + + int len = (datalen > sg_len) ? sg_len : datalen; + + if (i == host->dma.len - 1) + flags = DDMA_FLAGS_IE; + + if (host->flags & HOST_F_XMIT){ + ret = au1xxx_dbdma_put_source_flags(channel, + (void *) (page_address(sg->page) + + sg->offset), + len, flags); + } + else { + ret = au1xxx_dbdma_put_dest_flags(channel, + (void *) (page_address(sg->page) + + sg->offset), + len, flags); + } + + if (!ret) + goto dataerr; + + datalen -= len; + } + } + else { + host->pio.index = 0; + host->pio.offset = 0; + host->pio.len = datalen; + + if (host->flags & HOST_F_XMIT) + IRQ_ON(host, SD_CONFIG_TH); + else + IRQ_ON(host, SD_CONFIG_NE); + //IRQ_ON(host, SD_CONFIG_RA|SD_CONFIG_RF); + } + + return MMC_ERR_NONE; + + dataerr: + dma_unmap_sg(mmc_dev(host->mmc),data->sg,data->sg_len,host->dma.dir); + return MMC_ERR_TIMEOUT; +} + +/* static void au1xmmc_request + This actually starts a command or data transaction +*/ + +static void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq) +{ + + struct au1xmmc_host *host = mmc_priv(mmc); + int ret = MMC_ERR_NONE; + + WARN_ON(irqs_disabled()); + WARN_ON(host->status != HOST_S_IDLE); + + host->mrq = mrq; + host->status = HOST_S_CMD; + + bcsr->disk_leds &= ~(1 << 8); + + if (mrq->data) { + FLUSH_FIFO(host); + ret = au1xmmc_prepare_data(host, mrq->data); + } + + if (ret == MMC_ERR_NONE) + ret = au1xmmc_send_command(host, 0, mrq->cmd); + + if (ret != MMC_ERR_NONE) { + mrq->cmd->error = ret; + au1xmmc_finish_request(host); + } +} + +static void au1xmmc_reset_controller(struct au1xmmc_host *host) +{ + + /* Apply the clock */ + au_writel(SD_ENABLE_CE, HOST_ENABLE(host)); + au_sync_delay(1); + + au_writel(SD_ENABLE_R | SD_ENABLE_CE, HOST_ENABLE(host)); + au_sync_delay(5); + + au_writel(~0, HOST_STATUS(host)); + au_sync(); + + au_writel(0, HOST_BLKSIZE(host)); + au_writel(0x001fffff, HOST_TIMEOUT(host)); + au_sync(); + + au_writel(SD_CONFIG2_EN, HOST_CONFIG2(host)); + au_sync(); + + au_writel(SD_CONFIG2_EN | SD_CONFIG2_FF, HOST_CONFIG2(host)); + au_sync_delay(1); + + au_writel(SD_CONFIG2_EN, HOST_CONFIG2(host)); + au_sync(); + + /* Configure interrupts */ + au_writel(AU1XMMC_INTERRUPTS, HOST_CONFIG(host)); + au_sync(); +} + + +static void au1xmmc_set_ios(struct mmc_host* mmc, struct mmc_ios* ios) +{ + struct au1xmmc_host *host = mmc_priv(mmc); + + if (ios->power_mode == MMC_POWER_OFF) + au1xmmc_set_power(host, 0); + else if (ios->power_mode == MMC_POWER_ON) { + au1xmmc_set_power(host, 1); + } + + if (ios->clock && ios->clock != host->clock) { + au1xmmc_set_clock(host, ios->clock); + host->clock = ios->clock; + } +} + +static void au1xmmc_dma_callback(int irq, void *dev_id) +{ + struct au1xmmc_host *host = (struct au1xmmc_host *) dev_id; + + /* Avoid spurious interrupts */ + + if (!host->mrq) + return; + + if (host->flags & HOST_F_STOP) + SEND_STOP(host); + + tasklet_schedule(&host->data_task); +} + +#define STATUS_TIMEOUT (SD_STATUS_RAT | SD_STATUS_DT) +#define STATUS_DATA_IN (SD_STATUS_NE) +#define STATUS_DATA_OUT (SD_STATUS_TH) + +static irqreturn_t au1xmmc_irq(int irq, void *dev_id) +{ + + u32 status; + int i, ret = 0; + + disable_irq(AU1100_SD_IRQ); + + for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) { + struct au1xmmc_host * host = au1xmmc_hosts[i]; + u32 handled = 1; + + status = au_readl(HOST_STATUS(host)); + + if (host->mrq && (status & STATUS_TIMEOUT)) { + if (status & SD_STATUS_RAT) + host->mrq->cmd->error = MMC_ERR_TIMEOUT; + + else if (status & SD_STATUS_DT) + host->mrq->data->error = MMC_ERR_TIMEOUT; + + /* In PIO mode, interrupts might still be enabled */ + IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH); + + //IRQ_OFF(host, SD_CONFIG_TH|SD_CONFIG_RA|SD_CONFIG_RF); + tasklet_schedule(&host->finish_task); + } +#if 0 + else if (status & SD_STATUS_DD) { + + /* Sometimes we get a DD before a NE in PIO mode */ + + if (!(host->flags & HOST_F_DMA) && + (status & SD_STATUS_NE)) + au1xmmc_receive_pio(host); + else { + au1xmmc_data_complete(host, status); + //tasklet_schedule(&host->data_task); + } + } +#endif + else if (status & (SD_STATUS_CR)) { + if (host->status == HOST_S_CMD) + au1xmmc_cmd_complete(host,status); + } + else if (!(host->flags & HOST_F_DMA)) { + if ((host->flags & HOST_F_XMIT) && + (status & STATUS_DATA_OUT)) + au1xmmc_send_pio(host); + else if ((host->flags & HOST_F_RECV) && + (status & STATUS_DATA_IN)) + au1xmmc_receive_pio(host); + } + else if (status & 0x203FBC70) { + DBG("Unhandled status %8.8x\n", host->id, status); + handled = 0; + } + + au_writel(status, HOST_STATUS(host)); + au_sync(); + + ret |= handled; + } + + enable_irq(AU1100_SD_IRQ); + return ret; +} + +static void au1xmmc_poll_event(unsigned long arg) +{ + struct au1xmmc_host *host = (struct au1xmmc_host *) arg; + + int card = au1xmmc_card_inserted(host); + int controller = (host->flags & HOST_F_ACTIVE) ? 1 : 0; + + if (card != controller) { + host->flags &= ~HOST_F_ACTIVE; + if (card) host->flags |= HOST_F_ACTIVE; + mmc_detect_change(host->mmc, 0); + } + + if (host->mrq != NULL) { + u32 status = au_readl(HOST_STATUS(host)); + DBG("PENDING - %8.8x\n", host->id, status); + } + + mod_timer(&host->timer, jiffies + AU1XMMC_DETECT_TIMEOUT); +} + +static dbdev_tab_t au1xmmc_mem_dbdev = +{ + DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 8, 0x00000000, 0, 0 +}; + +static void au1xmmc_init_dma(struct au1xmmc_host *host) +{ + + u32 rxchan, txchan; + + int txid = au1xmmc_card_table[host->id].tx_devid; + int rxid = au1xmmc_card_table[host->id].rx_devid; + + /* DSCR_CMD0_ALWAYS has a stride of 32 bits, we need a stride + of 8 bits. And since devices are shared, we need to create + our own to avoid freaking out other devices + */ + + int memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev); + + txchan = au1xxx_dbdma_chan_alloc(memid, txid, + au1xmmc_dma_callback, (void *) host); + + rxchan = au1xxx_dbdma_chan_alloc(rxid, memid, + au1xmmc_dma_callback, (void *) host); + + au1xxx_dbdma_set_devwidth(txchan, 8); + au1xxx_dbdma_set_devwidth(rxchan, 8); + + au1xxx_dbdma_ring_alloc(txchan, AU1XMMC_DESCRIPTOR_COUNT); + au1xxx_dbdma_ring_alloc(rxchan, AU1XMMC_DESCRIPTOR_COUNT); + + host->tx_chan = txchan; + host->rx_chan = rxchan; +} + +static const struct mmc_host_ops au1xmmc_ops = { + .request = au1xmmc_request, + .set_ios = au1xmmc_set_ios, + .get_ro = au1xmmc_card_readonly, +}; + +static int __devinit au1xmmc_probe(struct platform_device *pdev) +{ + + int i, ret = 0; + + /* THe interrupt is shared among all controllers */ + ret = request_irq(AU1100_SD_IRQ, au1xmmc_irq, IRQF_DISABLED, "MMC", 0); + + if (ret) { + printk(DRIVER_NAME "ERROR: Couldn't get int %d: %d\n", + AU1100_SD_IRQ, ret); + return -ENXIO; + } + + disable_irq(AU1100_SD_IRQ); + + for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) { + struct mmc_host *mmc = mmc_alloc_host(sizeof(struct au1xmmc_host), &pdev->dev); + struct au1xmmc_host *host = 0; + + if (!mmc) { + printk(DRIVER_NAME "ERROR: no mem for host %d\n", i); + au1xmmc_hosts[i] = 0; + continue; + } + + mmc->ops = &au1xmmc_ops; + + mmc->f_min = 450000; + mmc->f_max = 24000000; + + mmc->max_seg_size = AU1XMMC_DESCRIPTOR_SIZE; + mmc->max_phys_segs = AU1XMMC_DESCRIPTOR_COUNT; + + mmc->max_blk_size = 2048; + mmc->max_blk_count = 512; + + mmc->ocr_avail = AU1XMMC_OCR; + + host = mmc_priv(mmc); + host->mmc = mmc; + + host->id = i; + host->iobase = au1xmmc_card_table[host->id].iobase; + host->clock = 0; + host->power_mode = MMC_POWER_OFF; + + host->flags = au1xmmc_card_inserted(host) ? HOST_F_ACTIVE : 0; + host->status = HOST_S_IDLE; + + init_timer(&host->timer); + + host->timer.function = au1xmmc_poll_event; + host->timer.data = (unsigned long) host; + host->timer.expires = jiffies + AU1XMMC_DETECT_TIMEOUT; + + tasklet_init(&host->data_task, au1xmmc_tasklet_data, + (unsigned long) host); + + tasklet_init(&host->finish_task, au1xmmc_tasklet_finish, + (unsigned long) host); + + spin_lock_init(&host->lock); + + if (dma != 0) + au1xmmc_init_dma(host); + + au1xmmc_reset_controller(host); + + mmc_add_host(mmc); + au1xmmc_hosts[i] = host; + + add_timer(&host->timer); + + printk(KERN_INFO DRIVER_NAME ": MMC Controller %d set up at %8.8X (mode=%s)\n", + host->id, host->iobase, dma ? "dma" : "pio"); + } + + enable_irq(AU1100_SD_IRQ); + + return 0; +} + +static int __devexit au1xmmc_remove(struct platform_device *pdev) +{ + + int i; + + disable_irq(AU1100_SD_IRQ); + + for(i = 0; i < AU1XMMC_CONTROLLER_COUNT; i++) { + struct au1xmmc_host *host = au1xmmc_hosts[i]; + if (!host) continue; + + tasklet_kill(&host->data_task); + tasklet_kill(&host->finish_task); + + del_timer_sync(&host->timer); + au1xmmc_set_power(host, 0); + + mmc_remove_host(host->mmc); + + au1xxx_dbdma_chan_free(host->tx_chan); + au1xxx_dbdma_chan_free(host->rx_chan); + + au_writel(0x0, HOST_ENABLE(host)); + au_sync(); + } + + free_irq(AU1100_SD_IRQ, 0); + return 0; +} + +static struct platform_driver au1xmmc_driver = { + .probe = au1xmmc_probe, + .remove = au1xmmc_remove, + .suspend = NULL, + .resume = NULL, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init au1xmmc_init(void) +{ + return platform_driver_register(&au1xmmc_driver); +} + +static void __exit au1xmmc_exit(void) +{ + platform_driver_unregister(&au1xmmc_driver); +} + +module_init(au1xmmc_init); +module_exit(au1xmmc_exit); + +#ifdef MODULE +MODULE_AUTHOR("Advanced Micro Devices, Inc"); +MODULE_DESCRIPTION("MMC/SD driver for the Alchemy Au1XXX"); +MODULE_LICENSE("GPL"); +#endif + diff --git a/drivers/mmc/host/au1xmmc.h b/drivers/mmc/host/au1xmmc.h new file mode 100644 index 00000000000..341cbdf0bac --- /dev/null +++ b/drivers/mmc/host/au1xmmc.h @@ -0,0 +1,96 @@ +#ifndef _AU1XMMC_H_ +#define _AU1XMMC_H_ + +/* Hardware definitions */ + +#define AU1XMMC_DESCRIPTOR_COUNT 1 +#define AU1XMMC_DESCRIPTOR_SIZE 2048 + +#define AU1XMMC_OCR ( MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 | \ + MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33 | \ + MMC_VDD_33_34 | MMC_VDD_34_35 | MMC_VDD_35_36) + +/* Easy access macros */ + +#define HOST_STATUS(h) ((h)->iobase + SD_STATUS) +#define HOST_CONFIG(h) ((h)->iobase + SD_CONFIG) +#define HOST_ENABLE(h) ((h)->iobase + SD_ENABLE) +#define HOST_TXPORT(h) ((h)->iobase + SD_TXPORT) +#define HOST_RXPORT(h) ((h)->iobase + SD_RXPORT) +#define HOST_CMDARG(h) ((h)->iobase + SD_CMDARG) +#define HOST_BLKSIZE(h) ((h)->iobase + SD_BLKSIZE) +#define HOST_CMD(h) ((h)->iobase + SD_CMD) +#define HOST_CONFIG2(h) ((h)->iobase + SD_CONFIG2) +#define HOST_TIMEOUT(h) ((h)->iobase + SD_TIMEOUT) +#define HOST_DEBUG(h) ((h)->iobase + SD_DEBUG) + +#define DMA_CHANNEL(h) \ + ( ((h)->flags & HOST_F_XMIT) ? (h)->tx_chan : (h)->rx_chan) + +/* This gives us a hard value for the stop command that we can write directly + * to the command register + */ + +#define STOP_CMD (SD_CMD_RT_1B|SD_CMD_CT_7|(0xC << SD_CMD_CI_SHIFT)|SD_CMD_GO) + +/* This is the set of interrupts that we configure by default */ + +#if 0 +#define AU1XMMC_INTERRUPTS (SD_CONFIG_SC | SD_CONFIG_DT | SD_CONFIG_DD | \ + SD_CONFIG_RAT | SD_CONFIG_CR | SD_CONFIG_I) +#endif + +#define AU1XMMC_INTERRUPTS (SD_CONFIG_SC | SD_CONFIG_DT | \ + SD_CONFIG_RAT | SD_CONFIG_CR | SD_CONFIG_I) +/* The poll event (looking for insert/remove events runs twice a second */ +#define AU1XMMC_DETECT_TIMEOUT (HZ/2) + +struct au1xmmc_host { + struct mmc_host *mmc; + struct mmc_request *mrq; + + u32 id; + + u32 flags; + u32 iobase; + u32 clock; + u32 bus_width; + u32 power_mode; + + int status; + + struct { + int len; + int dir; + } dma; + + struct { + int index; + int offset; + int len; + } pio; + + u32 tx_chan; + u32 rx_chan; + + struct timer_list timer; + struct tasklet_struct finish_task; + struct tasklet_struct data_task; + + spinlock_t lock; +}; + +/* Status flags used by the host structure */ + +#define HOST_F_XMIT 0x0001 +#define HOST_F_RECV 0x0002 +#define HOST_F_DMA 0x0010 +#define HOST_F_ACTIVE 0x0100 +#define HOST_F_STOP 0x1000 + +#define HOST_S_IDLE 0x0001 +#define HOST_S_CMD 0x0002 +#define HOST_S_DATA 0x0003 +#define HOST_S_STOP 0x0004 + +#endif diff --git a/drivers/mmc/host/imxmmc.c b/drivers/mmc/host/imxmmc.c new file mode 100644 index 00000000000..7ee2045acbe --- /dev/null +++ b/drivers/mmc/host/imxmmc.c @@ -0,0 +1,1137 @@ +/* + * linux/drivers/mmc/imxmmc.c - Motorola i.MX MMCI driver + * + * Copyright (C) 2004 Sascha Hauer, Pengutronix + * Copyright (C) 2006 Pavel Pisa, PiKRON + * + * derived from pxamci.c by Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 2005-04-17 Pavel Pisa + * Changed to conform redesigned i.MX scatter gather DMA interface + * + * 2005-11-04 Pavel Pisa + * Updated for 2.6.14 kernel + * + * 2005-12-13 Jay Monkman + * Found and corrected problems in the write path + * + * 2005-12-30 Pavel Pisa + * The event handling rewritten right way in softirq. + * Added many ugly hacks and delays to overcome SDHC + * deficiencies + * + */ + +#ifdef CONFIG_MMC_DEBUG +#define DEBUG +#else +#undef DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "imxmmc.h" + +#define DRIVER_NAME "imx-mmc" + +#define IMXMCI_INT_MASK_DEFAULT (INT_MASK_BUF_READY | INT_MASK_DATA_TRAN | \ + INT_MASK_WRITE_OP_DONE | INT_MASK_END_CMD_RES | \ + INT_MASK_AUTO_CARD_DETECT | INT_MASK_DAT0_EN | INT_MASK_SDIO) + +struct imxmci_host { + struct mmc_host *mmc; + spinlock_t lock; + struct resource *res; + int irq; + imx_dmach_t dma; + unsigned int clkrt; + unsigned int cmdat; + volatile unsigned int imask; + unsigned int power_mode; + unsigned int present; + struct imxmmc_platform_data *pdata; + + struct mmc_request *req; + struct mmc_command *cmd; + struct mmc_data *data; + + struct timer_list timer; + struct tasklet_struct tasklet; + unsigned int status_reg; + unsigned long pending_events; + /* Next to fields are there for CPU driven transfers to overcome SDHC deficiencies */ + u16 *data_ptr; + unsigned int data_cnt; + atomic_t stuck_timeout; + + unsigned int dma_nents; + unsigned int dma_size; + unsigned int dma_dir; + int dma_allocated; + + unsigned char actual_bus_width; + + int prev_cmd_code; +}; + +#define IMXMCI_PEND_IRQ_b 0 +#define IMXMCI_PEND_DMA_END_b 1 +#define IMXMCI_PEND_DMA_ERR_b 2 +#define IMXMCI_PEND_WAIT_RESP_b 3 +#define IMXMCI_PEND_DMA_DATA_b 4 +#define IMXMCI_PEND_CPU_DATA_b 5 +#define IMXMCI_PEND_CARD_XCHG_b 6 +#define IMXMCI_PEND_SET_INIT_b 7 +#define IMXMCI_PEND_STARTED_b 8 + +#define IMXMCI_PEND_IRQ_m (1 << IMXMCI_PEND_IRQ_b) +#define IMXMCI_PEND_DMA_END_m (1 << IMXMCI_PEND_DMA_END_b) +#define IMXMCI_PEND_DMA_ERR_m (1 << IMXMCI_PEND_DMA_ERR_b) +#define IMXMCI_PEND_WAIT_RESP_m (1 << IMXMCI_PEND_WAIT_RESP_b) +#define IMXMCI_PEND_DMA_DATA_m (1 << IMXMCI_PEND_DMA_DATA_b) +#define IMXMCI_PEND_CPU_DATA_m (1 << IMXMCI_PEND_CPU_DATA_b) +#define IMXMCI_PEND_CARD_XCHG_m (1 << IMXMCI_PEND_CARD_XCHG_b) +#define IMXMCI_PEND_SET_INIT_m (1 << IMXMCI_PEND_SET_INIT_b) +#define IMXMCI_PEND_STARTED_m (1 << IMXMCI_PEND_STARTED_b) + +static void imxmci_stop_clock(struct imxmci_host *host) +{ + int i = 0; + MMC_STR_STP_CLK &= ~STR_STP_CLK_START_CLK; + while(i < 0x1000) { + if(!(i & 0x7f)) + MMC_STR_STP_CLK |= STR_STP_CLK_STOP_CLK; + + if(!(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)) { + /* Check twice before cut */ + if(!(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)) + return; + } + + i++; + } + dev_dbg(mmc_dev(host->mmc), "imxmci_stop_clock blocked, no luck\n"); +} + +static int imxmci_start_clock(struct imxmci_host *host) +{ + unsigned int trials = 0; + unsigned int delay_limit = 128; + unsigned long flags; + + MMC_STR_STP_CLK &= ~STR_STP_CLK_STOP_CLK; + + clear_bit(IMXMCI_PEND_STARTED_b, &host->pending_events); + + /* + * Command start of the clock, this usually succeeds in less + * then 6 delay loops, but during card detection (low clockrate) + * it takes up to 5000 delay loops and sometimes fails for the first time + */ + MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK; + + do { + unsigned int delay = delay_limit; + + while(delay--){ + if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN) + /* Check twice before cut */ + if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN) + return 0; + + if(test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events)) + return 0; + } + + local_irq_save(flags); + /* + * Ensure, that request is not doubled under all possible circumstances. + * It is possible, that cock running state is missed, because some other + * IRQ or schedule delays this function execution and the clocks has + * been already stopped by other means (response processing, SDHC HW) + */ + if(!test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events)) + MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK; + local_irq_restore(flags); + + } while(++trials<256); + + dev_err(mmc_dev(host->mmc), "imxmci_start_clock blocked, no luck\n"); + + return -1; +} + +static void imxmci_softreset(void) +{ + /* reset sequence */ + MMC_STR_STP_CLK = 0x8; + MMC_STR_STP_CLK = 0xD; + MMC_STR_STP_CLK = 0x5; + MMC_STR_STP_CLK = 0x5; + MMC_STR_STP_CLK = 0x5; + MMC_STR_STP_CLK = 0x5; + MMC_STR_STP_CLK = 0x5; + MMC_STR_STP_CLK = 0x5; + MMC_STR_STP_CLK = 0x5; + MMC_STR_STP_CLK = 0x5; + + MMC_RES_TO = 0xff; + MMC_BLK_LEN = 512; + MMC_NOB = 1; +} + +static int imxmci_busy_wait_for_status(struct imxmci_host *host, + unsigned int *pstat, unsigned int stat_mask, + int timeout, const char *where) +{ + int loops=0; + while(!(*pstat & stat_mask)) { + loops+=2; + if(loops >= timeout) { + dev_dbg(mmc_dev(host->mmc), "busy wait timeout in %s, STATUS = 0x%x (0x%x)\n", + where, *pstat, stat_mask); + return -1; + } + udelay(2); + *pstat |= MMC_STATUS; + } + if(!loops) + return 0; + + /* The busy-wait is expected there for clock <8MHz due to SDHC hardware flaws */ + if(!(stat_mask & STATUS_END_CMD_RESP) || (host->mmc->ios.clock>=8000000)) + dev_info(mmc_dev(host->mmc), "busy wait for %d usec in %s, STATUS = 0x%x (0x%x)\n", + loops, where, *pstat, stat_mask); + return loops; +} + +static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data) +{ + unsigned int nob = data->blocks; + unsigned int blksz = data->blksz; + unsigned int datasz = nob * blksz; + int i; + + if (data->flags & MMC_DATA_STREAM) + nob = 0xffff; + + host->data = data; + data->bytes_xfered = 0; + + MMC_NOB = nob; + MMC_BLK_LEN = blksz; + + /* + * DMA cannot be used for small block sizes, we have to use CPU driven transfers otherwise. + * We are in big troubles for non-512 byte transfers according to note in the paragraph + * 20.6.7 of User Manual anyway, but we need to be able to transfer SCR at least. + * The situation is even more complex in reality. The SDHC in not able to handle wll + * partial FIFO fills and reads. The length has to be rounded up to burst size multiple. + * This is required for SCR read at least. + */ + if (datasz < 512) { + host->dma_size = datasz; + if (data->flags & MMC_DATA_READ) { + host->dma_dir = DMA_FROM_DEVICE; + + /* Hack to enable read SCR */ + MMC_NOB = 1; + MMC_BLK_LEN = 512; + } else { + host->dma_dir = DMA_TO_DEVICE; + } + + /* Convert back to virtual address */ + host->data_ptr = (u16*)(page_address(data->sg->page) + data->sg->offset); + host->data_cnt = 0; + + clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events); + set_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events); + + return; + } + + if (data->flags & MMC_DATA_READ) { + host->dma_dir = DMA_FROM_DEVICE; + host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, host->dma_dir); + + imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz, + host->res->start + MMC_BUFFER_ACCESS_OFS, DMA_MODE_READ); + + /*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_READ, IMX_DMA_WIDTH_16, CCR_REN);*/ + CCR(host->dma) = CCR_DMOD_LINEAR | CCR_DSIZ_32 | CCR_SMOD_FIFO | CCR_SSIZ_16 | CCR_REN; + } else { + host->dma_dir = DMA_TO_DEVICE; + + host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, host->dma_dir); + + imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz, + host->res->start + MMC_BUFFER_ACCESS_OFS, DMA_MODE_WRITE); + + /*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_WRITE, IMX_DMA_WIDTH_16, CCR_REN);*/ + CCR(host->dma) = CCR_SMOD_LINEAR | CCR_SSIZ_32 | CCR_DMOD_FIFO | CCR_DSIZ_16 | CCR_REN; + } + +#if 1 /* This code is there only for consistency checking and can be disabled in future */ + host->dma_size = 0; + for(i=0; idma_nents; i++) + host->dma_size+=data->sg[i].length; + + if (datasz > host->dma_size) { + dev_err(mmc_dev(host->mmc), "imxmci_setup_data datasz 0x%x > 0x%x dm_size\n", + datasz, host->dma_size); + } +#endif + + host->dma_size = datasz; + + wmb(); + + if(host->actual_bus_width == MMC_BUS_WIDTH_4) + BLR(host->dma) = 0; /* burst 64 byte read / 64 bytes write */ + else + BLR(host->dma) = 16; /* burst 16 byte read / 16 bytes write */ + + RSSR(host->dma) = DMA_REQ_SDHC; + + set_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events); + clear_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events); + + /* start DMA engine for read, write is delayed after initial response */ + if (host->dma_dir == DMA_FROM_DEVICE) { + imx_dma_enable(host->dma); + } +} + +static void imxmci_start_cmd(struct imxmci_host *host, struct mmc_command *cmd, unsigned int cmdat) +{ + unsigned long flags; + u32 imask; + + WARN_ON(host->cmd != NULL); + host->cmd = cmd; + + /* Ensure, that clock are stopped else command programming and start fails */ + imxmci_stop_clock(host); + + if (cmd->flags & MMC_RSP_BUSY) + cmdat |= CMD_DAT_CONT_BUSY; + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_R1: /* short CRC, OPCODE */ + case MMC_RSP_R1B:/* short CRC, OPCODE, BUSY */ + cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R1; + break; + case MMC_RSP_R2: /* long 136 bit + CRC */ + cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R2; + break; + case MMC_RSP_R3: /* short */ + cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R3; + break; + default: + break; + } + + if ( test_and_clear_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events) ) + cmdat |= CMD_DAT_CONT_INIT; /* This command needs init */ + + if ( host->actual_bus_width == MMC_BUS_WIDTH_4 ) + cmdat |= CMD_DAT_CONT_BUS_WIDTH_4; + + MMC_CMD = cmd->opcode; + MMC_ARGH = cmd->arg >> 16; + MMC_ARGL = cmd->arg & 0xffff; + MMC_CMD_DAT_CONT = cmdat; + + atomic_set(&host->stuck_timeout, 0); + set_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events); + + + imask = IMXMCI_INT_MASK_DEFAULT; + imask &= ~INT_MASK_END_CMD_RES; + if ( cmdat & CMD_DAT_CONT_DATA_ENABLE ) { + /*imask &= ~INT_MASK_BUF_READY;*/ + imask &= ~INT_MASK_DATA_TRAN; + if ( cmdat & CMD_DAT_CONT_WRITE ) + imask &= ~INT_MASK_WRITE_OP_DONE; + if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) + imask &= ~INT_MASK_BUF_READY; + } + + spin_lock_irqsave(&host->lock, flags); + host->imask = imask; + MMC_INT_MASK = host->imask; + spin_unlock_irqrestore(&host->lock, flags); + + dev_dbg(mmc_dev(host->mmc), "CMD%02d (0x%02x) mask set to 0x%04x\n", + cmd->opcode, cmd->opcode, imask); + + imxmci_start_clock(host); +} + +static void imxmci_finish_request(struct imxmci_host *host, struct mmc_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + + host->pending_events &= ~(IMXMCI_PEND_WAIT_RESP_m | IMXMCI_PEND_DMA_END_m | + IMXMCI_PEND_DMA_DATA_m | IMXMCI_PEND_CPU_DATA_m); + + host->imask = IMXMCI_INT_MASK_DEFAULT; + MMC_INT_MASK = host->imask; + + spin_unlock_irqrestore(&host->lock, flags); + + if(req && req->cmd) + host->prev_cmd_code = req->cmd->opcode; + + host->req = NULL; + host->cmd = NULL; + host->data = NULL; + mmc_request_done(host->mmc, req); +} + +static int imxmci_finish_data(struct imxmci_host *host, unsigned int stat) +{ + struct mmc_data *data = host->data; + int data_error; + + if(test_and_clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)){ + imx_dma_disable(host->dma); + dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_nents, + host->dma_dir); + } + + if ( stat & STATUS_ERR_MASK ) { + dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",stat); + if(stat & (STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR)) + data->error = MMC_ERR_BADCRC; + else if(stat & STATUS_TIME_OUT_READ) + data->error = MMC_ERR_TIMEOUT; + else + data->error = MMC_ERR_FAILED; + } else { + data->bytes_xfered = host->dma_size; + } + + data_error = data->error; + + host->data = NULL; + + return data_error; +} + +static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat) +{ + struct mmc_command *cmd = host->cmd; + int i; + u32 a,b,c; + struct mmc_data *data = host->data; + + if (!cmd) + return 0; + + host->cmd = NULL; + + if (stat & STATUS_TIME_OUT_RESP) { + dev_dbg(mmc_dev(host->mmc), "CMD TIMEOUT\n"); + cmd->error = MMC_ERR_TIMEOUT; + } else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) { + dev_dbg(mmc_dev(host->mmc), "cmd crc error\n"); + cmd->error = MMC_ERR_BADCRC; + } + + if(cmd->flags & MMC_RSP_PRESENT) { + if(cmd->flags & MMC_RSP_136) { + for (i = 0; i < 4; i++) { + u32 a = MMC_RES_FIFO & 0xffff; + u32 b = MMC_RES_FIFO & 0xffff; + cmd->resp[i] = a<<16 | b; + } + } else { + a = MMC_RES_FIFO & 0xffff; + b = MMC_RES_FIFO & 0xffff; + c = MMC_RES_FIFO & 0xffff; + cmd->resp[0] = a<<24 | b<<8 | c>>8; + } + } + + dev_dbg(mmc_dev(host->mmc), "RESP 0x%08x, 0x%08x, 0x%08x, 0x%08x, error %d\n", + cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], cmd->error); + + if (data && (cmd->error == MMC_ERR_NONE) && !(stat & STATUS_ERR_MASK)) { + if (host->req->data->flags & MMC_DATA_WRITE) { + + /* Wait for FIFO to be empty before starting DMA write */ + + stat = MMC_STATUS; + if(imxmci_busy_wait_for_status(host, &stat, + STATUS_APPL_BUFF_FE, + 40, "imxmci_cmd_done DMA WR") < 0) { + cmd->error = MMC_ERR_FIFO; + imxmci_finish_data(host, stat); + if(host->req) + imxmci_finish_request(host, host->req); + dev_warn(mmc_dev(host->mmc), "STATUS = 0x%04x\n", + stat); + return 0; + } + + if(test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) { + imx_dma_enable(host->dma); + } + } + } else { + struct mmc_request *req; + imxmci_stop_clock(host); + req = host->req; + + if(data) + imxmci_finish_data(host, stat); + + if( req ) { + imxmci_finish_request(host, req); + } else { + dev_warn(mmc_dev(host->mmc), "imxmci_cmd_done: no request to finish\n"); + } + } + + return 1; +} + +static int imxmci_data_done(struct imxmci_host *host, unsigned int stat) +{ + struct mmc_data *data = host->data; + int data_error; + + if (!data) + return 0; + + data_error = imxmci_finish_data(host, stat); + + if (host->req->stop) { + imxmci_stop_clock(host); + imxmci_start_cmd(host, host->req->stop, 0); + } else { + struct mmc_request *req; + req = host->req; + if( req ) { + imxmci_finish_request(host, req); + } else { + dev_warn(mmc_dev(host->mmc), "imxmci_data_done: no request to finish\n"); + } + } + + return 1; +} + +static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat) +{ + int i; + int burst_len; + int trans_done = 0; + unsigned int stat = *pstat; + + if(host->actual_bus_width != MMC_BUS_WIDTH_4) + burst_len = 16; + else + burst_len = 64; + + /* This is unfortunately required */ + dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data running STATUS = 0x%x\n", + stat); + + udelay(20); /* required for clocks < 8MHz*/ + + if(host->dma_dir == DMA_FROM_DEVICE) { + imxmci_busy_wait_for_status(host, &stat, + STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE | + STATUS_TIME_OUT_READ, + 50, "imxmci_cpu_driven_data read"); + + while((stat & (STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE)) && + !(stat & STATUS_TIME_OUT_READ) && + (host->data_cnt < 512)) { + + udelay(20); /* required for clocks < 8MHz*/ + + for(i = burst_len; i>=2 ; i-=2) { + u16 data; + data = MMC_BUFFER_ACCESS; + udelay(10); /* required for clocks < 8MHz*/ + if(host->data_cnt+2 <= host->dma_size) { + *(host->data_ptr++) = data; + } else { + if(host->data_cnt < host->dma_size) + *(u8*)(host->data_ptr) = data; + } + host->data_cnt += 2; + } + + stat = MMC_STATUS; + + dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read %d burst %d STATUS = 0x%x\n", + host->data_cnt, burst_len, stat); + } + + if((stat & STATUS_DATA_TRANS_DONE) && (host->data_cnt >= 512)) + trans_done = 1; + + if(host->dma_size & 0x1ff) + stat &= ~STATUS_CRC_READ_ERR; + + if(stat & STATUS_TIME_OUT_READ) { + dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read timeout STATUS = 0x%x\n", + stat); + trans_done = -1; + } + + } else { + imxmci_busy_wait_for_status(host, &stat, + STATUS_APPL_BUFF_FE, + 20, "imxmci_cpu_driven_data write"); + + while((stat & STATUS_APPL_BUFF_FE) && + (host->data_cnt < host->dma_size)) { + if(burst_len >= host->dma_size - host->data_cnt) { + burst_len = host->dma_size - host->data_cnt; + host->data_cnt = host->dma_size; + trans_done = 1; + } else { + host->data_cnt += burst_len; + } + + for(i = burst_len; i>0 ; i-=2) + MMC_BUFFER_ACCESS = *(host->data_ptr++); + + stat = MMC_STATUS; + + dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data write burst %d STATUS = 0x%x\n", + burst_len, stat); + } + } + + *pstat = stat; + + return trans_done; +} + +static void imxmci_dma_irq(int dma, void *devid) +{ + struct imxmci_host *host = devid; + uint32_t stat = MMC_STATUS; + + atomic_set(&host->stuck_timeout, 0); + host->status_reg = stat; + set_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events); + tasklet_schedule(&host->tasklet); +} + +static irqreturn_t imxmci_irq(int irq, void *devid) +{ + struct imxmci_host *host = devid; + uint32_t stat = MMC_STATUS; + int handled = 1; + + MMC_INT_MASK = host->imask | INT_MASK_SDIO | INT_MASK_AUTO_CARD_DETECT; + + atomic_set(&host->stuck_timeout, 0); + host->status_reg = stat; + set_bit(IMXMCI_PEND_IRQ_b, &host->pending_events); + set_bit(IMXMCI_PEND_STARTED_b, &host->pending_events); + tasklet_schedule(&host->tasklet); + + return IRQ_RETVAL(handled);; +} + +static void imxmci_tasklet_fnc(unsigned long data) +{ + struct imxmci_host *host = (struct imxmci_host *)data; + u32 stat; + unsigned int data_dir_mask = 0; /* STATUS_WR_CRC_ERROR_CODE_MASK */ + int timeout = 0; + + if(atomic_read(&host->stuck_timeout) > 4) { + char *what; + timeout = 1; + stat = MMC_STATUS; + host->status_reg = stat; + if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) + if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) + what = "RESP+DMA"; + else + what = "RESP"; + else + if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) + if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events)) + what = "DATA"; + else + what = "DMA"; + else + what = "???"; + + dev_err(mmc_dev(host->mmc), "%s TIMEOUT, hardware stucked STATUS = 0x%04x IMASK = 0x%04x\n", + what, stat, MMC_INT_MASK); + dev_err(mmc_dev(host->mmc), "CMD_DAT_CONT = 0x%04x, MMC_BLK_LEN = 0x%04x, MMC_NOB = 0x%04x, DMA_CCR = 0x%08x\n", + MMC_CMD_DAT_CONT, MMC_BLK_LEN, MMC_NOB, CCR(host->dma)); + dev_err(mmc_dev(host->mmc), "CMD%d, prevCMD%d, bus %d-bit, dma_size = 0x%x\n", + host->cmd?host->cmd->opcode:0, host->prev_cmd_code, 1<actual_bus_width, host->dma_size); + } + + if(!host->present || timeout) + host->status_reg = STATUS_TIME_OUT_RESP | STATUS_TIME_OUT_READ | + STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR; + + if(test_bit(IMXMCI_PEND_IRQ_b, &host->pending_events) || timeout) { + clear_bit(IMXMCI_PEND_IRQ_b, &host->pending_events); + + stat = MMC_STATUS; + /* + * This is not required in theory, but there is chance to miss some flag + * which clears automatically by mask write, FreeScale original code keeps + * stat from IRQ time so do I + */ + stat |= host->status_reg; + + if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) + stat &= ~STATUS_CRC_READ_ERR; + + if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) { + imxmci_busy_wait_for_status(host, &stat, + STATUS_END_CMD_RESP | STATUS_ERR_MASK, + 20, "imxmci_tasklet_fnc resp (ERRATUM #4)"); + } + + if(stat & (STATUS_END_CMD_RESP | STATUS_ERR_MASK)) { + if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) + imxmci_cmd_done(host, stat); + if(host->data && (stat & STATUS_ERR_MASK)) + imxmci_data_done(host, stat); + } + + if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) { + stat |= MMC_STATUS; + if(imxmci_cpu_driven_data(host, &stat)){ + if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) + imxmci_cmd_done(host, stat); + atomic_clear_mask(IMXMCI_PEND_IRQ_m|IMXMCI_PEND_CPU_DATA_m, + &host->pending_events); + imxmci_data_done(host, stat); + } + } + } + + if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events) && + !test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) { + + stat = MMC_STATUS; + /* Same as above */ + stat |= host->status_reg; + + if(host->dma_dir == DMA_TO_DEVICE) { + data_dir_mask = STATUS_WRITE_OP_DONE; + } else { + data_dir_mask = STATUS_DATA_TRANS_DONE; + } + + if(stat & data_dir_mask) { + clear_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events); + imxmci_data_done(host, stat); + } + } + + if(test_and_clear_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events)) { + + if(host->cmd) + imxmci_cmd_done(host, STATUS_TIME_OUT_RESP); + + if(host->data) + imxmci_data_done(host, STATUS_TIME_OUT_READ | + STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR); + + if(host->req) + imxmci_finish_request(host, host->req); + + mmc_detect_change(host->mmc, msecs_to_jiffies(100)); + + } +} + +static void imxmci_request(struct mmc_host *mmc, struct mmc_request *req) +{ + struct imxmci_host *host = mmc_priv(mmc); + unsigned int cmdat; + + WARN_ON(host->req != NULL); + + host->req = req; + + cmdat = 0; + + if (req->data) { + imxmci_setup_data(host, req->data); + + cmdat |= CMD_DAT_CONT_DATA_ENABLE; + + if (req->data->flags & MMC_DATA_WRITE) + cmdat |= CMD_DAT_CONT_WRITE; + + if (req->data->flags & MMC_DATA_STREAM) { + cmdat |= CMD_DAT_CONT_STREAM_BLOCK; + } + } + + imxmci_start_cmd(host, req->cmd, cmdat); +} + +#define CLK_RATE 19200000 + +static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct imxmci_host *host = mmc_priv(mmc); + int prescaler; + + if( ios->bus_width==MMC_BUS_WIDTH_4 ) { + host->actual_bus_width = MMC_BUS_WIDTH_4; + imx_gpio_mode(PB11_PF_SD_DAT3); + }else{ + host->actual_bus_width = MMC_BUS_WIDTH_1; + imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11); + } + + if ( host->power_mode != ios->power_mode ) { + switch (ios->power_mode) { + case MMC_POWER_OFF: + break; + case MMC_POWER_UP: + set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events); + break; + case MMC_POWER_ON: + break; + } + host->power_mode = ios->power_mode; + } + + if ( ios->clock ) { + unsigned int clk; + + /* The prescaler is 5 for PERCLK2 equal to 96MHz + * then 96MHz / 5 = 19.2 MHz + */ + clk=imx_get_perclk2(); + prescaler=(clk+(CLK_RATE*7)/8)/CLK_RATE; + switch(prescaler) { + case 0: + case 1: prescaler = 0; + break; + case 2: prescaler = 1; + break; + case 3: prescaler = 2; + break; + case 4: prescaler = 4; + break; + default: + case 5: prescaler = 5; + break; + } + + dev_dbg(mmc_dev(host->mmc), "PERCLK2 %d MHz -> prescaler %d\n", + clk, prescaler); + + for(clk=0; clk<8; clk++) { + int x; + x = CLK_RATE / (1<clock) + break; + } + + MMC_STR_STP_CLK |= STR_STP_CLK_ENABLE; /* enable controller */ + + imxmci_stop_clock(host); + MMC_CLK_RATE = (prescaler<<3) | clk; + /* + * Under my understanding, clock should not be started there, because it would + * initiate SDHC sequencer and send last or random command into card + */ + /*imxmci_start_clock(host);*/ + + dev_dbg(mmc_dev(host->mmc), "MMC_CLK_RATE: 0x%08x\n", MMC_CLK_RATE); + } else { + imxmci_stop_clock(host); + } +} + +static const struct mmc_host_ops imxmci_ops = { + .request = imxmci_request, + .set_ios = imxmci_set_ios, +}; + +static struct resource *platform_device_resource(struct platform_device *dev, unsigned int mask, int nr) +{ + int i; + + for (i = 0; i < dev->num_resources; i++) + if (dev->resource[i].flags == mask && nr-- == 0) + return &dev->resource[i]; + return NULL; +} + +static int platform_device_irq(struct platform_device *dev, int nr) +{ + int i; + + for (i = 0; i < dev->num_resources; i++) + if (dev->resource[i].flags == IORESOURCE_IRQ && nr-- == 0) + return dev->resource[i].start; + return NO_IRQ; +} + +static void imxmci_check_status(unsigned long data) +{ + struct imxmci_host *host = (struct imxmci_host *)data; + + if( host->pdata->card_present() != host->present ) { + host->present ^= 1; + dev_info(mmc_dev(host->mmc), "card %s\n", + host->present ? "inserted" : "removed"); + + set_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events); + tasklet_schedule(&host->tasklet); + } + + if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events) || + test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) { + atomic_inc(&host->stuck_timeout); + if(atomic_read(&host->stuck_timeout) > 4) + tasklet_schedule(&host->tasklet); + } else { + atomic_set(&host->stuck_timeout, 0); + + } + + mod_timer(&host->timer, jiffies + (HZ>>1)); +} + +static int imxmci_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct imxmci_host *host = NULL; + struct resource *r; + int ret = 0, irq; + + printk(KERN_INFO "i.MX mmc driver\n"); + + r = platform_device_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_device_irq(pdev, 0); + if (!r || irq == NO_IRQ) + return -ENXIO; + + r = request_mem_region(r->start, 0x100, "IMXMCI"); + if (!r) + return -EBUSY; + + mmc = mmc_alloc_host(sizeof(struct imxmci_host), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + goto out; + } + + mmc->ops = &imxmci_ops; + mmc->f_min = 150000; + mmc->f_max = CLK_RATE/2; + mmc->ocr_avail = MMC_VDD_32_33; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_BYTEBLOCK; + + /* MMC core transfer sizes tunable parameters */ + mmc->max_hw_segs = 64; + mmc->max_phys_segs = 64; + mmc->max_seg_size = 64*512; /* default PAGE_CACHE_SIZE */ + mmc->max_req_size = 64*512; /* default PAGE_CACHE_SIZE */ + mmc->max_blk_size = 2048; + mmc->max_blk_count = 65535; + + host = mmc_priv(mmc); + host->mmc = mmc; + host->dma_allocated = 0; + host->pdata = pdev->dev.platform_data; + + spin_lock_init(&host->lock); + host->res = r; + host->irq = irq; + + imx_gpio_mode(PB8_PF_SD_DAT0); + imx_gpio_mode(PB9_PF_SD_DAT1); + imx_gpio_mode(PB10_PF_SD_DAT2); + /* Configured as GPIO with pull-up to ensure right MCC card mode */ + /* Switched to PB11_PF_SD_DAT3 if 4 bit bus is configured */ + imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11); + /* imx_gpio_mode(PB11_PF_SD_DAT3); */ + imx_gpio_mode(PB12_PF_SD_CLK); + imx_gpio_mode(PB13_PF_SD_CMD); + + imxmci_softreset(); + + if ( MMC_REV_NO != 0x390 ) { + dev_err(mmc_dev(host->mmc), "wrong rev.no. 0x%08x. aborting.\n", + MMC_REV_NO); + goto out; + } + + MMC_READ_TO = 0x2db4; /* recommended in data sheet */ + + host->imask = IMXMCI_INT_MASK_DEFAULT; + MMC_INT_MASK = host->imask; + + + if(imx_dma_request_by_prio(&host->dma, DRIVER_NAME, DMA_PRIO_LOW)<0){ + dev_err(mmc_dev(host->mmc), "imx_dma_request_by_prio failed\n"); + ret = -EBUSY; + goto out; + } + host->dma_allocated=1; + imx_dma_setup_handlers(host->dma, imxmci_dma_irq, NULL, host); + + tasklet_init(&host->tasklet, imxmci_tasklet_fnc, (unsigned long)host); + host->status_reg=0; + host->pending_events=0; + + ret = request_irq(host->irq, imxmci_irq, 0, DRIVER_NAME, host); + if (ret) + goto out; + + host->present = host->pdata->card_present(); + init_timer(&host->timer); + host->timer.data = (unsigned long)host; + host->timer.function = imxmci_check_status; + add_timer(&host->timer); + mod_timer(&host->timer, jiffies + (HZ>>1)); + + platform_set_drvdata(pdev, mmc); + + mmc_add_host(mmc); + + return 0; + +out: + if (host) { + if(host->dma_allocated){ + imx_dma_free(host->dma); + host->dma_allocated=0; + } + } + if (mmc) + mmc_free_host(mmc); + release_resource(r); + return ret; +} + +static int imxmci_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + if (mmc) { + struct imxmci_host *host = mmc_priv(mmc); + + tasklet_disable(&host->tasklet); + + del_timer_sync(&host->timer); + mmc_remove_host(mmc); + + free_irq(host->irq, host); + if(host->dma_allocated){ + imx_dma_free(host->dma); + host->dma_allocated=0; + } + + tasklet_kill(&host->tasklet); + + release_resource(host->res); + + mmc_free_host(mmc); + } + return 0; +} + +#ifdef CONFIG_PM +static int imxmci_suspend(struct platform_device *dev, pm_message_t state) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + int ret = 0; + + if (mmc) + ret = mmc_suspend_host(mmc, state); + + return ret; +} + +static int imxmci_resume(struct platform_device *dev) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + struct imxmci_host *host; + int ret = 0; + + if (mmc) { + host = mmc_priv(mmc); + if(host) + set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events); + ret = mmc_resume_host(mmc); + } + + return ret; +} +#else +#define imxmci_suspend NULL +#define imxmci_resume NULL +#endif /* CONFIG_PM */ + +static struct platform_driver imxmci_driver = { + .probe = imxmci_probe, + .remove = imxmci_remove, + .suspend = imxmci_suspend, + .resume = imxmci_resume, + .driver = { + .name = DRIVER_NAME, + } +}; + +static int __init imxmci_init(void) +{ + return platform_driver_register(&imxmci_driver); +} + +static void __exit imxmci_exit(void) +{ + platform_driver_unregister(&imxmci_driver); +} + +module_init(imxmci_init); +module_exit(imxmci_exit); + +MODULE_DESCRIPTION("i.MX Multimedia Card Interface Driver"); +MODULE_AUTHOR("Sascha Hauer, Pengutronix"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/imxmmc.h b/drivers/mmc/host/imxmmc.h new file mode 100644 index 00000000000..e5339e334db --- /dev/null +++ b/drivers/mmc/host/imxmmc.h @@ -0,0 +1,67 @@ + +# define __REG16(x) (*((volatile u16 *)IO_ADDRESS(x))) + +#define MMC_STR_STP_CLK __REG16(IMX_MMC_BASE + 0x00) +#define MMC_STATUS __REG16(IMX_MMC_BASE + 0x04) +#define MMC_CLK_RATE __REG16(IMX_MMC_BASE + 0x08) +#define MMC_CMD_DAT_CONT __REG16(IMX_MMC_BASE + 0x0C) +#define MMC_RES_TO __REG16(IMX_MMC_BASE + 0x10) +#define MMC_READ_TO __REG16(IMX_MMC_BASE + 0x14) +#define MMC_BLK_LEN __REG16(IMX_MMC_BASE + 0x18) +#define MMC_NOB __REG16(IMX_MMC_BASE + 0x1C) +#define MMC_REV_NO __REG16(IMX_MMC_BASE + 0x20) +#define MMC_INT_MASK __REG16(IMX_MMC_BASE + 0x24) +#define MMC_CMD __REG16(IMX_MMC_BASE + 0x28) +#define MMC_ARGH __REG16(IMX_MMC_BASE + 0x2C) +#define MMC_ARGL __REG16(IMX_MMC_BASE + 0x30) +#define MMC_RES_FIFO __REG16(IMX_MMC_BASE + 0x34) +#define MMC_BUFFER_ACCESS __REG16(IMX_MMC_BASE + 0x38) +#define MMC_BUFFER_ACCESS_OFS 0x38 + + +#define STR_STP_CLK_ENDIAN (1<<5) +#define STR_STP_CLK_RESET (1<<3) +#define STR_STP_CLK_ENABLE (1<<2) +#define STR_STP_CLK_START_CLK (1<<1) +#define STR_STP_CLK_STOP_CLK (1<<0) +#define STATUS_CARD_PRESENCE (1<<15) +#define STATUS_SDIO_INT_ACTIVE (1<<14) +#define STATUS_END_CMD_RESP (1<<13) +#define STATUS_WRITE_OP_DONE (1<<12) +#define STATUS_DATA_TRANS_DONE (1<<11) +#define STATUS_WR_CRC_ERROR_CODE_MASK (3<<10) +#define STATUS_CARD_BUS_CLK_RUN (1<<8) +#define STATUS_APPL_BUFF_FF (1<<7) +#define STATUS_APPL_BUFF_FE (1<<6) +#define STATUS_RESP_CRC_ERR (1<<5) +#define STATUS_CRC_READ_ERR (1<<3) +#define STATUS_CRC_WRITE_ERR (1<<2) +#define STATUS_TIME_OUT_RESP (1<<1) +#define STATUS_TIME_OUT_READ (1<<0) +#define STATUS_ERR_MASK 0x2f +#define CLK_RATE_PRESCALER(x) ((x) & 0x7) +#define CLK_RATE_CLK_RATE(x) (((x) & 0x7) << 3) +#define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1<<12) +#define CMD_DAT_CONT_STOP_READWAIT (1<<11) +#define CMD_DAT_CONT_START_READWAIT (1<<10) +#define CMD_DAT_CONT_BUS_WIDTH_1 (0<<8) +#define CMD_DAT_CONT_BUS_WIDTH_4 (2<<8) +#define CMD_DAT_CONT_INIT (1<<7) +#define CMD_DAT_CONT_BUSY (1<<6) +#define CMD_DAT_CONT_STREAM_BLOCK (1<<5) +#define CMD_DAT_CONT_WRITE (1<<4) +#define CMD_DAT_CONT_DATA_ENABLE (1<<3) +#define CMD_DAT_CONT_RESPONSE_FORMAT_R1 (1) +#define CMD_DAT_CONT_RESPONSE_FORMAT_R2 (2) +#define CMD_DAT_CONT_RESPONSE_FORMAT_R3 (3) +#define CMD_DAT_CONT_RESPONSE_FORMAT_R4 (4) +#define CMD_DAT_CONT_RESPONSE_FORMAT_R5 (5) +#define CMD_DAT_CONT_RESPONSE_FORMAT_R6 (6) +#define INT_MASK_AUTO_CARD_DETECT (1<<6) +#define INT_MASK_DAT0_EN (1<<5) +#define INT_MASK_SDIO (1<<4) +#define INT_MASK_BUF_READY (1<<3) +#define INT_MASK_END_CMD_RES (1<<2) +#define INT_MASK_WRITE_OP_DONE (1<<1) +#define INT_MASK_DATA_TRAN (1<<0) +#define INT_ALL (0x7f) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c new file mode 100644 index 00000000000..d11c2d23cee --- /dev/null +++ b/drivers/mmc/host/mmci.c @@ -0,0 +1,702 @@ +/* + * linux/drivers/mmc/mmci.c - ARM PrimeCell MMCI PL180/1 driver + * + * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "mmci.h" + +#define DRIVER_NAME "mmci-pl18x" + +#define DBG(host,fmt,args...) \ + pr_debug("%s: %s: " fmt, mmc_hostname(host->mmc), __func__ , args) + +static unsigned int fmax = 515633; + +static void +mmci_request_end(struct mmci_host *host, struct mmc_request *mrq) +{ + writel(0, host->base + MMCICOMMAND); + + BUG_ON(host->data); + + host->mrq = NULL; + host->cmd = NULL; + + if (mrq->data) + mrq->data->bytes_xfered = host->data_xfered; + + /* + * Need to drop the host lock here; mmc_request_done may call + * back into the driver... + */ + spin_unlock(&host->lock); + mmc_request_done(host->mmc, mrq); + spin_lock(&host->lock); +} + +static void mmci_stop_data(struct mmci_host *host) +{ + writel(0, host->base + MMCIDATACTRL); + writel(0, host->base + MMCIMASK1); + host->data = NULL; +} + +static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) +{ + unsigned int datactrl, timeout, irqmask; + unsigned long long clks; + void __iomem *base; + int blksz_bits; + + DBG(host, "blksz %04x blks %04x flags %08x\n", + data->blksz, data->blocks, data->flags); + + host->data = data; + host->size = data->blksz; + host->data_xfered = 0; + + mmci_init_sg(host, data); + + clks = (unsigned long long)data->timeout_ns * host->cclk; + do_div(clks, 1000000000UL); + + timeout = data->timeout_clks + (unsigned int)clks; + + base = host->base; + writel(timeout, base + MMCIDATATIMER); + writel(host->size, base + MMCIDATALENGTH); + + blksz_bits = ffs(data->blksz) - 1; + BUG_ON(1 << blksz_bits != data->blksz); + + datactrl = MCI_DPSM_ENABLE | blksz_bits << 4; + if (data->flags & MMC_DATA_READ) { + datactrl |= MCI_DPSM_DIRECTION; + irqmask = MCI_RXFIFOHALFFULLMASK; + + /* + * If we have less than a FIFOSIZE of bytes to transfer, + * trigger a PIO interrupt as soon as any data is available. + */ + if (host->size < MCI_FIFOSIZE) + irqmask |= MCI_RXDATAAVLBLMASK; + } else { + /* + * We don't actually need to include "FIFO empty" here + * since its implicit in "FIFO half empty". + */ + irqmask = MCI_TXFIFOHALFEMPTYMASK; + } + + writel(datactrl, base + MMCIDATACTRL); + writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0); + writel(irqmask, base + MMCIMASK1); +} + +static void +mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) +{ + void __iomem *base = host->base; + + DBG(host, "op %02x arg %08x flags %08x\n", + cmd->opcode, cmd->arg, cmd->flags); + + if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) { + writel(0, base + MMCICOMMAND); + udelay(1); + } + + c |= cmd->opcode | MCI_CPSM_ENABLE; + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) + c |= MCI_CPSM_LONGRSP; + c |= MCI_CPSM_RESPONSE; + } + if (/*interrupt*/0) + c |= MCI_CPSM_INTERRUPT; + + host->cmd = cmd; + + writel(cmd->arg, base + MMCIARGUMENT); + writel(c, base + MMCICOMMAND); +} + +static void +mmci_data_irq(struct mmci_host *host, struct mmc_data *data, + unsigned int status) +{ + if (status & MCI_DATABLOCKEND) { + host->data_xfered += data->blksz; + } + if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) { + if (status & MCI_DATACRCFAIL) + data->error = MMC_ERR_BADCRC; + else if (status & MCI_DATATIMEOUT) + data->error = MMC_ERR_TIMEOUT; + else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN)) + data->error = MMC_ERR_FIFO; + status |= MCI_DATAEND; + + /* + * We hit an error condition. Ensure that any data + * partially written to a page is properly coherent. + */ + if (host->sg_len && data->flags & MMC_DATA_READ) + flush_dcache_page(host->sg_ptr->page); + } + if (status & MCI_DATAEND) { + mmci_stop_data(host); + + if (!data->stop) { + mmci_request_end(host, data->mrq); + } else { + mmci_start_command(host, data->stop, 0); + } + } +} + +static void +mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, + unsigned int status) +{ + void __iomem *base = host->base; + + host->cmd = NULL; + + cmd->resp[0] = readl(base + MMCIRESPONSE0); + cmd->resp[1] = readl(base + MMCIRESPONSE1); + cmd->resp[2] = readl(base + MMCIRESPONSE2); + cmd->resp[3] = readl(base + MMCIRESPONSE3); + + if (status & MCI_CMDTIMEOUT) { + cmd->error = MMC_ERR_TIMEOUT; + } else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) { + cmd->error = MMC_ERR_BADCRC; + } + + if (!cmd->data || cmd->error != MMC_ERR_NONE) { + if (host->data) + mmci_stop_data(host); + mmci_request_end(host, cmd->mrq); + } else if (!(cmd->data->flags & MMC_DATA_READ)) { + mmci_start_data(host, cmd->data); + } +} + +static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain) +{ + void __iomem *base = host->base; + char *ptr = buffer; + u32 status; + + do { + int count = host->size - (readl(base + MMCIFIFOCNT) << 2); + + if (count > remain) + count = remain; + + if (count <= 0) + break; + + readsl(base + MMCIFIFO, ptr, count >> 2); + + ptr += count; + remain -= count; + + if (remain == 0) + break; + + status = readl(base + MMCISTATUS); + } while (status & MCI_RXDATAAVLBL); + + return ptr - buffer; +} + +static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int remain, u32 status) +{ + void __iomem *base = host->base; + char *ptr = buffer; + + do { + unsigned int count, maxcnt; + + maxcnt = status & MCI_TXFIFOEMPTY ? MCI_FIFOSIZE : MCI_FIFOHALFSIZE; + count = min(remain, maxcnt); + + writesl(base + MMCIFIFO, ptr, count >> 2); + + ptr += count; + remain -= count; + + if (remain == 0) + break; + + status = readl(base + MMCISTATUS); + } while (status & MCI_TXFIFOHALFEMPTY); + + return ptr - buffer; +} + +/* + * PIO data transfer IRQ handler. + */ +static irqreturn_t mmci_pio_irq(int irq, void *dev_id) +{ + struct mmci_host *host = dev_id; + void __iomem *base = host->base; + u32 status; + + status = readl(base + MMCISTATUS); + + DBG(host, "irq1 %08x\n", status); + + do { + unsigned long flags; + unsigned int remain, len; + char *buffer; + + /* + * For write, we only need to test the half-empty flag + * here - if the FIFO is completely empty, then by + * definition it is more than half empty. + * + * For read, check for data available. + */ + if (!(status & (MCI_TXFIFOHALFEMPTY|MCI_RXDATAAVLBL))) + break; + + /* + * Map the current scatter buffer. + */ + buffer = mmci_kmap_atomic(host, &flags) + host->sg_off; + remain = host->sg_ptr->length - host->sg_off; + + len = 0; + if (status & MCI_RXACTIVE) + len = mmci_pio_read(host, buffer, remain); + if (status & MCI_TXACTIVE) + len = mmci_pio_write(host, buffer, remain, status); + + /* + * Unmap the buffer. + */ + mmci_kunmap_atomic(host, buffer, &flags); + + host->sg_off += len; + host->size -= len; + remain -= len; + + if (remain) + break; + + /* + * If we were reading, and we have completed this + * page, ensure that the data cache is coherent. + */ + if (status & MCI_RXACTIVE) + flush_dcache_page(host->sg_ptr->page); + + if (!mmci_next_sg(host)) + break; + + status = readl(base + MMCISTATUS); + } while (1); + + /* + * If we're nearing the end of the read, switch to + * "any data available" mode. + */ + if (status & MCI_RXACTIVE && host->size < MCI_FIFOSIZE) + writel(MCI_RXDATAAVLBLMASK, base + MMCIMASK1); + + /* + * If we run out of data, disable the data IRQs; this + * prevents a race where the FIFO becomes empty before + * the chip itself has disabled the data path, and + * stops us racing with our data end IRQ. + */ + if (host->size == 0) { + writel(0, base + MMCIMASK1); + writel(readl(base + MMCIMASK0) | MCI_DATAENDMASK, base + MMCIMASK0); + } + + return IRQ_HANDLED; +} + +/* + * Handle completion of command and data transfers. + */ +static irqreturn_t mmci_irq(int irq, void *dev_id) +{ + struct mmci_host *host = dev_id; + u32 status; + int ret = 0; + + spin_lock(&host->lock); + + do { + struct mmc_command *cmd; + struct mmc_data *data; + + status = readl(host->base + MMCISTATUS); + status &= readl(host->base + MMCIMASK0); + writel(status, host->base + MMCICLEAR); + + DBG(host, "irq0 %08x\n", status); + + data = host->data; + if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN| + MCI_RXOVERRUN|MCI_DATAEND|MCI_DATABLOCKEND) && data) + mmci_data_irq(host, data, status); + + cmd = host->cmd; + if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND) && cmd) + mmci_cmd_irq(host, cmd, status); + + ret = 1; + } while (status); + + spin_unlock(&host->lock); + + return IRQ_RETVAL(ret); +} + +static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct mmci_host *host = mmc_priv(mmc); + + WARN_ON(host->mrq != NULL); + + spin_lock_irq(&host->lock); + + host->mrq = mrq; + + if (mrq->data && mrq->data->flags & MMC_DATA_READ) + mmci_start_data(host, mrq->data); + + mmci_start_command(host, mrq->cmd, 0); + + spin_unlock_irq(&host->lock); +} + +static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct mmci_host *host = mmc_priv(mmc); + u32 clk = 0, pwr = 0; + + if (ios->clock) { + if (ios->clock >= host->mclk) { + clk = MCI_CLK_BYPASS; + host->cclk = host->mclk; + } else { + clk = host->mclk / (2 * ios->clock) - 1; + if (clk > 256) + clk = 255; + host->cclk = host->mclk / (2 * (clk + 1)); + } + clk |= MCI_CLK_ENABLE; + } + + if (host->plat->translate_vdd) + pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd); + + switch (ios->power_mode) { + case MMC_POWER_OFF: + break; + case MMC_POWER_UP: + pwr |= MCI_PWR_UP; + break; + case MMC_POWER_ON: + pwr |= MCI_PWR_ON; + break; + } + + if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) + pwr |= MCI_ROD; + + writel(clk, host->base + MMCICLOCK); + + if (host->pwr != pwr) { + host->pwr = pwr; + writel(pwr, host->base + MMCIPOWER); + } +} + +static const struct mmc_host_ops mmci_ops = { + .request = mmci_request, + .set_ios = mmci_set_ios, +}; + +static void mmci_check_status(unsigned long data) +{ + struct mmci_host *host = (struct mmci_host *)data; + unsigned int status; + + status = host->plat->status(mmc_dev(host->mmc)); + if (status ^ host->oldstat) + mmc_detect_change(host->mmc, 0); + + host->oldstat = status; + mod_timer(&host->timer, jiffies + HZ); +} + +static int mmci_probe(struct amba_device *dev, void *id) +{ + struct mmc_platform_data *plat = dev->dev.platform_data; + struct mmci_host *host; + struct mmc_host *mmc; + int ret; + + /* must have platform data */ + if (!plat) { + ret = -EINVAL; + goto out; + } + + ret = amba_request_regions(dev, DRIVER_NAME); + if (ret) + goto out; + + mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev); + if (!mmc) { + ret = -ENOMEM; + goto rel_regions; + } + + host = mmc_priv(mmc); + host->clk = clk_get(&dev->dev, "MCLK"); + if (IS_ERR(host->clk)) { + ret = PTR_ERR(host->clk); + host->clk = NULL; + goto host_free; + } + + ret = clk_enable(host->clk); + if (ret) + goto clk_free; + + host->plat = plat; + host->mclk = clk_get_rate(host->clk); + host->mmc = mmc; + host->base = ioremap(dev->res.start, SZ_4K); + if (!host->base) { + ret = -ENOMEM; + goto clk_disable; + } + + mmc->ops = &mmci_ops; + mmc->f_min = (host->mclk + 511) / 512; + mmc->f_max = min(host->mclk, fmax); + mmc->ocr_avail = plat->ocr_mask; + mmc->caps = MMC_CAP_MULTIWRITE; + + /* + * We can do SGIO + */ + mmc->max_hw_segs = 16; + mmc->max_phys_segs = NR_SG; + + /* + * Since we only have a 16-bit data length register, we must + * ensure that we don't exceed 2^16-1 bytes in a single request. + */ + mmc->max_req_size = 65535; + + /* + * Set the maximum segment size. Since we aren't doing DMA + * (yet) we are only limited by the data length register. + */ + mmc->max_seg_size = mmc->max_req_size; + + /* + * Block size can be up to 2048 bytes, but must be a power of two. + */ + mmc->max_blk_size = 2048; + + /* + * No limit on the number of blocks transferred. + */ + mmc->max_blk_count = mmc->max_req_size; + + spin_lock_init(&host->lock); + + writel(0, host->base + MMCIMASK0); + writel(0, host->base + MMCIMASK1); + writel(0xfff, host->base + MMCICLEAR); + + ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host); + if (ret) + goto unmap; + + ret = request_irq(dev->irq[1], mmci_pio_irq, IRQF_SHARED, DRIVER_NAME " (pio)", host); + if (ret) + goto irq0_free; + + writel(MCI_IRQENABLE, host->base + MMCIMASK0); + + amba_set_drvdata(dev, mmc); + + mmc_add_host(mmc); + + printk(KERN_INFO "%s: MMCI rev %x cfg %02x at 0x%016llx irq %d,%d\n", + mmc_hostname(mmc), amba_rev(dev), amba_config(dev), + (unsigned long long)dev->res.start, dev->irq[0], dev->irq[1]); + + init_timer(&host->timer); + host->timer.data = (unsigned long)host; + host->timer.function = mmci_check_status; + host->timer.expires = jiffies + HZ; + add_timer(&host->timer); + + return 0; + + irq0_free: + free_irq(dev->irq[0], host); + unmap: + iounmap(host->base); + clk_disable: + clk_disable(host->clk); + clk_free: + clk_put(host->clk); + host_free: + mmc_free_host(mmc); + rel_regions: + amba_release_regions(dev); + out: + return ret; +} + +static int mmci_remove(struct amba_device *dev) +{ + struct mmc_host *mmc = amba_get_drvdata(dev); + + amba_set_drvdata(dev, NULL); + + if (mmc) { + struct mmci_host *host = mmc_priv(mmc); + + del_timer_sync(&host->timer); + + mmc_remove_host(mmc); + + writel(0, host->base + MMCIMASK0); + writel(0, host->base + MMCIMASK1); + + writel(0, host->base + MMCICOMMAND); + writel(0, host->base + MMCIDATACTRL); + + free_irq(dev->irq[0], host); + free_irq(dev->irq[1], host); + + iounmap(host->base); + clk_disable(host->clk); + clk_put(host->clk); + + mmc_free_host(mmc); + + amba_release_regions(dev); + } + + return 0; +} + +#ifdef CONFIG_PM +static int mmci_suspend(struct amba_device *dev, pm_message_t state) +{ + struct mmc_host *mmc = amba_get_drvdata(dev); + int ret = 0; + + if (mmc) { + struct mmci_host *host = mmc_priv(mmc); + + ret = mmc_suspend_host(mmc, state); + if (ret == 0) + writel(0, host->base + MMCIMASK0); + } + + return ret; +} + +static int mmci_resume(struct amba_device *dev) +{ + struct mmc_host *mmc = amba_get_drvdata(dev); + int ret = 0; + + if (mmc) { + struct mmci_host *host = mmc_priv(mmc); + + writel(MCI_IRQENABLE, host->base + MMCIMASK0); + + ret = mmc_resume_host(mmc); + } + + return ret; +} +#else +#define mmci_suspend NULL +#define mmci_resume NULL +#endif + +static struct amba_id mmci_ids[] = { + { + .id = 0x00041180, + .mask = 0x000fffff, + }, + { + .id = 0x00041181, + .mask = 0x000fffff, + }, + { 0, 0 }, +}; + +static struct amba_driver mmci_driver = { + .drv = { + .name = DRIVER_NAME, + }, + .probe = mmci_probe, + .remove = mmci_remove, + .suspend = mmci_suspend, + .resume = mmci_resume, + .id_table = mmci_ids, +}; + +static int __init mmci_init(void) +{ + return amba_driver_register(&mmci_driver); +} + +static void __exit mmci_exit(void) +{ + amba_driver_unregister(&mmci_driver); +} + +module_init(mmci_init); +module_exit(mmci_exit); +module_param(fmax, uint, 0444); + +MODULE_DESCRIPTION("ARM PrimeCell PL180/181 Multimedia Card Interface driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h new file mode 100644 index 00000000000..6d7eadc9a67 --- /dev/null +++ b/drivers/mmc/host/mmci.h @@ -0,0 +1,179 @@ +/* + * linux/drivers/mmc/mmci.h - ARM PrimeCell MMCI PL180/1 driver + * + * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#define MMCIPOWER 0x000 +#define MCI_PWR_OFF 0x00 +#define MCI_PWR_UP 0x02 +#define MCI_PWR_ON 0x03 +#define MCI_OD (1 << 6) +#define MCI_ROD (1 << 7) + +#define MMCICLOCK 0x004 +#define MCI_CLK_ENABLE (1 << 8) +#define MCI_CLK_PWRSAVE (1 << 9) +#define MCI_CLK_BYPASS (1 << 10) + +#define MMCIARGUMENT 0x008 +#define MMCICOMMAND 0x00c +#define MCI_CPSM_RESPONSE (1 << 6) +#define MCI_CPSM_LONGRSP (1 << 7) +#define MCI_CPSM_INTERRUPT (1 << 8) +#define MCI_CPSM_PENDING (1 << 9) +#define MCI_CPSM_ENABLE (1 << 10) + +#define MMCIRESPCMD 0x010 +#define MMCIRESPONSE0 0x014 +#define MMCIRESPONSE1 0x018 +#define MMCIRESPONSE2 0x01c +#define MMCIRESPONSE3 0x020 +#define MMCIDATATIMER 0x024 +#define MMCIDATALENGTH 0x028 +#define MMCIDATACTRL 0x02c +#define MCI_DPSM_ENABLE (1 << 0) +#define MCI_DPSM_DIRECTION (1 << 1) +#define MCI_DPSM_MODE (1 << 2) +#define MCI_DPSM_DMAENABLE (1 << 3) + +#define MMCIDATACNT 0x030 +#define MMCISTATUS 0x034 +#define MCI_CMDCRCFAIL (1 << 0) +#define MCI_DATACRCFAIL (1 << 1) +#define MCI_CMDTIMEOUT (1 << 2) +#define MCI_DATATIMEOUT (1 << 3) +#define MCI_TXUNDERRUN (1 << 4) +#define MCI_RXOVERRUN (1 << 5) +#define MCI_CMDRESPEND (1 << 6) +#define MCI_CMDSENT (1 << 7) +#define MCI_DATAEND (1 << 8) +#define MCI_DATABLOCKEND (1 << 10) +#define MCI_CMDACTIVE (1 << 11) +#define MCI_TXACTIVE (1 << 12) +#define MCI_RXACTIVE (1 << 13) +#define MCI_TXFIFOHALFEMPTY (1 << 14) +#define MCI_RXFIFOHALFFULL (1 << 15) +#define MCI_TXFIFOFULL (1 << 16) +#define MCI_RXFIFOFULL (1 << 17) +#define MCI_TXFIFOEMPTY (1 << 18) +#define MCI_RXFIFOEMPTY (1 << 19) +#define MCI_TXDATAAVLBL (1 << 20) +#define MCI_RXDATAAVLBL (1 << 21) + +#define MMCICLEAR 0x038 +#define MCI_CMDCRCFAILCLR (1 << 0) +#define MCI_DATACRCFAILCLR (1 << 1) +#define MCI_CMDTIMEOUTCLR (1 << 2) +#define MCI_DATATIMEOUTCLR (1 << 3) +#define MCI_TXUNDERRUNCLR (1 << 4) +#define MCI_RXOVERRUNCLR (1 << 5) +#define MCI_CMDRESPENDCLR (1 << 6) +#define MCI_CMDSENTCLR (1 << 7) +#define MCI_DATAENDCLR (1 << 8) +#define MCI_DATABLOCKENDCLR (1 << 10) + +#define MMCIMASK0 0x03c +#define MCI_CMDCRCFAILMASK (1 << 0) +#define MCI_DATACRCFAILMASK (1 << 1) +#define MCI_CMDTIMEOUTMASK (1 << 2) +#define MCI_DATATIMEOUTMASK (1 << 3) +#define MCI_TXUNDERRUNMASK (1 << 4) +#define MCI_RXOVERRUNMASK (1 << 5) +#define MCI_CMDRESPENDMASK (1 << 6) +#define MCI_CMDSENTMASK (1 << 7) +#define MCI_DATAENDMASK (1 << 8) +#define MCI_DATABLOCKENDMASK (1 << 10) +#define MCI_CMDACTIVEMASK (1 << 11) +#define MCI_TXACTIVEMASK (1 << 12) +#define MCI_RXACTIVEMASK (1 << 13) +#define MCI_TXFIFOHALFEMPTYMASK (1 << 14) +#define MCI_RXFIFOHALFFULLMASK (1 << 15) +#define MCI_TXFIFOFULLMASK (1 << 16) +#define MCI_RXFIFOFULLMASK (1 << 17) +#define MCI_TXFIFOEMPTYMASK (1 << 18) +#define MCI_RXFIFOEMPTYMASK (1 << 19) +#define MCI_TXDATAAVLBLMASK (1 << 20) +#define MCI_RXDATAAVLBLMASK (1 << 21) + +#define MMCIMASK1 0x040 +#define MMCIFIFOCNT 0x048 +#define MMCIFIFO 0x080 /* to 0x0bc */ + +#define MCI_IRQENABLE \ + (MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK| \ + MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \ + MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATABLOCKENDMASK) + +/* + * The size of the FIFO in bytes. + */ +#define MCI_FIFOSIZE (16*4) + +#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2) + +#define NR_SG 16 + +struct clk; + +struct mmci_host { + void __iomem *base; + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + struct mmc_host *mmc; + struct clk *clk; + + unsigned int data_xfered; + + spinlock_t lock; + + unsigned int mclk; + unsigned int cclk; + u32 pwr; + struct mmc_platform_data *plat; + + struct timer_list timer; + unsigned int oldstat; + + unsigned int sg_len; + + /* pio stuff */ + struct scatterlist *sg_ptr; + unsigned int sg_off; + unsigned int size; +}; + +static inline void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) +{ + /* + * Ideally, we want the higher levels to pass us a scatter list. + */ + host->sg_len = data->sg_len; + host->sg_ptr = data->sg; + host->sg_off = 0; +} + +static inline int mmci_next_sg(struct mmci_host *host) +{ + host->sg_ptr++; + host->sg_off = 0; + return --host->sg_len; +} + +static inline char *mmci_kmap_atomic(struct mmci_host *host, unsigned long *flags) +{ + struct scatterlist *sg = host->sg_ptr; + + local_irq_save(*flags); + return kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset; +} + +static inline void mmci_kunmap_atomic(struct mmci_host *host, void *buffer, unsigned long *flags) +{ + kunmap_atomic(buffer, KM_BIO_SRC_IRQ); + local_irq_restore(*flags); +} diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c new file mode 100644 index 00000000000..e851384e51f --- /dev/null +++ b/drivers/mmc/host/omap.c @@ -0,0 +1,1288 @@ +/* + * linux/drivers/media/mmc/omap.c + * + * Copyright (C) 2004 Nokia Corporation + * Written by Tuukka Tikkanen and Juha Yrjölä + * Misc hacks here and there by Tony Lindgren + * Other hacks (DMA, SD, etc) by David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define OMAP_MMC_REG_CMD 0x00 +#define OMAP_MMC_REG_ARGL 0x04 +#define OMAP_MMC_REG_ARGH 0x08 +#define OMAP_MMC_REG_CON 0x0c +#define OMAP_MMC_REG_STAT 0x10 +#define OMAP_MMC_REG_IE 0x14 +#define OMAP_MMC_REG_CTO 0x18 +#define OMAP_MMC_REG_DTO 0x1c +#define OMAP_MMC_REG_DATA 0x20 +#define OMAP_MMC_REG_BLEN 0x24 +#define OMAP_MMC_REG_NBLK 0x28 +#define OMAP_MMC_REG_BUF 0x2c +#define OMAP_MMC_REG_SDIO 0x34 +#define OMAP_MMC_REG_REV 0x3c +#define OMAP_MMC_REG_RSP0 0x40 +#define OMAP_MMC_REG_RSP1 0x44 +#define OMAP_MMC_REG_RSP2 0x48 +#define OMAP_MMC_REG_RSP3 0x4c +#define OMAP_MMC_REG_RSP4 0x50 +#define OMAP_MMC_REG_RSP5 0x54 +#define OMAP_MMC_REG_RSP6 0x58 +#define OMAP_MMC_REG_RSP7 0x5c +#define OMAP_MMC_REG_IOSR 0x60 +#define OMAP_MMC_REG_SYSC 0x64 +#define OMAP_MMC_REG_SYSS 0x68 + +#define OMAP_MMC_STAT_CARD_ERR (1 << 14) +#define OMAP_MMC_STAT_CARD_IRQ (1 << 13) +#define OMAP_MMC_STAT_OCR_BUSY (1 << 12) +#define OMAP_MMC_STAT_A_EMPTY (1 << 11) +#define OMAP_MMC_STAT_A_FULL (1 << 10) +#define OMAP_MMC_STAT_CMD_CRC (1 << 8) +#define OMAP_MMC_STAT_CMD_TOUT (1 << 7) +#define OMAP_MMC_STAT_DATA_CRC (1 << 6) +#define OMAP_MMC_STAT_DATA_TOUT (1 << 5) +#define OMAP_MMC_STAT_END_BUSY (1 << 4) +#define OMAP_MMC_STAT_END_OF_DATA (1 << 3) +#define OMAP_MMC_STAT_CARD_BUSY (1 << 2) +#define OMAP_MMC_STAT_END_OF_CMD (1 << 0) + +#define OMAP_MMC_READ(host, reg) __raw_readw((host)->virt_base + OMAP_MMC_REG_##reg) +#define OMAP_MMC_WRITE(host, reg, val) __raw_writew((val), (host)->virt_base + OMAP_MMC_REG_##reg) + +/* + * Command types + */ +#define OMAP_MMC_CMDTYPE_BC 0 +#define OMAP_MMC_CMDTYPE_BCR 1 +#define OMAP_MMC_CMDTYPE_AC 2 +#define OMAP_MMC_CMDTYPE_ADTC 3 + + +#define DRIVER_NAME "mmci-omap" + +/* Specifies how often in millisecs to poll for card status changes + * when the cover switch is open */ +#define OMAP_MMC_SWITCH_POLL_DELAY 500 + +static int mmc_omap_enable_poll = 1; + +struct mmc_omap_host { + int initialized; + int suspended; + struct mmc_request * mrq; + struct mmc_command * cmd; + struct mmc_data * data; + struct mmc_host * mmc; + struct device * dev; + unsigned char id; /* 16xx chips have 2 MMC blocks */ + struct clk * iclk; + struct clk * fclk; + struct resource *mem_res; + void __iomem *virt_base; + unsigned int phys_base; + int irq; + unsigned char bus_mode; + unsigned char hw_bus_mode; + + unsigned int sg_len; + int sg_idx; + u16 * buffer; + u32 buffer_bytes_left; + u32 total_bytes_left; + + unsigned use_dma:1; + unsigned brs_received:1, dma_done:1; + unsigned dma_is_read:1; + unsigned dma_in_use:1; + int dma_ch; + spinlock_t dma_lock; + struct timer_list dma_timer; + unsigned dma_len; + + short power_pin; + short wp_pin; + + int switch_pin; + struct work_struct switch_work; + struct timer_list switch_timer; + int switch_last_state; +}; + +static inline int +mmc_omap_cover_is_open(struct mmc_omap_host *host) +{ + if (host->switch_pin < 0) + return 0; + return omap_get_gpio_datain(host->switch_pin); +} + +static ssize_t +mmc_omap_show_cover_switch(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mmc_omap_host *host = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", mmc_omap_cover_is_open(host) ? "open" : + "closed"); +} + +static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL); + +static ssize_t +mmc_omap_show_enable_poll(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", mmc_omap_enable_poll); +} + +static ssize_t +mmc_omap_store_enable_poll(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + int enable_poll; + + if (sscanf(buf, "%10d", &enable_poll) != 1) + return -EINVAL; + + if (enable_poll != mmc_omap_enable_poll) { + struct mmc_omap_host *host = dev_get_drvdata(dev); + + mmc_omap_enable_poll = enable_poll; + if (enable_poll && host->switch_pin >= 0) + schedule_work(&host->switch_work); + } + return size; +} + +static DEVICE_ATTR(enable_poll, 0664, + mmc_omap_show_enable_poll, mmc_omap_store_enable_poll); + +static void +mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) +{ + u32 cmdreg; + u32 resptype; + u32 cmdtype; + + host->cmd = cmd; + + resptype = 0; + cmdtype = 0; + + /* Our hardware needs to know exact type */ + switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + break; + case MMC_RSP_R1: + case MMC_RSP_R1B: + /* resp 1, 1b, 6, 7 */ + resptype = 1; + break; + case MMC_RSP_R2: + resptype = 2; + break; + case MMC_RSP_R3: + resptype = 3; + break; + default: + dev_err(mmc_dev(host->mmc), "Invalid response type: %04x\n", mmc_resp_type(cmd)); + break; + } + + if (mmc_cmd_type(cmd) == MMC_CMD_ADTC) { + cmdtype = OMAP_MMC_CMDTYPE_ADTC; + } else if (mmc_cmd_type(cmd) == MMC_CMD_BC) { + cmdtype = OMAP_MMC_CMDTYPE_BC; + } else if (mmc_cmd_type(cmd) == MMC_CMD_BCR) { + cmdtype = OMAP_MMC_CMDTYPE_BCR; + } else { + cmdtype = OMAP_MMC_CMDTYPE_AC; + } + + cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12); + + if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) + cmdreg |= 1 << 6; + + if (cmd->flags & MMC_RSP_BUSY) + cmdreg |= 1 << 11; + + if (host->data && !(host->data->flags & MMC_DATA_WRITE)) + cmdreg |= 1 << 15; + + clk_enable(host->fclk); + + OMAP_MMC_WRITE(host, CTO, 200); + OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff); + OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16); + OMAP_MMC_WRITE(host, IE, + OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL | + OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT | + OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT | + OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR | + OMAP_MMC_STAT_END_OF_DATA); + OMAP_MMC_WRITE(host, CMD, cmdreg); +} + +static void +mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) +{ + if (host->dma_in_use) { + enum dma_data_direction dma_data_dir; + + BUG_ON(host->dma_ch < 0); + if (data->error != MMC_ERR_NONE) + omap_stop_dma(host->dma_ch); + /* Release DMA channel lazily */ + mod_timer(&host->dma_timer, jiffies + HZ); + if (data->flags & MMC_DATA_WRITE) + dma_data_dir = DMA_TO_DEVICE; + else + dma_data_dir = DMA_FROM_DEVICE; + dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len, + dma_data_dir); + } + host->data = NULL; + host->sg_len = 0; + clk_disable(host->fclk); + + /* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing + * dozens of requests until the card finishes writing data. + * It'd be cheaper to just wait till an EOFB interrupt arrives... + */ + + if (!data->stop) { + host->mrq = NULL; + mmc_request_done(host->mmc, data->mrq); + return; + } + + mmc_omap_start_command(host, data->stop); +} + +static void +mmc_omap_end_of_data(struct mmc_omap_host *host, struct mmc_data *data) +{ + unsigned long flags; + int done; + + if (!host->dma_in_use) { + mmc_omap_xfer_done(host, data); + return; + } + done = 0; + spin_lock_irqsave(&host->dma_lock, flags); + if (host->dma_done) + done = 1; + else + host->brs_received = 1; + spin_unlock_irqrestore(&host->dma_lock, flags); + if (done) + mmc_omap_xfer_done(host, data); +} + +static void +mmc_omap_dma_timer(unsigned long data) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *) data; + + BUG_ON(host->dma_ch < 0); + omap_free_dma(host->dma_ch); + host->dma_ch = -1; +} + +static void +mmc_omap_dma_done(struct mmc_omap_host *host, struct mmc_data *data) +{ + unsigned long flags; + int done; + + done = 0; + spin_lock_irqsave(&host->dma_lock, flags); + if (host->brs_received) + done = 1; + else + host->dma_done = 1; + spin_unlock_irqrestore(&host->dma_lock, flags); + if (done) + mmc_omap_xfer_done(host, data); +} + +static void +mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) +{ + host->cmd = NULL; + + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) { + /* response type 2 */ + cmd->resp[3] = + OMAP_MMC_READ(host, RSP0) | + (OMAP_MMC_READ(host, RSP1) << 16); + cmd->resp[2] = + OMAP_MMC_READ(host, RSP2) | + (OMAP_MMC_READ(host, RSP3) << 16); + cmd->resp[1] = + OMAP_MMC_READ(host, RSP4) | + (OMAP_MMC_READ(host, RSP5) << 16); + cmd->resp[0] = + OMAP_MMC_READ(host, RSP6) | + (OMAP_MMC_READ(host, RSP7) << 16); + } else { + /* response types 1, 1b, 3, 4, 5, 6 */ + cmd->resp[0] = + OMAP_MMC_READ(host, RSP6) | + (OMAP_MMC_READ(host, RSP7) << 16); + } + } + + if (host->data == NULL || cmd->error != MMC_ERR_NONE) { + host->mrq = NULL; + clk_disable(host->fclk); + mmc_request_done(host->mmc, cmd->mrq); + } +} + +/* PIO only */ +static void +mmc_omap_sg_to_buf(struct mmc_omap_host *host) +{ + struct scatterlist *sg; + + sg = host->data->sg + host->sg_idx; + host->buffer_bytes_left = sg->length; + host->buffer = page_address(sg->page) + sg->offset; + if (host->buffer_bytes_left > host->total_bytes_left) + host->buffer_bytes_left = host->total_bytes_left; +} + +/* PIO only */ +static void +mmc_omap_xfer_data(struct mmc_omap_host *host, int write) +{ + int n; + + if (host->buffer_bytes_left == 0) { + host->sg_idx++; + BUG_ON(host->sg_idx == host->sg_len); + mmc_omap_sg_to_buf(host); + } + n = 64; + if (n > host->buffer_bytes_left) + n = host->buffer_bytes_left; + host->buffer_bytes_left -= n; + host->total_bytes_left -= n; + host->data->bytes_xfered += n; + + if (write) { + __raw_writesw(host->virt_base + OMAP_MMC_REG_DATA, host->buffer, n); + } else { + __raw_readsw(host->virt_base + OMAP_MMC_REG_DATA, host->buffer, n); + } +} + +static inline void mmc_omap_report_irq(u16 status) +{ + static const char *mmc_omap_status_bits[] = { + "EOC", "CD", "CB", "BRS", "EOFB", "DTO", "DCRC", "CTO", + "CCRC", "CRW", "AF", "AE", "OCRB", "CIRQ", "CERR" + }; + int i, c = 0; + + for (i = 0; i < ARRAY_SIZE(mmc_omap_status_bits); i++) + if (status & (1 << i)) { + if (c) + printk(" "); + printk("%s", mmc_omap_status_bits[i]); + c++; + } +} + +static irqreturn_t mmc_omap_irq(int irq, void *dev_id) +{ + struct mmc_omap_host * host = (struct mmc_omap_host *)dev_id; + u16 status; + int end_command; + int end_transfer; + int transfer_error; + + if (host->cmd == NULL && host->data == NULL) { + status = OMAP_MMC_READ(host, STAT); + dev_info(mmc_dev(host->mmc),"spurious irq 0x%04x\n", status); + if (status != 0) { + OMAP_MMC_WRITE(host, STAT, status); + OMAP_MMC_WRITE(host, IE, 0); + } + return IRQ_HANDLED; + } + + end_command = 0; + end_transfer = 0; + transfer_error = 0; + + while ((status = OMAP_MMC_READ(host, STAT)) != 0) { + OMAP_MMC_WRITE(host, STAT, status); +#ifdef CONFIG_MMC_DEBUG + dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ", + status, host->cmd != NULL ? host->cmd->opcode : -1); + mmc_omap_report_irq(status); + printk("\n"); +#endif + if (host->total_bytes_left) { + if ((status & OMAP_MMC_STAT_A_FULL) || + (status & OMAP_MMC_STAT_END_OF_DATA)) + mmc_omap_xfer_data(host, 0); + if (status & OMAP_MMC_STAT_A_EMPTY) + mmc_omap_xfer_data(host, 1); + } + + if (status & OMAP_MMC_STAT_END_OF_DATA) { + end_transfer = 1; + } + + if (status & OMAP_MMC_STAT_DATA_TOUT) { + dev_dbg(mmc_dev(host->mmc), "data timeout\n"); + if (host->data) { + host->data->error |= MMC_ERR_TIMEOUT; + transfer_error = 1; + } + } + + if (status & OMAP_MMC_STAT_DATA_CRC) { + if (host->data) { + host->data->error |= MMC_ERR_BADCRC; + dev_dbg(mmc_dev(host->mmc), + "data CRC error, bytes left %d\n", + host->total_bytes_left); + transfer_error = 1; + } else { + dev_dbg(mmc_dev(host->mmc), "data CRC error\n"); + } + } + + if (status & OMAP_MMC_STAT_CMD_TOUT) { + /* Timeouts are routine with some commands */ + if (host->cmd) { + if (host->cmd->opcode != MMC_ALL_SEND_CID && + host->cmd->opcode != + MMC_SEND_OP_COND && + host->cmd->opcode != + MMC_APP_CMD && + !mmc_omap_cover_is_open(host)) + dev_err(mmc_dev(host->mmc), + "command timeout, CMD %d\n", + host->cmd->opcode); + host->cmd->error = MMC_ERR_TIMEOUT; + end_command = 1; + } + } + + if (status & OMAP_MMC_STAT_CMD_CRC) { + if (host->cmd) { + dev_err(mmc_dev(host->mmc), + "command CRC error (CMD%d, arg 0x%08x)\n", + host->cmd->opcode, host->cmd->arg); + host->cmd->error = MMC_ERR_BADCRC; + end_command = 1; + } else + dev_err(mmc_dev(host->mmc), + "command CRC error without cmd?\n"); + } + + if (status & OMAP_MMC_STAT_CARD_ERR) { + if (host->cmd && host->cmd->opcode == MMC_STOP_TRANSMISSION) { + u32 response = OMAP_MMC_READ(host, RSP6) + | (OMAP_MMC_READ(host, RSP7) << 16); + /* STOP sometimes sets must-ignore bits */ + if (!(response & (R1_CC_ERROR + | R1_ILLEGAL_COMMAND + | R1_COM_CRC_ERROR))) { + end_command = 1; + continue; + } + } + + dev_dbg(mmc_dev(host->mmc), "card status error (CMD%d)\n", + host->cmd->opcode); + if (host->cmd) { + host->cmd->error = MMC_ERR_FAILED; + end_command = 1; + } + if (host->data) { + host->data->error = MMC_ERR_FAILED; + transfer_error = 1; + } + } + + /* + * NOTE: On 1610 the END_OF_CMD may come too early when + * starting a write + */ + if ((status & OMAP_MMC_STAT_END_OF_CMD) && + (!(status & OMAP_MMC_STAT_A_EMPTY))) { + end_command = 1; + } + } + + if (end_command) { + mmc_omap_cmd_done(host, host->cmd); + } + if (transfer_error) + mmc_omap_xfer_done(host, host->data); + else if (end_transfer) + mmc_omap_end_of_data(host, host->data); + + return IRQ_HANDLED; +} + +static irqreturn_t mmc_omap_switch_irq(int irq, void *dev_id) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *) dev_id; + + schedule_work(&host->switch_work); + + return IRQ_HANDLED; +} + +static void mmc_omap_switch_timer(unsigned long arg) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *) arg; + + schedule_work(&host->switch_work); +} + +static void mmc_omap_switch_handler(struct work_struct *work) +{ + struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, switch_work); + struct mmc_card *card; + static int complained = 0; + int cards = 0, cover_open; + + if (host->switch_pin == -1) + return; + cover_open = mmc_omap_cover_is_open(host); + if (cover_open != host->switch_last_state) { + kobject_uevent(&host->dev->kobj, KOBJ_CHANGE); + host->switch_last_state = cover_open; + } + mmc_detect_change(host->mmc, 0); + list_for_each_entry(card, &host->mmc->cards, node) { + if (mmc_card_present(card)) + cards++; + } + if (mmc_omap_cover_is_open(host)) { + if (!complained) { + dev_info(mmc_dev(host->mmc), "cover is open"); + complained = 1; + } + if (mmc_omap_enable_poll) + mod_timer(&host->switch_timer, jiffies + + msecs_to_jiffies(OMAP_MMC_SWITCH_POLL_DELAY)); + } else { + complained = 0; + } +} + +/* Prepare to transfer the next segment of a scatterlist */ +static void +mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data) +{ + int dma_ch = host->dma_ch; + unsigned long data_addr; + u16 buf, frame; + u32 count; + struct scatterlist *sg = &data->sg[host->sg_idx]; + int src_port = 0; + int dst_port = 0; + int sync_dev = 0; + + data_addr = host->phys_base + OMAP_MMC_REG_DATA; + frame = data->blksz; + count = sg_dma_len(sg); + + if ((data->blocks == 1) && (count > data->blksz)) + count = frame; + + host->dma_len = count; + + /* FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx and 24xx. + * Use 16 or 32 word frames when the blocksize is at least that large. + * Blocksize is usually 512 bytes; but not for some SD reads. + */ + if (cpu_is_omap15xx() && frame > 32) + frame = 32; + else if (frame > 64) + frame = 64; + count /= frame; + frame >>= 1; + + if (!(data->flags & MMC_DATA_WRITE)) { + buf = 0x800f | ((frame - 1) << 8); + + if (cpu_class_is_omap1()) { + src_port = OMAP_DMA_PORT_TIPB; + dst_port = OMAP_DMA_PORT_EMIFF; + } + if (cpu_is_omap24xx()) + sync_dev = OMAP24XX_DMA_MMC1_RX; + + omap_set_dma_src_params(dma_ch, src_port, + OMAP_DMA_AMODE_CONSTANT, + data_addr, 0, 0); + omap_set_dma_dest_params(dma_ch, dst_port, + OMAP_DMA_AMODE_POST_INC, + sg_dma_address(sg), 0, 0); + omap_set_dma_dest_data_pack(dma_ch, 1); + omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4); + } else { + buf = 0x0f80 | ((frame - 1) << 0); + + if (cpu_class_is_omap1()) { + src_port = OMAP_DMA_PORT_EMIFF; + dst_port = OMAP_DMA_PORT_TIPB; + } + if (cpu_is_omap24xx()) + sync_dev = OMAP24XX_DMA_MMC1_TX; + + omap_set_dma_dest_params(dma_ch, dst_port, + OMAP_DMA_AMODE_CONSTANT, + data_addr, 0, 0); + omap_set_dma_src_params(dma_ch, src_port, + OMAP_DMA_AMODE_POST_INC, + sg_dma_address(sg), 0, 0); + omap_set_dma_src_data_pack(dma_ch, 1); + omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4); + } + + /* Max limit for DMA frame count is 0xffff */ + BUG_ON(count > 0xffff); + + OMAP_MMC_WRITE(host, BUF, buf); + omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S16, + frame, count, OMAP_DMA_SYNC_FRAME, + sync_dev, 0); +} + +/* A scatterlist segment completed */ +static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *) data; + struct mmc_data *mmcdat = host->data; + + if (unlikely(host->dma_ch < 0)) { + dev_err(mmc_dev(host->mmc), + "DMA callback while DMA not enabled\n"); + return; + } + /* FIXME: We really should do something to _handle_ the errors */ + if (ch_status & OMAP1_DMA_TOUT_IRQ) { + dev_err(mmc_dev(host->mmc),"DMA timeout\n"); + return; + } + if (ch_status & OMAP_DMA_DROP_IRQ) { + dev_err(mmc_dev(host->mmc), "DMA sync error\n"); + return; + } + if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) { + return; + } + mmcdat->bytes_xfered += host->dma_len; + host->sg_idx++; + if (host->sg_idx < host->sg_len) { + mmc_omap_prepare_dma(host, host->data); + omap_start_dma(host->dma_ch); + } else + mmc_omap_dma_done(host, host->data); +} + +static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data *data) +{ + const char *dev_name; + int sync_dev, dma_ch, is_read, r; + + is_read = !(data->flags & MMC_DATA_WRITE); + del_timer_sync(&host->dma_timer); + if (host->dma_ch >= 0) { + if (is_read == host->dma_is_read) + return 0; + omap_free_dma(host->dma_ch); + host->dma_ch = -1; + } + + if (is_read) { + if (host->id == 1) { + sync_dev = OMAP_DMA_MMC_RX; + dev_name = "MMC1 read"; + } else { + sync_dev = OMAP_DMA_MMC2_RX; + dev_name = "MMC2 read"; + } + } else { + if (host->id == 1) { + sync_dev = OMAP_DMA_MMC_TX; + dev_name = "MMC1 write"; + } else { + sync_dev = OMAP_DMA_MMC2_TX; + dev_name = "MMC2 write"; + } + } + r = omap_request_dma(sync_dev, dev_name, mmc_omap_dma_cb, + host, &dma_ch); + if (r != 0) { + dev_dbg(mmc_dev(host->mmc), "omap_request_dma() failed with %d\n", r); + return r; + } + host->dma_ch = dma_ch; + host->dma_is_read = is_read; + + return 0; +} + +static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_request *req) +{ + u16 reg; + + reg = OMAP_MMC_READ(host, SDIO); + reg &= ~(1 << 5); + OMAP_MMC_WRITE(host, SDIO, reg); + /* Set maximum timeout */ + OMAP_MMC_WRITE(host, CTO, 0xff); +} + +static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req) +{ + int timeout; + u16 reg; + + /* Convert ns to clock cycles by assuming 20MHz frequency + * 1 cycle at 20MHz = 500 ns + */ + timeout = req->data->timeout_clks + req->data->timeout_ns / 500; + + /* Check if we need to use timeout multiplier register */ + reg = OMAP_MMC_READ(host, SDIO); + if (timeout > 0xffff) { + reg |= (1 << 5); + timeout /= 1024; + } else + reg &= ~(1 << 5); + OMAP_MMC_WRITE(host, SDIO, reg); + OMAP_MMC_WRITE(host, DTO, timeout); +} + +static void +mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) +{ + struct mmc_data *data = req->data; + int i, use_dma, block_size; + unsigned sg_len; + + host->data = data; + if (data == NULL) { + OMAP_MMC_WRITE(host, BLEN, 0); + OMAP_MMC_WRITE(host, NBLK, 0); + OMAP_MMC_WRITE(host, BUF, 0); + host->dma_in_use = 0; + set_cmd_timeout(host, req); + return; + } + + block_size = data->blksz; + + OMAP_MMC_WRITE(host, NBLK, data->blocks - 1); + OMAP_MMC_WRITE(host, BLEN, block_size - 1); + set_data_timeout(host, req); + + /* cope with calling layer confusion; it issues "single + * block" writes using multi-block scatterlists. + */ + sg_len = (data->blocks == 1) ? 1 : data->sg_len; + + /* Only do DMA for entire blocks */ + use_dma = host->use_dma; + if (use_dma) { + for (i = 0; i < sg_len; i++) { + if ((data->sg[i].length % block_size) != 0) { + use_dma = 0; + break; + } + } + } + + host->sg_idx = 0; + if (use_dma) { + if (mmc_omap_get_dma_channel(host, data) == 0) { + enum dma_data_direction dma_data_dir; + + if (data->flags & MMC_DATA_WRITE) + dma_data_dir = DMA_TO_DEVICE; + else + dma_data_dir = DMA_FROM_DEVICE; + + host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, + sg_len, dma_data_dir); + host->total_bytes_left = 0; + mmc_omap_prepare_dma(host, req->data); + host->brs_received = 0; + host->dma_done = 0; + host->dma_in_use = 1; + } else + use_dma = 0; + } + + /* Revert to PIO? */ + if (!use_dma) { + OMAP_MMC_WRITE(host, BUF, 0x1f1f); + host->total_bytes_left = data->blocks * block_size; + host->sg_len = sg_len; + mmc_omap_sg_to_buf(host); + host->dma_in_use = 0; + } +} + +static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) +{ + struct mmc_omap_host *host = mmc_priv(mmc); + + WARN_ON(host->mrq != NULL); + + host->mrq = req; + + /* only touch fifo AFTER the controller readies it */ + mmc_omap_prepare_data(host, req); + mmc_omap_start_command(host, req->cmd); + if (host->dma_in_use) + omap_start_dma(host->dma_ch); +} + +static void innovator_fpga_socket_power(int on) +{ +#if defined(CONFIG_MACH_OMAP_INNOVATOR) && defined(CONFIG_ARCH_OMAP15XX) + if (on) { + fpga_write(fpga_read(OMAP1510_FPGA_POWER) | (1 << 3), + OMAP1510_FPGA_POWER); + } else { + fpga_write(fpga_read(OMAP1510_FPGA_POWER) & ~(1 << 3), + OMAP1510_FPGA_POWER); + } +#endif +} + +/* + * Turn the socket power on/off. Innovator uses FPGA, most boards + * probably use GPIO. + */ +static void mmc_omap_power(struct mmc_omap_host *host, int on) +{ + if (on) { + if (machine_is_omap_innovator()) + innovator_fpga_socket_power(1); + else if (machine_is_omap_h2()) + tps65010_set_gpio_out_value(GPIO3, HIGH); + else if (machine_is_omap_h3()) + /* GPIO 4 of TPS65010 sends SD_EN signal */ + tps65010_set_gpio_out_value(GPIO4, HIGH); + else if (cpu_is_omap24xx()) { + u16 reg = OMAP_MMC_READ(host, CON); + OMAP_MMC_WRITE(host, CON, reg | (1 << 11)); + } else + if (host->power_pin >= 0) + omap_set_gpio_dataout(host->power_pin, 1); + } else { + if (machine_is_omap_innovator()) + innovator_fpga_socket_power(0); + else if (machine_is_omap_h2()) + tps65010_set_gpio_out_value(GPIO3, LOW); + else if (machine_is_omap_h3()) + tps65010_set_gpio_out_value(GPIO4, LOW); + else if (cpu_is_omap24xx()) { + u16 reg = OMAP_MMC_READ(host, CON); + OMAP_MMC_WRITE(host, CON, reg & ~(1 << 11)); + } else + if (host->power_pin >= 0) + omap_set_gpio_dataout(host->power_pin, 0); + } +} + +static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct mmc_omap_host *host = mmc_priv(mmc); + int dsor; + int realclock, i; + + realclock = ios->clock; + + if (ios->clock == 0) + dsor = 0; + else { + int func_clk_rate = clk_get_rate(host->fclk); + + dsor = func_clk_rate / realclock; + if (dsor < 1) + dsor = 1; + + if (func_clk_rate / dsor > realclock) + dsor++; + + if (dsor > 250) + dsor = 250; + dsor++; + + if (ios->bus_width == MMC_BUS_WIDTH_4) + dsor |= 1 << 15; + } + + switch (ios->power_mode) { + case MMC_POWER_OFF: + mmc_omap_power(host, 0); + break; + case MMC_POWER_UP: + case MMC_POWER_ON: + mmc_omap_power(host, 1); + dsor |= 1 << 11; + break; + } + + host->bus_mode = ios->bus_mode; + host->hw_bus_mode = host->bus_mode; + + clk_enable(host->fclk); + + /* On insanely high arm_per frequencies something sometimes + * goes somehow out of sync, and the POW bit is not being set, + * which results in the while loop below getting stuck. + * Writing to the CON register twice seems to do the trick. */ + for (i = 0; i < 2; i++) + OMAP_MMC_WRITE(host, CON, dsor); + if (ios->power_mode == MMC_POWER_UP) { + /* Send clock cycles, poll completion */ + OMAP_MMC_WRITE(host, IE, 0); + OMAP_MMC_WRITE(host, STAT, 0xffff); + OMAP_MMC_WRITE(host, CMD, 1 << 7); + while ((OMAP_MMC_READ(host, STAT) & 1) == 0); + OMAP_MMC_WRITE(host, STAT, 1); + } + clk_disable(host->fclk); +} + +static int mmc_omap_get_ro(struct mmc_host *mmc) +{ + struct mmc_omap_host *host = mmc_priv(mmc); + + return host->wp_pin && omap_get_gpio_datain(host->wp_pin); +} + +static const struct mmc_host_ops mmc_omap_ops = { + .request = mmc_omap_request, + .set_ios = mmc_omap_set_ios, + .get_ro = mmc_omap_get_ro, +}; + +static int __init mmc_omap_probe(struct platform_device *pdev) +{ + struct omap_mmc_conf *minfo = pdev->dev.platform_data; + struct mmc_host *mmc; + struct mmc_omap_host *host = NULL; + struct resource *res; + int ret = 0; + int irq; + + if (minfo == NULL) { + dev_err(&pdev->dev, "platform data missing\n"); + return -ENXIO; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (res == NULL || irq < 0) + return -ENXIO; + + res = request_mem_region(res->start, res->end - res->start + 1, + pdev->name); + if (res == NULL) + return -EBUSY; + + mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev); + if (mmc == NULL) { + ret = -ENOMEM; + goto err_free_mem_region; + } + + host = mmc_priv(mmc); + host->mmc = mmc; + + spin_lock_init(&host->dma_lock); + init_timer(&host->dma_timer); + host->dma_timer.function = mmc_omap_dma_timer; + host->dma_timer.data = (unsigned long) host; + + host->id = pdev->id; + host->mem_res = res; + host->irq = irq; + + if (cpu_is_omap24xx()) { + host->iclk = clk_get(&pdev->dev, "mmc_ick"); + if (IS_ERR(host->iclk)) + goto err_free_mmc_host; + clk_enable(host->iclk); + } + + if (!cpu_is_omap24xx()) + host->fclk = clk_get(&pdev->dev, "mmc_ck"); + else + host->fclk = clk_get(&pdev->dev, "mmc_fck"); + + if (IS_ERR(host->fclk)) { + ret = PTR_ERR(host->fclk); + goto err_free_iclk; + } + + /* REVISIT: + * Also, use minfo->cover to decide how to manage + * the card detect sensing. + */ + host->power_pin = minfo->power_pin; + host->switch_pin = minfo->switch_pin; + host->wp_pin = minfo->wp_pin; + host->use_dma = 1; + host->dma_ch = -1; + + host->irq = irq; + host->phys_base = host->mem_res->start; + host->virt_base = (void __iomem *) IO_ADDRESS(host->phys_base); + + mmc->ops = &mmc_omap_ops; + mmc->f_min = 400000; + mmc->f_max = 24000000; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->caps = MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; + + if (minfo->wire4) + mmc->caps |= MMC_CAP_4_BIT_DATA; + + /* Use scatterlist DMA to reduce per-transfer costs. + * NOTE max_seg_size assumption that small blocks aren't + * normally used (except e.g. for reading SD registers). + */ + mmc->max_phys_segs = 32; + mmc->max_hw_segs = 32; + mmc->max_blk_size = 2048; /* BLEN is 11 bits (+1) */ + mmc->max_blk_count = 2048; /* NBLK is 11 bits (+1) */ + mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; + mmc->max_seg_size = mmc->max_req_size; + + if (host->power_pin >= 0) { + if ((ret = omap_request_gpio(host->power_pin)) != 0) { + dev_err(mmc_dev(host->mmc), + "Unable to get GPIO pin for MMC power\n"); + goto err_free_fclk; + } + omap_set_gpio_direction(host->power_pin, 0); + } + + ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host); + if (ret) + goto err_free_power_gpio; + + host->dev = &pdev->dev; + platform_set_drvdata(pdev, host); + + if (host->switch_pin >= 0) { + INIT_WORK(&host->switch_work, mmc_omap_switch_handler); + init_timer(&host->switch_timer); + host->switch_timer.function = mmc_omap_switch_timer; + host->switch_timer.data = (unsigned long) host; + if (omap_request_gpio(host->switch_pin) != 0) { + dev_warn(mmc_dev(host->mmc), "Unable to get GPIO pin for MMC cover switch\n"); + host->switch_pin = -1; + goto no_switch; + } + + omap_set_gpio_direction(host->switch_pin, 1); + ret = request_irq(OMAP_GPIO_IRQ(host->switch_pin), + mmc_omap_switch_irq, IRQF_TRIGGER_RISING, DRIVER_NAME, host); + if (ret) { + dev_warn(mmc_dev(host->mmc), "Unable to get IRQ for MMC cover switch\n"); + omap_free_gpio(host->switch_pin); + host->switch_pin = -1; + goto no_switch; + } + ret = device_create_file(&pdev->dev, &dev_attr_cover_switch); + if (ret == 0) { + ret = device_create_file(&pdev->dev, &dev_attr_enable_poll); + if (ret != 0) + device_remove_file(&pdev->dev, &dev_attr_cover_switch); + } + if (ret) { + dev_warn(mmc_dev(host->mmc), "Unable to create sysfs attributes\n"); + free_irq(OMAP_GPIO_IRQ(host->switch_pin), host); + omap_free_gpio(host->switch_pin); + host->switch_pin = -1; + goto no_switch; + } + if (mmc_omap_enable_poll && mmc_omap_cover_is_open(host)) + schedule_work(&host->switch_work); + } + + mmc_add_host(mmc); + + return 0; + +no_switch: + /* FIXME: Free other resources too. */ + if (host) { + if (host->iclk && !IS_ERR(host->iclk)) + clk_put(host->iclk); + if (host->fclk && !IS_ERR(host->fclk)) + clk_put(host->fclk); + mmc_free_host(host->mmc); + } +err_free_power_gpio: + if (host->power_pin >= 0) + omap_free_gpio(host->power_pin); +err_free_fclk: + clk_put(host->fclk); +err_free_iclk: + if (host->iclk != NULL) { + clk_disable(host->iclk); + clk_put(host->iclk); + } +err_free_mmc_host: + mmc_free_host(host->mmc); +err_free_mem_region: + release_mem_region(res->start, res->end - res->start + 1); + return ret; +} + +static int mmc_omap_remove(struct platform_device *pdev) +{ + struct mmc_omap_host *host = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + BUG_ON(host == NULL); + + mmc_remove_host(host->mmc); + free_irq(host->irq, host); + + if (host->power_pin >= 0) + omap_free_gpio(host->power_pin); + if (host->switch_pin >= 0) { + device_remove_file(&pdev->dev, &dev_attr_enable_poll); + device_remove_file(&pdev->dev, &dev_attr_cover_switch); + free_irq(OMAP_GPIO_IRQ(host->switch_pin), host); + omap_free_gpio(host->switch_pin); + host->switch_pin = -1; + del_timer_sync(&host->switch_timer); + flush_scheduled_work(); + } + if (host->iclk && !IS_ERR(host->iclk)) + clk_put(host->iclk); + if (host->fclk && !IS_ERR(host->fclk)) + clk_put(host->fclk); + + release_mem_region(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1); + + mmc_free_host(host->mmc); + + return 0; +} + +#ifdef CONFIG_PM +static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + int ret = 0; + struct mmc_omap_host *host = platform_get_drvdata(pdev); + + if (host && host->suspended) + return 0; + + if (host) { + ret = mmc_suspend_host(host->mmc, mesg); + if (ret == 0) + host->suspended = 1; + } + return ret; +} + +static int mmc_omap_resume(struct platform_device *pdev) +{ + int ret = 0; + struct mmc_omap_host *host = platform_get_drvdata(pdev); + + if (host && !host->suspended) + return 0; + + if (host) { + ret = mmc_resume_host(host->mmc); + if (ret == 0) + host->suspended = 0; + } + + return ret; +} +#else +#define mmc_omap_suspend NULL +#define mmc_omap_resume NULL +#endif + +static struct platform_driver mmc_omap_driver = { + .probe = mmc_omap_probe, + .remove = mmc_omap_remove, + .suspend = mmc_omap_suspend, + .resume = mmc_omap_resume, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init mmc_omap_init(void) +{ + return platform_driver_register(&mmc_omap_driver); +} + +static void __exit mmc_omap_exit(void) +{ + platform_driver_unregister(&mmc_omap_driver); +} + +module_init(mmc_omap_init); +module_exit(mmc_omap_exit); + +MODULE_DESCRIPTION("OMAP Multimedia Card driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS(DRIVER_NAME); +MODULE_AUTHOR("Juha Yrjölä"); diff --git a/drivers/mmc/host/pxamci.c b/drivers/mmc/host/pxamci.c new file mode 100644 index 00000000000..a98ff98fa56 --- /dev/null +++ b/drivers/mmc/host/pxamci.c @@ -0,0 +1,616 @@ +/* + * linux/drivers/mmc/pxa.c - PXA MMCI driver + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This hardware is really sick: + * - No way to clear interrupts. + * - Have to turn off the clock whenever we touch the device. + * - Doesn't tell you how many data blocks were transferred. + * Yuck! + * + * 1 and 3 byte data transfers not supported + * max block length up to 1023 + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "pxamci.h" + +#define DRIVER_NAME "pxa2xx-mci" + +#define NR_SG 1 + +struct pxamci_host { + struct mmc_host *mmc; + spinlock_t lock; + struct resource *res; + void __iomem *base; + int irq; + int dma; + unsigned int clkrt; + unsigned int cmdat; + unsigned int imask; + unsigned int power_mode; + struct pxamci_platform_data *pdata; + + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + + dma_addr_t sg_dma; + struct pxa_dma_desc *sg_cpu; + unsigned int dma_len; + + unsigned int dma_dir; +}; + +static void pxamci_stop_clock(struct pxamci_host *host) +{ + if (readl(host->base + MMC_STAT) & STAT_CLK_EN) { + unsigned long timeout = 10000; + unsigned int v; + + writel(STOP_CLOCK, host->base + MMC_STRPCL); + + do { + v = readl(host->base + MMC_STAT); + if (!(v & STAT_CLK_EN)) + break; + udelay(1); + } while (timeout--); + + if (v & STAT_CLK_EN) + dev_err(mmc_dev(host->mmc), "unable to stop clock\n"); + } +} + +static void pxamci_enable_irq(struct pxamci_host *host, unsigned int mask) +{ + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + host->imask &= ~mask; + writel(host->imask, host->base + MMC_I_MASK); + spin_unlock_irqrestore(&host->lock, flags); +} + +static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask) +{ + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + host->imask |= mask; + writel(host->imask, host->base + MMC_I_MASK); + spin_unlock_irqrestore(&host->lock, flags); +} + +static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) +{ + unsigned int nob = data->blocks; + unsigned long long clks; + unsigned int timeout; + u32 dcmd; + int i; + + host->data = data; + + if (data->flags & MMC_DATA_STREAM) + nob = 0xffff; + + writel(nob, host->base + MMC_NOB); + writel(data->blksz, host->base + MMC_BLKLEN); + + clks = (unsigned long long)data->timeout_ns * CLOCKRATE; + do_div(clks, 1000000000UL); + timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt); + writel((timeout + 255) / 256, host->base + MMC_RDTO); + + if (data->flags & MMC_DATA_READ) { + host->dma_dir = DMA_FROM_DEVICE; + dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG; + DRCMRTXMMC = 0; + DRCMRRXMMC = host->dma | DRCMR_MAPVLD; + } else { + host->dma_dir = DMA_TO_DEVICE; + dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC; + DRCMRRXMMC = 0; + DRCMRTXMMC = host->dma | DRCMR_MAPVLD; + } + + dcmd |= DCMD_BURST32 | DCMD_WIDTH1; + + 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 = sg_dma_address(&data->sg[i]); + } else { + 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 | 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; + DCSR(host->dma) = DCSR_RUN; +} + +static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat) +{ + WARN_ON(host->cmd != NULL); + host->cmd = cmd; + + if (cmd->flags & MMC_RSP_BUSY) + cmdat |= CMDAT_BUSY; + +#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE)) + switch (RSP_TYPE(mmc_resp_type(cmd))) { + case RSP_TYPE(MMC_RSP_R1): /* r1, r1b, r6, r7 */ + cmdat |= CMDAT_RESP_SHORT; + break; + case RSP_TYPE(MMC_RSP_R3): + cmdat |= CMDAT_RESP_R3; + break; + case RSP_TYPE(MMC_RSP_R2): + cmdat |= CMDAT_RESP_R2; + break; + default: + break; + } + + writel(cmd->opcode, host->base + MMC_CMD); + writel(cmd->arg >> 16, host->base + MMC_ARGH); + writel(cmd->arg & 0xffff, host->base + MMC_ARGL); + writel(cmdat, host->base + MMC_CMDAT); + writel(host->clkrt, host->base + MMC_CLKRT); + + writel(START_CLOCK, host->base + MMC_STRPCL); + + pxamci_enable_irq(host, END_CMD_RES); +} + +static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq) +{ + host->mrq = NULL; + host->cmd = NULL; + host->data = NULL; + mmc_request_done(host->mmc, mrq); +} + +static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat) +{ + struct mmc_command *cmd = host->cmd; + int i; + u32 v; + + if (!cmd) + return 0; + + host->cmd = NULL; + + /* + * Did I mention this is Sick. We always need to + * discard the upper 8 bits of the first 16-bit word. + */ + v = readl(host->base + MMC_RES) & 0xffff; + for (i = 0; i < 4; i++) { + u32 w1 = readl(host->base + MMC_RES) & 0xffff; + u32 w2 = readl(host->base + MMC_RES) & 0xffff; + cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8; + v = w2; + } + + 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 { + pr_debug("ignoring CRC from command %d - *risky*\n",cmd->opcode); + } +#else + cmd->error = MMC_ERR_BADCRC; +#endif + } + + pxamci_disable_irq(host, END_CMD_RES); + if (host->data && cmd->error == MMC_ERR_NONE) { + pxamci_enable_irq(host, DATA_TRAN_DONE); + } else { + pxamci_finish_request(host, host->mrq); + } + + return 1; +} + +static int pxamci_data_done(struct pxamci_host *host, unsigned int stat) +{ + struct mmc_data *data = host->data; + + if (!data) + return 0; + + DCSR(host->dma) = 0; + 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; + else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR)) + data->error = MMC_ERR_BADCRC; + + /* + * There appears to be a hardware design bug here. There seems to + * be no way to find out how much data was transferred to the card. + * This means that if there was an error on any block, we mark all + * data blocks as being in error. + */ + if (data->error == MMC_ERR_NONE) + data->bytes_xfered = data->blocks * data->blksz; + else + data->bytes_xfered = 0; + + pxamci_disable_irq(host, DATA_TRAN_DONE); + + host->data = NULL; + if (host->mrq->stop) { + pxamci_stop_clock(host); + pxamci_start_cmd(host, host->mrq->stop, 0); + } else { + pxamci_finish_request(host, host->mrq); + } + + return 1; +} + +static irqreturn_t pxamci_irq(int irq, void *devid) +{ + struct pxamci_host *host = devid; + unsigned int ireg; + int handled = 0; + + ireg = readl(host->base + MMC_I_REG); + + if (ireg) { + unsigned stat = readl(host->base + MMC_STAT); + + pr_debug("PXAMCI: irq %08x stat %08x\n", ireg, stat); + + if (ireg & END_CMD_RES) + handled |= pxamci_cmd_done(host, stat); + if (ireg & DATA_TRAN_DONE) + handled |= pxamci_data_done(host, stat); + } + + return IRQ_RETVAL(handled); +} + +static void pxamci_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct pxamci_host *host = mmc_priv(mmc); + unsigned int cmdat; + + WARN_ON(host->mrq != NULL); + + host->mrq = mrq; + + pxamci_stop_clock(host); + + cmdat = host->cmdat; + host->cmdat &= ~CMDAT_INIT; + + if (mrq->data) { + pxamci_setup_data(host, mrq->data); + + cmdat &= ~CMDAT_BUSY; + cmdat |= CMDAT_DATAEN | CMDAT_DMAEN; + if (mrq->data->flags & MMC_DATA_WRITE) + cmdat |= CMDAT_WRITE; + + if (mrq->data->flags & MMC_DATA_STREAM) + cmdat |= CMDAT_STREAM; + } + + pxamci_start_cmd(host, mrq->cmd, cmdat); +} + +static int pxamci_get_ro(struct mmc_host *mmc) +{ + struct pxamci_host *host = mmc_priv(mmc); + + if (host->pdata && host->pdata->get_ro) + return host->pdata->get_ro(mmc_dev(mmc)); + /* Host doesn't support read only detection so assume writeable */ + return 0; +} + +static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct pxamci_host *host = mmc_priv(mmc); + + if (ios->clock) { + unsigned int clk = CLOCKRATE / ios->clock; + 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 { + pxamci_stop_clock(host); + pxa_set_cken(CKEN12_MMC, 0); + } + + if (host->power_mode != ios->power_mode) { + host->power_mode = ios->power_mode; + + if (host->pdata && host->pdata->setpower) + host->pdata->setpower(mmc_dev(mmc), ios->vdd); + + if (ios->power_mode == MMC_POWER_ON) + host->cmdat |= CMDAT_INIT; + } + + pr_debug("PXAMCI: clkrt = %x cmdat = %x\n", + host->clkrt, host->cmdat); +} + +static const struct mmc_host_ops pxamci_ops = { + .request = pxamci_request, + .get_ro = pxamci_get_ro, + .set_ios = pxamci_set_ios, +}; + +static void pxamci_dma_irq(int dma, void *devid) +{ + printk(KERN_ERR "DMA%d: IRQ???\n", dma); + DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; +} + +static irqreturn_t pxamci_detect_irq(int irq, void *devid) +{ + struct pxamci_host *host = mmc_priv(devid); + + mmc_detect_change(devid, host->pdata->detect_delay); + return IRQ_HANDLED; +} + +static int pxamci_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc; + struct pxamci_host *host = NULL; + struct resource *r; + int ret, irq; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!r || irq < 0) + return -ENXIO; + + r = request_mem_region(r->start, SZ_4K, DRIVER_NAME); + if (!r) + return -EBUSY; + + mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + goto out; + } + + mmc->ops = &pxamci_ops; + 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; + + /* + * Block length register is 10 bits. + */ + mmc->max_blk_size = 1023; + + /* + * Block count register is 16 bits. + */ + mmc->max_blk_count = 65535; + + host = mmc_priv(mmc); + host->mmc = mmc; + host->dma = -1; + host->pdata = pdev->dev.platform_data; + mmc->ocr_avail = host->pdata ? + host->pdata->ocr_mask : + MMC_VDD_32_33|MMC_VDD_33_34; + + host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); + if (!host->sg_cpu) { + ret = -ENOMEM; + goto out; + } + + spin_lock_init(&host->lock); + host->res = r; + host->irq = irq; + host->imask = MMC_I_MASK_ALL; + + host->base = ioremap(r->start, SZ_4K); + if (!host->base) { + ret = -ENOMEM; + goto out; + } + + /* + * Ensure that the host controller is shut down, and setup + * with our defaults. + */ + pxamci_stop_clock(host); + writel(0, host->base + MMC_SPI); + writel(64, host->base + MMC_RESTO); + writel(host->imask, host->base + MMC_I_MASK); + + 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, DRIVER_NAME, host); + if (ret) + goto out; + + platform_set_drvdata(pdev, mmc); + + if (host->pdata && host->pdata->init) + host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc); + + mmc_add_host(mmc); + + return 0; + + out: + if (host) { + if (host->dma >= 0) + pxa_free_dma(host->dma); + if (host->base) + iounmap(host->base); + if (host->sg_cpu) + dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); + } + if (mmc) + mmc_free_host(mmc); + release_resource(r); + return ret; +} + +static int pxamci_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + if (mmc) { + struct pxamci_host *host = mmc_priv(mmc); + + if (host->pdata && host->pdata->exit) + host->pdata->exit(&pdev->dev, mmc); + + mmc_remove_host(mmc); + + pxamci_stop_clock(host); + writel(TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD| + END_CMD_RES|PRG_DONE|DATA_TRAN_DONE, + host->base + MMC_I_MASK); + + DRCMRRXMMC = 0; + DRCMRTXMMC = 0; + + free_irq(host->irq, host); + pxa_free_dma(host->dma); + iounmap(host->base); + dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); + + release_resource(host->res); + + mmc_free_host(mmc); + } + return 0; +} + +#ifdef CONFIG_PM +static int pxamci_suspend(struct platform_device *dev, pm_message_t state) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + int ret = 0; + + if (mmc) + ret = mmc_suspend_host(mmc, state); + + return ret; +} + +static int pxamci_resume(struct platform_device *dev) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + int ret = 0; + + if (mmc) + ret = mmc_resume_host(mmc); + + return ret; +} +#else +#define pxamci_suspend NULL +#define pxamci_resume NULL +#endif + +static struct platform_driver pxamci_driver = { + .probe = pxamci_probe, + .remove = pxamci_remove, + .suspend = pxamci_suspend, + .resume = pxamci_resume, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init pxamci_init(void) +{ + return platform_driver_register(&pxamci_driver); +} + +static void __exit pxamci_exit(void) +{ + platform_driver_unregister(&pxamci_driver); +} + +module_init(pxamci_init); +module_exit(pxamci_exit); + +MODULE_DESCRIPTION("PXA Multimedia Card Interface Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/pxamci.h b/drivers/mmc/host/pxamci.h new file mode 100644 index 00000000000..1b163220df2 --- /dev/null +++ b/drivers/mmc/host/pxamci.h @@ -0,0 +1,124 @@ +#undef MMC_STRPCL +#undef MMC_STAT +#undef MMC_CLKRT +#undef MMC_SPI +#undef MMC_CMDAT +#undef MMC_RESTO +#undef MMC_RDTO +#undef MMC_BLKLEN +#undef MMC_NOB +#undef MMC_PRTBUF +#undef MMC_I_MASK +#undef END_CMD_RES +#undef PRG_DONE +#undef DATA_TRAN_DONE +#undef MMC_I_REG +#undef MMC_CMD +#undef MMC_ARGH +#undef MMC_ARGL +#undef MMC_RES +#undef MMC_RXFIFO +#undef MMC_TXFIFO + +#define MMC_STRPCL 0x0000 +#define STOP_CLOCK (1 << 0) +#define START_CLOCK (2 << 0) + +#define MMC_STAT 0x0004 +#define STAT_END_CMD_RES (1 << 13) +#define STAT_PRG_DONE (1 << 12) +#define STAT_DATA_TRAN_DONE (1 << 11) +#define STAT_CLK_EN (1 << 8) +#define STAT_RECV_FIFO_FULL (1 << 7) +#define STAT_XMIT_FIFO_EMPTY (1 << 6) +#define STAT_RES_CRC_ERR (1 << 5) +#define STAT_SPI_READ_ERROR_TOKEN (1 << 4) +#define STAT_CRC_READ_ERROR (1 << 3) +#define STAT_CRC_WRITE_ERROR (1 << 2) +#define STAT_TIME_OUT_RESPONSE (1 << 1) +#define STAT_READ_TIME_OUT (1 << 0) + +#define MMC_CLKRT 0x0008 /* 3 bit */ + +#define MMC_SPI 0x000c +#define SPI_CS_ADDRESS (1 << 3) +#define SPI_CS_EN (1 << 2) +#define CRC_ON (1 << 1) +#define SPI_EN (1 << 0) + +#define MMC_CMDAT 0x0010 +#define CMDAT_DMAEN (1 << 7) +#define CMDAT_INIT (1 << 6) +#define CMDAT_BUSY (1 << 5) +#define CMDAT_STREAM (1 << 4) /* 1 = stream */ +#define CMDAT_WRITE (1 << 3) /* 1 = write */ +#define CMDAT_DATAEN (1 << 2) +#define CMDAT_RESP_NONE (0 << 0) +#define CMDAT_RESP_SHORT (1 << 0) +#define CMDAT_RESP_R2 (2 << 0) +#define CMDAT_RESP_R3 (3 << 0) + +#define MMC_RESTO 0x0014 /* 7 bit */ + +#define MMC_RDTO 0x0018 /* 16 bit */ + +#define MMC_BLKLEN 0x001c /* 10 bit */ + +#define MMC_NOB 0x0020 /* 16 bit */ + +#define MMC_PRTBUF 0x0024 +#define BUF_PART_FULL (1 << 0) + +#define MMC_I_MASK 0x0028 + +/*PXA27x MMC interrupts*/ +#define SDIO_SUSPEND_ACK (1 << 12) +#define SDIO_INT (1 << 11) +#define RD_STALLED (1 << 10) +#define RES_ERR (1 << 9) +#define DAT_ERR (1 << 8) +#define TINT (1 << 7) + +/*PXA2xx MMC interrupts*/ +#define TXFIFO_WR_REQ (1 << 6) +#define RXFIFO_RD_REQ (1 << 5) +#define CLK_IS_OFF (1 << 4) +#define STOP_CMD (1 << 3) +#define END_CMD_RES (1 << 2) +#define PRG_DONE (1 << 1) +#define DATA_TRAN_DONE (1 << 0) + +#ifdef CONFIG_PXA27x +#define MMC_I_MASK_ALL 0x00001fff +#else +#define MMC_I_MASK_ALL 0x0000007f +#endif + +#define MMC_I_REG 0x002c +/* same as MMC_I_MASK */ + +#define MMC_CMD 0x0030 + +#define MMC_ARGH 0x0034 /* 16 bit */ + +#define MMC_ARGL 0x0038 /* 16 bit */ + +#define MMC_RES 0x003c /* 16 bit */ + +#define MMC_RXFIFO 0x0040 /* 8 bit */ + +#define MMC_TXFIFO 0x0044 /* 8 bit */ + +/* + * The base MMC clock rate + */ +#ifdef CONFIG_PXA27x +#define CLOCKRATE_MIN 304688 +#define CLOCKRATE_MAX 19500000 +#else +#define CLOCKRATE_MIN 312500 +#define CLOCKRATE_MAX 20000000 +#endif + +#define CLOCKRATE CLOCKRATE_MAX + diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c new file mode 100644 index 00000000000..579142a7904 --- /dev/null +++ b/drivers/mmc/host/sdhci.c @@ -0,0 +1,1539 @@ +/* + * linux/drivers/mmc/sdhci.c - Secure Digital Host Controller Interface driver + * + * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#include +#include +#include +#include + +#include + +#include + +#include "sdhci.h" + +#define DRIVER_NAME "sdhci" + +#define DBG(f, x...) \ + pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x) + +static unsigned int debug_nodma = 0; +static unsigned int debug_forcedma = 0; +static unsigned int debug_quirks = 0; + +#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0) +#define SDHCI_QUIRK_FORCE_DMA (1<<1) +/* Controller doesn't like some resets when there is no card inserted. */ +#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2) +#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3) + +static const struct pci_device_id pci_ids[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_RICOH, + .device = PCI_DEVICE_ID_RICOH_R5C822, + .subvendor = PCI_VENDOR_ID_IBM, + .subdevice = PCI_ANY_ID, + .driver_data = SDHCI_QUIRK_CLOCK_BEFORE_RESET | + SDHCI_QUIRK_FORCE_DMA, + }, + + { + .vendor = PCI_VENDOR_ID_RICOH, + .device = PCI_DEVICE_ID_RICOH_R5C822, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = SDHCI_QUIRK_FORCE_DMA | + SDHCI_QUIRK_NO_CARD_NO_RESET, + }, + + { + .vendor = PCI_VENDOR_ID_TI, + .device = PCI_DEVICE_ID_TI_XX21_XX11_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = SDHCI_QUIRK_FORCE_DMA, + }, + + { + .vendor = PCI_VENDOR_ID_ENE, + .device = PCI_DEVICE_ID_ENE_CB712_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE, + }, + + { /* Generic SD host controller */ + PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) + }, + + { /* end: all zeroes */ }, +}; + +MODULE_DEVICE_TABLE(pci, pci_ids); + +static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *); +static void sdhci_finish_data(struct sdhci_host *); + +static void sdhci_send_command(struct sdhci_host *, struct mmc_command *); +static void sdhci_finish_command(struct sdhci_host *); + +static void sdhci_dumpregs(struct sdhci_host *host) +{ + printk(KERN_DEBUG DRIVER_NAME ": ============== REGISTER DUMP ==============\n"); + + printk(KERN_DEBUG DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n", + readl(host->ioaddr + SDHCI_DMA_ADDRESS), + readw(host->ioaddr + SDHCI_HOST_VERSION)); + printk(KERN_DEBUG DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n", + readw(host->ioaddr + SDHCI_BLOCK_SIZE), + readw(host->ioaddr + SDHCI_BLOCK_COUNT)); + printk(KERN_DEBUG DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n", + readl(host->ioaddr + SDHCI_ARGUMENT), + readw(host->ioaddr + SDHCI_TRANSFER_MODE)); + printk(KERN_DEBUG DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n", + readl(host->ioaddr + SDHCI_PRESENT_STATE), + readb(host->ioaddr + SDHCI_HOST_CONTROL)); + printk(KERN_DEBUG DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n", + readb(host->ioaddr + SDHCI_POWER_CONTROL), + readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL)); + printk(KERN_DEBUG DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n", + readb(host->ioaddr + SDHCI_WALK_UP_CONTROL), + readw(host->ioaddr + SDHCI_CLOCK_CONTROL)); + printk(KERN_DEBUG DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n", + readb(host->ioaddr + SDHCI_TIMEOUT_CONTROL), + readl(host->ioaddr + SDHCI_INT_STATUS)); + printk(KERN_DEBUG DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n", + readl(host->ioaddr + SDHCI_INT_ENABLE), + readl(host->ioaddr + SDHCI_SIGNAL_ENABLE)); + printk(KERN_DEBUG DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n", + readw(host->ioaddr + SDHCI_ACMD12_ERR), + readw(host->ioaddr + SDHCI_SLOT_INT_STATUS)); + printk(KERN_DEBUG DRIVER_NAME ": Caps: 0x%08x | Max curr: 0x%08x\n", + readl(host->ioaddr + SDHCI_CAPABILITIES), + readl(host->ioaddr + SDHCI_MAX_CURRENT)); + + printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n"); +} + +/*****************************************************************************\ + * * + * Low level functions * + * * +\*****************************************************************************/ + +static void sdhci_reset(struct sdhci_host *host, u8 mask) +{ + unsigned long timeout; + + if (host->chip->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { + if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & + SDHCI_CARD_PRESENT)) + return; + } + + writeb(mask, host->ioaddr + SDHCI_SOFTWARE_RESET); + + if (mask & SDHCI_RESET_ALL) + host->clock = 0; + + /* Wait max 100 ms */ + timeout = 100; + + /* hw clears the bit when it's done */ + while (readb(host->ioaddr + SDHCI_SOFTWARE_RESET) & mask) { + if (timeout == 0) { + printk(KERN_ERR "%s: Reset 0x%x never completed.\n", + mmc_hostname(host->mmc), (int)mask); + sdhci_dumpregs(host); + return; + } + timeout--; + mdelay(1); + } +} + +static void sdhci_init(struct sdhci_host *host) +{ + u32 intmask; + + sdhci_reset(host, SDHCI_RESET_ALL); + + intmask = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | + SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | + SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | + SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT | + SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | + SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE; + + writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); + writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE); +} + +static void sdhci_activate_led(struct sdhci_host *host) +{ + u8 ctrl; + + ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); + ctrl |= SDHCI_CTRL_LED; + writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); +} + +static void sdhci_deactivate_led(struct sdhci_host *host) +{ + u8 ctrl; + + ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); + ctrl &= ~SDHCI_CTRL_LED; + writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); +} + +/*****************************************************************************\ + * * + * Core functions * + * * +\*****************************************************************************/ + +static inline char* sdhci_sg_to_buffer(struct sdhci_host* host) +{ + return page_address(host->cur_sg->page) + host->cur_sg->offset; +} + +static inline int sdhci_next_sg(struct sdhci_host* host) +{ + /* + * Skip to next SG entry. + */ + host->cur_sg++; + host->num_sg--; + + /* + * Any entries left? + */ + if (host->num_sg > 0) { + host->offset = 0; + host->remain = host->cur_sg->length; + } + + return host->num_sg; +} + +static void sdhci_read_block_pio(struct sdhci_host *host) +{ + int blksize, chunk_remain; + u32 data; + char *buffer; + int size; + + DBG("PIO reading\n"); + + blksize = host->data->blksz; + chunk_remain = 0; + data = 0; + + buffer = sdhci_sg_to_buffer(host) + host->offset; + + while (blksize) { + if (chunk_remain == 0) { + data = readl(host->ioaddr + SDHCI_BUFFER); + chunk_remain = min(blksize, 4); + } + + size = min(host->remain, chunk_remain); + + chunk_remain -= size; + blksize -= size; + host->offset += size; + host->remain -= size; + + while (size) { + *buffer = data & 0xFF; + buffer++; + data >>= 8; + size--; + } + + if (host->remain == 0) { + if (sdhci_next_sg(host) == 0) { + BUG_ON(blksize != 0); + return; + } + buffer = sdhci_sg_to_buffer(host); + } + } +} + +static void sdhci_write_block_pio(struct sdhci_host *host) +{ + int blksize, chunk_remain; + u32 data; + char *buffer; + int bytes, size; + + DBG("PIO writing\n"); + + blksize = host->data->blksz; + chunk_remain = 4; + data = 0; + + bytes = 0; + buffer = sdhci_sg_to_buffer(host) + host->offset; + + while (blksize) { + size = min(host->remain, chunk_remain); + + chunk_remain -= size; + blksize -= size; + host->offset += size; + host->remain -= size; + + while (size) { + data >>= 8; + data |= (u32)*buffer << 24; + buffer++; + size--; + } + + if (chunk_remain == 0) { + writel(data, host->ioaddr + SDHCI_BUFFER); + chunk_remain = min(blksize, 4); + } + + if (host->remain == 0) { + if (sdhci_next_sg(host) == 0) { + BUG_ON(blksize != 0); + return; + } + buffer = sdhci_sg_to_buffer(host); + } + } +} + +static void sdhci_transfer_pio(struct sdhci_host *host) +{ + u32 mask; + + BUG_ON(!host->data); + + if (host->num_sg == 0) + return; + + if (host->data->flags & MMC_DATA_READ) + mask = SDHCI_DATA_AVAILABLE; + else + mask = SDHCI_SPACE_AVAILABLE; + + while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) { + if (host->data->flags & MMC_DATA_READ) + sdhci_read_block_pio(host); + else + sdhci_write_block_pio(host); + + if (host->num_sg == 0) + break; + } + + DBG("PIO transfer complete.\n"); +} + +static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) +{ + u8 count; + unsigned target_timeout, current_timeout; + + WARN_ON(host->data); + + if (data == NULL) + return; + + DBG("blksz %04x blks %04x flags %08x\n", + data->blksz, data->blocks, data->flags); + DBG("tsac %d ms nsac %d clk\n", + data->timeout_ns / 1000000, data->timeout_clks); + + /* Sanity checks */ + BUG_ON(data->blksz * data->blocks > 524288); + BUG_ON(data->blksz > host->mmc->max_blk_size); + BUG_ON(data->blocks > 65535); + + /* timeout in us */ + target_timeout = data->timeout_ns / 1000 + + data->timeout_clks / host->clock; + + /* + * Figure out needed cycles. + * We do this in steps in order to fit inside a 32 bit int. + * The first step is the minimum timeout, which will have a + * minimum resolution of 6 bits: + * (1) 2^13*1000 > 2^22, + * (2) host->timeout_clk < 2^16 + * => + * (1) / (2) > 2^6 + */ + count = 0; + current_timeout = (1 << 13) * 1000 / host->timeout_clk; + while (current_timeout < target_timeout) { + count++; + current_timeout <<= 1; + if (count >= 0xF) + break; + } + + if (count >= 0xF) { + printk(KERN_WARNING "%s: Too large timeout requested!\n", + mmc_hostname(host->mmc)); + count = 0xE; + } + + writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL); + + if (host->flags & SDHCI_USE_DMA) { + int count; + + count = pci_map_sg(host->chip->pdev, data->sg, data->sg_len, + (data->flags & MMC_DATA_READ)?PCI_DMA_FROMDEVICE:PCI_DMA_TODEVICE); + BUG_ON(count != 1); + + writel(sg_dma_address(data->sg), host->ioaddr + SDHCI_DMA_ADDRESS); + } else { + host->cur_sg = data->sg; + host->num_sg = data->sg_len; + + host->offset = 0; + host->remain = host->cur_sg->length; + } + + /* We do not handle DMA boundaries, so set it to max (512 KiB) */ + writew(SDHCI_MAKE_BLKSZ(7, data->blksz), + host->ioaddr + SDHCI_BLOCK_SIZE); + writew(data->blocks, host->ioaddr + SDHCI_BLOCK_COUNT); +} + +static void sdhci_set_transfer_mode(struct sdhci_host *host, + struct mmc_data *data) +{ + u16 mode; + + WARN_ON(host->data); + + if (data == NULL) + return; + + mode = SDHCI_TRNS_BLK_CNT_EN; + if (data->blocks > 1) + mode |= SDHCI_TRNS_MULTI; + if (data->flags & MMC_DATA_READ) + mode |= SDHCI_TRNS_READ; + if (host->flags & SDHCI_USE_DMA) + mode |= SDHCI_TRNS_DMA; + + writew(mode, host->ioaddr + SDHCI_TRANSFER_MODE); +} + +static void sdhci_finish_data(struct sdhci_host *host) +{ + struct mmc_data *data; + u16 blocks; + + BUG_ON(!host->data); + + data = host->data; + host->data = NULL; + + if (host->flags & SDHCI_USE_DMA) { + pci_unmap_sg(host->chip->pdev, data->sg, data->sg_len, + (data->flags & MMC_DATA_READ)?PCI_DMA_FROMDEVICE:PCI_DMA_TODEVICE); + } + + /* + * Controller doesn't count down when in single block mode. + */ + if ((data->blocks == 1) && (data->error == MMC_ERR_NONE)) + blocks = 0; + else + blocks = readw(host->ioaddr + SDHCI_BLOCK_COUNT); + data->bytes_xfered = data->blksz * (data->blocks - blocks); + + if ((data->error == MMC_ERR_NONE) && blocks) { + printk(KERN_ERR "%s: Controller signalled completion even " + "though there were blocks left.\n", + mmc_hostname(host->mmc)); + data->error = MMC_ERR_FAILED; + } + + DBG("Ending data transfer (%d bytes)\n", data->bytes_xfered); + + if (data->stop) { + /* + * The controller needs a reset of internal state machines + * upon error conditions. + */ + if (data->error != MMC_ERR_NONE) { + sdhci_reset(host, SDHCI_RESET_CMD); + sdhci_reset(host, SDHCI_RESET_DATA); + } + + sdhci_send_command(host, data->stop); + } else + tasklet_schedule(&host->finish_tasklet); +} + +static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) +{ + int flags; + u32 mask; + unsigned long timeout; + + WARN_ON(host->cmd); + + DBG("Sending cmd (%x)\n", cmd->opcode); + + /* Wait max 10 ms */ + timeout = 10; + + mask = SDHCI_CMD_INHIBIT; + if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY)) + mask |= SDHCI_DATA_INHIBIT; + + /* We shouldn't wait for data inihibit for stop commands, even + though they might use busy signaling */ + if (host->mrq->data && (cmd == host->mrq->data->stop)) + mask &= ~SDHCI_DATA_INHIBIT; + + while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) { + if (timeout == 0) { + printk(KERN_ERR "%s: Controller never released " + "inhibit bit(s).\n", mmc_hostname(host->mmc)); + sdhci_dumpregs(host); + cmd->error = MMC_ERR_FAILED; + tasklet_schedule(&host->finish_tasklet); + return; + } + timeout--; + mdelay(1); + } + + mod_timer(&host->timer, jiffies + 10 * HZ); + + host->cmd = cmd; + + sdhci_prepare_data(host, cmd->data); + + writel(cmd->arg, host->ioaddr + SDHCI_ARGUMENT); + + sdhci_set_transfer_mode(host, cmd->data); + + if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) { + printk(KERN_ERR "%s: Unsupported response type!\n", + mmc_hostname(host->mmc)); + cmd->error = MMC_ERR_INVALID; + tasklet_schedule(&host->finish_tasklet); + return; + } + + if (!(cmd->flags & MMC_RSP_PRESENT)) + flags = SDHCI_CMD_RESP_NONE; + else if (cmd->flags & MMC_RSP_136) + flags = SDHCI_CMD_RESP_LONG; + else if (cmd->flags & MMC_RSP_BUSY) + flags = SDHCI_CMD_RESP_SHORT_BUSY; + else + flags = SDHCI_CMD_RESP_SHORT; + + if (cmd->flags & MMC_RSP_CRC) + flags |= SDHCI_CMD_CRC; + if (cmd->flags & MMC_RSP_OPCODE) + flags |= SDHCI_CMD_INDEX; + if (cmd->data) + flags |= SDHCI_CMD_DATA; + + writew(SDHCI_MAKE_CMD(cmd->opcode, flags), + host->ioaddr + SDHCI_COMMAND); +} + +static void sdhci_finish_command(struct sdhci_host *host) +{ + int i; + + BUG_ON(host->cmd == NULL); + + if (host->cmd->flags & MMC_RSP_PRESENT) { + if (host->cmd->flags & MMC_RSP_136) { + /* CRC is stripped so we need to do some shifting. */ + for (i = 0;i < 4;i++) { + host->cmd->resp[i] = readl(host->ioaddr + + SDHCI_RESPONSE + (3-i)*4) << 8; + if (i != 3) + host->cmd->resp[i] |= + readb(host->ioaddr + + SDHCI_RESPONSE + (3-i)*4-1); + } + } else { + host->cmd->resp[0] = readl(host->ioaddr + SDHCI_RESPONSE); + } + } + + host->cmd->error = MMC_ERR_NONE; + + DBG("Ending cmd (%x)\n", host->cmd->opcode); + + if (host->cmd->data) + host->data = host->cmd->data; + else + tasklet_schedule(&host->finish_tasklet); + + host->cmd = NULL; +} + +static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) +{ + int div; + u16 clk; + unsigned long timeout; + + if (clock == host->clock) + return; + + writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL); + + if (clock == 0) + goto out; + + for (div = 1;div < 256;div *= 2) { + if ((host->max_clk / div) <= clock) + break; + } + div >>= 1; + + clk = div << SDHCI_DIVIDER_SHIFT; + clk |= SDHCI_CLOCK_INT_EN; + writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL); + + /* Wait max 10 ms */ + timeout = 10; + while (!((clk = readw(host->ioaddr + SDHCI_CLOCK_CONTROL)) + & SDHCI_CLOCK_INT_STABLE)) { + if (timeout == 0) { + printk(KERN_ERR "%s: Internal clock never " + "stabilised.\n", mmc_hostname(host->mmc)); + sdhci_dumpregs(host); + return; + } + timeout--; + mdelay(1); + } + + clk |= SDHCI_CLOCK_CARD_EN; + writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL); + +out: + host->clock = clock; +} + +static void sdhci_set_power(struct sdhci_host *host, unsigned short power) +{ + u8 pwr; + + if (host->power == power) + return; + + if (power == (unsigned short)-1) { + writeb(0, host->ioaddr + SDHCI_POWER_CONTROL); + goto out; + } + + /* + * Spec says that we should clear the power reg before setting + * a new value. Some controllers don't seem to like this though. + */ + if (!(host->chip->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) + writeb(0, host->ioaddr + SDHCI_POWER_CONTROL); + + pwr = SDHCI_POWER_ON; + + switch (power) { + case MMC_VDD_170: + case MMC_VDD_180: + case MMC_VDD_190: + pwr |= SDHCI_POWER_180; + break; + case MMC_VDD_290: + case MMC_VDD_300: + case MMC_VDD_310: + pwr |= SDHCI_POWER_300; + break; + case MMC_VDD_320: + case MMC_VDD_330: + case MMC_VDD_340: + pwr |= SDHCI_POWER_330; + break; + default: + BUG(); + } + + writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL); + +out: + host->power = power; +} + +/*****************************************************************************\ + * * + * MMC callbacks * + * * +\*****************************************************************************/ + +static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct sdhci_host *host; + unsigned long flags; + + host = mmc_priv(mmc); + + spin_lock_irqsave(&host->lock, flags); + + WARN_ON(host->mrq != NULL); + + sdhci_activate_led(host); + + host->mrq = mrq; + + if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) { + host->mrq->cmd->error = MMC_ERR_TIMEOUT; + tasklet_schedule(&host->finish_tasklet); + } else + sdhci_send_command(host, mrq->cmd); + + mmiowb(); + spin_unlock_irqrestore(&host->lock, flags); +} + +static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct sdhci_host *host; + unsigned long flags; + u8 ctrl; + + host = mmc_priv(mmc); + + spin_lock_irqsave(&host->lock, flags); + + /* + * Reset the chip on each power off. + * Should clear out any weird states. + */ + if (ios->power_mode == MMC_POWER_OFF) { + writel(0, host->ioaddr + SDHCI_SIGNAL_ENABLE); + sdhci_init(host); + } + + sdhci_set_clock(host, ios->clock); + + if (ios->power_mode == MMC_POWER_OFF) + sdhci_set_power(host, -1); + else + sdhci_set_power(host, ios->vdd); + + ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); + + if (ios->bus_width == MMC_BUS_WIDTH_4) + ctrl |= SDHCI_CTRL_4BITBUS; + else + ctrl &= ~SDHCI_CTRL_4BITBUS; + + if (ios->timing == MMC_TIMING_SD_HS) + ctrl |= SDHCI_CTRL_HISPD; + else + ctrl &= ~SDHCI_CTRL_HISPD; + + writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); + + mmiowb(); + spin_unlock_irqrestore(&host->lock, flags); +} + +static int sdhci_get_ro(struct mmc_host *mmc) +{ + struct sdhci_host *host; + unsigned long flags; + int present; + + host = mmc_priv(mmc); + + spin_lock_irqsave(&host->lock, flags); + + present = readl(host->ioaddr + SDHCI_PRESENT_STATE); + + spin_unlock_irqrestore(&host->lock, flags); + + return !(present & SDHCI_WRITE_PROTECT); +} + +static const struct mmc_host_ops sdhci_ops = { + .request = sdhci_request, + .set_ios = sdhci_set_ios, + .get_ro = sdhci_get_ro, +}; + +/*****************************************************************************\ + * * + * Tasklets * + * * +\*****************************************************************************/ + +static void sdhci_tasklet_card(unsigned long param) +{ + struct sdhci_host *host; + unsigned long flags; + + host = (struct sdhci_host*)param; + + spin_lock_irqsave(&host->lock, flags); + + if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) { + if (host->mrq) { + printk(KERN_ERR "%s: Card removed during transfer!\n", + mmc_hostname(host->mmc)); + printk(KERN_ERR "%s: Resetting controller.\n", + mmc_hostname(host->mmc)); + + sdhci_reset(host, SDHCI_RESET_CMD); + sdhci_reset(host, SDHCI_RESET_DATA); + + host->mrq->cmd->error = MMC_ERR_FAILED; + tasklet_schedule(&host->finish_tasklet); + } + } + + spin_unlock_irqrestore(&host->lock, flags); + + mmc_detect_change(host->mmc, msecs_to_jiffies(500)); +} + +static void sdhci_tasklet_finish(unsigned long param) +{ + struct sdhci_host *host; + unsigned long flags; + struct mmc_request *mrq; + + host = (struct sdhci_host*)param; + + spin_lock_irqsave(&host->lock, flags); + + del_timer(&host->timer); + + mrq = host->mrq; + + DBG("Ending request, cmd (%x)\n", mrq->cmd->opcode); + + /* + * The controller needs a reset of internal state machines + * upon error conditions. + */ + if ((mrq->cmd->error != MMC_ERR_NONE) || + (mrq->data && ((mrq->data->error != MMC_ERR_NONE) || + (mrq->data->stop && (mrq->data->stop->error != MMC_ERR_NONE))))) { + + /* Some controllers need this kick or reset won't work here */ + if (host->chip->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) { + unsigned int clock; + + /* This is to force an update */ + clock = host->clock; + host->clock = 0; + sdhci_set_clock(host, clock); + } + + /* Spec says we should do both at the same time, but Ricoh + controllers do not like that. */ + sdhci_reset(host, SDHCI_RESET_CMD); + sdhci_reset(host, SDHCI_RESET_DATA); + } + + host->mrq = NULL; + host->cmd = NULL; + host->data = NULL; + + sdhci_deactivate_led(host); + + mmiowb(); + spin_unlock_irqrestore(&host->lock, flags); + + mmc_request_done(host->mmc, mrq); +} + +static void sdhci_timeout_timer(unsigned long data) +{ + struct sdhci_host *host; + unsigned long flags; + + host = (struct sdhci_host*)data; + + spin_lock_irqsave(&host->lock, flags); + + if (host->mrq) { + printk(KERN_ERR "%s: Timeout waiting for hardware " + "interrupt.\n", mmc_hostname(host->mmc)); + sdhci_dumpregs(host); + + if (host->data) { + host->data->error = MMC_ERR_TIMEOUT; + sdhci_finish_data(host); + } else { + if (host->cmd) + host->cmd->error = MMC_ERR_TIMEOUT; + else + host->mrq->cmd->error = MMC_ERR_TIMEOUT; + + tasklet_schedule(&host->finish_tasklet); + } + } + + mmiowb(); + spin_unlock_irqrestore(&host->lock, flags); +} + +/*****************************************************************************\ + * * + * Interrupt handling * + * * +\*****************************************************************************/ + +static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) +{ + BUG_ON(intmask == 0); + + if (!host->cmd) { + printk(KERN_ERR "%s: Got command interrupt even though no " + "command operation was in progress.\n", + mmc_hostname(host->mmc)); + sdhci_dumpregs(host); + return; + } + + if (intmask & SDHCI_INT_RESPONSE) + sdhci_finish_command(host); + else { + if (intmask & SDHCI_INT_TIMEOUT) + host->cmd->error = MMC_ERR_TIMEOUT; + else if (intmask & SDHCI_INT_CRC) + host->cmd->error = MMC_ERR_BADCRC; + else if (intmask & (SDHCI_INT_END_BIT | SDHCI_INT_INDEX)) + host->cmd->error = MMC_ERR_FAILED; + else + host->cmd->error = MMC_ERR_INVALID; + + tasklet_schedule(&host->finish_tasklet); + } +} + +static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) +{ + BUG_ON(intmask == 0); + + if (!host->data) { + /* + * A data end interrupt is sent together with the response + * for the stop command. + */ + if (intmask & SDHCI_INT_DATA_END) + return; + + printk(KERN_ERR "%s: Got data interrupt even though no " + "data operation was in progress.\n", + mmc_hostname(host->mmc)); + sdhci_dumpregs(host); + + return; + } + + if (intmask & SDHCI_INT_DATA_TIMEOUT) + host->data->error = MMC_ERR_TIMEOUT; + else if (intmask & SDHCI_INT_DATA_CRC) + host->data->error = MMC_ERR_BADCRC; + else if (intmask & SDHCI_INT_DATA_END_BIT) + host->data->error = MMC_ERR_FAILED; + + if (host->data->error != MMC_ERR_NONE) + sdhci_finish_data(host); + else { + if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) + sdhci_transfer_pio(host); + + if (intmask & SDHCI_INT_DATA_END) + sdhci_finish_data(host); + } +} + +static irqreturn_t sdhci_irq(int irq, void *dev_id) +{ + irqreturn_t result; + struct sdhci_host* host = dev_id; + u32 intmask; + + spin_lock(&host->lock); + + intmask = readl(host->ioaddr + SDHCI_INT_STATUS); + + if (!intmask || intmask == 0xffffffff) { + result = IRQ_NONE; + goto out; + } + + DBG("*** %s got interrupt: 0x%08x\n", host->slot_descr, intmask); + + if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { + writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE), + host->ioaddr + SDHCI_INT_STATUS); + tasklet_schedule(&host->card_tasklet); + } + + intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE); + + if (intmask & SDHCI_INT_CMD_MASK) { + writel(intmask & SDHCI_INT_CMD_MASK, + host->ioaddr + SDHCI_INT_STATUS); + sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); + } + + if (intmask & SDHCI_INT_DATA_MASK) { + writel(intmask & SDHCI_INT_DATA_MASK, + host->ioaddr + SDHCI_INT_STATUS); + sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); + } + + intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK); + + if (intmask & SDHCI_INT_BUS_POWER) { + printk(KERN_ERR "%s: Card is consuming too much power!\n", + mmc_hostname(host->mmc)); + writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS); + } + + intmask &= SDHCI_INT_BUS_POWER; + + if (intmask) { + printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n", + mmc_hostname(host->mmc), intmask); + sdhci_dumpregs(host); + + writel(intmask, host->ioaddr + SDHCI_INT_STATUS); + } + + result = IRQ_HANDLED; + + mmiowb(); +out: + spin_unlock(&host->lock); + + return result; +} + +/*****************************************************************************\ + * * + * Suspend/resume * + * * +\*****************************************************************************/ + +#ifdef CONFIG_PM + +static int sdhci_suspend (struct pci_dev *pdev, pm_message_t state) +{ + struct sdhci_chip *chip; + int i, ret; + + chip = pci_get_drvdata(pdev); + if (!chip) + return 0; + + DBG("Suspending...\n"); + + for (i = 0;i < chip->num_slots;i++) { + if (!chip->hosts[i]) + continue; + ret = mmc_suspend_host(chip->hosts[i]->mmc, state); + if (ret) { + for (i--;i >= 0;i--) + mmc_resume_host(chip->hosts[i]->mmc); + return ret; + } + } + + pci_save_state(pdev); + pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); + + for (i = 0;i < chip->num_slots;i++) { + if (!chip->hosts[i]) + continue; + free_irq(chip->hosts[i]->irq, chip->hosts[i]); + } + + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int sdhci_resume (struct pci_dev *pdev) +{ + struct sdhci_chip *chip; + int i, ret; + + chip = pci_get_drvdata(pdev); + if (!chip) + return 0; + + DBG("Resuming...\n"); + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + ret = pci_enable_device(pdev); + if (ret) + return ret; + + for (i = 0;i < chip->num_slots;i++) { + if (!chip->hosts[i]) + continue; + if (chip->hosts[i]->flags & SDHCI_USE_DMA) + pci_set_master(pdev); + ret = request_irq(chip->hosts[i]->irq, sdhci_irq, + IRQF_SHARED, chip->hosts[i]->slot_descr, + chip->hosts[i]); + if (ret) + return ret; + sdhci_init(chip->hosts[i]); + mmiowb(); + ret = mmc_resume_host(chip->hosts[i]->mmc); + if (ret) + return ret; + } + + return 0; +} + +#else /* CONFIG_PM */ + +#define sdhci_suspend NULL +#define sdhci_resume NULL + +#endif /* CONFIG_PM */ + +/*****************************************************************************\ + * * + * Device probing/removal * + * * +\*****************************************************************************/ + +static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) +{ + int ret; + unsigned int version; + struct sdhci_chip *chip; + struct mmc_host *mmc; + struct sdhci_host *host; + + u8 first_bar; + unsigned int caps; + + chip = pci_get_drvdata(pdev); + BUG_ON(!chip); + + ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar); + if (ret) + return ret; + + first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK; + + if (first_bar > 5) { + printk(KERN_ERR DRIVER_NAME ": Invalid first BAR. Aborting.\n"); + return -ENODEV; + } + + if (!(pci_resource_flags(pdev, first_bar + slot) & IORESOURCE_MEM)) { + printk(KERN_ERR DRIVER_NAME ": BAR is not iomem. Aborting.\n"); + return -ENODEV; + } + + if (pci_resource_len(pdev, first_bar + slot) != 0x100) { + printk(KERN_ERR DRIVER_NAME ": Invalid iomem size. " + "You may experience problems.\n"); + } + + if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) { + printk(KERN_ERR DRIVER_NAME ": Vendor specific interface. Aborting.\n"); + return -ENODEV; + } + + if ((pdev->class & 0x0000FF) > PCI_SDHCI_IFVENDOR) { + printk(KERN_ERR DRIVER_NAME ": Unknown interface. Aborting.\n"); + return -ENODEV; + } + + mmc = mmc_alloc_host(sizeof(struct sdhci_host), &pdev->dev); + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); + host->mmc = mmc; + + host->chip = chip; + chip->hosts[slot] = host; + + host->bar = first_bar + slot; + + host->addr = pci_resource_start(pdev, host->bar); + host->irq = pdev->irq; + + DBG("slot %d at 0x%08lx, irq %d\n", slot, host->addr, host->irq); + + snprintf(host->slot_descr, 20, "sdhci:slot%d", slot); + + ret = pci_request_region(pdev, host->bar, host->slot_descr); + if (ret) + goto free; + + host->ioaddr = ioremap_nocache(host->addr, + pci_resource_len(pdev, host->bar)); + if (!host->ioaddr) { + ret = -ENOMEM; + goto release; + } + + sdhci_reset(host, SDHCI_RESET_ALL); + + version = readw(host->ioaddr + SDHCI_HOST_VERSION); + version = (version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT; + if (version != 0) { + printk(KERN_ERR "%s: Unknown controller version (%d). " + "You may experience problems.\n", host->slot_descr, + version); + } + + caps = readl(host->ioaddr + SDHCI_CAPABILITIES); + + if (debug_nodma) + DBG("DMA forced off\n"); + else if (debug_forcedma) { + DBG("DMA forced on\n"); + host->flags |= SDHCI_USE_DMA; + } else if (chip->quirks & SDHCI_QUIRK_FORCE_DMA) + host->flags |= SDHCI_USE_DMA; + else if ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) + DBG("Controller doesn't have DMA interface\n"); + else if (!(caps & SDHCI_CAN_DO_DMA)) + DBG("Controller doesn't have DMA capability\n"); + else + host->flags |= SDHCI_USE_DMA; + + if (host->flags & SDHCI_USE_DMA) { + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { + printk(KERN_WARNING "%s: No suitable DMA available. " + "Falling back to PIO.\n", host->slot_descr); + host->flags &= ~SDHCI_USE_DMA; + } + } + + if (host->flags & SDHCI_USE_DMA) + pci_set_master(pdev); + else /* XXX: Hack to get MMC layer to avoid highmem */ + pdev->dma_mask = 0; + + host->max_clk = + (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; + if (host->max_clk == 0) { + printk(KERN_ERR "%s: Hardware doesn't specify base clock " + "frequency.\n", host->slot_descr); + ret = -ENODEV; + goto unmap; + } + host->max_clk *= 1000000; + + host->timeout_clk = + (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT; + if (host->timeout_clk == 0) { + printk(KERN_ERR "%s: Hardware doesn't specify timeout clock " + "frequency.\n", host->slot_descr); + ret = -ENODEV; + goto unmap; + } + if (caps & SDHCI_TIMEOUT_CLK_UNIT) + host->timeout_clk *= 1000; + + /* + * Set host parameters. + */ + mmc->ops = &sdhci_ops; + mmc->f_min = host->max_clk / 256; + mmc->f_max = host->max_clk; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; + + if (caps & SDHCI_CAN_DO_HISPD) + mmc->caps |= MMC_CAP_SD_HIGHSPEED; + + mmc->ocr_avail = 0; + if (caps & SDHCI_CAN_VDD_330) + mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34; + if (caps & SDHCI_CAN_VDD_300) + mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31; + if (caps & SDHCI_CAN_VDD_180) + mmc->ocr_avail |= MMC_VDD_17_18|MMC_VDD_18_19; + + if (mmc->ocr_avail == 0) { + printk(KERN_ERR "%s: Hardware doesn't report any " + "support voltages.\n", host->slot_descr); + ret = -ENODEV; + goto unmap; + } + + spin_lock_init(&host->lock); + + /* + * Maximum number of segments. Hardware cannot do scatter lists. + */ + if (host->flags & SDHCI_USE_DMA) + mmc->max_hw_segs = 1; + else + mmc->max_hw_segs = 16; + mmc->max_phys_segs = 16; + + /* + * Maximum number of sectors in one transfer. Limited by DMA boundary + * size (512KiB). + */ + mmc->max_req_size = 524288; + + /* + * Maximum segment size. Could be one segment with the maximum number + * of bytes. + */ + mmc->max_seg_size = mmc->max_req_size; + + /* + * Maximum block size. This varies from controller to controller and + * is specified in the capabilities register. + */ + mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT; + if (mmc->max_blk_size >= 3) { + printk(KERN_ERR "%s: Invalid maximum block size.\n", + host->slot_descr); + ret = -ENODEV; + goto unmap; + } + mmc->max_blk_size = 512 << mmc->max_blk_size; + + /* + * Maximum block count. + */ + mmc->max_blk_count = 65535; + + /* + * Init tasklets. + */ + tasklet_init(&host->card_tasklet, + sdhci_tasklet_card, (unsigned long)host); + tasklet_init(&host->finish_tasklet, + sdhci_tasklet_finish, (unsigned long)host); + + setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host); + + ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, + host->slot_descr, host); + if (ret) + goto untasklet; + + sdhci_init(host); + +#ifdef CONFIG_MMC_DEBUG + sdhci_dumpregs(host); +#endif + + mmiowb(); + + mmc_add_host(mmc); + + printk(KERN_INFO "%s: SDHCI at 0x%08lx irq %d %s\n", mmc_hostname(mmc), + host->addr, host->irq, + (host->flags & SDHCI_USE_DMA)?"DMA":"PIO"); + + return 0; + +untasklet: + tasklet_kill(&host->card_tasklet); + tasklet_kill(&host->finish_tasklet); +unmap: + iounmap(host->ioaddr); +release: + pci_release_region(pdev, host->bar); +free: + mmc_free_host(mmc); + + return ret; +} + +static void sdhci_remove_slot(struct pci_dev *pdev, int slot) +{ + struct sdhci_chip *chip; + struct mmc_host *mmc; + struct sdhci_host *host; + + chip = pci_get_drvdata(pdev); + host = chip->hosts[slot]; + mmc = host->mmc; + + chip->hosts[slot] = NULL; + + mmc_remove_host(mmc); + + sdhci_reset(host, SDHCI_RESET_ALL); + + free_irq(host->irq, host); + + del_timer_sync(&host->timer); + + tasklet_kill(&host->card_tasklet); + tasklet_kill(&host->finish_tasklet); + + iounmap(host->ioaddr); + + pci_release_region(pdev, host->bar); + + mmc_free_host(mmc); +} + +static int __devinit sdhci_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int ret, i; + u8 slots, rev; + struct sdhci_chip *chip; + + BUG_ON(pdev == NULL); + BUG_ON(ent == NULL); + + pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev); + + printk(KERN_INFO DRIVER_NAME + ": SDHCI controller found at %s [%04x:%04x] (rev %x)\n", + pci_name(pdev), (int)pdev->vendor, (int)pdev->device, + (int)rev); + + ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots); + if (ret) + return ret; + + slots = PCI_SLOT_INFO_SLOTS(slots) + 1; + DBG("found %d slot(s)\n", slots); + if (slots == 0) + return -ENODEV; + + ret = pci_enable_device(pdev); + if (ret) + return ret; + + chip = kzalloc(sizeof(struct sdhci_chip) + + sizeof(struct sdhci_host*) * slots, GFP_KERNEL); + if (!chip) { + ret = -ENOMEM; + goto err; + } + + chip->pdev = pdev; + chip->quirks = ent->driver_data; + + if (debug_quirks) + chip->quirks = debug_quirks; + + chip->num_slots = slots; + pci_set_drvdata(pdev, chip); + + for (i = 0;i < slots;i++) { + ret = sdhci_probe_slot(pdev, i); + if (ret) { + for (i--;i >= 0;i--) + sdhci_remove_slot(pdev, i); + goto free; + } + } + + return 0; + +free: + pci_set_drvdata(pdev, NULL); + kfree(chip); + +err: + pci_disable_device(pdev); + return ret; +} + +static void __devexit sdhci_remove(struct pci_dev *pdev) +{ + int i; + struct sdhci_chip *chip; + + chip = pci_get_drvdata(pdev); + + if (chip) { + for (i = 0;i < chip->num_slots;i++) + sdhci_remove_slot(pdev, i); + + pci_set_drvdata(pdev, NULL); + + kfree(chip); + } + + pci_disable_device(pdev); +} + +static struct pci_driver sdhci_driver = { + .name = DRIVER_NAME, + .id_table = pci_ids, + .probe = sdhci_probe, + .remove = __devexit_p(sdhci_remove), + .suspend = sdhci_suspend, + .resume = sdhci_resume, +}; + +/*****************************************************************************\ + * * + * Driver init/exit * + * * +\*****************************************************************************/ + +static int __init sdhci_drv_init(void) +{ + printk(KERN_INFO DRIVER_NAME + ": Secure Digital Host Controller Interface driver\n"); + printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); + + return pci_register_driver(&sdhci_driver); +} + +static void __exit sdhci_drv_exit(void) +{ + DBG("Exiting\n"); + + pci_unregister_driver(&sdhci_driver); +} + +module_init(sdhci_drv_init); +module_exit(sdhci_drv_exit); + +module_param(debug_nodma, uint, 0444); +module_param(debug_forcedma, uint, 0444); +module_param(debug_quirks, uint, 0444); + +MODULE_AUTHOR("Pierre Ossman "); +MODULE_DESCRIPTION("Secure Digital Host Controller Interface driver"); +MODULE_LICENSE("GPL"); + +MODULE_PARM_DESC(debug_nodma, "Forcefully disable DMA transfers. (default 0)"); +MODULE_PARM_DESC(debug_forcedma, "Forcefully enable DMA transfers. (default 0)"); +MODULE_PARM_DESC(debug_quirks, "Force certain quirks."); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h new file mode 100644 index 00000000000..7400f4bc114 --- /dev/null +++ b/drivers/mmc/host/sdhci.h @@ -0,0 +1,210 @@ +/* + * linux/drivers/mmc/sdhci.h - Secure Digital Host Controller Interface driver + * + * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +/* + * PCI registers + */ + +#define PCI_SDHCI_IFPIO 0x00 +#define PCI_SDHCI_IFDMA 0x01 +#define PCI_SDHCI_IFVENDOR 0x02 + +#define PCI_SLOT_INFO 0x40 /* 8 bits */ +#define PCI_SLOT_INFO_SLOTS(x) ((x >> 4) & 7) +#define PCI_SLOT_INFO_FIRST_BAR_MASK 0x07 + +/* + * Controller registers + */ + +#define SDHCI_DMA_ADDRESS 0x00 + +#define SDHCI_BLOCK_SIZE 0x04 +#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF)) + +#define SDHCI_BLOCK_COUNT 0x06 + +#define SDHCI_ARGUMENT 0x08 + +#define SDHCI_TRANSFER_MODE 0x0C +#define SDHCI_TRNS_DMA 0x01 +#define SDHCI_TRNS_BLK_CNT_EN 0x02 +#define SDHCI_TRNS_ACMD12 0x04 +#define SDHCI_TRNS_READ 0x10 +#define SDHCI_TRNS_MULTI 0x20 + +#define SDHCI_COMMAND 0x0E +#define SDHCI_CMD_RESP_MASK 0x03 +#define SDHCI_CMD_CRC 0x08 +#define SDHCI_CMD_INDEX 0x10 +#define SDHCI_CMD_DATA 0x20 + +#define SDHCI_CMD_RESP_NONE 0x00 +#define SDHCI_CMD_RESP_LONG 0x01 +#define SDHCI_CMD_RESP_SHORT 0x02 +#define SDHCI_CMD_RESP_SHORT_BUSY 0x03 + +#define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff)) + +#define SDHCI_RESPONSE 0x10 + +#define SDHCI_BUFFER 0x20 + +#define SDHCI_PRESENT_STATE 0x24 +#define SDHCI_CMD_INHIBIT 0x00000001 +#define SDHCI_DATA_INHIBIT 0x00000002 +#define SDHCI_DOING_WRITE 0x00000100 +#define SDHCI_DOING_READ 0x00000200 +#define SDHCI_SPACE_AVAILABLE 0x00000400 +#define SDHCI_DATA_AVAILABLE 0x00000800 +#define SDHCI_CARD_PRESENT 0x00010000 +#define SDHCI_WRITE_PROTECT 0x00080000 + +#define SDHCI_HOST_CONTROL 0x28 +#define SDHCI_CTRL_LED 0x01 +#define SDHCI_CTRL_4BITBUS 0x02 +#define SDHCI_CTRL_HISPD 0x04 + +#define SDHCI_POWER_CONTROL 0x29 +#define SDHCI_POWER_ON 0x01 +#define SDHCI_POWER_180 0x0A +#define SDHCI_POWER_300 0x0C +#define SDHCI_POWER_330 0x0E + +#define SDHCI_BLOCK_GAP_CONTROL 0x2A + +#define SDHCI_WALK_UP_CONTROL 0x2B + +#define SDHCI_CLOCK_CONTROL 0x2C +#define SDHCI_DIVIDER_SHIFT 8 +#define SDHCI_CLOCK_CARD_EN 0x0004 +#define SDHCI_CLOCK_INT_STABLE 0x0002 +#define SDHCI_CLOCK_INT_EN 0x0001 + +#define SDHCI_TIMEOUT_CONTROL 0x2E + +#define SDHCI_SOFTWARE_RESET 0x2F +#define SDHCI_RESET_ALL 0x01 +#define SDHCI_RESET_CMD 0x02 +#define SDHCI_RESET_DATA 0x04 + +#define SDHCI_INT_STATUS 0x30 +#define SDHCI_INT_ENABLE 0x34 +#define SDHCI_SIGNAL_ENABLE 0x38 +#define SDHCI_INT_RESPONSE 0x00000001 +#define SDHCI_INT_DATA_END 0x00000002 +#define SDHCI_INT_DMA_END 0x00000008 +#define SDHCI_INT_SPACE_AVAIL 0x00000010 +#define SDHCI_INT_DATA_AVAIL 0x00000020 +#define SDHCI_INT_CARD_INSERT 0x00000040 +#define SDHCI_INT_CARD_REMOVE 0x00000080 +#define SDHCI_INT_CARD_INT 0x00000100 +#define SDHCI_INT_TIMEOUT 0x00010000 +#define SDHCI_INT_CRC 0x00020000 +#define SDHCI_INT_END_BIT 0x00040000 +#define SDHCI_INT_INDEX 0x00080000 +#define SDHCI_INT_DATA_TIMEOUT 0x00100000 +#define SDHCI_INT_DATA_CRC 0x00200000 +#define SDHCI_INT_DATA_END_BIT 0x00400000 +#define SDHCI_INT_BUS_POWER 0x00800000 +#define SDHCI_INT_ACMD12ERR 0x01000000 + +#define SDHCI_INT_NORMAL_MASK 0x00007FFF +#define SDHCI_INT_ERROR_MASK 0xFFFF8000 + +#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \ + SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX) +#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \ + SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \ + SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \ + SDHCI_INT_DATA_END_BIT) + +#define SDHCI_ACMD12_ERR 0x3C + +/* 3E-3F reserved */ + +#define SDHCI_CAPABILITIES 0x40 +#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F +#define SDHCI_TIMEOUT_CLK_SHIFT 0 +#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080 +#define SDHCI_CLOCK_BASE_MASK 0x00003F00 +#define SDHCI_CLOCK_BASE_SHIFT 8 +#define SDHCI_MAX_BLOCK_MASK 0x00030000 +#define SDHCI_MAX_BLOCK_SHIFT 16 +#define SDHCI_CAN_DO_HISPD 0x00200000 +#define SDHCI_CAN_DO_DMA 0x00400000 +#define SDHCI_CAN_VDD_330 0x01000000 +#define SDHCI_CAN_VDD_300 0x02000000 +#define SDHCI_CAN_VDD_180 0x04000000 + +/* 44-47 reserved for more caps */ + +#define SDHCI_MAX_CURRENT 0x48 + +/* 4C-4F reserved for more max current */ + +/* 50-FB reserved */ + +#define SDHCI_SLOT_INT_STATUS 0xFC + +#define SDHCI_HOST_VERSION 0xFE +#define SDHCI_VENDOR_VER_MASK 0xFF00 +#define SDHCI_VENDOR_VER_SHIFT 8 +#define SDHCI_SPEC_VER_MASK 0x00FF +#define SDHCI_SPEC_VER_SHIFT 0 + +struct sdhci_chip; + +struct sdhci_host { + struct sdhci_chip *chip; + struct mmc_host *mmc; /* MMC structure */ + + spinlock_t lock; /* Mutex */ + + int flags; /* Host attributes */ +#define SDHCI_USE_DMA (1<<0) + + unsigned int max_clk; /* Max possible freq (MHz) */ + unsigned int timeout_clk; /* Timeout freq (KHz) */ + + unsigned int clock; /* Current clock (MHz) */ + unsigned short power; /* Current voltage */ + + struct mmc_request *mrq; /* Current request */ + struct mmc_command *cmd; /* Current command */ + struct mmc_data *data; /* Current data request */ + + struct scatterlist *cur_sg; /* We're working on this */ + int num_sg; /* Entries left */ + int offset; /* Offset into current sg */ + int remain; /* Bytes left in current */ + + char slot_descr[20]; /* Name for reservations */ + + int irq; /* Device IRQ */ + int bar; /* PCI BAR index */ + unsigned long addr; /* Bus address */ + void __iomem * ioaddr; /* Mapped address */ + + struct tasklet_struct card_tasklet; /* Tasklet structures */ + struct tasklet_struct finish_tasklet; + + struct timer_list timer; /* Timer for timeouts */ +}; + +struct sdhci_chip { + struct pci_dev *pdev; + + unsigned long quirks; + + int num_slots; /* Slots on controller */ + struct sdhci_host *hosts[0]; /* Pointers to hosts */ +}; diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c new file mode 100644 index 00000000000..b0d77d29841 --- /dev/null +++ b/drivers/mmc/host/tifm_sd.c @@ -0,0 +1,1102 @@ +/* + * tifm_sd.c - TI FlashMedia driver + * + * Copyright (C) 2006 Alex Dubov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Special thanks to Brad Campbell for extensive testing of this driver. + * + */ + + +#include +#include +#include +#include +#include + +#define DRIVER_NAME "tifm_sd" +#define DRIVER_VERSION "0.8" + +static int no_dma = 0; +static int fixed_timeout = 0; +module_param(no_dma, bool, 0644); +module_param(fixed_timeout, bool, 0644); + +/* Constants here are mostly from OMAP5912 datasheet */ +#define TIFM_MMCSD_RESET 0x0002 +#define TIFM_MMCSD_CLKMASK 0x03ff +#define TIFM_MMCSD_POWER 0x0800 +#define TIFM_MMCSD_4BBUS 0x8000 +#define TIFM_MMCSD_RXDE 0x8000 /* rx dma enable */ +#define TIFM_MMCSD_TXDE 0x0080 /* tx dma enable */ +#define TIFM_MMCSD_BUFINT 0x0c00 /* set bits: AE, AF */ +#define TIFM_MMCSD_DPE 0x0020 /* data timeout counted in kilocycles */ +#define TIFM_MMCSD_INAB 0x0080 /* abort / initialize command */ +#define TIFM_MMCSD_READ 0x8000 + +#define TIFM_MMCSD_ERRMASK 0x01e0 /* set bits: CCRC, CTO, DCRC, DTO */ +#define TIFM_MMCSD_EOC 0x0001 /* end of command phase */ +#define TIFM_MMCSD_CD 0x0002 /* card detect */ +#define TIFM_MMCSD_CB 0x0004 /* card enter busy state */ +#define TIFM_MMCSD_BRS 0x0008 /* block received/sent */ +#define TIFM_MMCSD_EOFB 0x0010 /* card exit busy state */ +#define TIFM_MMCSD_DTO 0x0020 /* data time-out */ +#define TIFM_MMCSD_DCRC 0x0040 /* data crc error */ +#define TIFM_MMCSD_CTO 0x0080 /* command time-out */ +#define TIFM_MMCSD_CCRC 0x0100 /* command crc error */ +#define TIFM_MMCSD_AF 0x0400 /* fifo almost full */ +#define TIFM_MMCSD_AE 0x0800 /* fifo almost empty */ +#define TIFM_MMCSD_OCRB 0x1000 /* OCR busy */ +#define TIFM_MMCSD_CIRQ 0x2000 /* card irq (cmd40/sdio) */ +#define TIFM_MMCSD_CERR 0x4000 /* card status error */ + +#define TIFM_MMCSD_ODTO 0x0040 /* open drain / extended timeout */ +#define TIFM_MMCSD_CARD_RO 0x0200 /* card is read-only */ + +#define TIFM_MMCSD_FIFO_SIZE 0x0020 + +#define TIFM_MMCSD_RSP_R0 0x0000 +#define TIFM_MMCSD_RSP_R1 0x0100 +#define TIFM_MMCSD_RSP_R2 0x0200 +#define TIFM_MMCSD_RSP_R3 0x0300 +#define TIFM_MMCSD_RSP_R4 0x0400 +#define TIFM_MMCSD_RSP_R5 0x0500 +#define TIFM_MMCSD_RSP_R6 0x0600 + +#define TIFM_MMCSD_RSP_BUSY 0x0800 + +#define TIFM_MMCSD_CMD_BC 0x0000 +#define TIFM_MMCSD_CMD_BCR 0x1000 +#define TIFM_MMCSD_CMD_AC 0x2000 +#define TIFM_MMCSD_CMD_ADTC 0x3000 + +#define TIFM_MMCSD_MAX_BLOCK_SIZE 0x0800UL + +enum { + CMD_READY = 0x0001, + FIFO_READY = 0x0002, + BRS_READY = 0x0004, + SCMD_ACTIVE = 0x0008, + SCMD_READY = 0x0010, + CARD_BUSY = 0x0020, + DATA_CARRY = 0x0040 +}; + +struct tifm_sd { + struct tifm_dev *dev; + + unsigned short eject:1, + open_drain:1, + no_dma:1; + unsigned short cmd_flags; + + unsigned int clk_freq; + unsigned int clk_div; + unsigned long timeout_jiffies; + + struct tasklet_struct finish_tasklet; + struct timer_list timer; + struct mmc_request *req; + + int sg_len; + int sg_pos; + unsigned int block_pos; + struct scatterlist bounce_buf; + unsigned char bounce_buf_data[TIFM_MMCSD_MAX_BLOCK_SIZE]; +}; + +/* for some reason, host won't respond correctly to readw/writew */ +static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg, + unsigned int off, unsigned int cnt) +{ + struct tifm_dev *sock = host->dev; + unsigned char *buf; + unsigned int pos = 0, val; + + buf = kmap_atomic(pg, KM_BIO_DST_IRQ) + off; + if (host->cmd_flags & DATA_CARRY) { + buf[pos++] = host->bounce_buf_data[0]; + host->cmd_flags &= ~DATA_CARRY; + } + + while (pos < cnt) { + val = readl(sock->addr + SOCK_MMCSD_DATA); + buf[pos++] = val & 0xff; + if (pos == cnt) { + host->bounce_buf_data[0] = (val >> 8) & 0xff; + host->cmd_flags |= DATA_CARRY; + break; + } + buf[pos++] = (val >> 8) & 0xff; + } + kunmap_atomic(buf - off, KM_BIO_DST_IRQ); +} + +static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg, + unsigned int off, unsigned int cnt) +{ + struct tifm_dev *sock = host->dev; + unsigned char *buf; + unsigned int pos = 0, val; + + buf = kmap_atomic(pg, KM_BIO_SRC_IRQ) + off; + if (host->cmd_flags & DATA_CARRY) { + val = host->bounce_buf_data[0] | ((buf[pos++] << 8) & 0xff00); + writel(val, sock->addr + SOCK_MMCSD_DATA); + host->cmd_flags &= ~DATA_CARRY; + } + + while (pos < cnt) { + val = buf[pos++]; + if (pos == cnt) { + host->bounce_buf_data[0] = val & 0xff; + host->cmd_flags |= DATA_CARRY; + break; + } + val |= (buf[pos++] << 8) & 0xff00; + writel(val, sock->addr + SOCK_MMCSD_DATA); + } + kunmap_atomic(buf - off, KM_BIO_SRC_IRQ); +} + +static void tifm_sd_transfer_data(struct tifm_sd *host) +{ + struct mmc_data *r_data = host->req->cmd->data; + struct scatterlist *sg = r_data->sg; + unsigned int off, cnt, t_size = TIFM_MMCSD_FIFO_SIZE * 2; + unsigned int p_off, p_cnt; + struct page *pg; + + if (host->sg_pos == host->sg_len) + return; + while (t_size) { + cnt = sg[host->sg_pos].length - host->block_pos; + if (!cnt) { + host->block_pos = 0; + host->sg_pos++; + if (host->sg_pos == host->sg_len) { + if ((r_data->flags & MMC_DATA_WRITE) + && DATA_CARRY) + writel(host->bounce_buf_data[0], + host->dev->addr + + SOCK_MMCSD_DATA); + + return; + } + cnt = sg[host->sg_pos].length; + } + off = sg[host->sg_pos].offset + host->block_pos; + + pg = nth_page(sg[host->sg_pos].page, off >> PAGE_SHIFT); + p_off = offset_in_page(off); + p_cnt = PAGE_SIZE - p_off; + p_cnt = min(p_cnt, cnt); + p_cnt = min(p_cnt, t_size); + + if (r_data->flags & MMC_DATA_READ) + tifm_sd_read_fifo(host, pg, p_off, p_cnt); + else if (r_data->flags & MMC_DATA_WRITE) + tifm_sd_write_fifo(host, pg, p_off, p_cnt); + + t_size -= p_cnt; + host->block_pos += p_cnt; + } +} + +static void tifm_sd_copy_page(struct page *dst, unsigned int dst_off, + struct page *src, unsigned int src_off, + unsigned int count) +{ + unsigned char *src_buf = kmap_atomic(src, KM_BIO_SRC_IRQ) + src_off; + unsigned char *dst_buf = kmap_atomic(dst, KM_BIO_DST_IRQ) + dst_off; + + memcpy(dst_buf, src_buf, count); + + kunmap_atomic(dst_buf - dst_off, KM_BIO_DST_IRQ); + kunmap_atomic(src_buf - src_off, KM_BIO_SRC_IRQ); +} + +static void tifm_sd_bounce_block(struct tifm_sd *host, struct mmc_data *r_data) +{ + struct scatterlist *sg = r_data->sg; + unsigned int t_size = r_data->blksz; + unsigned int off, cnt; + unsigned int p_off, p_cnt; + struct page *pg; + + dev_dbg(&host->dev->dev, "bouncing block\n"); + while (t_size) { + cnt = sg[host->sg_pos].length - host->block_pos; + if (!cnt) { + host->block_pos = 0; + host->sg_pos++; + if (host->sg_pos == host->sg_len) + return; + cnt = sg[host->sg_pos].length; + } + off = sg[host->sg_pos].offset + host->block_pos; + + pg = nth_page(sg[host->sg_pos].page, off >> PAGE_SHIFT); + p_off = offset_in_page(off); + p_cnt = PAGE_SIZE - p_off; + p_cnt = min(p_cnt, cnt); + p_cnt = min(p_cnt, t_size); + + if (r_data->flags & MMC_DATA_WRITE) + tifm_sd_copy_page(host->bounce_buf.page, + r_data->blksz - t_size, + pg, p_off, p_cnt); + else if (r_data->flags & MMC_DATA_READ) + tifm_sd_copy_page(pg, p_off, host->bounce_buf.page, + r_data->blksz - t_size, p_cnt); + + t_size -= p_cnt; + host->block_pos += p_cnt; + } +} + +int tifm_sd_set_dma_data(struct tifm_sd *host, struct mmc_data *r_data) +{ + struct tifm_dev *sock = host->dev; + unsigned int t_size = TIFM_DMA_TSIZE * r_data->blksz; + unsigned int dma_len, dma_blk_cnt, dma_off; + struct scatterlist *sg = NULL; + unsigned long flags; + + if (host->sg_pos == host->sg_len) + return 1; + + if (host->cmd_flags & DATA_CARRY) { + host->cmd_flags &= ~DATA_CARRY; + local_irq_save(flags); + tifm_sd_bounce_block(host, r_data); + local_irq_restore(flags); + if (host->sg_pos == host->sg_len) + return 1; + } + + dma_len = sg_dma_len(&r_data->sg[host->sg_pos]) - host->block_pos; + if (!dma_len) { + host->block_pos = 0; + host->sg_pos++; + if (host->sg_pos == host->sg_len) + return 1; + dma_len = sg_dma_len(&r_data->sg[host->sg_pos]); + } + + if (dma_len < t_size) { + dma_blk_cnt = dma_len / r_data->blksz; + dma_off = host->block_pos; + host->block_pos += dma_blk_cnt * r_data->blksz; + } else { + dma_blk_cnt = TIFM_DMA_TSIZE; + dma_off = host->block_pos; + host->block_pos += t_size; + } + + if (dma_blk_cnt) + sg = &r_data->sg[host->sg_pos]; + else if (dma_len) { + if (r_data->flags & MMC_DATA_WRITE) { + local_irq_save(flags); + tifm_sd_bounce_block(host, r_data); + local_irq_restore(flags); + } else + host->cmd_flags |= DATA_CARRY; + + sg = &host->bounce_buf; + dma_off = 0; + dma_blk_cnt = 1; + } else + return 1; + + dev_dbg(&sock->dev, "setting dma for %d blocks\n", dma_blk_cnt); + writel(sg_dma_address(sg) + dma_off, sock->addr + SOCK_DMA_ADDRESS); + if (r_data->flags & MMC_DATA_WRITE) + writel((dma_blk_cnt << 8) | TIFM_DMA_TX | TIFM_DMA_EN, + sock->addr + SOCK_DMA_CONTROL); + else + writel((dma_blk_cnt << 8) | TIFM_DMA_EN, + sock->addr + SOCK_DMA_CONTROL); + + return 0; +} + +static unsigned int tifm_sd_op_flags(struct mmc_command *cmd) +{ + unsigned int rc = 0; + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_NONE: + rc |= TIFM_MMCSD_RSP_R0; + break; + case MMC_RSP_R1B: + rc |= TIFM_MMCSD_RSP_BUSY; // deliberate fall-through + case MMC_RSP_R1: + rc |= TIFM_MMCSD_RSP_R1; + break; + case MMC_RSP_R2: + rc |= TIFM_MMCSD_RSP_R2; + break; + case MMC_RSP_R3: + rc |= TIFM_MMCSD_RSP_R3; + break; + default: + BUG(); + } + + switch (mmc_cmd_type(cmd)) { + case MMC_CMD_BC: + rc |= TIFM_MMCSD_CMD_BC; + break; + case MMC_CMD_BCR: + rc |= TIFM_MMCSD_CMD_BCR; + break; + case MMC_CMD_AC: + rc |= TIFM_MMCSD_CMD_AC; + break; + case MMC_CMD_ADTC: + rc |= TIFM_MMCSD_CMD_ADTC; + break; + default: + BUG(); + } + return rc; +} + +static void tifm_sd_exec(struct tifm_sd *host, struct mmc_command *cmd) +{ + struct tifm_dev *sock = host->dev; + unsigned int cmd_mask = tifm_sd_op_flags(cmd); + + if (host->open_drain) + cmd_mask |= TIFM_MMCSD_ODTO; + + if (cmd->data && (cmd->data->flags & MMC_DATA_READ)) + cmd_mask |= TIFM_MMCSD_READ; + + dev_dbg(&sock->dev, "executing opcode 0x%x, arg: 0x%x, mask: 0x%x\n", + cmd->opcode, cmd->arg, cmd_mask); + + writel((cmd->arg >> 16) & 0xffff, sock->addr + SOCK_MMCSD_ARG_HIGH); + writel(cmd->arg & 0xffff, sock->addr + SOCK_MMCSD_ARG_LOW); + writel(cmd->opcode | cmd_mask, sock->addr + SOCK_MMCSD_COMMAND); +} + +static void tifm_sd_fetch_resp(struct mmc_command *cmd, struct tifm_dev *sock) +{ + cmd->resp[0] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x1c) << 16) + | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x18); + cmd->resp[1] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x14) << 16) + | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x10); + cmd->resp[2] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x0c) << 16) + | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x08); + cmd->resp[3] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x04) << 16) + | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x00); +} + +static void tifm_sd_check_status(struct tifm_sd *host) +{ + struct tifm_dev *sock = host->dev; + struct mmc_command *cmd = host->req->cmd; + + if (cmd->error != MMC_ERR_NONE) + goto finish_request; + + if (!(host->cmd_flags & CMD_READY)) + return; + + if (cmd->data) { + if (cmd->data->error != MMC_ERR_NONE) { + if ((host->cmd_flags & SCMD_ACTIVE) + && !(host->cmd_flags & SCMD_READY)) + return; + + goto finish_request; + } + + if (!(host->cmd_flags & BRS_READY)) + return; + + if (!(host->no_dma || (host->cmd_flags & FIFO_READY))) + return; + + if (cmd->data->flags & MMC_DATA_WRITE) { + if (host->req->stop) { + if (!(host->cmd_flags & SCMD_ACTIVE)) { + host->cmd_flags |= SCMD_ACTIVE; + writel(TIFM_MMCSD_EOFB + | readl(sock->addr + + SOCK_MMCSD_INT_ENABLE), + sock->addr + + SOCK_MMCSD_INT_ENABLE); + tifm_sd_exec(host, host->req->stop); + return; + } else { + if (!(host->cmd_flags & SCMD_READY) + || (host->cmd_flags & CARD_BUSY)) + return; + writel((~TIFM_MMCSD_EOFB) + & readl(sock->addr + + SOCK_MMCSD_INT_ENABLE), + sock->addr + + SOCK_MMCSD_INT_ENABLE); + } + } else { + if (host->cmd_flags & CARD_BUSY) + return; + writel((~TIFM_MMCSD_EOFB) + & readl(sock->addr + + SOCK_MMCSD_INT_ENABLE), + sock->addr + SOCK_MMCSD_INT_ENABLE); + } + } else { + if (host->req->stop) { + if (!(host->cmd_flags & SCMD_ACTIVE)) { + host->cmd_flags |= SCMD_ACTIVE; + tifm_sd_exec(host, host->req->stop); + return; + } else { + if (!(host->cmd_flags & SCMD_READY)) + return; + } + } + } + } +finish_request: + tasklet_schedule(&host->finish_tasklet); +} + +/* Called from interrupt handler */ +static void tifm_sd_data_event(struct tifm_dev *sock) +{ + struct tifm_sd *host; + unsigned int fifo_status = 0; + struct mmc_data *r_data = NULL; + + spin_lock(&sock->lock); + host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); + fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS); + dev_dbg(&sock->dev, "data event: fifo_status %x, flags %x\n", + fifo_status, host->cmd_flags); + + if (host->req) { + r_data = host->req->cmd->data; + + if (r_data && (fifo_status & TIFM_FIFO_READY)) { + if (tifm_sd_set_dma_data(host, r_data)) { + host->cmd_flags |= FIFO_READY; + tifm_sd_check_status(host); + } + } + } + + writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS); + spin_unlock(&sock->lock); +} + +/* Called from interrupt handler */ +static void tifm_sd_card_event(struct tifm_dev *sock) +{ + struct tifm_sd *host; + unsigned int host_status = 0; + int cmd_error = MMC_ERR_NONE; + struct mmc_command *cmd = NULL; + unsigned long flags; + + spin_lock(&sock->lock); + host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); + host_status = readl(sock->addr + SOCK_MMCSD_STATUS); + dev_dbg(&sock->dev, "host event: host_status %x, flags %x\n", + host_status, host->cmd_flags); + + if (host->req) { + cmd = host->req->cmd; + + if (host_status & TIFM_MMCSD_ERRMASK) { + writel(host_status & TIFM_MMCSD_ERRMASK, + sock->addr + SOCK_MMCSD_STATUS); + if (host_status & TIFM_MMCSD_CTO) + cmd_error = MMC_ERR_TIMEOUT; + else if (host_status & TIFM_MMCSD_CCRC) + cmd_error = MMC_ERR_BADCRC; + + if (cmd->data) { + if (host_status & TIFM_MMCSD_DTO) + cmd->data->error = MMC_ERR_TIMEOUT; + else if (host_status & TIFM_MMCSD_DCRC) + cmd->data->error = MMC_ERR_BADCRC; + } + + writel(TIFM_FIFO_INT_SETALL, + sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); + writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL); + + if (host->req->stop) { + if (host->cmd_flags & SCMD_ACTIVE) { + host->req->stop->error = cmd_error; + host->cmd_flags |= SCMD_READY; + } else { + cmd->error = cmd_error; + host->cmd_flags |= SCMD_ACTIVE; + tifm_sd_exec(host, host->req->stop); + goto done; + } + } else + cmd->error = cmd_error; + } else { + if (host_status & (TIFM_MMCSD_EOC | TIFM_MMCSD_CERR)) { + if (!(host->cmd_flags & CMD_READY)) { + host->cmd_flags |= CMD_READY; + tifm_sd_fetch_resp(cmd, sock); + } else if (host->cmd_flags & SCMD_ACTIVE) { + host->cmd_flags |= SCMD_READY; + tifm_sd_fetch_resp(host->req->stop, + sock); + } + } + if (host_status & TIFM_MMCSD_BRS) + host->cmd_flags |= BRS_READY; + } + + if (host->no_dma && cmd->data) { + if (host_status & TIFM_MMCSD_AE) + writel(host_status & TIFM_MMCSD_AE, + sock->addr + SOCK_MMCSD_STATUS); + + if (host_status & (TIFM_MMCSD_AE | TIFM_MMCSD_AF + | TIFM_MMCSD_BRS)) { + local_irq_save(flags); + tifm_sd_transfer_data(host); + local_irq_restore(flags); + host_status &= ~TIFM_MMCSD_AE; + } + } + + if (host_status & TIFM_MMCSD_EOFB) + host->cmd_flags &= ~CARD_BUSY; + else if (host_status & TIFM_MMCSD_CB) + host->cmd_flags |= CARD_BUSY; + + tifm_sd_check_status(host); + } +done: + writel(host_status, sock->addr + SOCK_MMCSD_STATUS); + spin_unlock(&sock->lock); +} + +static void tifm_sd_set_data_timeout(struct tifm_sd *host, + struct mmc_data *data) +{ + struct tifm_dev *sock = host->dev; + unsigned int data_timeout = data->timeout_clks; + + if (fixed_timeout) + return; + + data_timeout += data->timeout_ns / + ((1000000000UL / host->clk_freq) * host->clk_div); + + if (data_timeout < 0xffff) { + writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); + writel((~TIFM_MMCSD_DPE) + & readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), + sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); + } else { + data_timeout = (data_timeout >> 10) + 1; + if (data_timeout > 0xffff) + data_timeout = 0; /* set to unlimited */ + writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); + writel(TIFM_MMCSD_DPE + | readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), + sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); + } +} + +static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct tifm_sd *host = mmc_priv(mmc); + struct tifm_dev *sock = host->dev; + unsigned long flags; + struct mmc_data *r_data = mrq->cmd->data; + + spin_lock_irqsave(&sock->lock, flags); + if (host->eject) { + spin_unlock_irqrestore(&sock->lock, flags); + goto err_out; + } + + if (host->req) { + printk(KERN_ERR "%s : unfinished request detected\n", + sock->dev.bus_id); + spin_unlock_irqrestore(&sock->lock, flags); + goto err_out; + } + + host->cmd_flags = 0; + host->block_pos = 0; + host->sg_pos = 0; + + if (r_data) { + tifm_sd_set_data_timeout(host, r_data); + + if ((r_data->flags & MMC_DATA_WRITE) && !mrq->stop) + writel(TIFM_MMCSD_EOFB + | readl(sock->addr + SOCK_MMCSD_INT_ENABLE), + sock->addr + SOCK_MMCSD_INT_ENABLE); + + if (host->no_dma) { + writel(TIFM_MMCSD_BUFINT + | readl(sock->addr + SOCK_MMCSD_INT_ENABLE), + sock->addr + SOCK_MMCSD_INT_ENABLE); + writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8) + | (TIFM_MMCSD_FIFO_SIZE - 1), + sock->addr + SOCK_MMCSD_BUFFER_CONFIG); + + host->sg_len = r_data->sg_len; + } else { + sg_init_one(&host->bounce_buf, host->bounce_buf_data, + r_data->blksz); + + if(1 != tifm_map_sg(sock, &host->bounce_buf, 1, + r_data->flags & MMC_DATA_WRITE + ? PCI_DMA_TODEVICE + : PCI_DMA_FROMDEVICE)) { + printk(KERN_ERR "%s : scatterlist map failed\n", + sock->dev.bus_id); + spin_unlock_irqrestore(&sock->lock, flags); + goto err_out; + } + host->sg_len = tifm_map_sg(sock, r_data->sg, + r_data->sg_len, + r_data->flags + & MMC_DATA_WRITE + ? PCI_DMA_TODEVICE + : PCI_DMA_FROMDEVICE); + if (host->sg_len < 1) { + printk(KERN_ERR "%s : scatterlist map failed\n", + sock->dev.bus_id); + tifm_unmap_sg(sock, &host->bounce_buf, 1, + r_data->flags & MMC_DATA_WRITE + ? PCI_DMA_TODEVICE + : PCI_DMA_FROMDEVICE); + spin_unlock_irqrestore(&sock->lock, flags); + goto err_out; + } + + writel(TIFM_FIFO_INT_SETALL, + sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); + writel(ilog2(r_data->blksz) - 2, + sock->addr + SOCK_FIFO_PAGE_SIZE); + writel(TIFM_FIFO_ENABLE, + sock->addr + SOCK_FIFO_CONTROL); + writel(TIFM_FIFO_INTMASK, + sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); + + if (r_data->flags & MMC_DATA_WRITE) + writel(TIFM_MMCSD_TXDE, + sock->addr + SOCK_MMCSD_BUFFER_CONFIG); + else + writel(TIFM_MMCSD_RXDE, + sock->addr + SOCK_MMCSD_BUFFER_CONFIG); + + tifm_sd_set_dma_data(host, r_data); + } + + writel(r_data->blocks - 1, + sock->addr + SOCK_MMCSD_NUM_BLOCKS); + writel(r_data->blksz - 1, + sock->addr + SOCK_MMCSD_BLOCK_LEN); + } + + host->req = mrq; + mod_timer(&host->timer, jiffies + host->timeout_jiffies); + writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + tifm_sd_exec(host, mrq->cmd); + spin_unlock_irqrestore(&sock->lock, flags); + return; + +err_out: + mrq->cmd->error = MMC_ERR_TIMEOUT; + mmc_request_done(mmc, mrq); +} + +static void tifm_sd_end_cmd(unsigned long data) +{ + struct tifm_sd *host = (struct tifm_sd*)data; + struct tifm_dev *sock = host->dev; + struct mmc_host *mmc = tifm_get_drvdata(sock); + struct mmc_request *mrq; + struct mmc_data *r_data = NULL; + unsigned long flags; + + spin_lock_irqsave(&sock->lock, flags); + + del_timer(&host->timer); + mrq = host->req; + host->req = NULL; + + if (!mrq) { + printk(KERN_ERR " %s : no request to complete?\n", + sock->dev.bus_id); + spin_unlock_irqrestore(&sock->lock, flags); + return; + } + + r_data = mrq->cmd->data; + if (r_data) { + if (host->no_dma) { + writel((~TIFM_MMCSD_BUFINT) + & readl(sock->addr + SOCK_MMCSD_INT_ENABLE), + sock->addr + SOCK_MMCSD_INT_ENABLE); + } else { + tifm_unmap_sg(sock, &host->bounce_buf, 1, + (r_data->flags & MMC_DATA_WRITE) + ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + tifm_unmap_sg(sock, r_data->sg, r_data->sg_len, + (r_data->flags & MMC_DATA_WRITE) + ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + } + + r_data->bytes_xfered = r_data->blocks + - readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; + r_data->bytes_xfered *= r_data->blksz; + r_data->bytes_xfered += r_data->blksz + - readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1; + } + + writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + + spin_unlock_irqrestore(&sock->lock, flags); + mmc_request_done(mmc, mrq); +} + +static void tifm_sd_abort(unsigned long data) +{ + struct tifm_sd *host = (struct tifm_sd*)data; + + printk(KERN_ERR + "%s : card failed to respond for a long period of time " + "(%x, %x)\n", + host->dev->dev.bus_id, host->req->cmd->opcode, host->cmd_flags); + + tifm_eject(host->dev); +} + +static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct tifm_sd *host = mmc_priv(mmc); + struct tifm_dev *sock = host->dev; + unsigned int clk_div1, clk_div2; + unsigned long flags; + + spin_lock_irqsave(&sock->lock, flags); + + dev_dbg(&sock->dev, "ios: clock = %u, vdd = %x, bus_mode = %x, " + "chip_select = %x, power_mode = %x, bus_width = %x\n", + ios->clock, ios->vdd, ios->bus_mode, ios->chip_select, + ios->power_mode, ios->bus_width); + + if (ios->bus_width == MMC_BUS_WIDTH_4) { + writel(TIFM_MMCSD_4BBUS | readl(sock->addr + SOCK_MMCSD_CONFIG), + sock->addr + SOCK_MMCSD_CONFIG); + } else { + writel((~TIFM_MMCSD_4BBUS) + & readl(sock->addr + SOCK_MMCSD_CONFIG), + sock->addr + SOCK_MMCSD_CONFIG); + } + + if (ios->clock) { + clk_div1 = 20000000 / ios->clock; + if (!clk_div1) + clk_div1 = 1; + + clk_div2 = 24000000 / ios->clock; + if (!clk_div2) + clk_div2 = 1; + + if ((20000000 / clk_div1) > ios->clock) + clk_div1++; + if ((24000000 / clk_div2) > ios->clock) + clk_div2++; + if ((20000000 / clk_div1) > (24000000 / clk_div2)) { + host->clk_freq = 20000000; + host->clk_div = clk_div1; + writel((~TIFM_CTRL_FAST_CLK) + & readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + } else { + host->clk_freq = 24000000; + host->clk_div = clk_div2; + writel(TIFM_CTRL_FAST_CLK + | readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + } + } else { + host->clk_div = 0; + } + host->clk_div &= TIFM_MMCSD_CLKMASK; + writel(host->clk_div + | ((~TIFM_MMCSD_CLKMASK) + & readl(sock->addr + SOCK_MMCSD_CONFIG)), + sock->addr + SOCK_MMCSD_CONFIG); + + host->open_drain = (ios->bus_mode == MMC_BUSMODE_OPENDRAIN); + + /* chip_select : maybe later */ + //vdd + //power is set before probe / after remove + + spin_unlock_irqrestore(&sock->lock, flags); +} + +static int tifm_sd_ro(struct mmc_host *mmc) +{ + int rc = 0; + struct tifm_sd *host = mmc_priv(mmc); + struct tifm_dev *sock = host->dev; + unsigned long flags; + + spin_lock_irqsave(&sock->lock, flags); + if (TIFM_MMCSD_CARD_RO & readl(sock->addr + SOCK_PRESENT_STATE)) + rc = 1; + spin_unlock_irqrestore(&sock->lock, flags); + return rc; +} + +static const struct mmc_host_ops tifm_sd_ops = { + .request = tifm_sd_request, + .set_ios = tifm_sd_ios, + .get_ro = tifm_sd_ro +}; + +static int tifm_sd_initialize_host(struct tifm_sd *host) +{ + int rc; + unsigned int host_status = 0; + struct tifm_dev *sock = host->dev; + + writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); + mmiowb(); + host->clk_div = 61; + host->clk_freq = 20000000; + writel(TIFM_MMCSD_RESET, sock->addr + SOCK_MMCSD_SYSTEM_CONTROL); + writel(host->clk_div | TIFM_MMCSD_POWER, + sock->addr + SOCK_MMCSD_CONFIG); + + /* wait up to 0.51 sec for reset */ + for (rc = 32; rc <= 256; rc <<= 1) { + if (1 & readl(sock->addr + SOCK_MMCSD_SYSTEM_STATUS)) { + rc = 0; + break; + } + msleep(rc); + } + + if (rc) { + printk(KERN_ERR "%s : controller failed to reset\n", + sock->dev.bus_id); + return -ENODEV; + } + + writel(0, sock->addr + SOCK_MMCSD_NUM_BLOCKS); + writel(host->clk_div | TIFM_MMCSD_POWER, + sock->addr + SOCK_MMCSD_CONFIG); + writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); + + // command timeout fixed to 64 clocks for now + writel(64, sock->addr + SOCK_MMCSD_COMMAND_TO); + writel(TIFM_MMCSD_INAB, sock->addr + SOCK_MMCSD_COMMAND); + + for (rc = 16; rc <= 64; rc <<= 1) { + host_status = readl(sock->addr + SOCK_MMCSD_STATUS); + writel(host_status, sock->addr + SOCK_MMCSD_STATUS); + if (!(host_status & TIFM_MMCSD_ERRMASK) + && (host_status & TIFM_MMCSD_EOC)) { + rc = 0; + break; + } + msleep(rc); + } + + if (rc) { + printk(KERN_ERR + "%s : card not ready - probe failed on initialization\n", + sock->dev.bus_id); + return -ENODEV; + } + + writel(TIFM_MMCSD_CERR | TIFM_MMCSD_BRS | TIFM_MMCSD_EOC + | TIFM_MMCSD_ERRMASK, + sock->addr + SOCK_MMCSD_INT_ENABLE); + mmiowb(); + + return 0; +} + +static int tifm_sd_probe(struct tifm_dev *sock) +{ + struct mmc_host *mmc; + struct tifm_sd *host; + int rc = -EIO; + + if (!(TIFM_SOCK_STATE_OCCUPIED + & readl(sock->addr + SOCK_PRESENT_STATE))) { + printk(KERN_WARNING "%s : card gone, unexpectedly\n", + sock->dev.bus_id); + return rc; + } + + mmc = mmc_alloc_host(sizeof(struct tifm_sd), &sock->dev); + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); + host->no_dma = no_dma; + tifm_set_drvdata(sock, mmc); + host->dev = sock; + host->timeout_jiffies = msecs_to_jiffies(1000); + + tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd, + (unsigned long)host); + setup_timer(&host->timer, tifm_sd_abort, (unsigned long)host); + + mmc->ops = &tifm_sd_ops; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE; + mmc->f_min = 20000000 / 60; + mmc->f_max = 24000000; + + mmc->max_blk_count = 2048; + mmc->max_hw_segs = mmc->max_blk_count; + mmc->max_blk_size = min(TIFM_MMCSD_MAX_BLOCK_SIZE, PAGE_SIZE); + mmc->max_seg_size = mmc->max_blk_count * mmc->max_blk_size; + mmc->max_req_size = mmc->max_seg_size; + mmc->max_phys_segs = mmc->max_hw_segs; + + sock->card_event = tifm_sd_card_event; + sock->data_event = tifm_sd_data_event; + rc = tifm_sd_initialize_host(host); + + if (!rc) + rc = mmc_add_host(mmc); + if (!rc) + return 0; + + mmc_free_host(mmc); + return rc; +} + +static void tifm_sd_remove(struct tifm_dev *sock) +{ + struct mmc_host *mmc = tifm_get_drvdata(sock); + struct tifm_sd *host = mmc_priv(mmc); + unsigned long flags; + + spin_lock_irqsave(&sock->lock, flags); + host->eject = 1; + writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); + mmiowb(); + spin_unlock_irqrestore(&sock->lock, flags); + + tasklet_kill(&host->finish_tasklet); + + spin_lock_irqsave(&sock->lock, flags); + if (host->req) { + writel(TIFM_FIFO_INT_SETALL, + sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); + writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); + host->req->cmd->error = MMC_ERR_TIMEOUT; + if (host->req->stop) + host->req->stop->error = MMC_ERR_TIMEOUT; + tasklet_schedule(&host->finish_tasklet); + } + spin_unlock_irqrestore(&sock->lock, flags); + mmc_remove_host(mmc); + dev_dbg(&sock->dev, "after remove\n"); + + /* The meaning of the bit majority in this constant is unknown. */ + writel(0xfff8 & readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + + mmc_free_host(mmc); +} + +#ifdef CONFIG_PM + +static int tifm_sd_suspend(struct tifm_dev *sock, pm_message_t state) +{ + struct mmc_host *mmc = tifm_get_drvdata(sock); + int rc; + + rc = mmc_suspend_host(mmc, state); + /* The meaning of the bit majority in this constant is unknown. */ + writel(0xfff8 & readl(sock->addr + SOCK_CONTROL), + sock->addr + SOCK_CONTROL); + return rc; +} + +static int tifm_sd_resume(struct tifm_dev *sock) +{ + struct mmc_host *mmc = tifm_get_drvdata(sock); + struct tifm_sd *host = mmc_priv(mmc); + int rc; + + rc = tifm_sd_initialize_host(host); + dev_dbg(&sock->dev, "resume initialize %d\n", rc); + + if (rc) + host->eject = 1; + else + rc = mmc_resume_host(mmc); + + return rc; +} + +#else + +#define tifm_sd_suspend NULL +#define tifm_sd_resume NULL + +#endif /* CONFIG_PM */ + +static struct tifm_device_id tifm_sd_id_tbl[] = { + { TIFM_TYPE_SD }, { } +}; + +static struct tifm_driver tifm_sd_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE + }, + .id_table = tifm_sd_id_tbl, + .probe = tifm_sd_probe, + .remove = tifm_sd_remove, + .suspend = tifm_sd_suspend, + .resume = tifm_sd_resume +}; + +static int __init tifm_sd_init(void) +{ + return tifm_register_driver(&tifm_sd_driver); +} + +static void __exit tifm_sd_exit(void) +{ + tifm_unregister_driver(&tifm_sd_driver); +} + +MODULE_AUTHOR("Alex Dubov"); +MODULE_DESCRIPTION("TI FlashMedia SD driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(tifm, tifm_sd_id_tbl); +MODULE_VERSION(DRIVER_VERSION); + +module_init(tifm_sd_init); +module_exit(tifm_sd_exit); diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c new file mode 100644 index 00000000000..9f7518b37c3 --- /dev/null +++ b/drivers/mmc/host/wbsd.c @@ -0,0 +1,2062 @@ +/* + * linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver + * + * Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * + * Warning! + * + * Changes to the FIFO system should be done with extreme care since + * the hardware is full of bugs related to the FIFO. Known issues are: + * + * - FIFO size field in FSR is always zero. + * + * - FIFO interrupts tend not to work as they should. Interrupts are + * triggered only for full/empty events, not for threshold values. + * + * - On APIC systems the FIFO empty interrupt is sometimes lost. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "wbsd.h" + +#define DRIVER_NAME "wbsd" + +#define DBG(x...) \ + pr_debug(DRIVER_NAME ": " x) +#define DBGF(f, x...) \ + pr_debug(DRIVER_NAME " [%s()]: " f, __func__ , ##x) + +/* + * Device resources + */ + +#ifdef CONFIG_PNP + +static const struct pnp_device_id pnp_dev_table[] = { + { "WEC0517", 0 }, + { "WEC0518", 0 }, + { "", 0 }, +}; + +MODULE_DEVICE_TABLE(pnp, pnp_dev_table); + +#endif /* CONFIG_PNP */ + +static const int config_ports[] = { 0x2E, 0x4E }; +static const int unlock_codes[] = { 0x83, 0x87 }; + +static const int valid_ids[] = { + 0x7112, + }; + +#ifdef CONFIG_PNP +static unsigned int nopnp = 0; +#else +static const unsigned int nopnp = 1; +#endif +static unsigned int io = 0x248; +static unsigned int irq = 6; +static int dma = 2; + +/* + * Basic functions + */ + +static inline void wbsd_unlock_config(struct wbsd_host *host) +{ + BUG_ON(host->config == 0); + + outb(host->unlock_code, host->config); + outb(host->unlock_code, host->config); +} + +static inline void wbsd_lock_config(struct wbsd_host *host) +{ + BUG_ON(host->config == 0); + + outb(LOCK_CODE, host->config); +} + +static inline void wbsd_write_config(struct wbsd_host *host, u8 reg, u8 value) +{ + BUG_ON(host->config == 0); + + outb(reg, host->config); + outb(value, host->config + 1); +} + +static inline u8 wbsd_read_config(struct wbsd_host *host, u8 reg) +{ + BUG_ON(host->config == 0); + + outb(reg, host->config); + return inb(host->config + 1); +} + +static inline void wbsd_write_index(struct wbsd_host *host, u8 index, u8 value) +{ + outb(index, host->base + WBSD_IDXR); + outb(value, host->base + WBSD_DATAR); +} + +static inline u8 wbsd_read_index(struct wbsd_host *host, u8 index) +{ + outb(index, host->base + WBSD_IDXR); + return inb(host->base + WBSD_DATAR); +} + +/* + * Common routines + */ + +static void wbsd_init_device(struct wbsd_host *host) +{ + u8 setup, ier; + + /* + * Reset chip (SD/MMC part) and fifo. + */ + setup = wbsd_read_index(host, WBSD_IDX_SETUP); + setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET; + wbsd_write_index(host, WBSD_IDX_SETUP, setup); + + /* + * Set DAT3 to input + */ + setup &= ~WBSD_DAT3_H; + wbsd_write_index(host, WBSD_IDX_SETUP, setup); + host->flags &= ~WBSD_FIGNORE_DETECT; + + /* + * Read back default clock. + */ + host->clk = wbsd_read_index(host, WBSD_IDX_CLK); + + /* + * Power down port. + */ + outb(WBSD_POWER_N, host->base + WBSD_CSR); + + /* + * Set maximum timeout. + */ + wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F); + + /* + * Test for card presence + */ + if (inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT) + host->flags |= WBSD_FCARD_PRESENT; + else + host->flags &= ~WBSD_FCARD_PRESENT; + + /* + * Enable interesting interrupts. + */ + ier = 0; + ier |= WBSD_EINT_CARD; + ier |= WBSD_EINT_FIFO_THRE; + ier |= WBSD_EINT_CRC; + ier |= WBSD_EINT_TIMEOUT; + ier |= WBSD_EINT_TC; + + outb(ier, host->base + WBSD_EIR); + + /* + * Clear interrupts. + */ + inb(host->base + WBSD_ISR); +} + +static void wbsd_reset(struct wbsd_host *host) +{ + u8 setup; + + printk(KERN_ERR "%s: Resetting chip\n", mmc_hostname(host->mmc)); + + /* + * Soft reset of chip (SD/MMC part). + */ + setup = wbsd_read_index(host, WBSD_IDX_SETUP); + setup |= WBSD_SOFT_RESET; + wbsd_write_index(host, WBSD_IDX_SETUP, setup); +} + +static void wbsd_request_end(struct wbsd_host *host, struct mmc_request *mrq) +{ + unsigned long dmaflags; + + DBGF("Ending request, cmd (%x)\n", mrq->cmd->opcode); + + if (host->dma >= 0) { + /* + * Release ISA DMA controller. + */ + dmaflags = claim_dma_lock(); + disable_dma(host->dma); + clear_dma_ff(host->dma); + release_dma_lock(dmaflags); + + /* + * Disable DMA on host. + */ + wbsd_write_index(host, WBSD_IDX_DMA, 0); + } + + host->mrq = NULL; + + /* + * MMC layer might call back into the driver so first unlock. + */ + spin_unlock(&host->lock); + mmc_request_done(host->mmc, mrq); + spin_lock(&host->lock); +} + +/* + * Scatter/gather functions + */ + +static inline void wbsd_init_sg(struct wbsd_host *host, struct mmc_data *data) +{ + /* + * Get info. about SG list from data structure. + */ + host->cur_sg = data->sg; + host->num_sg = data->sg_len; + + host->offset = 0; + host->remain = host->cur_sg->length; +} + +static inline int wbsd_next_sg(struct wbsd_host *host) +{ + /* + * Skip to next SG entry. + */ + host->cur_sg++; + host->num_sg--; + + /* + * Any entries left? + */ + if (host->num_sg > 0) { + host->offset = 0; + host->remain = host->cur_sg->length; + } + + return host->num_sg; +} + +static inline char *wbsd_sg_to_buffer(struct wbsd_host *host) +{ + return page_address(host->cur_sg->page) + host->cur_sg->offset; +} + +static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data) +{ + unsigned int len, i; + struct scatterlist *sg; + char *dmabuf = host->dma_buffer; + char *sgbuf; + + sg = data->sg; + len = data->sg_len; + + for (i = 0; i < len; i++) { + sgbuf = page_address(sg[i].page) + sg[i].offset; + memcpy(dmabuf, sgbuf, sg[i].length); + dmabuf += sg[i].length; + } +} + +static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data) +{ + unsigned int len, i; + struct scatterlist *sg; + char *dmabuf = host->dma_buffer; + char *sgbuf; + + sg = data->sg; + len = data->sg_len; + + for (i = 0; i < len; i++) { + sgbuf = page_address(sg[i].page) + sg[i].offset; + memcpy(sgbuf, dmabuf, sg[i].length); + dmabuf += sg[i].length; + } +} + +/* + * Command handling + */ + +static inline void wbsd_get_short_reply(struct wbsd_host *host, + struct mmc_command *cmd) +{ + /* + * Correct response type? + */ + if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT) { + cmd->error = MMC_ERR_INVALID; + return; + } + + cmd->resp[0] = wbsd_read_index(host, WBSD_IDX_RESP12) << 24; + cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP13) << 16; + cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP14) << 8; + cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP15) << 0; + cmd->resp[1] = wbsd_read_index(host, WBSD_IDX_RESP16) << 24; +} + +static inline void wbsd_get_long_reply(struct wbsd_host *host, + struct mmc_command *cmd) +{ + int i; + + /* + * Correct response type? + */ + if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) { + cmd->error = MMC_ERR_INVALID; + return; + } + + for (i = 0; i < 4; i++) { + cmd->resp[i] = + wbsd_read_index(host, WBSD_IDX_RESP1 + i * 4) << 24; + cmd->resp[i] |= + wbsd_read_index(host, WBSD_IDX_RESP2 + i * 4) << 16; + cmd->resp[i] |= + wbsd_read_index(host, WBSD_IDX_RESP3 + i * 4) << 8; + cmd->resp[i] |= + wbsd_read_index(host, WBSD_IDX_RESP4 + i * 4) << 0; + } +} + +static void wbsd_send_command(struct wbsd_host *host, struct mmc_command *cmd) +{ + int i; + u8 status, isr; + + DBGF("Sending cmd (%x)\n", cmd->opcode); + + /* + * Clear accumulated ISR. The interrupt routine + * will fill this one with events that occur during + * transfer. + */ + host->isr = 0; + + /* + * Send the command (CRC calculated by host). + */ + outb(cmd->opcode, host->base + WBSD_CMDR); + for (i = 3; i >= 0; i--) + outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR); + + cmd->error = MMC_ERR_NONE; + + /* + * Wait for the request to complete. + */ + do { + status = wbsd_read_index(host, WBSD_IDX_STATUS); + } while (status & WBSD_CARDTRAFFIC); + + /* + * Do we expect a reply? + */ + if (cmd->flags & MMC_RSP_PRESENT) { + /* + * Read back status. + */ + isr = host->isr; + + /* Card removed? */ + if (isr & WBSD_INT_CARD) + cmd->error = MMC_ERR_TIMEOUT; + /* Timeout? */ + else if (isr & WBSD_INT_TIMEOUT) + cmd->error = MMC_ERR_TIMEOUT; + /* CRC? */ + else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC)) + cmd->error = MMC_ERR_BADCRC; + /* All ok */ + else { + if (cmd->flags & MMC_RSP_136) + wbsd_get_long_reply(host, cmd); + else + wbsd_get_short_reply(host, cmd); + } + } + + DBGF("Sent cmd (%x), res %d\n", cmd->opcode, cmd->error); +} + +/* + * Data functions + */ + +static void wbsd_empty_fifo(struct wbsd_host *host) +{ + struct mmc_data *data = host->mrq->cmd->data; + char *buffer; + int i, fsr, fifo; + + /* + * Handle excessive data. + */ + if (host->num_sg == 0) + return; + + buffer = wbsd_sg_to_buffer(host) + host->offset; + + /* + * Drain the fifo. This has a tendency to loop longer + * than the FIFO length (usually one block). + */ + while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_EMPTY)) { + /* + * The size field in the FSR is broken so we have to + * do some guessing. + */ + if (fsr & WBSD_FIFO_FULL) + fifo = 16; + else if (fsr & WBSD_FIFO_FUTHRE) + fifo = 8; + else + fifo = 1; + + for (i = 0; i < fifo; i++) { + *buffer = inb(host->base + WBSD_DFR); + buffer++; + host->offset++; + host->remain--; + + data->bytes_xfered++; + + /* + * End of scatter list entry? + */ + if (host->remain == 0) { + /* + * Get next entry. Check if last. + */ + if (!wbsd_next_sg(host)) + return; + + buffer = wbsd_sg_to_buffer(host); + } + } + } + + /* + * This is a very dirty hack to solve a + * hardware problem. The chip doesn't trigger + * FIFO threshold interrupts properly. + */ + if ((data->blocks * data->blksz - data->bytes_xfered) < 16) + tasklet_schedule(&host->fifo_tasklet); +} + +static void wbsd_fill_fifo(struct wbsd_host *host) +{ + struct mmc_data *data = host->mrq->cmd->data; + char *buffer; + int i, fsr, fifo; + + /* + * Check that we aren't being called after the + * entire buffer has been transfered. + */ + if (host->num_sg == 0) + return; + + buffer = wbsd_sg_to_buffer(host) + host->offset; + + /* + * Fill the fifo. This has a tendency to loop longer + * than the FIFO length (usually one block). + */ + while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_FULL)) { + /* + * The size field in the FSR is broken so we have to + * do some guessing. + */ + if (fsr & WBSD_FIFO_EMPTY) + fifo = 0; + else if (fsr & WBSD_FIFO_EMTHRE) + fifo = 8; + else + fifo = 15; + + for (i = 16; i > fifo; i--) { + outb(*buffer, host->base + WBSD_DFR); + buffer++; + host->offset++; + host->remain--; + + data->bytes_xfered++; + + /* + * End of scatter list entry? + */ + if (host->remain == 0) { + /* + * Get next entry. Check if last. + */ + if (!wbsd_next_sg(host)) + return; + + buffer = wbsd_sg_to_buffer(host); + } + } + } + + /* + * The controller stops sending interrupts for + * 'FIFO empty' under certain conditions. So we + * need to be a bit more pro-active. + */ + tasklet_schedule(&host->fifo_tasklet); +} + +static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) +{ + u16 blksize; + u8 setup; + unsigned long dmaflags; + unsigned int size; + + DBGF("blksz %04x blks %04x flags %08x\n", + data->blksz, data->blocks, data->flags); + DBGF("tsac %d ms nsac %d clk\n", + data->timeout_ns / 1000000, data->timeout_clks); + + /* + * Calculate size. + */ + size = data->blocks * data->blksz; + + /* + * Check timeout values for overflow. + * (Yes, some cards cause this value to overflow). + */ + if (data->timeout_ns > 127000000) + wbsd_write_index(host, WBSD_IDX_TAAC, 127); + else { + wbsd_write_index(host, WBSD_IDX_TAAC, + data->timeout_ns / 1000000); + } + + if (data->timeout_clks > 255) + wbsd_write_index(host, WBSD_IDX_NSAC, 255); + else + wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks); + + /* + * Inform the chip of how large blocks will be + * sent. It needs this to determine when to + * calculate CRC. + * + * Space for CRC must be included in the size. + * Two bytes are needed for each data line. + */ + if (host->bus_width == MMC_BUS_WIDTH_1) { + blksize = data->blksz + 2; + + wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0); + wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); + } else if (host->bus_width == MMC_BUS_WIDTH_4) { + blksize = data->blksz + 2 * 4; + + wbsd_write_index(host, WBSD_IDX_PBSMSB, + ((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH); + wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); + } else { + data->error = MMC_ERR_INVALID; + return; + } + + /* + * Clear the FIFO. This is needed even for DMA + * transfers since the chip still uses the FIFO + * internally. + */ + setup = wbsd_read_index(host, WBSD_IDX_SETUP); + setup |= WBSD_FIFO_RESET; + wbsd_write_index(host, WBSD_IDX_SETUP, setup); + + /* + * DMA transfer? + */ + if (host->dma >= 0) { + /* + * The buffer for DMA is only 64 kB. + */ + BUG_ON(size > 0x10000); + if (size > 0x10000) { + data->error = MMC_ERR_INVALID; + return; + } + + /* + * Transfer data from the SG list to + * the DMA buffer. + */ + if (data->flags & MMC_DATA_WRITE) + wbsd_sg_to_dma(host, data); + + /* + * Initialise the ISA DMA controller. + */ + dmaflags = claim_dma_lock(); + disable_dma(host->dma); + clear_dma_ff(host->dma); + if (data->flags & MMC_DATA_READ) + set_dma_mode(host->dma, DMA_MODE_READ & ~0x40); + else + set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40); + set_dma_addr(host->dma, host->dma_addr); + set_dma_count(host->dma, size); + + enable_dma(host->dma); + release_dma_lock(dmaflags); + + /* + * Enable DMA on the host. + */ + wbsd_write_index(host, WBSD_IDX_DMA, WBSD_DMA_ENABLE); + } else { + /* + * This flag is used to keep printk + * output to a minimum. + */ + host->firsterr = 1; + + /* + * Initialise the SG list. + */ + wbsd_init_sg(host, data); + + /* + * Turn off DMA. + */ + wbsd_write_index(host, WBSD_IDX_DMA, 0); + + /* + * Set up FIFO threshold levels (and fill + * buffer if doing a write). + */ + if (data->flags & MMC_DATA_READ) { + wbsd_write_index(host, WBSD_IDX_FIFOEN, + WBSD_FIFOEN_FULL | 8); + } else { + wbsd_write_index(host, WBSD_IDX_FIFOEN, + WBSD_FIFOEN_EMPTY | 8); + wbsd_fill_fifo(host); + } + } + + data->error = MMC_ERR_NONE; +} + +static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data) +{ + unsigned long dmaflags; + int count; + u8 status; + + WARN_ON(host->mrq == NULL); + + /* + * Send a stop command if needed. + */ + if (data->stop) + wbsd_send_command(host, data->stop); + + /* + * Wait for the controller to leave data + * transfer state. + */ + do { + status = wbsd_read_index(host, WBSD_IDX_STATUS); + } while (status & (WBSD_BLOCK_READ | WBSD_BLOCK_WRITE)); + + /* + * DMA transfer? + */ + if (host->dma >= 0) { + /* + * Disable DMA on the host. + */ + wbsd_write_index(host, WBSD_IDX_DMA, 0); + + /* + * Turn of ISA DMA controller. + */ + dmaflags = claim_dma_lock(); + disable_dma(host->dma); + clear_dma_ff(host->dma); + count = get_dma_residue(host->dma); + release_dma_lock(dmaflags); + + data->bytes_xfered = host->mrq->data->blocks * + host->mrq->data->blksz - count; + data->bytes_xfered -= data->bytes_xfered % data->blksz; + + /* + * Any leftover data? + */ + if (count) { + printk(KERN_ERR "%s: Incomplete DMA transfer. " + "%d bytes left.\n", + mmc_hostname(host->mmc), count); + + if (data->error == MMC_ERR_NONE) + data->error = MMC_ERR_FAILED; + } else { + /* + * Transfer data from DMA buffer to + * SG list. + */ + if (data->flags & MMC_DATA_READ) + wbsd_dma_to_sg(host, data); + } + + if (data->error != MMC_ERR_NONE) { + if (data->bytes_xfered) + data->bytes_xfered -= data->blksz; + } + } + + DBGF("Ending data transfer (%d bytes)\n", data->bytes_xfered); + + wbsd_request_end(host, host->mrq); +} + +/*****************************************************************************\ + * * + * MMC layer callbacks * + * * +\*****************************************************************************/ + +static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct wbsd_host *host = mmc_priv(mmc); + struct mmc_command *cmd; + + /* + * Disable tasklets to avoid a deadlock. + */ + spin_lock_bh(&host->lock); + + BUG_ON(host->mrq != NULL); + + cmd = mrq->cmd; + + host->mrq = mrq; + + /* + * If there is no card in the slot then + * timeout immediatly. + */ + if (!(host->flags & WBSD_FCARD_PRESENT)) { + cmd->error = MMC_ERR_TIMEOUT; + goto done; + } + + /* + * Does the request include data? + */ + if (cmd->data) { + wbsd_prepare_data(host, cmd->data); + + if (cmd->data->error != MMC_ERR_NONE) + goto done; + } + + wbsd_send_command(host, cmd); + + /* + * If this is a data transfer the request + * will be finished after the data has + * transfered. + */ + if (cmd->data && (cmd->error == MMC_ERR_NONE)) { + /* + * The hardware is so delightfully stupid that it has a list + * of "data" commands. If a command isn't on this list, it'll + * just go back to the idle state and won't send any data + * interrupts. + */ + switch (cmd->opcode) { + case 11: + case 17: + case 18: + case 20: + case 24: + case 25: + case 26: + case 27: + case 30: + case 42: + case 56: + break; + + /* ACMDs. We don't keep track of state, so we just treat them + * like any other command. */ + case 51: + break; + + default: +#ifdef CONFIG_MMC_DEBUG + printk(KERN_WARNING "%s: Data command %d is not " + "supported by this controller.\n", + mmc_hostname(host->mmc), cmd->opcode); +#endif + cmd->data->error = MMC_ERR_INVALID; + + if (cmd->data->stop) + wbsd_send_command(host, cmd->data->stop); + + goto done; + }; + + /* + * Dirty fix for hardware bug. + */ + if (host->dma == -1) + tasklet_schedule(&host->fifo_tasklet); + + spin_unlock_bh(&host->lock); + + return; + } + +done: + wbsd_request_end(host, mrq); + + spin_unlock_bh(&host->lock); +} + +static void wbsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct wbsd_host *host = mmc_priv(mmc); + u8 clk, setup, pwr; + + spin_lock_bh(&host->lock); + + /* + * Reset the chip on each power off. + * Should clear out any weird states. + */ + if (ios->power_mode == MMC_POWER_OFF) + wbsd_init_device(host); + + if (ios->clock >= 24000000) + clk = WBSD_CLK_24M; + else if (ios->clock >= 16000000) + clk = WBSD_CLK_16M; + else if (ios->clock >= 12000000) + clk = WBSD_CLK_12M; + else + clk = WBSD_CLK_375K; + + /* + * Only write to the clock register when + * there is an actual change. + */ + if (clk != host->clk) { + wbsd_write_index(host, WBSD_IDX_CLK, clk); + host->clk = clk; + } + + /* + * Power up card. + */ + if (ios->power_mode != MMC_POWER_OFF) { + pwr = inb(host->base + WBSD_CSR); + pwr &= ~WBSD_POWER_N; + outb(pwr, host->base + WBSD_CSR); + } + + /* + * MMC cards need to have pin 1 high during init. + * It wreaks havoc with the card detection though so + * that needs to be disabled. + */ + setup = wbsd_read_index(host, WBSD_IDX_SETUP); + if (ios->chip_select == MMC_CS_HIGH) { + BUG_ON(ios->bus_width != MMC_BUS_WIDTH_1); + setup |= WBSD_DAT3_H; + host->flags |= WBSD_FIGNORE_DETECT; + } else { + if (setup & WBSD_DAT3_H) { + setup &= ~WBSD_DAT3_H; + + /* + * We cannot resume card detection immediatly + * because of capacitance and delays in the chip. + */ + mod_timer(&host->ignore_timer, jiffies + HZ / 100); + } + } + wbsd_write_index(host, WBSD_IDX_SETUP, setup); + + /* + * Store bus width for later. Will be used when + * setting up the data transfer. + */ + host->bus_width = ios->bus_width; + + spin_unlock_bh(&host->lock); +} + +static int wbsd_get_ro(struct mmc_host *mmc) +{ + struct wbsd_host *host = mmc_priv(mmc); + u8 csr; + + spin_lock_bh(&host->lock); + + csr = inb(host->base + WBSD_CSR); + csr |= WBSD_MSLED; + outb(csr, host->base + WBSD_CSR); + + mdelay(1); + + csr = inb(host->base + WBSD_CSR); + csr &= ~WBSD_MSLED; + outb(csr, host->base + WBSD_CSR); + + spin_unlock_bh(&host->lock); + + return csr & WBSD_WRPT; +} + +static const struct mmc_host_ops wbsd_ops = { + .request = wbsd_request, + .set_ios = wbsd_set_ios, + .get_ro = wbsd_get_ro, +}; + +/*****************************************************************************\ + * * + * Interrupt handling * + * * +\*****************************************************************************/ + +/* + * Helper function to reset detection ignore + */ + +static void wbsd_reset_ignore(unsigned long data) +{ + struct wbsd_host *host = (struct wbsd_host *)data; + + BUG_ON(host == NULL); + + DBG("Resetting card detection ignore\n"); + + spin_lock_bh(&host->lock); + + host->flags &= ~WBSD_FIGNORE_DETECT; + + /* + * Card status might have changed during the + * blackout. + */ + tasklet_schedule(&host->card_tasklet); + + spin_unlock_bh(&host->lock); +} + +/* + * Tasklets + */ + +static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host) +{ + WARN_ON(!host->mrq); + if (!host->mrq) + return NULL; + + WARN_ON(!host->mrq->cmd); + if (!host->mrq->cmd) + return NULL; + + WARN_ON(!host->mrq->cmd->data); + if (!host->mrq->cmd->data) + return NULL; + + return host->mrq->cmd->data; +} + +static void wbsd_tasklet_card(unsigned long param) +{ + struct wbsd_host *host = (struct wbsd_host *)param; + u8 csr; + int delay = -1; + + spin_lock(&host->lock); + + if (host->flags & WBSD_FIGNORE_DETECT) { + spin_unlock(&host->lock); + return; + } + + csr = inb(host->base + WBSD_CSR); + WARN_ON(csr == 0xff); + + if (csr & WBSD_CARDPRESENT) { + if (!(host->flags & WBSD_FCARD_PRESENT)) { + DBG("Card inserted\n"); + host->flags |= WBSD_FCARD_PRESENT; + + delay = 500; + } + } else if (host->flags & WBSD_FCARD_PRESENT) { + DBG("Card removed\n"); + host->flags &= ~WBSD_FCARD_PRESENT; + + if (host->mrq) { + printk(KERN_ERR "%s: Card removed during transfer!\n", + mmc_hostname(host->mmc)); + wbsd_reset(host); + + host->mrq->cmd->error = MMC_ERR_FAILED; + tasklet_schedule(&host->finish_tasklet); + } + + delay = 0; + } + + /* + * Unlock first since we might get a call back. + */ + + spin_unlock(&host->lock); + + if (delay != -1) + mmc_detect_change(host->mmc, msecs_to_jiffies(delay)); +} + +static void wbsd_tasklet_fifo(unsigned long param) +{ + struct wbsd_host *host = (struct wbsd_host *)param; + struct mmc_data *data; + + spin_lock(&host->lock); + + if (!host->mrq) + goto end; + + data = wbsd_get_data(host); + if (!data) + goto end; + + if (data->flags & MMC_DATA_WRITE) + wbsd_fill_fifo(host); + else + wbsd_empty_fifo(host); + + /* + * Done? + */ + if (host->num_sg == 0) { + wbsd_write_index(host, WBSD_IDX_FIFOEN, 0); + tasklet_schedule(&host->finish_tasklet); + } + +end: + spin_unlock(&host->lock); +} + +static void wbsd_tasklet_crc(unsigned long param) +{ + struct wbsd_host *host = (struct wbsd_host *)param; + struct mmc_data *data; + + spin_lock(&host->lock); + + if (!host->mrq) + goto end; + + data = wbsd_get_data(host); + if (!data) + goto end; + + DBGF("CRC error\n"); + + data->error = MMC_ERR_BADCRC; + + tasklet_schedule(&host->finish_tasklet); + +end: + spin_unlock(&host->lock); +} + +static void wbsd_tasklet_timeout(unsigned long param) +{ + struct wbsd_host *host = (struct wbsd_host *)param; + struct mmc_data *data; + + spin_lock(&host->lock); + + if (!host->mrq) + goto end; + + data = wbsd_get_data(host); + if (!data) + goto end; + + DBGF("Timeout\n"); + + data->error = MMC_ERR_TIMEOUT; + + tasklet_schedule(&host->finish_tasklet); + +end: + spin_unlock(&host->lock); +} + +static void wbsd_tasklet_finish(unsigned long param) +{ + struct wbsd_host *host = (struct wbsd_host *)param; + struct mmc_data *data; + + spin_lock(&host->lock); + + WARN_ON(!host->mrq); + if (!host->mrq) + goto end; + + data = wbsd_get_data(host); + if (!data) + goto end; + + wbsd_finish_data(host, data); + +end: + spin_unlock(&host->lock); +} + +/* + * Interrupt handling + */ + +static irqreturn_t wbsd_irq(int irq, void *dev_id) +{ + struct wbsd_host *host = dev_id; + int isr; + + isr = inb(host->base + WBSD_ISR); + + /* + * Was it actually our hardware that caused the interrupt? + */ + if (isr == 0xff || isr == 0x00) + return IRQ_NONE; + + host->isr |= isr; + + /* + * Schedule tasklets as needed. + */ + if (isr & WBSD_INT_CARD) + tasklet_schedule(&host->card_tasklet); + if (isr & WBSD_INT_FIFO_THRE) + tasklet_schedule(&host->fifo_tasklet); + if (isr & WBSD_INT_CRC) + tasklet_hi_schedule(&host->crc_tasklet); + if (isr & WBSD_INT_TIMEOUT) + tasklet_hi_schedule(&host->timeout_tasklet); + if (isr & WBSD_INT_TC) + tasklet_schedule(&host->finish_tasklet); + + return IRQ_HANDLED; +} + +/*****************************************************************************\ + * * + * Device initialisation and shutdown * + * * +\*****************************************************************************/ + +/* + * Allocate/free MMC structure. + */ + +static int __devinit wbsd_alloc_mmc(struct device *dev) +{ + struct mmc_host *mmc; + struct wbsd_host *host; + + /* + * Allocate MMC structure. + */ + mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev); + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); + host->mmc = mmc; + + host->dma = -1; + + /* + * Set host parameters. + */ + mmc->ops = &wbsd_ops; + mmc->f_min = 375000; + mmc->f_max = 24000000; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; + + spin_lock_init(&host->lock); + + /* + * Set up timers + */ + init_timer(&host->ignore_timer); + host->ignore_timer.data = (unsigned long)host; + host->ignore_timer.function = wbsd_reset_ignore; + + /* + * Maximum number of segments. Worst case is one sector per segment + * so this will be 64kB/512. + */ + mmc->max_hw_segs = 128; + mmc->max_phys_segs = 128; + + /* + * Maximum request size. Also limited by 64KiB buffer. + */ + mmc->max_req_size = 65536; + + /* + * Maximum segment size. Could be one segment with the maximum number + * of bytes. + */ + mmc->max_seg_size = mmc->max_req_size; + + /* + * Maximum block size. We have 12 bits (= 4095) but have to subtract + * space for CRC. So the maximum is 4095 - 4*2 = 4087. + */ + mmc->max_blk_size = 4087; + + /* + * Maximum block count. There is no real limit so the maximum + * request size will be the only restriction. + */ + mmc->max_blk_count = mmc->max_req_size; + + dev_set_drvdata(dev, mmc); + + return 0; +} + +static void __devexit wbsd_free_mmc(struct device *dev) +{ + struct mmc_host *mmc; + struct wbsd_host *host; + + mmc = dev_get_drvdata(dev); + if (!mmc) + return; + + host = mmc_priv(mmc); + BUG_ON(host == NULL); + + del_timer_sync(&host->ignore_timer); + + mmc_free_host(mmc); + + dev_set_drvdata(dev, NULL); +} + +/* + * Scan for known chip id:s + */ + +static int __devinit wbsd_scan(struct wbsd_host *host) +{ + int i, j, k; + int id; + + /* + * Iterate through all ports, all codes to + * find hardware that is in our known list. + */ + for (i = 0; i < ARRAY_SIZE(config_ports); i++) { + if (!request_region(config_ports[i], 2, DRIVER_NAME)) + continue; + + for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) { + id = 0xFFFF; + + host->config = config_ports[i]; + host->unlock_code = unlock_codes[j]; + + wbsd_unlock_config(host); + + outb(WBSD_CONF_ID_HI, config_ports[i]); + id = inb(config_ports[i] + 1) << 8; + + outb(WBSD_CONF_ID_LO, config_ports[i]); + id |= inb(config_ports[i] + 1); + + wbsd_lock_config(host); + + for (k = 0; k < ARRAY_SIZE(valid_ids); k++) { + if (id == valid_ids[k]) { + host->chip_id = id; + + return 0; + } + } + + if (id != 0xFFFF) { + DBG("Unknown hardware (id %x) found at %x\n", + id, config_ports[i]); + } + } + + release_region(config_ports[i], 2); + } + + host->config = 0; + host->unlock_code = 0; + + return -ENODEV; +} + +/* + * Allocate/free io port ranges + */ + +static int __devinit wbsd_request_region(struct wbsd_host *host, int base) +{ + if (base & 0x7) + return -EINVAL; + + if (!request_region(base, 8, DRIVER_NAME)) + return -EIO; + + host->base = base; + + return 0; +} + +static void __devexit wbsd_release_regions(struct wbsd_host *host) +{ + if (host->base) + release_region(host->base, 8); + + host->base = 0; + + if (host->config) + release_region(host->config, 2); + + host->config = 0; +} + +/* + * Allocate/free DMA port and buffer + */ + +static void __devinit wbsd_request_dma(struct wbsd_host *host, int dma) +{ + if (dma < 0) + return; + + if (request_dma(dma, DRIVER_NAME)) + goto err; + + /* + * We need to allocate a special buffer in + * order for ISA to be able to DMA to it. + */ + host->dma_buffer = kmalloc(WBSD_DMA_SIZE, + GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN); + if (!host->dma_buffer) + goto free; + + /* + * Translate the address to a physical address. + */ + host->dma_addr = dma_map_single(mmc_dev(host->mmc), host->dma_buffer, + WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); + + /* + * ISA DMA must be aligned on a 64k basis. + */ + if ((host->dma_addr & 0xffff) != 0) + goto kfree; + /* + * ISA cannot access memory above 16 MB. + */ + else if (host->dma_addr >= 0x1000000) + goto kfree; + + host->dma = dma; + + return; + +kfree: + /* + * If we've gotten here then there is some kind of alignment bug + */ + BUG_ON(1); + + dma_unmap_single(mmc_dev(host->mmc), host->dma_addr, + WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); + host->dma_addr = (dma_addr_t)NULL; + + kfree(host->dma_buffer); + host->dma_buffer = NULL; + +free: + free_dma(dma); + +err: + printk(KERN_WARNING DRIVER_NAME ": Unable to allocate DMA %d. " + "Falling back on FIFO.\n", dma); +} + +static void __devexit wbsd_release_dma(struct wbsd_host *host) +{ + if (host->dma_addr) { + dma_unmap_single(mmc_dev(host->mmc), host->dma_addr, + WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); + } + kfree(host->dma_buffer); + if (host->dma >= 0) + free_dma(host->dma); + + host->dma = -1; + host->dma_buffer = NULL; + host->dma_addr = (dma_addr_t)NULL; +} + +/* + * Allocate/free IRQ. + */ + +static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq) +{ + int ret; + + /* + * Allocate interrupt. + */ + + ret = request_irq(irq, wbsd_irq, IRQF_SHARED, DRIVER_NAME, host); + if (ret) + return ret; + + host->irq = irq; + + /* + * Set up tasklets. + */ + tasklet_init(&host->card_tasklet, wbsd_tasklet_card, + (unsigned long)host); + tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo, + (unsigned long)host); + tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc, + (unsigned long)host); + tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout, + (unsigned long)host); + tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish, + (unsigned long)host); + + return 0; +} + +static void __devexit wbsd_release_irq(struct wbsd_host *host) +{ + if (!host->irq) + return; + + free_irq(host->irq, host); + + host->irq = 0; + + tasklet_kill(&host->card_tasklet); + tasklet_kill(&host->fifo_tasklet); + tasklet_kill(&host->crc_tasklet); + tasklet_kill(&host->timeout_tasklet); + tasklet_kill(&host->finish_tasklet); +} + +/* + * Allocate all resources for the host. + */ + +static int __devinit wbsd_request_resources(struct wbsd_host *host, + int base, int irq, int dma) +{ + int ret; + + /* + * Allocate I/O ports. + */ + ret = wbsd_request_region(host, base); + if (ret) + return ret; + + /* + * Allocate interrupt. + */ + ret = wbsd_request_irq(host, irq); + if (ret) + return ret; + + /* + * Allocate DMA. + */ + wbsd_request_dma(host, dma); + + return 0; +} + +/* + * Release all resources for the host. + */ + +static void __devexit wbsd_release_resources(struct wbsd_host *host) +{ + wbsd_release_dma(host); + wbsd_release_irq(host); + wbsd_release_regions(host); +} + +/* + * Configure the resources the chip should use. + */ + +static void wbsd_chip_config(struct wbsd_host *host) +{ + wbsd_unlock_config(host); + + /* + * Reset the chip. + */ + wbsd_write_config(host, WBSD_CONF_SWRST, 1); + wbsd_write_config(host, WBSD_CONF_SWRST, 0); + + /* + * Select SD/MMC function. + */ + wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); + + /* + * Set up card detection. + */ + wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11); + + /* + * Configure chip + */ + wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8); + wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff); + + wbsd_write_config(host, WBSD_CONF_IRQ, host->irq); + + if (host->dma >= 0) + wbsd_write_config(host, WBSD_CONF_DRQ, host->dma); + + /* + * Enable and power up chip. + */ + wbsd_write_config(host, WBSD_CONF_ENABLE, 1); + wbsd_write_config(host, WBSD_CONF_POWER, 0x20); + + wbsd_lock_config(host); +} + +/* + * Check that configured resources are correct. + */ + +static int wbsd_chip_validate(struct wbsd_host *host) +{ + int base, irq, dma; + + wbsd_unlock_config(host); + + /* + * Select SD/MMC function. + */ + wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); + + /* + * Read configuration. + */ + base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8; + base |= wbsd_read_config(host, WBSD_CONF_PORT_LO); + + irq = wbsd_read_config(host, WBSD_CONF_IRQ); + + dma = wbsd_read_config(host, WBSD_CONF_DRQ); + + wbsd_lock_config(host); + + /* + * Validate against given configuration. + */ + if (base != host->base) + return 0; + if (irq != host->irq) + return 0; + if ((dma != host->dma) && (host->dma != -1)) + return 0; + + return 1; +} + +/* + * Powers down the SD function + */ + +static void wbsd_chip_poweroff(struct wbsd_host *host) +{ + wbsd_unlock_config(host); + + wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); + wbsd_write_config(host, WBSD_CONF_ENABLE, 0); + + wbsd_lock_config(host); +} + +/*****************************************************************************\ + * * + * Devices setup and shutdown * + * * +\*****************************************************************************/ + +static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma, + int pnp) +{ + struct wbsd_host *host = NULL; + struct mmc_host *mmc = NULL; + int ret; + + ret = wbsd_alloc_mmc(dev); + if (ret) + return ret; + + mmc = dev_get_drvdata(dev); + host = mmc_priv(mmc); + + /* + * Scan for hardware. + */ + ret = wbsd_scan(host); + if (ret) { + if (pnp && (ret == -ENODEV)) { + printk(KERN_WARNING DRIVER_NAME + ": Unable to confirm device presence. You may " + "experience lock-ups.\n"); + } else { + wbsd_free_mmc(dev); + return ret; + } + } + + /* + * Request resources. + */ + ret = wbsd_request_resources(host, base, irq, dma); + if (ret) { + wbsd_release_resources(host); + wbsd_free_mmc(dev); + return ret; + } + + /* + * See if chip needs to be configured. + */ + if (pnp) { + if ((host->config != 0) && !wbsd_chip_validate(host)) { + printk(KERN_WARNING DRIVER_NAME + ": PnP active but chip not configured! " + "You probably have a buggy BIOS. " + "Configuring chip manually.\n"); + wbsd_chip_config(host); + } + } else + wbsd_chip_config(host); + + /* + * Power Management stuff. No idea how this works. + * Not tested. + */ +#ifdef CONFIG_PM + if (host->config) { + wbsd_unlock_config(host); + wbsd_write_config(host, WBSD_CONF_PME, 0xA0); + wbsd_lock_config(host); + } +#endif + /* + * Allow device to initialise itself properly. + */ + mdelay(5); + + /* + * Reset the chip into a known state. + */ + wbsd_init_device(host); + + mmc_add_host(mmc); + + printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc)); + if (host->chip_id != 0) + printk(" id %x", (int)host->chip_id); + printk(" at 0x%x irq %d", (int)host->base, (int)host->irq); + if (host->dma >= 0) + printk(" dma %d", (int)host->dma); + else + printk(" FIFO"); + if (pnp) + printk(" PnP"); + printk("\n"); + + return 0; +} + +static void __devexit wbsd_shutdown(struct device *dev, int pnp) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct wbsd_host *host; + + if (!mmc) + return; + + host = mmc_priv(mmc); + + mmc_remove_host(mmc); + + /* + * Power down the SD/MMC function. + */ + if (!pnp) + wbsd_chip_poweroff(host); + + wbsd_release_resources(host); + + wbsd_free_mmc(dev); +} + +/* + * Non-PnP + */ + +static int __devinit wbsd_probe(struct platform_device *dev) +{ + /* Use the module parameters for resources */ + return wbsd_init(&dev->dev, io, irq, dma, 0); +} + +static int __devexit wbsd_remove(struct platform_device *dev) +{ + wbsd_shutdown(&dev->dev, 0); + + return 0; +} + +/* + * PnP + */ + +#ifdef CONFIG_PNP + +static int __devinit +wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id) +{ + int io, irq, dma; + + /* + * Get resources from PnP layer. + */ + io = pnp_port_start(pnpdev, 0); + irq = pnp_irq(pnpdev, 0); + if (pnp_dma_valid(pnpdev, 0)) + dma = pnp_dma(pnpdev, 0); + else + dma = -1; + + DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma); + + return wbsd_init(&pnpdev->dev, io, irq, dma, 1); +} + +static void __devexit wbsd_pnp_remove(struct pnp_dev *dev) +{ + wbsd_shutdown(&dev->dev, 1); +} + +#endif /* CONFIG_PNP */ + +/* + * Power management + */ + +#ifdef CONFIG_PM + +static int wbsd_suspend(struct wbsd_host *host, pm_message_t state) +{ + BUG_ON(host == NULL); + + return mmc_suspend_host(host->mmc, state); +} + +static int wbsd_resume(struct wbsd_host *host) +{ + BUG_ON(host == NULL); + + wbsd_init_device(host); + + return mmc_resume_host(host->mmc); +} + +static int wbsd_platform_suspend(struct platform_device *dev, + pm_message_t state) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + struct wbsd_host *host; + int ret; + + if (mmc == NULL) + return 0; + + DBGF("Suspending...\n"); + + host = mmc_priv(mmc); + + ret = wbsd_suspend(host, state); + if (ret) + return ret; + + wbsd_chip_poweroff(host); + + return 0; +} + +static int wbsd_platform_resume(struct platform_device *dev) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + struct wbsd_host *host; + + if (mmc == NULL) + return 0; + + DBGF("Resuming...\n"); + + host = mmc_priv(mmc); + + wbsd_chip_config(host); + + /* + * Allow device to initialise itself properly. + */ + mdelay(5); + + return wbsd_resume(host); +} + +#ifdef CONFIG_PNP + +static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state) +{ + struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev); + struct wbsd_host *host; + + if (mmc == NULL) + return 0; + + DBGF("Suspending...\n"); + + host = mmc_priv(mmc); + + return wbsd_suspend(host, state); +} + +static int wbsd_pnp_resume(struct pnp_dev *pnp_dev) +{ + struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev); + struct wbsd_host *host; + + if (mmc == NULL) + return 0; + + DBGF("Resuming...\n"); + + host = mmc_priv(mmc); + + /* + * See if chip needs to be configured. + */ + if (host->config != 0) { + if (!wbsd_chip_validate(host)) { + printk(KERN_WARNING DRIVER_NAME + ": PnP active but chip not configured! " + "You probably have a buggy BIOS. " + "Configuring chip manually.\n"); + wbsd_chip_config(host); + } + } + + /* + * Allow device to initialise itself properly. + */ + mdelay(5); + + return wbsd_resume(host); +} + +#endif /* CONFIG_PNP */ + +#else /* CONFIG_PM */ + +#define wbsd_platform_suspend NULL +#define wbsd_platform_resume NULL + +#define wbsd_pnp_suspend NULL +#define wbsd_pnp_resume NULL + +#endif /* CONFIG_PM */ + +static struct platform_device *wbsd_device; + +static struct platform_driver wbsd_driver = { + .probe = wbsd_probe, + .remove = __devexit_p(wbsd_remove), + + .suspend = wbsd_platform_suspend, + .resume = wbsd_platform_resume, + .driver = { + .name = DRIVER_NAME, + }, +}; + +#ifdef CONFIG_PNP + +static struct pnp_driver wbsd_pnp_driver = { + .name = DRIVER_NAME, + .id_table = pnp_dev_table, + .probe = wbsd_pnp_probe, + .remove = __devexit_p(wbsd_pnp_remove), + + .suspend = wbsd_pnp_suspend, + .resume = wbsd_pnp_resume, +}; + +#endif /* CONFIG_PNP */ + +/* + * Module loading/unloading + */ + +static int __init wbsd_drv_init(void) +{ + int result; + + printk(KERN_INFO DRIVER_NAME + ": Winbond W83L51xD SD/MMC card interface driver\n"); + printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); + +#ifdef CONFIG_PNP + + if (!nopnp) { + result = pnp_register_driver(&wbsd_pnp_driver); + if (result < 0) + return result; + } +#endif /* CONFIG_PNP */ + + if (nopnp) { + result = platform_driver_register(&wbsd_driver); + if (result < 0) + return result; + + wbsd_device = platform_device_alloc(DRIVER_NAME, -1); + if (!wbsd_device) { + platform_driver_unregister(&wbsd_driver); + return -ENOMEM; + } + + result = platform_device_add(wbsd_device); + if (result) { + platform_device_put(wbsd_device); + platform_driver_unregister(&wbsd_driver); + return result; + } + } + + return 0; +} + +static void __exit wbsd_drv_exit(void) +{ +#ifdef CONFIG_PNP + + if (!nopnp) + pnp_unregister_driver(&wbsd_pnp_driver); + +#endif /* CONFIG_PNP */ + + if (nopnp) { + platform_device_unregister(wbsd_device); + + platform_driver_unregister(&wbsd_driver); + } + + DBG("unloaded\n"); +} + +module_init(wbsd_drv_init); +module_exit(wbsd_drv_exit); +#ifdef CONFIG_PNP +module_param(nopnp, uint, 0444); +#endif +module_param(io, uint, 0444); +module_param(irq, uint, 0444); +module_param(dma, int, 0444); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pierre Ossman "); +MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver"); + +#ifdef CONFIG_PNP +MODULE_PARM_DESC(nopnp, "Scan for device instead of relying on PNP. (default 0)"); +#endif +MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)"); +MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)"); +MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)"); diff --git a/drivers/mmc/host/wbsd.h b/drivers/mmc/host/wbsd.h new file mode 100644 index 00000000000..873bda1e59b --- /dev/null +++ b/drivers/mmc/host/wbsd.h @@ -0,0 +1,185 @@ +/* + * linux/drivers/mmc/wbsd.h - Winbond W83L51xD SD/MMC driver + * + * Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#define LOCK_CODE 0xAA + +#define WBSD_CONF_SWRST 0x02 +#define WBSD_CONF_DEVICE 0x07 +#define WBSD_CONF_ID_HI 0x20 +#define WBSD_CONF_ID_LO 0x21 +#define WBSD_CONF_POWER 0x22 +#define WBSD_CONF_PME 0x23 +#define WBSD_CONF_PMES 0x24 + +#define WBSD_CONF_ENABLE 0x30 +#define WBSD_CONF_PORT_HI 0x60 +#define WBSD_CONF_PORT_LO 0x61 +#define WBSD_CONF_IRQ 0x70 +#define WBSD_CONF_DRQ 0x74 + +#define WBSD_CONF_PINS 0xF0 + +#define DEVICE_SD 0x03 + +#define WBSD_PINS_DAT3_HI 0x20 +#define WBSD_PINS_DAT3_OUT 0x10 +#define WBSD_PINS_GP11_HI 0x04 +#define WBSD_PINS_DETECT_GP11 0x02 +#define WBSD_PINS_DETECT_DAT3 0x01 + +#define WBSD_CMDR 0x00 +#define WBSD_DFR 0x01 +#define WBSD_EIR 0x02 +#define WBSD_ISR 0x03 +#define WBSD_FSR 0x04 +#define WBSD_IDXR 0x05 +#define WBSD_DATAR 0x06 +#define WBSD_CSR 0x07 + +#define WBSD_EINT_CARD 0x40 +#define WBSD_EINT_FIFO_THRE 0x20 +#define WBSD_EINT_CRC 0x10 +#define WBSD_EINT_TIMEOUT 0x08 +#define WBSD_EINT_PROGEND 0x04 +#define WBSD_EINT_BUSYEND 0x02 +#define WBSD_EINT_TC 0x01 + +#define WBSD_INT_PENDING 0x80 +#define WBSD_INT_CARD 0x40 +#define WBSD_INT_FIFO_THRE 0x20 +#define WBSD_INT_CRC 0x10 +#define WBSD_INT_TIMEOUT 0x08 +#define WBSD_INT_PROGEND 0x04 +#define WBSD_INT_BUSYEND 0x02 +#define WBSD_INT_TC 0x01 + +#define WBSD_FIFO_EMPTY 0x80 +#define WBSD_FIFO_FULL 0x40 +#define WBSD_FIFO_EMTHRE 0x20 +#define WBSD_FIFO_FUTHRE 0x10 +#define WBSD_FIFO_SZMASK 0x0F + +#define WBSD_MSLED 0x20 +#define WBSD_POWER_N 0x10 +#define WBSD_WRPT 0x04 +#define WBSD_CARDPRESENT 0x01 + +#define WBSD_IDX_CLK 0x01 +#define WBSD_IDX_PBSMSB 0x02 +#define WBSD_IDX_TAAC 0x03 +#define WBSD_IDX_NSAC 0x04 +#define WBSD_IDX_PBSLSB 0x05 +#define WBSD_IDX_SETUP 0x06 +#define WBSD_IDX_DMA 0x07 +#define WBSD_IDX_FIFOEN 0x08 +#define WBSD_IDX_STATUS 0x10 +#define WBSD_IDX_RSPLEN 0x1E +#define WBSD_IDX_RESP0 0x1F +#define WBSD_IDX_RESP1 0x20 +#define WBSD_IDX_RESP2 0x21 +#define WBSD_IDX_RESP3 0x22 +#define WBSD_IDX_RESP4 0x23 +#define WBSD_IDX_RESP5 0x24 +#define WBSD_IDX_RESP6 0x25 +#define WBSD_IDX_RESP7 0x26 +#define WBSD_IDX_RESP8 0x27 +#define WBSD_IDX_RESP9 0x28 +#define WBSD_IDX_RESP10 0x29 +#define WBSD_IDX_RESP11 0x2A +#define WBSD_IDX_RESP12 0x2B +#define WBSD_IDX_RESP13 0x2C +#define WBSD_IDX_RESP14 0x2D +#define WBSD_IDX_RESP15 0x2E +#define WBSD_IDX_RESP16 0x2F +#define WBSD_IDX_CRCSTATUS 0x30 +#define WBSD_IDX_ISR 0x3F + +#define WBSD_CLK_375K 0x00 +#define WBSD_CLK_12M 0x01 +#define WBSD_CLK_16M 0x02 +#define WBSD_CLK_24M 0x03 + +#define WBSD_DATA_WIDTH 0x01 + +#define WBSD_DAT3_H 0x08 +#define WBSD_FIFO_RESET 0x04 +#define WBSD_SOFT_RESET 0x02 +#define WBSD_INC_INDEX 0x01 + +#define WBSD_DMA_SINGLE 0x02 +#define WBSD_DMA_ENABLE 0x01 + +#define WBSD_FIFOEN_EMPTY 0x20 +#define WBSD_FIFOEN_FULL 0x10 +#define WBSD_FIFO_THREMASK 0x0F + +#define WBSD_BLOCK_READ 0x80 +#define WBSD_BLOCK_WRITE 0x40 +#define WBSD_BUSY 0x20 +#define WBSD_CARDTRAFFIC 0x04 +#define WBSD_SENDCMD 0x02 +#define WBSD_RECVRES 0x01 + +#define WBSD_RSP_SHORT 0x00 +#define WBSD_RSP_LONG 0x01 + +#define WBSD_CRC_MASK 0x1F +#define WBSD_CRC_OK 0x05 /* S010E (00101) */ +#define WBSD_CRC_FAIL 0x0B /* S101E (01011) */ + +#define WBSD_DMA_SIZE 65536 + +struct wbsd_host +{ + struct mmc_host* mmc; /* MMC structure */ + + spinlock_t lock; /* Mutex */ + + int flags; /* Driver states */ + +#define WBSD_FCARD_PRESENT (1<<0) /* Card is present */ +#define WBSD_FIGNORE_DETECT (1<<1) /* Ignore card detection */ + + struct mmc_request* mrq; /* Current request */ + + u8 isr; /* Accumulated ISR */ + + struct scatterlist* cur_sg; /* Current SG entry */ + unsigned int num_sg; /* Number of entries left */ + + unsigned int offset; /* Offset into current entry */ + unsigned int remain; /* Data left in curren entry */ + + char* dma_buffer; /* ISA DMA buffer */ + dma_addr_t dma_addr; /* Physical address for same */ + + int firsterr; /* See fifo functions */ + + u8 clk; /* Current clock speed */ + unsigned char bus_width; /* Current bus width */ + + int config; /* Config port */ + u8 unlock_code; /* Code to unlock config */ + + int chip_id; /* ID of controller */ + + int base; /* I/O port base */ + int irq; /* Interrupt */ + int dma; /* DMA channel */ + + struct tasklet_struct card_tasklet; /* Tasklet structures */ + struct tasklet_struct fifo_tasklet; + struct tasklet_struct crc_tasklet; + struct tasklet_struct timeout_tasklet; + struct tasklet_struct finish_tasklet; + + struct timer_list ignore_timer; /* Ignore detection timer */ +}; diff --git a/drivers/mmc/imxmmc.c b/drivers/mmc/imxmmc.c deleted file mode 100644 index 7ee2045acbe..00000000000 --- a/drivers/mmc/imxmmc.c +++ /dev/null @@ -1,1137 +0,0 @@ -/* - * linux/drivers/mmc/imxmmc.c - Motorola i.MX MMCI driver - * - * Copyright (C) 2004 Sascha Hauer, Pengutronix - * Copyright (C) 2006 Pavel Pisa, PiKRON - * - * derived from pxamci.c by Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * 2005-04-17 Pavel Pisa - * Changed to conform redesigned i.MX scatter gather DMA interface - * - * 2005-11-04 Pavel Pisa - * Updated for 2.6.14 kernel - * - * 2005-12-13 Jay Monkman - * Found and corrected problems in the write path - * - * 2005-12-30 Pavel Pisa - * The event handling rewritten right way in softirq. - * Added many ugly hacks and delays to overcome SDHC - * deficiencies - * - */ - -#ifdef CONFIG_MMC_DEBUG -#define DEBUG -#else -#undef DEBUG -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "imxmmc.h" - -#define DRIVER_NAME "imx-mmc" - -#define IMXMCI_INT_MASK_DEFAULT (INT_MASK_BUF_READY | INT_MASK_DATA_TRAN | \ - INT_MASK_WRITE_OP_DONE | INT_MASK_END_CMD_RES | \ - INT_MASK_AUTO_CARD_DETECT | INT_MASK_DAT0_EN | INT_MASK_SDIO) - -struct imxmci_host { - struct mmc_host *mmc; - spinlock_t lock; - struct resource *res; - int irq; - imx_dmach_t dma; - unsigned int clkrt; - unsigned int cmdat; - volatile unsigned int imask; - unsigned int power_mode; - unsigned int present; - struct imxmmc_platform_data *pdata; - - struct mmc_request *req; - struct mmc_command *cmd; - struct mmc_data *data; - - struct timer_list timer; - struct tasklet_struct tasklet; - unsigned int status_reg; - unsigned long pending_events; - /* Next to fields are there for CPU driven transfers to overcome SDHC deficiencies */ - u16 *data_ptr; - unsigned int data_cnt; - atomic_t stuck_timeout; - - unsigned int dma_nents; - unsigned int dma_size; - unsigned int dma_dir; - int dma_allocated; - - unsigned char actual_bus_width; - - int prev_cmd_code; -}; - -#define IMXMCI_PEND_IRQ_b 0 -#define IMXMCI_PEND_DMA_END_b 1 -#define IMXMCI_PEND_DMA_ERR_b 2 -#define IMXMCI_PEND_WAIT_RESP_b 3 -#define IMXMCI_PEND_DMA_DATA_b 4 -#define IMXMCI_PEND_CPU_DATA_b 5 -#define IMXMCI_PEND_CARD_XCHG_b 6 -#define IMXMCI_PEND_SET_INIT_b 7 -#define IMXMCI_PEND_STARTED_b 8 - -#define IMXMCI_PEND_IRQ_m (1 << IMXMCI_PEND_IRQ_b) -#define IMXMCI_PEND_DMA_END_m (1 << IMXMCI_PEND_DMA_END_b) -#define IMXMCI_PEND_DMA_ERR_m (1 << IMXMCI_PEND_DMA_ERR_b) -#define IMXMCI_PEND_WAIT_RESP_m (1 << IMXMCI_PEND_WAIT_RESP_b) -#define IMXMCI_PEND_DMA_DATA_m (1 << IMXMCI_PEND_DMA_DATA_b) -#define IMXMCI_PEND_CPU_DATA_m (1 << IMXMCI_PEND_CPU_DATA_b) -#define IMXMCI_PEND_CARD_XCHG_m (1 << IMXMCI_PEND_CARD_XCHG_b) -#define IMXMCI_PEND_SET_INIT_m (1 << IMXMCI_PEND_SET_INIT_b) -#define IMXMCI_PEND_STARTED_m (1 << IMXMCI_PEND_STARTED_b) - -static void imxmci_stop_clock(struct imxmci_host *host) -{ - int i = 0; - MMC_STR_STP_CLK &= ~STR_STP_CLK_START_CLK; - while(i < 0x1000) { - if(!(i & 0x7f)) - MMC_STR_STP_CLK |= STR_STP_CLK_STOP_CLK; - - if(!(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)) { - /* Check twice before cut */ - if(!(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN)) - return; - } - - i++; - } - dev_dbg(mmc_dev(host->mmc), "imxmci_stop_clock blocked, no luck\n"); -} - -static int imxmci_start_clock(struct imxmci_host *host) -{ - unsigned int trials = 0; - unsigned int delay_limit = 128; - unsigned long flags; - - MMC_STR_STP_CLK &= ~STR_STP_CLK_STOP_CLK; - - clear_bit(IMXMCI_PEND_STARTED_b, &host->pending_events); - - /* - * Command start of the clock, this usually succeeds in less - * then 6 delay loops, but during card detection (low clockrate) - * it takes up to 5000 delay loops and sometimes fails for the first time - */ - MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK; - - do { - unsigned int delay = delay_limit; - - while(delay--){ - if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN) - /* Check twice before cut */ - if(MMC_STATUS & STATUS_CARD_BUS_CLK_RUN) - return 0; - - if(test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events)) - return 0; - } - - local_irq_save(flags); - /* - * Ensure, that request is not doubled under all possible circumstances. - * It is possible, that cock running state is missed, because some other - * IRQ or schedule delays this function execution and the clocks has - * been already stopped by other means (response processing, SDHC HW) - */ - if(!test_bit(IMXMCI_PEND_STARTED_b, &host->pending_events)) - MMC_STR_STP_CLK |= STR_STP_CLK_START_CLK; - local_irq_restore(flags); - - } while(++trials<256); - - dev_err(mmc_dev(host->mmc), "imxmci_start_clock blocked, no luck\n"); - - return -1; -} - -static void imxmci_softreset(void) -{ - /* reset sequence */ - MMC_STR_STP_CLK = 0x8; - MMC_STR_STP_CLK = 0xD; - MMC_STR_STP_CLK = 0x5; - MMC_STR_STP_CLK = 0x5; - MMC_STR_STP_CLK = 0x5; - MMC_STR_STP_CLK = 0x5; - MMC_STR_STP_CLK = 0x5; - MMC_STR_STP_CLK = 0x5; - MMC_STR_STP_CLK = 0x5; - MMC_STR_STP_CLK = 0x5; - - MMC_RES_TO = 0xff; - MMC_BLK_LEN = 512; - MMC_NOB = 1; -} - -static int imxmci_busy_wait_for_status(struct imxmci_host *host, - unsigned int *pstat, unsigned int stat_mask, - int timeout, const char *where) -{ - int loops=0; - while(!(*pstat & stat_mask)) { - loops+=2; - if(loops >= timeout) { - dev_dbg(mmc_dev(host->mmc), "busy wait timeout in %s, STATUS = 0x%x (0x%x)\n", - where, *pstat, stat_mask); - return -1; - } - udelay(2); - *pstat |= MMC_STATUS; - } - if(!loops) - return 0; - - /* The busy-wait is expected there for clock <8MHz due to SDHC hardware flaws */ - if(!(stat_mask & STATUS_END_CMD_RESP) || (host->mmc->ios.clock>=8000000)) - dev_info(mmc_dev(host->mmc), "busy wait for %d usec in %s, STATUS = 0x%x (0x%x)\n", - loops, where, *pstat, stat_mask); - return loops; -} - -static void imxmci_setup_data(struct imxmci_host *host, struct mmc_data *data) -{ - unsigned int nob = data->blocks; - unsigned int blksz = data->blksz; - unsigned int datasz = nob * blksz; - int i; - - if (data->flags & MMC_DATA_STREAM) - nob = 0xffff; - - host->data = data; - data->bytes_xfered = 0; - - MMC_NOB = nob; - MMC_BLK_LEN = blksz; - - /* - * DMA cannot be used for small block sizes, we have to use CPU driven transfers otherwise. - * We are in big troubles for non-512 byte transfers according to note in the paragraph - * 20.6.7 of User Manual anyway, but we need to be able to transfer SCR at least. - * The situation is even more complex in reality. The SDHC in not able to handle wll - * partial FIFO fills and reads. The length has to be rounded up to burst size multiple. - * This is required for SCR read at least. - */ - if (datasz < 512) { - host->dma_size = datasz; - if (data->flags & MMC_DATA_READ) { - host->dma_dir = DMA_FROM_DEVICE; - - /* Hack to enable read SCR */ - MMC_NOB = 1; - MMC_BLK_LEN = 512; - } else { - host->dma_dir = DMA_TO_DEVICE; - } - - /* Convert back to virtual address */ - host->data_ptr = (u16*)(page_address(data->sg->page) + data->sg->offset); - host->data_cnt = 0; - - clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events); - set_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events); - - return; - } - - if (data->flags & MMC_DATA_READ) { - host->dma_dir = DMA_FROM_DEVICE; - host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, host->dma_dir); - - imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz, - host->res->start + MMC_BUFFER_ACCESS_OFS, DMA_MODE_READ); - - /*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_READ, IMX_DMA_WIDTH_16, CCR_REN);*/ - CCR(host->dma) = CCR_DMOD_LINEAR | CCR_DSIZ_32 | CCR_SMOD_FIFO | CCR_SSIZ_16 | CCR_REN; - } else { - host->dma_dir = DMA_TO_DEVICE; - - host->dma_nents = dma_map_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, host->dma_dir); - - imx_dma_setup_sg(host->dma, data->sg, data->sg_len, datasz, - host->res->start + MMC_BUFFER_ACCESS_OFS, DMA_MODE_WRITE); - - /*imx_dma_setup_mem2dev_ccr(host->dma, DMA_MODE_WRITE, IMX_DMA_WIDTH_16, CCR_REN);*/ - CCR(host->dma) = CCR_SMOD_LINEAR | CCR_SSIZ_32 | CCR_DMOD_FIFO | CCR_DSIZ_16 | CCR_REN; - } - -#if 1 /* This code is there only for consistency checking and can be disabled in future */ - host->dma_size = 0; - for(i=0; idma_nents; i++) - host->dma_size+=data->sg[i].length; - - if (datasz > host->dma_size) { - dev_err(mmc_dev(host->mmc), "imxmci_setup_data datasz 0x%x > 0x%x dm_size\n", - datasz, host->dma_size); - } -#endif - - host->dma_size = datasz; - - wmb(); - - if(host->actual_bus_width == MMC_BUS_WIDTH_4) - BLR(host->dma) = 0; /* burst 64 byte read / 64 bytes write */ - else - BLR(host->dma) = 16; /* burst 16 byte read / 16 bytes write */ - - RSSR(host->dma) = DMA_REQ_SDHC; - - set_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events); - clear_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events); - - /* start DMA engine for read, write is delayed after initial response */ - if (host->dma_dir == DMA_FROM_DEVICE) { - imx_dma_enable(host->dma); - } -} - -static void imxmci_start_cmd(struct imxmci_host *host, struct mmc_command *cmd, unsigned int cmdat) -{ - unsigned long flags; - u32 imask; - - WARN_ON(host->cmd != NULL); - host->cmd = cmd; - - /* Ensure, that clock are stopped else command programming and start fails */ - imxmci_stop_clock(host); - - if (cmd->flags & MMC_RSP_BUSY) - cmdat |= CMD_DAT_CONT_BUSY; - - switch (mmc_resp_type(cmd)) { - case MMC_RSP_R1: /* short CRC, OPCODE */ - case MMC_RSP_R1B:/* short CRC, OPCODE, BUSY */ - cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R1; - break; - case MMC_RSP_R2: /* long 136 bit + CRC */ - cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R2; - break; - case MMC_RSP_R3: /* short */ - cmdat |= CMD_DAT_CONT_RESPONSE_FORMAT_R3; - break; - default: - break; - } - - if ( test_and_clear_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events) ) - cmdat |= CMD_DAT_CONT_INIT; /* This command needs init */ - - if ( host->actual_bus_width == MMC_BUS_WIDTH_4 ) - cmdat |= CMD_DAT_CONT_BUS_WIDTH_4; - - MMC_CMD = cmd->opcode; - MMC_ARGH = cmd->arg >> 16; - MMC_ARGL = cmd->arg & 0xffff; - MMC_CMD_DAT_CONT = cmdat; - - atomic_set(&host->stuck_timeout, 0); - set_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events); - - - imask = IMXMCI_INT_MASK_DEFAULT; - imask &= ~INT_MASK_END_CMD_RES; - if ( cmdat & CMD_DAT_CONT_DATA_ENABLE ) { - /*imask &= ~INT_MASK_BUF_READY;*/ - imask &= ~INT_MASK_DATA_TRAN; - if ( cmdat & CMD_DAT_CONT_WRITE ) - imask &= ~INT_MASK_WRITE_OP_DONE; - if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) - imask &= ~INT_MASK_BUF_READY; - } - - spin_lock_irqsave(&host->lock, flags); - host->imask = imask; - MMC_INT_MASK = host->imask; - spin_unlock_irqrestore(&host->lock, flags); - - dev_dbg(mmc_dev(host->mmc), "CMD%02d (0x%02x) mask set to 0x%04x\n", - cmd->opcode, cmd->opcode, imask); - - imxmci_start_clock(host); -} - -static void imxmci_finish_request(struct imxmci_host *host, struct mmc_request *req) -{ - unsigned long flags; - - spin_lock_irqsave(&host->lock, flags); - - host->pending_events &= ~(IMXMCI_PEND_WAIT_RESP_m | IMXMCI_PEND_DMA_END_m | - IMXMCI_PEND_DMA_DATA_m | IMXMCI_PEND_CPU_DATA_m); - - host->imask = IMXMCI_INT_MASK_DEFAULT; - MMC_INT_MASK = host->imask; - - spin_unlock_irqrestore(&host->lock, flags); - - if(req && req->cmd) - host->prev_cmd_code = req->cmd->opcode; - - host->req = NULL; - host->cmd = NULL; - host->data = NULL; - mmc_request_done(host->mmc, req); -} - -static int imxmci_finish_data(struct imxmci_host *host, unsigned int stat) -{ - struct mmc_data *data = host->data; - int data_error; - - if(test_and_clear_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)){ - imx_dma_disable(host->dma); - dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_nents, - host->dma_dir); - } - - if ( stat & STATUS_ERR_MASK ) { - dev_dbg(mmc_dev(host->mmc), "request failed. status: 0x%08x\n",stat); - if(stat & (STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR)) - data->error = MMC_ERR_BADCRC; - else if(stat & STATUS_TIME_OUT_READ) - data->error = MMC_ERR_TIMEOUT; - else - data->error = MMC_ERR_FAILED; - } else { - data->bytes_xfered = host->dma_size; - } - - data_error = data->error; - - host->data = NULL; - - return data_error; -} - -static int imxmci_cmd_done(struct imxmci_host *host, unsigned int stat) -{ - struct mmc_command *cmd = host->cmd; - int i; - u32 a,b,c; - struct mmc_data *data = host->data; - - if (!cmd) - return 0; - - host->cmd = NULL; - - if (stat & STATUS_TIME_OUT_RESP) { - dev_dbg(mmc_dev(host->mmc), "CMD TIMEOUT\n"); - cmd->error = MMC_ERR_TIMEOUT; - } else if (stat & STATUS_RESP_CRC_ERR && cmd->flags & MMC_RSP_CRC) { - dev_dbg(mmc_dev(host->mmc), "cmd crc error\n"); - cmd->error = MMC_ERR_BADCRC; - } - - if(cmd->flags & MMC_RSP_PRESENT) { - if(cmd->flags & MMC_RSP_136) { - for (i = 0; i < 4; i++) { - u32 a = MMC_RES_FIFO & 0xffff; - u32 b = MMC_RES_FIFO & 0xffff; - cmd->resp[i] = a<<16 | b; - } - } else { - a = MMC_RES_FIFO & 0xffff; - b = MMC_RES_FIFO & 0xffff; - c = MMC_RES_FIFO & 0xffff; - cmd->resp[0] = a<<24 | b<<8 | c>>8; - } - } - - dev_dbg(mmc_dev(host->mmc), "RESP 0x%08x, 0x%08x, 0x%08x, 0x%08x, error %d\n", - cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3], cmd->error); - - if (data && (cmd->error == MMC_ERR_NONE) && !(stat & STATUS_ERR_MASK)) { - if (host->req->data->flags & MMC_DATA_WRITE) { - - /* Wait for FIFO to be empty before starting DMA write */ - - stat = MMC_STATUS; - if(imxmci_busy_wait_for_status(host, &stat, - STATUS_APPL_BUFF_FE, - 40, "imxmci_cmd_done DMA WR") < 0) { - cmd->error = MMC_ERR_FIFO; - imxmci_finish_data(host, stat); - if(host->req) - imxmci_finish_request(host, host->req); - dev_warn(mmc_dev(host->mmc), "STATUS = 0x%04x\n", - stat); - return 0; - } - - if(test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) { - imx_dma_enable(host->dma); - } - } - } else { - struct mmc_request *req; - imxmci_stop_clock(host); - req = host->req; - - if(data) - imxmci_finish_data(host, stat); - - if( req ) { - imxmci_finish_request(host, req); - } else { - dev_warn(mmc_dev(host->mmc), "imxmci_cmd_done: no request to finish\n"); - } - } - - return 1; -} - -static int imxmci_data_done(struct imxmci_host *host, unsigned int stat) -{ - struct mmc_data *data = host->data; - int data_error; - - if (!data) - return 0; - - data_error = imxmci_finish_data(host, stat); - - if (host->req->stop) { - imxmci_stop_clock(host); - imxmci_start_cmd(host, host->req->stop, 0); - } else { - struct mmc_request *req; - req = host->req; - if( req ) { - imxmci_finish_request(host, req); - } else { - dev_warn(mmc_dev(host->mmc), "imxmci_data_done: no request to finish\n"); - } - } - - return 1; -} - -static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat) -{ - int i; - int burst_len; - int trans_done = 0; - unsigned int stat = *pstat; - - if(host->actual_bus_width != MMC_BUS_WIDTH_4) - burst_len = 16; - else - burst_len = 64; - - /* This is unfortunately required */ - dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data running STATUS = 0x%x\n", - stat); - - udelay(20); /* required for clocks < 8MHz*/ - - if(host->dma_dir == DMA_FROM_DEVICE) { - imxmci_busy_wait_for_status(host, &stat, - STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE | - STATUS_TIME_OUT_READ, - 50, "imxmci_cpu_driven_data read"); - - while((stat & (STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE)) && - !(stat & STATUS_TIME_OUT_READ) && - (host->data_cnt < 512)) { - - udelay(20); /* required for clocks < 8MHz*/ - - for(i = burst_len; i>=2 ; i-=2) { - u16 data; - data = MMC_BUFFER_ACCESS; - udelay(10); /* required for clocks < 8MHz*/ - if(host->data_cnt+2 <= host->dma_size) { - *(host->data_ptr++) = data; - } else { - if(host->data_cnt < host->dma_size) - *(u8*)(host->data_ptr) = data; - } - host->data_cnt += 2; - } - - stat = MMC_STATUS; - - dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read %d burst %d STATUS = 0x%x\n", - host->data_cnt, burst_len, stat); - } - - if((stat & STATUS_DATA_TRANS_DONE) && (host->data_cnt >= 512)) - trans_done = 1; - - if(host->dma_size & 0x1ff) - stat &= ~STATUS_CRC_READ_ERR; - - if(stat & STATUS_TIME_OUT_READ) { - dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read timeout STATUS = 0x%x\n", - stat); - trans_done = -1; - } - - } else { - imxmci_busy_wait_for_status(host, &stat, - STATUS_APPL_BUFF_FE, - 20, "imxmci_cpu_driven_data write"); - - while((stat & STATUS_APPL_BUFF_FE) && - (host->data_cnt < host->dma_size)) { - if(burst_len >= host->dma_size - host->data_cnt) { - burst_len = host->dma_size - host->data_cnt; - host->data_cnt = host->dma_size; - trans_done = 1; - } else { - host->data_cnt += burst_len; - } - - for(i = burst_len; i>0 ; i-=2) - MMC_BUFFER_ACCESS = *(host->data_ptr++); - - stat = MMC_STATUS; - - dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data write burst %d STATUS = 0x%x\n", - burst_len, stat); - } - } - - *pstat = stat; - - return trans_done; -} - -static void imxmci_dma_irq(int dma, void *devid) -{ - struct imxmci_host *host = devid; - uint32_t stat = MMC_STATUS; - - atomic_set(&host->stuck_timeout, 0); - host->status_reg = stat; - set_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events); - tasklet_schedule(&host->tasklet); -} - -static irqreturn_t imxmci_irq(int irq, void *devid) -{ - struct imxmci_host *host = devid; - uint32_t stat = MMC_STATUS; - int handled = 1; - - MMC_INT_MASK = host->imask | INT_MASK_SDIO | INT_MASK_AUTO_CARD_DETECT; - - atomic_set(&host->stuck_timeout, 0); - host->status_reg = stat; - set_bit(IMXMCI_PEND_IRQ_b, &host->pending_events); - set_bit(IMXMCI_PEND_STARTED_b, &host->pending_events); - tasklet_schedule(&host->tasklet); - - return IRQ_RETVAL(handled);; -} - -static void imxmci_tasklet_fnc(unsigned long data) -{ - struct imxmci_host *host = (struct imxmci_host *)data; - u32 stat; - unsigned int data_dir_mask = 0; /* STATUS_WR_CRC_ERROR_CODE_MASK */ - int timeout = 0; - - if(atomic_read(&host->stuck_timeout) > 4) { - char *what; - timeout = 1; - stat = MMC_STATUS; - host->status_reg = stat; - if (test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) - if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) - what = "RESP+DMA"; - else - what = "RESP"; - else - if (test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) - if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events)) - what = "DATA"; - else - what = "DMA"; - else - what = "???"; - - dev_err(mmc_dev(host->mmc), "%s TIMEOUT, hardware stucked STATUS = 0x%04x IMASK = 0x%04x\n", - what, stat, MMC_INT_MASK); - dev_err(mmc_dev(host->mmc), "CMD_DAT_CONT = 0x%04x, MMC_BLK_LEN = 0x%04x, MMC_NOB = 0x%04x, DMA_CCR = 0x%08x\n", - MMC_CMD_DAT_CONT, MMC_BLK_LEN, MMC_NOB, CCR(host->dma)); - dev_err(mmc_dev(host->mmc), "CMD%d, prevCMD%d, bus %d-bit, dma_size = 0x%x\n", - host->cmd?host->cmd->opcode:0, host->prev_cmd_code, 1<actual_bus_width, host->dma_size); - } - - if(!host->present || timeout) - host->status_reg = STATUS_TIME_OUT_RESP | STATUS_TIME_OUT_READ | - STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR; - - if(test_bit(IMXMCI_PEND_IRQ_b, &host->pending_events) || timeout) { - clear_bit(IMXMCI_PEND_IRQ_b, &host->pending_events); - - stat = MMC_STATUS; - /* - * This is not required in theory, but there is chance to miss some flag - * which clears automatically by mask write, FreeScale original code keeps - * stat from IRQ time so do I - */ - stat |= host->status_reg; - - if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) - stat &= ~STATUS_CRC_READ_ERR; - - if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) { - imxmci_busy_wait_for_status(host, &stat, - STATUS_END_CMD_RESP | STATUS_ERR_MASK, - 20, "imxmci_tasklet_fnc resp (ERRATUM #4)"); - } - - if(stat & (STATUS_END_CMD_RESP | STATUS_ERR_MASK)) { - if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) - imxmci_cmd_done(host, stat); - if(host->data && (stat & STATUS_ERR_MASK)) - imxmci_data_done(host, stat); - } - - if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) { - stat |= MMC_STATUS; - if(imxmci_cpu_driven_data(host, &stat)){ - if(test_and_clear_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) - imxmci_cmd_done(host, stat); - atomic_clear_mask(IMXMCI_PEND_IRQ_m|IMXMCI_PEND_CPU_DATA_m, - &host->pending_events); - imxmci_data_done(host, stat); - } - } - } - - if(test_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events) && - !test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) { - - stat = MMC_STATUS; - /* Same as above */ - stat |= host->status_reg; - - if(host->dma_dir == DMA_TO_DEVICE) { - data_dir_mask = STATUS_WRITE_OP_DONE; - } else { - data_dir_mask = STATUS_DATA_TRANS_DONE; - } - - if(stat & data_dir_mask) { - clear_bit(IMXMCI_PEND_DMA_END_b, &host->pending_events); - imxmci_data_done(host, stat); - } - } - - if(test_and_clear_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events)) { - - if(host->cmd) - imxmci_cmd_done(host, STATUS_TIME_OUT_RESP); - - if(host->data) - imxmci_data_done(host, STATUS_TIME_OUT_READ | - STATUS_CRC_READ_ERR | STATUS_CRC_WRITE_ERR); - - if(host->req) - imxmci_finish_request(host, host->req); - - mmc_detect_change(host->mmc, msecs_to_jiffies(100)); - - } -} - -static void imxmci_request(struct mmc_host *mmc, struct mmc_request *req) -{ - struct imxmci_host *host = mmc_priv(mmc); - unsigned int cmdat; - - WARN_ON(host->req != NULL); - - host->req = req; - - cmdat = 0; - - if (req->data) { - imxmci_setup_data(host, req->data); - - cmdat |= CMD_DAT_CONT_DATA_ENABLE; - - if (req->data->flags & MMC_DATA_WRITE) - cmdat |= CMD_DAT_CONT_WRITE; - - if (req->data->flags & MMC_DATA_STREAM) { - cmdat |= CMD_DAT_CONT_STREAM_BLOCK; - } - } - - imxmci_start_cmd(host, req->cmd, cmdat); -} - -#define CLK_RATE 19200000 - -static void imxmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -{ - struct imxmci_host *host = mmc_priv(mmc); - int prescaler; - - if( ios->bus_width==MMC_BUS_WIDTH_4 ) { - host->actual_bus_width = MMC_BUS_WIDTH_4; - imx_gpio_mode(PB11_PF_SD_DAT3); - }else{ - host->actual_bus_width = MMC_BUS_WIDTH_1; - imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11); - } - - if ( host->power_mode != ios->power_mode ) { - switch (ios->power_mode) { - case MMC_POWER_OFF: - break; - case MMC_POWER_UP: - set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events); - break; - case MMC_POWER_ON: - break; - } - host->power_mode = ios->power_mode; - } - - if ( ios->clock ) { - unsigned int clk; - - /* The prescaler is 5 for PERCLK2 equal to 96MHz - * then 96MHz / 5 = 19.2 MHz - */ - clk=imx_get_perclk2(); - prescaler=(clk+(CLK_RATE*7)/8)/CLK_RATE; - switch(prescaler) { - case 0: - case 1: prescaler = 0; - break; - case 2: prescaler = 1; - break; - case 3: prescaler = 2; - break; - case 4: prescaler = 4; - break; - default: - case 5: prescaler = 5; - break; - } - - dev_dbg(mmc_dev(host->mmc), "PERCLK2 %d MHz -> prescaler %d\n", - clk, prescaler); - - for(clk=0; clk<8; clk++) { - int x; - x = CLK_RATE / (1<clock) - break; - } - - MMC_STR_STP_CLK |= STR_STP_CLK_ENABLE; /* enable controller */ - - imxmci_stop_clock(host); - MMC_CLK_RATE = (prescaler<<3) | clk; - /* - * Under my understanding, clock should not be started there, because it would - * initiate SDHC sequencer and send last or random command into card - */ - /*imxmci_start_clock(host);*/ - - dev_dbg(mmc_dev(host->mmc), "MMC_CLK_RATE: 0x%08x\n", MMC_CLK_RATE); - } else { - imxmci_stop_clock(host); - } -} - -static const struct mmc_host_ops imxmci_ops = { - .request = imxmci_request, - .set_ios = imxmci_set_ios, -}; - -static struct resource *platform_device_resource(struct platform_device *dev, unsigned int mask, int nr) -{ - int i; - - for (i = 0; i < dev->num_resources; i++) - if (dev->resource[i].flags == mask && nr-- == 0) - return &dev->resource[i]; - return NULL; -} - -static int platform_device_irq(struct platform_device *dev, int nr) -{ - int i; - - for (i = 0; i < dev->num_resources; i++) - if (dev->resource[i].flags == IORESOURCE_IRQ && nr-- == 0) - return dev->resource[i].start; - return NO_IRQ; -} - -static void imxmci_check_status(unsigned long data) -{ - struct imxmci_host *host = (struct imxmci_host *)data; - - if( host->pdata->card_present() != host->present ) { - host->present ^= 1; - dev_info(mmc_dev(host->mmc), "card %s\n", - host->present ? "inserted" : "removed"); - - set_bit(IMXMCI_PEND_CARD_XCHG_b, &host->pending_events); - tasklet_schedule(&host->tasklet); - } - - if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events) || - test_bit(IMXMCI_PEND_DMA_DATA_b, &host->pending_events)) { - atomic_inc(&host->stuck_timeout); - if(atomic_read(&host->stuck_timeout) > 4) - tasklet_schedule(&host->tasklet); - } else { - atomic_set(&host->stuck_timeout, 0); - - } - - mod_timer(&host->timer, jiffies + (HZ>>1)); -} - -static int imxmci_probe(struct platform_device *pdev) -{ - struct mmc_host *mmc; - struct imxmci_host *host = NULL; - struct resource *r; - int ret = 0, irq; - - printk(KERN_INFO "i.MX mmc driver\n"); - - r = platform_device_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_device_irq(pdev, 0); - if (!r || irq == NO_IRQ) - return -ENXIO; - - r = request_mem_region(r->start, 0x100, "IMXMCI"); - if (!r) - return -EBUSY; - - mmc = mmc_alloc_host(sizeof(struct imxmci_host), &pdev->dev); - if (!mmc) { - ret = -ENOMEM; - goto out; - } - - mmc->ops = &imxmci_ops; - mmc->f_min = 150000; - mmc->f_max = CLK_RATE/2; - mmc->ocr_avail = MMC_VDD_32_33; - mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_BYTEBLOCK; - - /* MMC core transfer sizes tunable parameters */ - mmc->max_hw_segs = 64; - mmc->max_phys_segs = 64; - mmc->max_seg_size = 64*512; /* default PAGE_CACHE_SIZE */ - mmc->max_req_size = 64*512; /* default PAGE_CACHE_SIZE */ - mmc->max_blk_size = 2048; - mmc->max_blk_count = 65535; - - host = mmc_priv(mmc); - host->mmc = mmc; - host->dma_allocated = 0; - host->pdata = pdev->dev.platform_data; - - spin_lock_init(&host->lock); - host->res = r; - host->irq = irq; - - imx_gpio_mode(PB8_PF_SD_DAT0); - imx_gpio_mode(PB9_PF_SD_DAT1); - imx_gpio_mode(PB10_PF_SD_DAT2); - /* Configured as GPIO with pull-up to ensure right MCC card mode */ - /* Switched to PB11_PF_SD_DAT3 if 4 bit bus is configured */ - imx_gpio_mode(GPIO_PORTB | GPIO_IN | GPIO_PUEN | 11); - /* imx_gpio_mode(PB11_PF_SD_DAT3); */ - imx_gpio_mode(PB12_PF_SD_CLK); - imx_gpio_mode(PB13_PF_SD_CMD); - - imxmci_softreset(); - - if ( MMC_REV_NO != 0x390 ) { - dev_err(mmc_dev(host->mmc), "wrong rev.no. 0x%08x. aborting.\n", - MMC_REV_NO); - goto out; - } - - MMC_READ_TO = 0x2db4; /* recommended in data sheet */ - - host->imask = IMXMCI_INT_MASK_DEFAULT; - MMC_INT_MASK = host->imask; - - - if(imx_dma_request_by_prio(&host->dma, DRIVER_NAME, DMA_PRIO_LOW)<0){ - dev_err(mmc_dev(host->mmc), "imx_dma_request_by_prio failed\n"); - ret = -EBUSY; - goto out; - } - host->dma_allocated=1; - imx_dma_setup_handlers(host->dma, imxmci_dma_irq, NULL, host); - - tasklet_init(&host->tasklet, imxmci_tasklet_fnc, (unsigned long)host); - host->status_reg=0; - host->pending_events=0; - - ret = request_irq(host->irq, imxmci_irq, 0, DRIVER_NAME, host); - if (ret) - goto out; - - host->present = host->pdata->card_present(); - init_timer(&host->timer); - host->timer.data = (unsigned long)host; - host->timer.function = imxmci_check_status; - add_timer(&host->timer); - mod_timer(&host->timer, jiffies + (HZ>>1)); - - platform_set_drvdata(pdev, mmc); - - mmc_add_host(mmc); - - return 0; - -out: - if (host) { - if(host->dma_allocated){ - imx_dma_free(host->dma); - host->dma_allocated=0; - } - } - if (mmc) - mmc_free_host(mmc); - release_resource(r); - return ret; -} - -static int imxmci_remove(struct platform_device *pdev) -{ - struct mmc_host *mmc = platform_get_drvdata(pdev); - - platform_set_drvdata(pdev, NULL); - - if (mmc) { - struct imxmci_host *host = mmc_priv(mmc); - - tasklet_disable(&host->tasklet); - - del_timer_sync(&host->timer); - mmc_remove_host(mmc); - - free_irq(host->irq, host); - if(host->dma_allocated){ - imx_dma_free(host->dma); - host->dma_allocated=0; - } - - tasklet_kill(&host->tasklet); - - release_resource(host->res); - - mmc_free_host(mmc); - } - return 0; -} - -#ifdef CONFIG_PM -static int imxmci_suspend(struct platform_device *dev, pm_message_t state) -{ - struct mmc_host *mmc = platform_get_drvdata(dev); - int ret = 0; - - if (mmc) - ret = mmc_suspend_host(mmc, state); - - return ret; -} - -static int imxmci_resume(struct platform_device *dev) -{ - struct mmc_host *mmc = platform_get_drvdata(dev); - struct imxmci_host *host; - int ret = 0; - - if (mmc) { - host = mmc_priv(mmc); - if(host) - set_bit(IMXMCI_PEND_SET_INIT_b, &host->pending_events); - ret = mmc_resume_host(mmc); - } - - return ret; -} -#else -#define imxmci_suspend NULL -#define imxmci_resume NULL -#endif /* CONFIG_PM */ - -static struct platform_driver imxmci_driver = { - .probe = imxmci_probe, - .remove = imxmci_remove, - .suspend = imxmci_suspend, - .resume = imxmci_resume, - .driver = { - .name = DRIVER_NAME, - } -}; - -static int __init imxmci_init(void) -{ - return platform_driver_register(&imxmci_driver); -} - -static void __exit imxmci_exit(void) -{ - platform_driver_unregister(&imxmci_driver); -} - -module_init(imxmci_init); -module_exit(imxmci_exit); - -MODULE_DESCRIPTION("i.MX Multimedia Card Interface Driver"); -MODULE_AUTHOR("Sascha Hauer, Pengutronix"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/imxmmc.h b/drivers/mmc/imxmmc.h deleted file mode 100644 index e5339e334db..00000000000 --- a/drivers/mmc/imxmmc.h +++ /dev/null @@ -1,67 +0,0 @@ - -# define __REG16(x) (*((volatile u16 *)IO_ADDRESS(x))) - -#define MMC_STR_STP_CLK __REG16(IMX_MMC_BASE + 0x00) -#define MMC_STATUS __REG16(IMX_MMC_BASE + 0x04) -#define MMC_CLK_RATE __REG16(IMX_MMC_BASE + 0x08) -#define MMC_CMD_DAT_CONT __REG16(IMX_MMC_BASE + 0x0C) -#define MMC_RES_TO __REG16(IMX_MMC_BASE + 0x10) -#define MMC_READ_TO __REG16(IMX_MMC_BASE + 0x14) -#define MMC_BLK_LEN __REG16(IMX_MMC_BASE + 0x18) -#define MMC_NOB __REG16(IMX_MMC_BASE + 0x1C) -#define MMC_REV_NO __REG16(IMX_MMC_BASE + 0x20) -#define MMC_INT_MASK __REG16(IMX_MMC_BASE + 0x24) -#define MMC_CMD __REG16(IMX_MMC_BASE + 0x28) -#define MMC_ARGH __REG16(IMX_MMC_BASE + 0x2C) -#define MMC_ARGL __REG16(IMX_MMC_BASE + 0x30) -#define MMC_RES_FIFO __REG16(IMX_MMC_BASE + 0x34) -#define MMC_BUFFER_ACCESS __REG16(IMX_MMC_BASE + 0x38) -#define MMC_BUFFER_ACCESS_OFS 0x38 - - -#define STR_STP_CLK_ENDIAN (1<<5) -#define STR_STP_CLK_RESET (1<<3) -#define STR_STP_CLK_ENABLE (1<<2) -#define STR_STP_CLK_START_CLK (1<<1) -#define STR_STP_CLK_STOP_CLK (1<<0) -#define STATUS_CARD_PRESENCE (1<<15) -#define STATUS_SDIO_INT_ACTIVE (1<<14) -#define STATUS_END_CMD_RESP (1<<13) -#define STATUS_WRITE_OP_DONE (1<<12) -#define STATUS_DATA_TRANS_DONE (1<<11) -#define STATUS_WR_CRC_ERROR_CODE_MASK (3<<10) -#define STATUS_CARD_BUS_CLK_RUN (1<<8) -#define STATUS_APPL_BUFF_FF (1<<7) -#define STATUS_APPL_BUFF_FE (1<<6) -#define STATUS_RESP_CRC_ERR (1<<5) -#define STATUS_CRC_READ_ERR (1<<3) -#define STATUS_CRC_WRITE_ERR (1<<2) -#define STATUS_TIME_OUT_RESP (1<<1) -#define STATUS_TIME_OUT_READ (1<<0) -#define STATUS_ERR_MASK 0x2f -#define CLK_RATE_PRESCALER(x) ((x) & 0x7) -#define CLK_RATE_CLK_RATE(x) (((x) & 0x7) << 3) -#define CMD_DAT_CONT_CMD_RESP_LONG_OFF (1<<12) -#define CMD_DAT_CONT_STOP_READWAIT (1<<11) -#define CMD_DAT_CONT_START_READWAIT (1<<10) -#define CMD_DAT_CONT_BUS_WIDTH_1 (0<<8) -#define CMD_DAT_CONT_BUS_WIDTH_4 (2<<8) -#define CMD_DAT_CONT_INIT (1<<7) -#define CMD_DAT_CONT_BUSY (1<<6) -#define CMD_DAT_CONT_STREAM_BLOCK (1<<5) -#define CMD_DAT_CONT_WRITE (1<<4) -#define CMD_DAT_CONT_DATA_ENABLE (1<<3) -#define CMD_DAT_CONT_RESPONSE_FORMAT_R1 (1) -#define CMD_DAT_CONT_RESPONSE_FORMAT_R2 (2) -#define CMD_DAT_CONT_RESPONSE_FORMAT_R3 (3) -#define CMD_DAT_CONT_RESPONSE_FORMAT_R4 (4) -#define CMD_DAT_CONT_RESPONSE_FORMAT_R5 (5) -#define CMD_DAT_CONT_RESPONSE_FORMAT_R6 (6) -#define INT_MASK_AUTO_CARD_DETECT (1<<6) -#define INT_MASK_DAT0_EN (1<<5) -#define INT_MASK_SDIO (1<<4) -#define INT_MASK_BUF_READY (1<<3) -#define INT_MASK_END_CMD_RES (1<<2) -#define INT_MASK_WRITE_OP_DONE (1<<1) -#define INT_MASK_DATA_TRAN (1<<0) -#define INT_ALL (0x7f) diff --git a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c deleted file mode 100644 index d11c2d23cee..00000000000 --- a/drivers/mmc/mmci.c +++ /dev/null @@ -1,702 +0,0 @@ -/* - * linux/drivers/mmc/mmci.c - ARM PrimeCell MMCI PL180/1 driver - * - * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "mmci.h" - -#define DRIVER_NAME "mmci-pl18x" - -#define DBG(host,fmt,args...) \ - pr_debug("%s: %s: " fmt, mmc_hostname(host->mmc), __func__ , args) - -static unsigned int fmax = 515633; - -static void -mmci_request_end(struct mmci_host *host, struct mmc_request *mrq) -{ - writel(0, host->base + MMCICOMMAND); - - BUG_ON(host->data); - - host->mrq = NULL; - host->cmd = NULL; - - if (mrq->data) - mrq->data->bytes_xfered = host->data_xfered; - - /* - * Need to drop the host lock here; mmc_request_done may call - * back into the driver... - */ - spin_unlock(&host->lock); - mmc_request_done(host->mmc, mrq); - spin_lock(&host->lock); -} - -static void mmci_stop_data(struct mmci_host *host) -{ - writel(0, host->base + MMCIDATACTRL); - writel(0, host->base + MMCIMASK1); - host->data = NULL; -} - -static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) -{ - unsigned int datactrl, timeout, irqmask; - unsigned long long clks; - void __iomem *base; - int blksz_bits; - - DBG(host, "blksz %04x blks %04x flags %08x\n", - data->blksz, data->blocks, data->flags); - - host->data = data; - host->size = data->blksz; - host->data_xfered = 0; - - mmci_init_sg(host, data); - - clks = (unsigned long long)data->timeout_ns * host->cclk; - do_div(clks, 1000000000UL); - - timeout = data->timeout_clks + (unsigned int)clks; - - base = host->base; - writel(timeout, base + MMCIDATATIMER); - writel(host->size, base + MMCIDATALENGTH); - - blksz_bits = ffs(data->blksz) - 1; - BUG_ON(1 << blksz_bits != data->blksz); - - datactrl = MCI_DPSM_ENABLE | blksz_bits << 4; - if (data->flags & MMC_DATA_READ) { - datactrl |= MCI_DPSM_DIRECTION; - irqmask = MCI_RXFIFOHALFFULLMASK; - - /* - * If we have less than a FIFOSIZE of bytes to transfer, - * trigger a PIO interrupt as soon as any data is available. - */ - if (host->size < MCI_FIFOSIZE) - irqmask |= MCI_RXDATAAVLBLMASK; - } else { - /* - * We don't actually need to include "FIFO empty" here - * since its implicit in "FIFO half empty". - */ - irqmask = MCI_TXFIFOHALFEMPTYMASK; - } - - writel(datactrl, base + MMCIDATACTRL); - writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0); - writel(irqmask, base + MMCIMASK1); -} - -static void -mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) -{ - void __iomem *base = host->base; - - DBG(host, "op %02x arg %08x flags %08x\n", - cmd->opcode, cmd->arg, cmd->flags); - - if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) { - writel(0, base + MMCICOMMAND); - udelay(1); - } - - c |= cmd->opcode | MCI_CPSM_ENABLE; - if (cmd->flags & MMC_RSP_PRESENT) { - if (cmd->flags & MMC_RSP_136) - c |= MCI_CPSM_LONGRSP; - c |= MCI_CPSM_RESPONSE; - } - if (/*interrupt*/0) - c |= MCI_CPSM_INTERRUPT; - - host->cmd = cmd; - - writel(cmd->arg, base + MMCIARGUMENT); - writel(c, base + MMCICOMMAND); -} - -static void -mmci_data_irq(struct mmci_host *host, struct mmc_data *data, - unsigned int status) -{ - if (status & MCI_DATABLOCKEND) { - host->data_xfered += data->blksz; - } - if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) { - if (status & MCI_DATACRCFAIL) - data->error = MMC_ERR_BADCRC; - else if (status & MCI_DATATIMEOUT) - data->error = MMC_ERR_TIMEOUT; - else if (status & (MCI_TXUNDERRUN|MCI_RXOVERRUN)) - data->error = MMC_ERR_FIFO; - status |= MCI_DATAEND; - - /* - * We hit an error condition. Ensure that any data - * partially written to a page is properly coherent. - */ - if (host->sg_len && data->flags & MMC_DATA_READ) - flush_dcache_page(host->sg_ptr->page); - } - if (status & MCI_DATAEND) { - mmci_stop_data(host); - - if (!data->stop) { - mmci_request_end(host, data->mrq); - } else { - mmci_start_command(host, data->stop, 0); - } - } -} - -static void -mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, - unsigned int status) -{ - void __iomem *base = host->base; - - host->cmd = NULL; - - cmd->resp[0] = readl(base + MMCIRESPONSE0); - cmd->resp[1] = readl(base + MMCIRESPONSE1); - cmd->resp[2] = readl(base + MMCIRESPONSE2); - cmd->resp[3] = readl(base + MMCIRESPONSE3); - - if (status & MCI_CMDTIMEOUT) { - cmd->error = MMC_ERR_TIMEOUT; - } else if (status & MCI_CMDCRCFAIL && cmd->flags & MMC_RSP_CRC) { - cmd->error = MMC_ERR_BADCRC; - } - - if (!cmd->data || cmd->error != MMC_ERR_NONE) { - if (host->data) - mmci_stop_data(host); - mmci_request_end(host, cmd->mrq); - } else if (!(cmd->data->flags & MMC_DATA_READ)) { - mmci_start_data(host, cmd->data); - } -} - -static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain) -{ - void __iomem *base = host->base; - char *ptr = buffer; - u32 status; - - do { - int count = host->size - (readl(base + MMCIFIFOCNT) << 2); - - if (count > remain) - count = remain; - - if (count <= 0) - break; - - readsl(base + MMCIFIFO, ptr, count >> 2); - - ptr += count; - remain -= count; - - if (remain == 0) - break; - - status = readl(base + MMCISTATUS); - } while (status & MCI_RXDATAAVLBL); - - return ptr - buffer; -} - -static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int remain, u32 status) -{ - void __iomem *base = host->base; - char *ptr = buffer; - - do { - unsigned int count, maxcnt; - - maxcnt = status & MCI_TXFIFOEMPTY ? MCI_FIFOSIZE : MCI_FIFOHALFSIZE; - count = min(remain, maxcnt); - - writesl(base + MMCIFIFO, ptr, count >> 2); - - ptr += count; - remain -= count; - - if (remain == 0) - break; - - status = readl(base + MMCISTATUS); - } while (status & MCI_TXFIFOHALFEMPTY); - - return ptr - buffer; -} - -/* - * PIO data transfer IRQ handler. - */ -static irqreturn_t mmci_pio_irq(int irq, void *dev_id) -{ - struct mmci_host *host = dev_id; - void __iomem *base = host->base; - u32 status; - - status = readl(base + MMCISTATUS); - - DBG(host, "irq1 %08x\n", status); - - do { - unsigned long flags; - unsigned int remain, len; - char *buffer; - - /* - * For write, we only need to test the half-empty flag - * here - if the FIFO is completely empty, then by - * definition it is more than half empty. - * - * For read, check for data available. - */ - if (!(status & (MCI_TXFIFOHALFEMPTY|MCI_RXDATAAVLBL))) - break; - - /* - * Map the current scatter buffer. - */ - buffer = mmci_kmap_atomic(host, &flags) + host->sg_off; - remain = host->sg_ptr->length - host->sg_off; - - len = 0; - if (status & MCI_RXACTIVE) - len = mmci_pio_read(host, buffer, remain); - if (status & MCI_TXACTIVE) - len = mmci_pio_write(host, buffer, remain, status); - - /* - * Unmap the buffer. - */ - mmci_kunmap_atomic(host, buffer, &flags); - - host->sg_off += len; - host->size -= len; - remain -= len; - - if (remain) - break; - - /* - * If we were reading, and we have completed this - * page, ensure that the data cache is coherent. - */ - if (status & MCI_RXACTIVE) - flush_dcache_page(host->sg_ptr->page); - - if (!mmci_next_sg(host)) - break; - - status = readl(base + MMCISTATUS); - } while (1); - - /* - * If we're nearing the end of the read, switch to - * "any data available" mode. - */ - if (status & MCI_RXACTIVE && host->size < MCI_FIFOSIZE) - writel(MCI_RXDATAAVLBLMASK, base + MMCIMASK1); - - /* - * If we run out of data, disable the data IRQs; this - * prevents a race where the FIFO becomes empty before - * the chip itself has disabled the data path, and - * stops us racing with our data end IRQ. - */ - if (host->size == 0) { - writel(0, base + MMCIMASK1); - writel(readl(base + MMCIMASK0) | MCI_DATAENDMASK, base + MMCIMASK0); - } - - return IRQ_HANDLED; -} - -/* - * Handle completion of command and data transfers. - */ -static irqreturn_t mmci_irq(int irq, void *dev_id) -{ - struct mmci_host *host = dev_id; - u32 status; - int ret = 0; - - spin_lock(&host->lock); - - do { - struct mmc_command *cmd; - struct mmc_data *data; - - status = readl(host->base + MMCISTATUS); - status &= readl(host->base + MMCIMASK0); - writel(status, host->base + MMCICLEAR); - - DBG(host, "irq0 %08x\n", status); - - data = host->data; - if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN| - MCI_RXOVERRUN|MCI_DATAEND|MCI_DATABLOCKEND) && data) - mmci_data_irq(host, data, status); - - cmd = host->cmd; - if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND) && cmd) - mmci_cmd_irq(host, cmd, status); - - ret = 1; - } while (status); - - spin_unlock(&host->lock); - - return IRQ_RETVAL(ret); -} - -static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq) -{ - struct mmci_host *host = mmc_priv(mmc); - - WARN_ON(host->mrq != NULL); - - spin_lock_irq(&host->lock); - - host->mrq = mrq; - - if (mrq->data && mrq->data->flags & MMC_DATA_READ) - mmci_start_data(host, mrq->data); - - mmci_start_command(host, mrq->cmd, 0); - - spin_unlock_irq(&host->lock); -} - -static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -{ - struct mmci_host *host = mmc_priv(mmc); - u32 clk = 0, pwr = 0; - - if (ios->clock) { - if (ios->clock >= host->mclk) { - clk = MCI_CLK_BYPASS; - host->cclk = host->mclk; - } else { - clk = host->mclk / (2 * ios->clock) - 1; - if (clk > 256) - clk = 255; - host->cclk = host->mclk / (2 * (clk + 1)); - } - clk |= MCI_CLK_ENABLE; - } - - if (host->plat->translate_vdd) - pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd); - - switch (ios->power_mode) { - case MMC_POWER_OFF: - break; - case MMC_POWER_UP: - pwr |= MCI_PWR_UP; - break; - case MMC_POWER_ON: - pwr |= MCI_PWR_ON; - break; - } - - if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) - pwr |= MCI_ROD; - - writel(clk, host->base + MMCICLOCK); - - if (host->pwr != pwr) { - host->pwr = pwr; - writel(pwr, host->base + MMCIPOWER); - } -} - -static const struct mmc_host_ops mmci_ops = { - .request = mmci_request, - .set_ios = mmci_set_ios, -}; - -static void mmci_check_status(unsigned long data) -{ - struct mmci_host *host = (struct mmci_host *)data; - unsigned int status; - - status = host->plat->status(mmc_dev(host->mmc)); - if (status ^ host->oldstat) - mmc_detect_change(host->mmc, 0); - - host->oldstat = status; - mod_timer(&host->timer, jiffies + HZ); -} - -static int mmci_probe(struct amba_device *dev, void *id) -{ - struct mmc_platform_data *plat = dev->dev.platform_data; - struct mmci_host *host; - struct mmc_host *mmc; - int ret; - - /* must have platform data */ - if (!plat) { - ret = -EINVAL; - goto out; - } - - ret = amba_request_regions(dev, DRIVER_NAME); - if (ret) - goto out; - - mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev); - if (!mmc) { - ret = -ENOMEM; - goto rel_regions; - } - - host = mmc_priv(mmc); - host->clk = clk_get(&dev->dev, "MCLK"); - if (IS_ERR(host->clk)) { - ret = PTR_ERR(host->clk); - host->clk = NULL; - goto host_free; - } - - ret = clk_enable(host->clk); - if (ret) - goto clk_free; - - host->plat = plat; - host->mclk = clk_get_rate(host->clk); - host->mmc = mmc; - host->base = ioremap(dev->res.start, SZ_4K); - if (!host->base) { - ret = -ENOMEM; - goto clk_disable; - } - - mmc->ops = &mmci_ops; - mmc->f_min = (host->mclk + 511) / 512; - mmc->f_max = min(host->mclk, fmax); - mmc->ocr_avail = plat->ocr_mask; - mmc->caps = MMC_CAP_MULTIWRITE; - - /* - * We can do SGIO - */ - mmc->max_hw_segs = 16; - mmc->max_phys_segs = NR_SG; - - /* - * Since we only have a 16-bit data length register, we must - * ensure that we don't exceed 2^16-1 bytes in a single request. - */ - mmc->max_req_size = 65535; - - /* - * Set the maximum segment size. Since we aren't doing DMA - * (yet) we are only limited by the data length register. - */ - mmc->max_seg_size = mmc->max_req_size; - - /* - * Block size can be up to 2048 bytes, but must be a power of two. - */ - mmc->max_blk_size = 2048; - - /* - * No limit on the number of blocks transferred. - */ - mmc->max_blk_count = mmc->max_req_size; - - spin_lock_init(&host->lock); - - writel(0, host->base + MMCIMASK0); - writel(0, host->base + MMCIMASK1); - writel(0xfff, host->base + MMCICLEAR); - - ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host); - if (ret) - goto unmap; - - ret = request_irq(dev->irq[1], mmci_pio_irq, IRQF_SHARED, DRIVER_NAME " (pio)", host); - if (ret) - goto irq0_free; - - writel(MCI_IRQENABLE, host->base + MMCIMASK0); - - amba_set_drvdata(dev, mmc); - - mmc_add_host(mmc); - - printk(KERN_INFO "%s: MMCI rev %x cfg %02x at 0x%016llx irq %d,%d\n", - mmc_hostname(mmc), amba_rev(dev), amba_config(dev), - (unsigned long long)dev->res.start, dev->irq[0], dev->irq[1]); - - init_timer(&host->timer); - host->timer.data = (unsigned long)host; - host->timer.function = mmci_check_status; - host->timer.expires = jiffies + HZ; - add_timer(&host->timer); - - return 0; - - irq0_free: - free_irq(dev->irq[0], host); - unmap: - iounmap(host->base); - clk_disable: - clk_disable(host->clk); - clk_free: - clk_put(host->clk); - host_free: - mmc_free_host(mmc); - rel_regions: - amba_release_regions(dev); - out: - return ret; -} - -static int mmci_remove(struct amba_device *dev) -{ - struct mmc_host *mmc = amba_get_drvdata(dev); - - amba_set_drvdata(dev, NULL); - - if (mmc) { - struct mmci_host *host = mmc_priv(mmc); - - del_timer_sync(&host->timer); - - mmc_remove_host(mmc); - - writel(0, host->base + MMCIMASK0); - writel(0, host->base + MMCIMASK1); - - writel(0, host->base + MMCICOMMAND); - writel(0, host->base + MMCIDATACTRL); - - free_irq(dev->irq[0], host); - free_irq(dev->irq[1], host); - - iounmap(host->base); - clk_disable(host->clk); - clk_put(host->clk); - - mmc_free_host(mmc); - - amba_release_regions(dev); - } - - return 0; -} - -#ifdef CONFIG_PM -static int mmci_suspend(struct amba_device *dev, pm_message_t state) -{ - struct mmc_host *mmc = amba_get_drvdata(dev); - int ret = 0; - - if (mmc) { - struct mmci_host *host = mmc_priv(mmc); - - ret = mmc_suspend_host(mmc, state); - if (ret == 0) - writel(0, host->base + MMCIMASK0); - } - - return ret; -} - -static int mmci_resume(struct amba_device *dev) -{ - struct mmc_host *mmc = amba_get_drvdata(dev); - int ret = 0; - - if (mmc) { - struct mmci_host *host = mmc_priv(mmc); - - writel(MCI_IRQENABLE, host->base + MMCIMASK0); - - ret = mmc_resume_host(mmc); - } - - return ret; -} -#else -#define mmci_suspend NULL -#define mmci_resume NULL -#endif - -static struct amba_id mmci_ids[] = { - { - .id = 0x00041180, - .mask = 0x000fffff, - }, - { - .id = 0x00041181, - .mask = 0x000fffff, - }, - { 0, 0 }, -}; - -static struct amba_driver mmci_driver = { - .drv = { - .name = DRIVER_NAME, - }, - .probe = mmci_probe, - .remove = mmci_remove, - .suspend = mmci_suspend, - .resume = mmci_resume, - .id_table = mmci_ids, -}; - -static int __init mmci_init(void) -{ - return amba_driver_register(&mmci_driver); -} - -static void __exit mmci_exit(void) -{ - amba_driver_unregister(&mmci_driver); -} - -module_init(mmci_init); -module_exit(mmci_exit); -module_param(fmax, uint, 0444); - -MODULE_DESCRIPTION("ARM PrimeCell PL180/181 Multimedia Card Interface driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/mmci.h b/drivers/mmc/mmci.h deleted file mode 100644 index 6d7eadc9a67..00000000000 --- a/drivers/mmc/mmci.h +++ /dev/null @@ -1,179 +0,0 @@ -/* - * linux/drivers/mmc/mmci.h - ARM PrimeCell MMCI PL180/1 driver - * - * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#define MMCIPOWER 0x000 -#define MCI_PWR_OFF 0x00 -#define MCI_PWR_UP 0x02 -#define MCI_PWR_ON 0x03 -#define MCI_OD (1 << 6) -#define MCI_ROD (1 << 7) - -#define MMCICLOCK 0x004 -#define MCI_CLK_ENABLE (1 << 8) -#define MCI_CLK_PWRSAVE (1 << 9) -#define MCI_CLK_BYPASS (1 << 10) - -#define MMCIARGUMENT 0x008 -#define MMCICOMMAND 0x00c -#define MCI_CPSM_RESPONSE (1 << 6) -#define MCI_CPSM_LONGRSP (1 << 7) -#define MCI_CPSM_INTERRUPT (1 << 8) -#define MCI_CPSM_PENDING (1 << 9) -#define MCI_CPSM_ENABLE (1 << 10) - -#define MMCIRESPCMD 0x010 -#define MMCIRESPONSE0 0x014 -#define MMCIRESPONSE1 0x018 -#define MMCIRESPONSE2 0x01c -#define MMCIRESPONSE3 0x020 -#define MMCIDATATIMER 0x024 -#define MMCIDATALENGTH 0x028 -#define MMCIDATACTRL 0x02c -#define MCI_DPSM_ENABLE (1 << 0) -#define MCI_DPSM_DIRECTION (1 << 1) -#define MCI_DPSM_MODE (1 << 2) -#define MCI_DPSM_DMAENABLE (1 << 3) - -#define MMCIDATACNT 0x030 -#define MMCISTATUS 0x034 -#define MCI_CMDCRCFAIL (1 << 0) -#define MCI_DATACRCFAIL (1 << 1) -#define MCI_CMDTIMEOUT (1 << 2) -#define MCI_DATATIMEOUT (1 << 3) -#define MCI_TXUNDERRUN (1 << 4) -#define MCI_RXOVERRUN (1 << 5) -#define MCI_CMDRESPEND (1 << 6) -#define MCI_CMDSENT (1 << 7) -#define MCI_DATAEND (1 << 8) -#define MCI_DATABLOCKEND (1 << 10) -#define MCI_CMDACTIVE (1 << 11) -#define MCI_TXACTIVE (1 << 12) -#define MCI_RXACTIVE (1 << 13) -#define MCI_TXFIFOHALFEMPTY (1 << 14) -#define MCI_RXFIFOHALFFULL (1 << 15) -#define MCI_TXFIFOFULL (1 << 16) -#define MCI_RXFIFOFULL (1 << 17) -#define MCI_TXFIFOEMPTY (1 << 18) -#define MCI_RXFIFOEMPTY (1 << 19) -#define MCI_TXDATAAVLBL (1 << 20) -#define MCI_RXDATAAVLBL (1 << 21) - -#define MMCICLEAR 0x038 -#define MCI_CMDCRCFAILCLR (1 << 0) -#define MCI_DATACRCFAILCLR (1 << 1) -#define MCI_CMDTIMEOUTCLR (1 << 2) -#define MCI_DATATIMEOUTCLR (1 << 3) -#define MCI_TXUNDERRUNCLR (1 << 4) -#define MCI_RXOVERRUNCLR (1 << 5) -#define MCI_CMDRESPENDCLR (1 << 6) -#define MCI_CMDSENTCLR (1 << 7) -#define MCI_DATAENDCLR (1 << 8) -#define MCI_DATABLOCKENDCLR (1 << 10) - -#define MMCIMASK0 0x03c -#define MCI_CMDCRCFAILMASK (1 << 0) -#define MCI_DATACRCFAILMASK (1 << 1) -#define MCI_CMDTIMEOUTMASK (1 << 2) -#define MCI_DATATIMEOUTMASK (1 << 3) -#define MCI_TXUNDERRUNMASK (1 << 4) -#define MCI_RXOVERRUNMASK (1 << 5) -#define MCI_CMDRESPENDMASK (1 << 6) -#define MCI_CMDSENTMASK (1 << 7) -#define MCI_DATAENDMASK (1 << 8) -#define MCI_DATABLOCKENDMASK (1 << 10) -#define MCI_CMDACTIVEMASK (1 << 11) -#define MCI_TXACTIVEMASK (1 << 12) -#define MCI_RXACTIVEMASK (1 << 13) -#define MCI_TXFIFOHALFEMPTYMASK (1 << 14) -#define MCI_RXFIFOHALFFULLMASK (1 << 15) -#define MCI_TXFIFOFULLMASK (1 << 16) -#define MCI_RXFIFOFULLMASK (1 << 17) -#define MCI_TXFIFOEMPTYMASK (1 << 18) -#define MCI_RXFIFOEMPTYMASK (1 << 19) -#define MCI_TXDATAAVLBLMASK (1 << 20) -#define MCI_RXDATAAVLBLMASK (1 << 21) - -#define MMCIMASK1 0x040 -#define MMCIFIFOCNT 0x048 -#define MMCIFIFO 0x080 /* to 0x0bc */ - -#define MCI_IRQENABLE \ - (MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK| \ - MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK| \ - MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_DATABLOCKENDMASK) - -/* - * The size of the FIFO in bytes. - */ -#define MCI_FIFOSIZE (16*4) - -#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2) - -#define NR_SG 16 - -struct clk; - -struct mmci_host { - void __iomem *base; - struct mmc_request *mrq; - struct mmc_command *cmd; - struct mmc_data *data; - struct mmc_host *mmc; - struct clk *clk; - - unsigned int data_xfered; - - spinlock_t lock; - - unsigned int mclk; - unsigned int cclk; - u32 pwr; - struct mmc_platform_data *plat; - - struct timer_list timer; - unsigned int oldstat; - - unsigned int sg_len; - - /* pio stuff */ - struct scatterlist *sg_ptr; - unsigned int sg_off; - unsigned int size; -}; - -static inline void mmci_init_sg(struct mmci_host *host, struct mmc_data *data) -{ - /* - * Ideally, we want the higher levels to pass us a scatter list. - */ - host->sg_len = data->sg_len; - host->sg_ptr = data->sg; - host->sg_off = 0; -} - -static inline int mmci_next_sg(struct mmci_host *host) -{ - host->sg_ptr++; - host->sg_off = 0; - return --host->sg_len; -} - -static inline char *mmci_kmap_atomic(struct mmci_host *host, unsigned long *flags) -{ - struct scatterlist *sg = host->sg_ptr; - - local_irq_save(*flags); - return kmap_atomic(sg->page, KM_BIO_SRC_IRQ) + sg->offset; -} - -static inline void mmci_kunmap_atomic(struct mmci_host *host, void *buffer, unsigned long *flags) -{ - kunmap_atomic(buffer, KM_BIO_SRC_IRQ); - local_irq_restore(*flags); -} diff --git a/drivers/mmc/omap.c b/drivers/mmc/omap.c deleted file mode 100644 index e851384e51f..00000000000 --- a/drivers/mmc/omap.c +++ /dev/null @@ -1,1288 +0,0 @@ -/* - * linux/drivers/media/mmc/omap.c - * - * Copyright (C) 2004 Nokia Corporation - * Written by Tuukka Tikkanen and Juha Yrjölä - * Misc hacks here and there by Tony Lindgren - * Other hacks (DMA, SD, etc) by David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#define OMAP_MMC_REG_CMD 0x00 -#define OMAP_MMC_REG_ARGL 0x04 -#define OMAP_MMC_REG_ARGH 0x08 -#define OMAP_MMC_REG_CON 0x0c -#define OMAP_MMC_REG_STAT 0x10 -#define OMAP_MMC_REG_IE 0x14 -#define OMAP_MMC_REG_CTO 0x18 -#define OMAP_MMC_REG_DTO 0x1c -#define OMAP_MMC_REG_DATA 0x20 -#define OMAP_MMC_REG_BLEN 0x24 -#define OMAP_MMC_REG_NBLK 0x28 -#define OMAP_MMC_REG_BUF 0x2c -#define OMAP_MMC_REG_SDIO 0x34 -#define OMAP_MMC_REG_REV 0x3c -#define OMAP_MMC_REG_RSP0 0x40 -#define OMAP_MMC_REG_RSP1 0x44 -#define OMAP_MMC_REG_RSP2 0x48 -#define OMAP_MMC_REG_RSP3 0x4c -#define OMAP_MMC_REG_RSP4 0x50 -#define OMAP_MMC_REG_RSP5 0x54 -#define OMAP_MMC_REG_RSP6 0x58 -#define OMAP_MMC_REG_RSP7 0x5c -#define OMAP_MMC_REG_IOSR 0x60 -#define OMAP_MMC_REG_SYSC 0x64 -#define OMAP_MMC_REG_SYSS 0x68 - -#define OMAP_MMC_STAT_CARD_ERR (1 << 14) -#define OMAP_MMC_STAT_CARD_IRQ (1 << 13) -#define OMAP_MMC_STAT_OCR_BUSY (1 << 12) -#define OMAP_MMC_STAT_A_EMPTY (1 << 11) -#define OMAP_MMC_STAT_A_FULL (1 << 10) -#define OMAP_MMC_STAT_CMD_CRC (1 << 8) -#define OMAP_MMC_STAT_CMD_TOUT (1 << 7) -#define OMAP_MMC_STAT_DATA_CRC (1 << 6) -#define OMAP_MMC_STAT_DATA_TOUT (1 << 5) -#define OMAP_MMC_STAT_END_BUSY (1 << 4) -#define OMAP_MMC_STAT_END_OF_DATA (1 << 3) -#define OMAP_MMC_STAT_CARD_BUSY (1 << 2) -#define OMAP_MMC_STAT_END_OF_CMD (1 << 0) - -#define OMAP_MMC_READ(host, reg) __raw_readw((host)->virt_base + OMAP_MMC_REG_##reg) -#define OMAP_MMC_WRITE(host, reg, val) __raw_writew((val), (host)->virt_base + OMAP_MMC_REG_##reg) - -/* - * Command types - */ -#define OMAP_MMC_CMDTYPE_BC 0 -#define OMAP_MMC_CMDTYPE_BCR 1 -#define OMAP_MMC_CMDTYPE_AC 2 -#define OMAP_MMC_CMDTYPE_ADTC 3 - - -#define DRIVER_NAME "mmci-omap" - -/* Specifies how often in millisecs to poll for card status changes - * when the cover switch is open */ -#define OMAP_MMC_SWITCH_POLL_DELAY 500 - -static int mmc_omap_enable_poll = 1; - -struct mmc_omap_host { - int initialized; - int suspended; - struct mmc_request * mrq; - struct mmc_command * cmd; - struct mmc_data * data; - struct mmc_host * mmc; - struct device * dev; - unsigned char id; /* 16xx chips have 2 MMC blocks */ - struct clk * iclk; - struct clk * fclk; - struct resource *mem_res; - void __iomem *virt_base; - unsigned int phys_base; - int irq; - unsigned char bus_mode; - unsigned char hw_bus_mode; - - unsigned int sg_len; - int sg_idx; - u16 * buffer; - u32 buffer_bytes_left; - u32 total_bytes_left; - - unsigned use_dma:1; - unsigned brs_received:1, dma_done:1; - unsigned dma_is_read:1; - unsigned dma_in_use:1; - int dma_ch; - spinlock_t dma_lock; - struct timer_list dma_timer; - unsigned dma_len; - - short power_pin; - short wp_pin; - - int switch_pin; - struct work_struct switch_work; - struct timer_list switch_timer; - int switch_last_state; -}; - -static inline int -mmc_omap_cover_is_open(struct mmc_omap_host *host) -{ - if (host->switch_pin < 0) - return 0; - return omap_get_gpio_datain(host->switch_pin); -} - -static ssize_t -mmc_omap_show_cover_switch(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct mmc_omap_host *host = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", mmc_omap_cover_is_open(host) ? "open" : - "closed"); -} - -static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL); - -static ssize_t -mmc_omap_show_enable_poll(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return snprintf(buf, PAGE_SIZE, "%d\n", mmc_omap_enable_poll); -} - -static ssize_t -mmc_omap_store_enable_poll(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t size) -{ - int enable_poll; - - if (sscanf(buf, "%10d", &enable_poll) != 1) - return -EINVAL; - - if (enable_poll != mmc_omap_enable_poll) { - struct mmc_omap_host *host = dev_get_drvdata(dev); - - mmc_omap_enable_poll = enable_poll; - if (enable_poll && host->switch_pin >= 0) - schedule_work(&host->switch_work); - } - return size; -} - -static DEVICE_ATTR(enable_poll, 0664, - mmc_omap_show_enable_poll, mmc_omap_store_enable_poll); - -static void -mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) -{ - u32 cmdreg; - u32 resptype; - u32 cmdtype; - - host->cmd = cmd; - - resptype = 0; - cmdtype = 0; - - /* Our hardware needs to know exact type */ - switch (mmc_resp_type(cmd)) { - case MMC_RSP_NONE: - break; - case MMC_RSP_R1: - case MMC_RSP_R1B: - /* resp 1, 1b, 6, 7 */ - resptype = 1; - break; - case MMC_RSP_R2: - resptype = 2; - break; - case MMC_RSP_R3: - resptype = 3; - break; - default: - dev_err(mmc_dev(host->mmc), "Invalid response type: %04x\n", mmc_resp_type(cmd)); - break; - } - - if (mmc_cmd_type(cmd) == MMC_CMD_ADTC) { - cmdtype = OMAP_MMC_CMDTYPE_ADTC; - } else if (mmc_cmd_type(cmd) == MMC_CMD_BC) { - cmdtype = OMAP_MMC_CMDTYPE_BC; - } else if (mmc_cmd_type(cmd) == MMC_CMD_BCR) { - cmdtype = OMAP_MMC_CMDTYPE_BCR; - } else { - cmdtype = OMAP_MMC_CMDTYPE_AC; - } - - cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12); - - if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) - cmdreg |= 1 << 6; - - if (cmd->flags & MMC_RSP_BUSY) - cmdreg |= 1 << 11; - - if (host->data && !(host->data->flags & MMC_DATA_WRITE)) - cmdreg |= 1 << 15; - - clk_enable(host->fclk); - - OMAP_MMC_WRITE(host, CTO, 200); - OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff); - OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16); - OMAP_MMC_WRITE(host, IE, - OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL | - OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT | - OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT | - OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR | - OMAP_MMC_STAT_END_OF_DATA); - OMAP_MMC_WRITE(host, CMD, cmdreg); -} - -static void -mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) -{ - if (host->dma_in_use) { - enum dma_data_direction dma_data_dir; - - BUG_ON(host->dma_ch < 0); - if (data->error != MMC_ERR_NONE) - omap_stop_dma(host->dma_ch); - /* Release DMA channel lazily */ - mod_timer(&host->dma_timer, jiffies + HZ); - if (data->flags & MMC_DATA_WRITE) - dma_data_dir = DMA_TO_DEVICE; - else - dma_data_dir = DMA_FROM_DEVICE; - dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len, - dma_data_dir); - } - host->data = NULL; - host->sg_len = 0; - clk_disable(host->fclk); - - /* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing - * dozens of requests until the card finishes writing data. - * It'd be cheaper to just wait till an EOFB interrupt arrives... - */ - - if (!data->stop) { - host->mrq = NULL; - mmc_request_done(host->mmc, data->mrq); - return; - } - - mmc_omap_start_command(host, data->stop); -} - -static void -mmc_omap_end_of_data(struct mmc_omap_host *host, struct mmc_data *data) -{ - unsigned long flags; - int done; - - if (!host->dma_in_use) { - mmc_omap_xfer_done(host, data); - return; - } - done = 0; - spin_lock_irqsave(&host->dma_lock, flags); - if (host->dma_done) - done = 1; - else - host->brs_received = 1; - spin_unlock_irqrestore(&host->dma_lock, flags); - if (done) - mmc_omap_xfer_done(host, data); -} - -static void -mmc_omap_dma_timer(unsigned long data) -{ - struct mmc_omap_host *host = (struct mmc_omap_host *) data; - - BUG_ON(host->dma_ch < 0); - omap_free_dma(host->dma_ch); - host->dma_ch = -1; -} - -static void -mmc_omap_dma_done(struct mmc_omap_host *host, struct mmc_data *data) -{ - unsigned long flags; - int done; - - done = 0; - spin_lock_irqsave(&host->dma_lock, flags); - if (host->brs_received) - done = 1; - else - host->dma_done = 1; - spin_unlock_irqrestore(&host->dma_lock, flags); - if (done) - mmc_omap_xfer_done(host, data); -} - -static void -mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) -{ - host->cmd = NULL; - - if (cmd->flags & MMC_RSP_PRESENT) { - if (cmd->flags & MMC_RSP_136) { - /* response type 2 */ - cmd->resp[3] = - OMAP_MMC_READ(host, RSP0) | - (OMAP_MMC_READ(host, RSP1) << 16); - cmd->resp[2] = - OMAP_MMC_READ(host, RSP2) | - (OMAP_MMC_READ(host, RSP3) << 16); - cmd->resp[1] = - OMAP_MMC_READ(host, RSP4) | - (OMAP_MMC_READ(host, RSP5) << 16); - cmd->resp[0] = - OMAP_MMC_READ(host, RSP6) | - (OMAP_MMC_READ(host, RSP7) << 16); - } else { - /* response types 1, 1b, 3, 4, 5, 6 */ - cmd->resp[0] = - OMAP_MMC_READ(host, RSP6) | - (OMAP_MMC_READ(host, RSP7) << 16); - } - } - - if (host->data == NULL || cmd->error != MMC_ERR_NONE) { - host->mrq = NULL; - clk_disable(host->fclk); - mmc_request_done(host->mmc, cmd->mrq); - } -} - -/* PIO only */ -static void -mmc_omap_sg_to_buf(struct mmc_omap_host *host) -{ - struct scatterlist *sg; - - sg = host->data->sg + host->sg_idx; - host->buffer_bytes_left = sg->length; - host->buffer = page_address(sg->page) + sg->offset; - if (host->buffer_bytes_left > host->total_bytes_left) - host->buffer_bytes_left = host->total_bytes_left; -} - -/* PIO only */ -static void -mmc_omap_xfer_data(struct mmc_omap_host *host, int write) -{ - int n; - - if (host->buffer_bytes_left == 0) { - host->sg_idx++; - BUG_ON(host->sg_idx == host->sg_len); - mmc_omap_sg_to_buf(host); - } - n = 64; - if (n > host->buffer_bytes_left) - n = host->buffer_bytes_left; - host->buffer_bytes_left -= n; - host->total_bytes_left -= n; - host->data->bytes_xfered += n; - - if (write) { - __raw_writesw(host->virt_base + OMAP_MMC_REG_DATA, host->buffer, n); - } else { - __raw_readsw(host->virt_base + OMAP_MMC_REG_DATA, host->buffer, n); - } -} - -static inline void mmc_omap_report_irq(u16 status) -{ - static const char *mmc_omap_status_bits[] = { - "EOC", "CD", "CB", "BRS", "EOFB", "DTO", "DCRC", "CTO", - "CCRC", "CRW", "AF", "AE", "OCRB", "CIRQ", "CERR" - }; - int i, c = 0; - - for (i = 0; i < ARRAY_SIZE(mmc_omap_status_bits); i++) - if (status & (1 << i)) { - if (c) - printk(" "); - printk("%s", mmc_omap_status_bits[i]); - c++; - } -} - -static irqreturn_t mmc_omap_irq(int irq, void *dev_id) -{ - struct mmc_omap_host * host = (struct mmc_omap_host *)dev_id; - u16 status; - int end_command; - int end_transfer; - int transfer_error; - - if (host->cmd == NULL && host->data == NULL) { - status = OMAP_MMC_READ(host, STAT); - dev_info(mmc_dev(host->mmc),"spurious irq 0x%04x\n", status); - if (status != 0) { - OMAP_MMC_WRITE(host, STAT, status); - OMAP_MMC_WRITE(host, IE, 0); - } - return IRQ_HANDLED; - } - - end_command = 0; - end_transfer = 0; - transfer_error = 0; - - while ((status = OMAP_MMC_READ(host, STAT)) != 0) { - OMAP_MMC_WRITE(host, STAT, status); -#ifdef CONFIG_MMC_DEBUG - dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ", - status, host->cmd != NULL ? host->cmd->opcode : -1); - mmc_omap_report_irq(status); - printk("\n"); -#endif - if (host->total_bytes_left) { - if ((status & OMAP_MMC_STAT_A_FULL) || - (status & OMAP_MMC_STAT_END_OF_DATA)) - mmc_omap_xfer_data(host, 0); - if (status & OMAP_MMC_STAT_A_EMPTY) - mmc_omap_xfer_data(host, 1); - } - - if (status & OMAP_MMC_STAT_END_OF_DATA) { - end_transfer = 1; - } - - if (status & OMAP_MMC_STAT_DATA_TOUT) { - dev_dbg(mmc_dev(host->mmc), "data timeout\n"); - if (host->data) { - host->data->error |= MMC_ERR_TIMEOUT; - transfer_error = 1; - } - } - - if (status & OMAP_MMC_STAT_DATA_CRC) { - if (host->data) { - host->data->error |= MMC_ERR_BADCRC; - dev_dbg(mmc_dev(host->mmc), - "data CRC error, bytes left %d\n", - host->total_bytes_left); - transfer_error = 1; - } else { - dev_dbg(mmc_dev(host->mmc), "data CRC error\n"); - } - } - - if (status & OMAP_MMC_STAT_CMD_TOUT) { - /* Timeouts are routine with some commands */ - if (host->cmd) { - if (host->cmd->opcode != MMC_ALL_SEND_CID && - host->cmd->opcode != - MMC_SEND_OP_COND && - host->cmd->opcode != - MMC_APP_CMD && - !mmc_omap_cover_is_open(host)) - dev_err(mmc_dev(host->mmc), - "command timeout, CMD %d\n", - host->cmd->opcode); - host->cmd->error = MMC_ERR_TIMEOUT; - end_command = 1; - } - } - - if (status & OMAP_MMC_STAT_CMD_CRC) { - if (host->cmd) { - dev_err(mmc_dev(host->mmc), - "command CRC error (CMD%d, arg 0x%08x)\n", - host->cmd->opcode, host->cmd->arg); - host->cmd->error = MMC_ERR_BADCRC; - end_command = 1; - } else - dev_err(mmc_dev(host->mmc), - "command CRC error without cmd?\n"); - } - - if (status & OMAP_MMC_STAT_CARD_ERR) { - if (host->cmd && host->cmd->opcode == MMC_STOP_TRANSMISSION) { - u32 response = OMAP_MMC_READ(host, RSP6) - | (OMAP_MMC_READ(host, RSP7) << 16); - /* STOP sometimes sets must-ignore bits */ - if (!(response & (R1_CC_ERROR - | R1_ILLEGAL_COMMAND - | R1_COM_CRC_ERROR))) { - end_command = 1; - continue; - } - } - - dev_dbg(mmc_dev(host->mmc), "card status error (CMD%d)\n", - host->cmd->opcode); - if (host->cmd) { - host->cmd->error = MMC_ERR_FAILED; - end_command = 1; - } - if (host->data) { - host->data->error = MMC_ERR_FAILED; - transfer_error = 1; - } - } - - /* - * NOTE: On 1610 the END_OF_CMD may come too early when - * starting a write - */ - if ((status & OMAP_MMC_STAT_END_OF_CMD) && - (!(status & OMAP_MMC_STAT_A_EMPTY))) { - end_command = 1; - } - } - - if (end_command) { - mmc_omap_cmd_done(host, host->cmd); - } - if (transfer_error) - mmc_omap_xfer_done(host, host->data); - else if (end_transfer) - mmc_omap_end_of_data(host, host->data); - - return IRQ_HANDLED; -} - -static irqreturn_t mmc_omap_switch_irq(int irq, void *dev_id) -{ - struct mmc_omap_host *host = (struct mmc_omap_host *) dev_id; - - schedule_work(&host->switch_work); - - return IRQ_HANDLED; -} - -static void mmc_omap_switch_timer(unsigned long arg) -{ - struct mmc_omap_host *host = (struct mmc_omap_host *) arg; - - schedule_work(&host->switch_work); -} - -static void mmc_omap_switch_handler(struct work_struct *work) -{ - struct mmc_omap_host *host = container_of(work, struct mmc_omap_host, switch_work); - struct mmc_card *card; - static int complained = 0; - int cards = 0, cover_open; - - if (host->switch_pin == -1) - return; - cover_open = mmc_omap_cover_is_open(host); - if (cover_open != host->switch_last_state) { - kobject_uevent(&host->dev->kobj, KOBJ_CHANGE); - host->switch_last_state = cover_open; - } - mmc_detect_change(host->mmc, 0); - list_for_each_entry(card, &host->mmc->cards, node) { - if (mmc_card_present(card)) - cards++; - } - if (mmc_omap_cover_is_open(host)) { - if (!complained) { - dev_info(mmc_dev(host->mmc), "cover is open"); - complained = 1; - } - if (mmc_omap_enable_poll) - mod_timer(&host->switch_timer, jiffies + - msecs_to_jiffies(OMAP_MMC_SWITCH_POLL_DELAY)); - } else { - complained = 0; - } -} - -/* Prepare to transfer the next segment of a scatterlist */ -static void -mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data) -{ - int dma_ch = host->dma_ch; - unsigned long data_addr; - u16 buf, frame; - u32 count; - struct scatterlist *sg = &data->sg[host->sg_idx]; - int src_port = 0; - int dst_port = 0; - int sync_dev = 0; - - data_addr = host->phys_base + OMAP_MMC_REG_DATA; - frame = data->blksz; - count = sg_dma_len(sg); - - if ((data->blocks == 1) && (count > data->blksz)) - count = frame; - - host->dma_len = count; - - /* FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx and 24xx. - * Use 16 or 32 word frames when the blocksize is at least that large. - * Blocksize is usually 512 bytes; but not for some SD reads. - */ - if (cpu_is_omap15xx() && frame > 32) - frame = 32; - else if (frame > 64) - frame = 64; - count /= frame; - frame >>= 1; - - if (!(data->flags & MMC_DATA_WRITE)) { - buf = 0x800f | ((frame - 1) << 8); - - if (cpu_class_is_omap1()) { - src_port = OMAP_DMA_PORT_TIPB; - dst_port = OMAP_DMA_PORT_EMIFF; - } - if (cpu_is_omap24xx()) - sync_dev = OMAP24XX_DMA_MMC1_RX; - - omap_set_dma_src_params(dma_ch, src_port, - OMAP_DMA_AMODE_CONSTANT, - data_addr, 0, 0); - omap_set_dma_dest_params(dma_ch, dst_port, - OMAP_DMA_AMODE_POST_INC, - sg_dma_address(sg), 0, 0); - omap_set_dma_dest_data_pack(dma_ch, 1); - omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4); - } else { - buf = 0x0f80 | ((frame - 1) << 0); - - if (cpu_class_is_omap1()) { - src_port = OMAP_DMA_PORT_EMIFF; - dst_port = OMAP_DMA_PORT_TIPB; - } - if (cpu_is_omap24xx()) - sync_dev = OMAP24XX_DMA_MMC1_TX; - - omap_set_dma_dest_params(dma_ch, dst_port, - OMAP_DMA_AMODE_CONSTANT, - data_addr, 0, 0); - omap_set_dma_src_params(dma_ch, src_port, - OMAP_DMA_AMODE_POST_INC, - sg_dma_address(sg), 0, 0); - omap_set_dma_src_data_pack(dma_ch, 1); - omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4); - } - - /* Max limit for DMA frame count is 0xffff */ - BUG_ON(count > 0xffff); - - OMAP_MMC_WRITE(host, BUF, buf); - omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S16, - frame, count, OMAP_DMA_SYNC_FRAME, - sync_dev, 0); -} - -/* A scatterlist segment completed */ -static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data) -{ - struct mmc_omap_host *host = (struct mmc_omap_host *) data; - struct mmc_data *mmcdat = host->data; - - if (unlikely(host->dma_ch < 0)) { - dev_err(mmc_dev(host->mmc), - "DMA callback while DMA not enabled\n"); - return; - } - /* FIXME: We really should do something to _handle_ the errors */ - if (ch_status & OMAP1_DMA_TOUT_IRQ) { - dev_err(mmc_dev(host->mmc),"DMA timeout\n"); - return; - } - if (ch_status & OMAP_DMA_DROP_IRQ) { - dev_err(mmc_dev(host->mmc), "DMA sync error\n"); - return; - } - if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) { - return; - } - mmcdat->bytes_xfered += host->dma_len; - host->sg_idx++; - if (host->sg_idx < host->sg_len) { - mmc_omap_prepare_dma(host, host->data); - omap_start_dma(host->dma_ch); - } else - mmc_omap_dma_done(host, host->data); -} - -static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data *data) -{ - const char *dev_name; - int sync_dev, dma_ch, is_read, r; - - is_read = !(data->flags & MMC_DATA_WRITE); - del_timer_sync(&host->dma_timer); - if (host->dma_ch >= 0) { - if (is_read == host->dma_is_read) - return 0; - omap_free_dma(host->dma_ch); - host->dma_ch = -1; - } - - if (is_read) { - if (host->id == 1) { - sync_dev = OMAP_DMA_MMC_RX; - dev_name = "MMC1 read"; - } else { - sync_dev = OMAP_DMA_MMC2_RX; - dev_name = "MMC2 read"; - } - } else { - if (host->id == 1) { - sync_dev = OMAP_DMA_MMC_TX; - dev_name = "MMC1 write"; - } else { - sync_dev = OMAP_DMA_MMC2_TX; - dev_name = "MMC2 write"; - } - } - r = omap_request_dma(sync_dev, dev_name, mmc_omap_dma_cb, - host, &dma_ch); - if (r != 0) { - dev_dbg(mmc_dev(host->mmc), "omap_request_dma() failed with %d\n", r); - return r; - } - host->dma_ch = dma_ch; - host->dma_is_read = is_read; - - return 0; -} - -static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_request *req) -{ - u16 reg; - - reg = OMAP_MMC_READ(host, SDIO); - reg &= ~(1 << 5); - OMAP_MMC_WRITE(host, SDIO, reg); - /* Set maximum timeout */ - OMAP_MMC_WRITE(host, CTO, 0xff); -} - -static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req) -{ - int timeout; - u16 reg; - - /* Convert ns to clock cycles by assuming 20MHz frequency - * 1 cycle at 20MHz = 500 ns - */ - timeout = req->data->timeout_clks + req->data->timeout_ns / 500; - - /* Check if we need to use timeout multiplier register */ - reg = OMAP_MMC_READ(host, SDIO); - if (timeout > 0xffff) { - reg |= (1 << 5); - timeout /= 1024; - } else - reg &= ~(1 << 5); - OMAP_MMC_WRITE(host, SDIO, reg); - OMAP_MMC_WRITE(host, DTO, timeout); -} - -static void -mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) -{ - struct mmc_data *data = req->data; - int i, use_dma, block_size; - unsigned sg_len; - - host->data = data; - if (data == NULL) { - OMAP_MMC_WRITE(host, BLEN, 0); - OMAP_MMC_WRITE(host, NBLK, 0); - OMAP_MMC_WRITE(host, BUF, 0); - host->dma_in_use = 0; - set_cmd_timeout(host, req); - return; - } - - block_size = data->blksz; - - OMAP_MMC_WRITE(host, NBLK, data->blocks - 1); - OMAP_MMC_WRITE(host, BLEN, block_size - 1); - set_data_timeout(host, req); - - /* cope with calling layer confusion; it issues "single - * block" writes using multi-block scatterlists. - */ - sg_len = (data->blocks == 1) ? 1 : data->sg_len; - - /* Only do DMA for entire blocks */ - use_dma = host->use_dma; - if (use_dma) { - for (i = 0; i < sg_len; i++) { - if ((data->sg[i].length % block_size) != 0) { - use_dma = 0; - break; - } - } - } - - host->sg_idx = 0; - if (use_dma) { - if (mmc_omap_get_dma_channel(host, data) == 0) { - enum dma_data_direction dma_data_dir; - - if (data->flags & MMC_DATA_WRITE) - dma_data_dir = DMA_TO_DEVICE; - else - dma_data_dir = DMA_FROM_DEVICE; - - host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, - sg_len, dma_data_dir); - host->total_bytes_left = 0; - mmc_omap_prepare_dma(host, req->data); - host->brs_received = 0; - host->dma_done = 0; - host->dma_in_use = 1; - } else - use_dma = 0; - } - - /* Revert to PIO? */ - if (!use_dma) { - OMAP_MMC_WRITE(host, BUF, 0x1f1f); - host->total_bytes_left = data->blocks * block_size; - host->sg_len = sg_len; - mmc_omap_sg_to_buf(host); - host->dma_in_use = 0; - } -} - -static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) -{ - struct mmc_omap_host *host = mmc_priv(mmc); - - WARN_ON(host->mrq != NULL); - - host->mrq = req; - - /* only touch fifo AFTER the controller readies it */ - mmc_omap_prepare_data(host, req); - mmc_omap_start_command(host, req->cmd); - if (host->dma_in_use) - omap_start_dma(host->dma_ch); -} - -static void innovator_fpga_socket_power(int on) -{ -#if defined(CONFIG_MACH_OMAP_INNOVATOR) && defined(CONFIG_ARCH_OMAP15XX) - if (on) { - fpga_write(fpga_read(OMAP1510_FPGA_POWER) | (1 << 3), - OMAP1510_FPGA_POWER); - } else { - fpga_write(fpga_read(OMAP1510_FPGA_POWER) & ~(1 << 3), - OMAP1510_FPGA_POWER); - } -#endif -} - -/* - * Turn the socket power on/off. Innovator uses FPGA, most boards - * probably use GPIO. - */ -static void mmc_omap_power(struct mmc_omap_host *host, int on) -{ - if (on) { - if (machine_is_omap_innovator()) - innovator_fpga_socket_power(1); - else if (machine_is_omap_h2()) - tps65010_set_gpio_out_value(GPIO3, HIGH); - else if (machine_is_omap_h3()) - /* GPIO 4 of TPS65010 sends SD_EN signal */ - tps65010_set_gpio_out_value(GPIO4, HIGH); - else if (cpu_is_omap24xx()) { - u16 reg = OMAP_MMC_READ(host, CON); - OMAP_MMC_WRITE(host, CON, reg | (1 << 11)); - } else - if (host->power_pin >= 0) - omap_set_gpio_dataout(host->power_pin, 1); - } else { - if (machine_is_omap_innovator()) - innovator_fpga_socket_power(0); - else if (machine_is_omap_h2()) - tps65010_set_gpio_out_value(GPIO3, LOW); - else if (machine_is_omap_h3()) - tps65010_set_gpio_out_value(GPIO4, LOW); - else if (cpu_is_omap24xx()) { - u16 reg = OMAP_MMC_READ(host, CON); - OMAP_MMC_WRITE(host, CON, reg & ~(1 << 11)); - } else - if (host->power_pin >= 0) - omap_set_gpio_dataout(host->power_pin, 0); - } -} - -static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -{ - struct mmc_omap_host *host = mmc_priv(mmc); - int dsor; - int realclock, i; - - realclock = ios->clock; - - if (ios->clock == 0) - dsor = 0; - else { - int func_clk_rate = clk_get_rate(host->fclk); - - dsor = func_clk_rate / realclock; - if (dsor < 1) - dsor = 1; - - if (func_clk_rate / dsor > realclock) - dsor++; - - if (dsor > 250) - dsor = 250; - dsor++; - - if (ios->bus_width == MMC_BUS_WIDTH_4) - dsor |= 1 << 15; - } - - switch (ios->power_mode) { - case MMC_POWER_OFF: - mmc_omap_power(host, 0); - break; - case MMC_POWER_UP: - case MMC_POWER_ON: - mmc_omap_power(host, 1); - dsor |= 1 << 11; - break; - } - - host->bus_mode = ios->bus_mode; - host->hw_bus_mode = host->bus_mode; - - clk_enable(host->fclk); - - /* On insanely high arm_per frequencies something sometimes - * goes somehow out of sync, and the POW bit is not being set, - * which results in the while loop below getting stuck. - * Writing to the CON register twice seems to do the trick. */ - for (i = 0; i < 2; i++) - OMAP_MMC_WRITE(host, CON, dsor); - if (ios->power_mode == MMC_POWER_UP) { - /* Send clock cycles, poll completion */ - OMAP_MMC_WRITE(host, IE, 0); - OMAP_MMC_WRITE(host, STAT, 0xffff); - OMAP_MMC_WRITE(host, CMD, 1 << 7); - while ((OMAP_MMC_READ(host, STAT) & 1) == 0); - OMAP_MMC_WRITE(host, STAT, 1); - } - clk_disable(host->fclk); -} - -static int mmc_omap_get_ro(struct mmc_host *mmc) -{ - struct mmc_omap_host *host = mmc_priv(mmc); - - return host->wp_pin && omap_get_gpio_datain(host->wp_pin); -} - -static const struct mmc_host_ops mmc_omap_ops = { - .request = mmc_omap_request, - .set_ios = mmc_omap_set_ios, - .get_ro = mmc_omap_get_ro, -}; - -static int __init mmc_omap_probe(struct platform_device *pdev) -{ - struct omap_mmc_conf *minfo = pdev->dev.platform_data; - struct mmc_host *mmc; - struct mmc_omap_host *host = NULL; - struct resource *res; - int ret = 0; - int irq; - - if (minfo == NULL) { - dev_err(&pdev->dev, "platform data missing\n"); - return -ENXIO; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (res == NULL || irq < 0) - return -ENXIO; - - res = request_mem_region(res->start, res->end - res->start + 1, - pdev->name); - if (res == NULL) - return -EBUSY; - - mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev); - if (mmc == NULL) { - ret = -ENOMEM; - goto err_free_mem_region; - } - - host = mmc_priv(mmc); - host->mmc = mmc; - - spin_lock_init(&host->dma_lock); - init_timer(&host->dma_timer); - host->dma_timer.function = mmc_omap_dma_timer; - host->dma_timer.data = (unsigned long) host; - - host->id = pdev->id; - host->mem_res = res; - host->irq = irq; - - if (cpu_is_omap24xx()) { - host->iclk = clk_get(&pdev->dev, "mmc_ick"); - if (IS_ERR(host->iclk)) - goto err_free_mmc_host; - clk_enable(host->iclk); - } - - if (!cpu_is_omap24xx()) - host->fclk = clk_get(&pdev->dev, "mmc_ck"); - else - host->fclk = clk_get(&pdev->dev, "mmc_fck"); - - if (IS_ERR(host->fclk)) { - ret = PTR_ERR(host->fclk); - goto err_free_iclk; - } - - /* REVISIT: - * Also, use minfo->cover to decide how to manage - * the card detect sensing. - */ - host->power_pin = minfo->power_pin; - host->switch_pin = minfo->switch_pin; - host->wp_pin = minfo->wp_pin; - host->use_dma = 1; - host->dma_ch = -1; - - host->irq = irq; - host->phys_base = host->mem_res->start; - host->virt_base = (void __iomem *) IO_ADDRESS(host->phys_base); - - mmc->ops = &mmc_omap_ops; - mmc->f_min = 400000; - mmc->f_max = 24000000; - mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; - - if (minfo->wire4) - mmc->caps |= MMC_CAP_4_BIT_DATA; - - /* Use scatterlist DMA to reduce per-transfer costs. - * NOTE max_seg_size assumption that small blocks aren't - * normally used (except e.g. for reading SD registers). - */ - mmc->max_phys_segs = 32; - mmc->max_hw_segs = 32; - mmc->max_blk_size = 2048; /* BLEN is 11 bits (+1) */ - mmc->max_blk_count = 2048; /* NBLK is 11 bits (+1) */ - mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; - mmc->max_seg_size = mmc->max_req_size; - - if (host->power_pin >= 0) { - if ((ret = omap_request_gpio(host->power_pin)) != 0) { - dev_err(mmc_dev(host->mmc), - "Unable to get GPIO pin for MMC power\n"); - goto err_free_fclk; - } - omap_set_gpio_direction(host->power_pin, 0); - } - - ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host); - if (ret) - goto err_free_power_gpio; - - host->dev = &pdev->dev; - platform_set_drvdata(pdev, host); - - if (host->switch_pin >= 0) { - INIT_WORK(&host->switch_work, mmc_omap_switch_handler); - init_timer(&host->switch_timer); - host->switch_timer.function = mmc_omap_switch_timer; - host->switch_timer.data = (unsigned long) host; - if (omap_request_gpio(host->switch_pin) != 0) { - dev_warn(mmc_dev(host->mmc), "Unable to get GPIO pin for MMC cover switch\n"); - host->switch_pin = -1; - goto no_switch; - } - - omap_set_gpio_direction(host->switch_pin, 1); - ret = request_irq(OMAP_GPIO_IRQ(host->switch_pin), - mmc_omap_switch_irq, IRQF_TRIGGER_RISING, DRIVER_NAME, host); - if (ret) { - dev_warn(mmc_dev(host->mmc), "Unable to get IRQ for MMC cover switch\n"); - omap_free_gpio(host->switch_pin); - host->switch_pin = -1; - goto no_switch; - } - ret = device_create_file(&pdev->dev, &dev_attr_cover_switch); - if (ret == 0) { - ret = device_create_file(&pdev->dev, &dev_attr_enable_poll); - if (ret != 0) - device_remove_file(&pdev->dev, &dev_attr_cover_switch); - } - if (ret) { - dev_warn(mmc_dev(host->mmc), "Unable to create sysfs attributes\n"); - free_irq(OMAP_GPIO_IRQ(host->switch_pin), host); - omap_free_gpio(host->switch_pin); - host->switch_pin = -1; - goto no_switch; - } - if (mmc_omap_enable_poll && mmc_omap_cover_is_open(host)) - schedule_work(&host->switch_work); - } - - mmc_add_host(mmc); - - return 0; - -no_switch: - /* FIXME: Free other resources too. */ - if (host) { - if (host->iclk && !IS_ERR(host->iclk)) - clk_put(host->iclk); - if (host->fclk && !IS_ERR(host->fclk)) - clk_put(host->fclk); - mmc_free_host(host->mmc); - } -err_free_power_gpio: - if (host->power_pin >= 0) - omap_free_gpio(host->power_pin); -err_free_fclk: - clk_put(host->fclk); -err_free_iclk: - if (host->iclk != NULL) { - clk_disable(host->iclk); - clk_put(host->iclk); - } -err_free_mmc_host: - mmc_free_host(host->mmc); -err_free_mem_region: - release_mem_region(res->start, res->end - res->start + 1); - return ret; -} - -static int mmc_omap_remove(struct platform_device *pdev) -{ - struct mmc_omap_host *host = platform_get_drvdata(pdev); - - platform_set_drvdata(pdev, NULL); - - BUG_ON(host == NULL); - - mmc_remove_host(host->mmc); - free_irq(host->irq, host); - - if (host->power_pin >= 0) - omap_free_gpio(host->power_pin); - if (host->switch_pin >= 0) { - device_remove_file(&pdev->dev, &dev_attr_enable_poll); - device_remove_file(&pdev->dev, &dev_attr_cover_switch); - free_irq(OMAP_GPIO_IRQ(host->switch_pin), host); - omap_free_gpio(host->switch_pin); - host->switch_pin = -1; - del_timer_sync(&host->switch_timer); - flush_scheduled_work(); - } - if (host->iclk && !IS_ERR(host->iclk)) - clk_put(host->iclk); - if (host->fclk && !IS_ERR(host->fclk)) - clk_put(host->fclk); - - release_mem_region(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1); - - mmc_free_host(host->mmc); - - return 0; -} - -#ifdef CONFIG_PM -static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg) -{ - int ret = 0; - struct mmc_omap_host *host = platform_get_drvdata(pdev); - - if (host && host->suspended) - return 0; - - if (host) { - ret = mmc_suspend_host(host->mmc, mesg); - if (ret == 0) - host->suspended = 1; - } - return ret; -} - -static int mmc_omap_resume(struct platform_device *pdev) -{ - int ret = 0; - struct mmc_omap_host *host = platform_get_drvdata(pdev); - - if (host && !host->suspended) - return 0; - - if (host) { - ret = mmc_resume_host(host->mmc); - if (ret == 0) - host->suspended = 0; - } - - return ret; -} -#else -#define mmc_omap_suspend NULL -#define mmc_omap_resume NULL -#endif - -static struct platform_driver mmc_omap_driver = { - .probe = mmc_omap_probe, - .remove = mmc_omap_remove, - .suspend = mmc_omap_suspend, - .resume = mmc_omap_resume, - .driver = { - .name = DRIVER_NAME, - }, -}; - -static int __init mmc_omap_init(void) -{ - return platform_driver_register(&mmc_omap_driver); -} - -static void __exit mmc_omap_exit(void) -{ - platform_driver_unregister(&mmc_omap_driver); -} - -module_init(mmc_omap_init); -module_exit(mmc_omap_exit); - -MODULE_DESCRIPTION("OMAP Multimedia Card driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS(DRIVER_NAME); -MODULE_AUTHOR("Juha Yrjölä"); diff --git a/drivers/mmc/pxamci.c b/drivers/mmc/pxamci.c deleted file mode 100644 index a98ff98fa56..00000000000 --- a/drivers/mmc/pxamci.c +++ /dev/null @@ -1,616 +0,0 @@ -/* - * linux/drivers/mmc/pxa.c - PXA MMCI driver - * - * Copyright (C) 2003 Russell King, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This hardware is really sick: - * - No way to clear interrupts. - * - Have to turn off the clock whenever we touch the device. - * - Doesn't tell you how many data blocks were transferred. - * Yuck! - * - * 1 and 3 byte data transfers not supported - * max block length up to 1023 - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include "pxamci.h" - -#define DRIVER_NAME "pxa2xx-mci" - -#define NR_SG 1 - -struct pxamci_host { - struct mmc_host *mmc; - spinlock_t lock; - struct resource *res; - void __iomem *base; - int irq; - int dma; - unsigned int clkrt; - unsigned int cmdat; - unsigned int imask; - unsigned int power_mode; - struct pxamci_platform_data *pdata; - - struct mmc_request *mrq; - struct mmc_command *cmd; - struct mmc_data *data; - - dma_addr_t sg_dma; - struct pxa_dma_desc *sg_cpu; - unsigned int dma_len; - - unsigned int dma_dir; -}; - -static void pxamci_stop_clock(struct pxamci_host *host) -{ - if (readl(host->base + MMC_STAT) & STAT_CLK_EN) { - unsigned long timeout = 10000; - unsigned int v; - - writel(STOP_CLOCK, host->base + MMC_STRPCL); - - do { - v = readl(host->base + MMC_STAT); - if (!(v & STAT_CLK_EN)) - break; - udelay(1); - } while (timeout--); - - if (v & STAT_CLK_EN) - dev_err(mmc_dev(host->mmc), "unable to stop clock\n"); - } -} - -static void pxamci_enable_irq(struct pxamci_host *host, unsigned int mask) -{ - unsigned long flags; - - spin_lock_irqsave(&host->lock, flags); - host->imask &= ~mask; - writel(host->imask, host->base + MMC_I_MASK); - spin_unlock_irqrestore(&host->lock, flags); -} - -static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask) -{ - unsigned long flags; - - spin_lock_irqsave(&host->lock, flags); - host->imask |= mask; - writel(host->imask, host->base + MMC_I_MASK); - spin_unlock_irqrestore(&host->lock, flags); -} - -static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data) -{ - unsigned int nob = data->blocks; - unsigned long long clks; - unsigned int timeout; - u32 dcmd; - int i; - - host->data = data; - - if (data->flags & MMC_DATA_STREAM) - nob = 0xffff; - - writel(nob, host->base + MMC_NOB); - writel(data->blksz, host->base + MMC_BLKLEN); - - clks = (unsigned long long)data->timeout_ns * CLOCKRATE; - do_div(clks, 1000000000UL); - timeout = (unsigned int)clks + (data->timeout_clks << host->clkrt); - writel((timeout + 255) / 256, host->base + MMC_RDTO); - - if (data->flags & MMC_DATA_READ) { - host->dma_dir = DMA_FROM_DEVICE; - dcmd = DCMD_INCTRGADDR | DCMD_FLOWTRG; - DRCMRTXMMC = 0; - DRCMRRXMMC = host->dma | DRCMR_MAPVLD; - } else { - host->dma_dir = DMA_TO_DEVICE; - dcmd = DCMD_INCSRCADDR | DCMD_FLOWSRC; - DRCMRRXMMC = 0; - DRCMRTXMMC = host->dma | DRCMR_MAPVLD; - } - - dcmd |= DCMD_BURST32 | DCMD_WIDTH1; - - 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 = sg_dma_address(&data->sg[i]); - } else { - 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 | 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; - DCSR(host->dma) = DCSR_RUN; -} - -static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd, unsigned int cmdat) -{ - WARN_ON(host->cmd != NULL); - host->cmd = cmd; - - if (cmd->flags & MMC_RSP_BUSY) - cmdat |= CMDAT_BUSY; - -#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE)) - switch (RSP_TYPE(mmc_resp_type(cmd))) { - case RSP_TYPE(MMC_RSP_R1): /* r1, r1b, r6, r7 */ - cmdat |= CMDAT_RESP_SHORT; - break; - case RSP_TYPE(MMC_RSP_R3): - cmdat |= CMDAT_RESP_R3; - break; - case RSP_TYPE(MMC_RSP_R2): - cmdat |= CMDAT_RESP_R2; - break; - default: - break; - } - - writel(cmd->opcode, host->base + MMC_CMD); - writel(cmd->arg >> 16, host->base + MMC_ARGH); - writel(cmd->arg & 0xffff, host->base + MMC_ARGL); - writel(cmdat, host->base + MMC_CMDAT); - writel(host->clkrt, host->base + MMC_CLKRT); - - writel(START_CLOCK, host->base + MMC_STRPCL); - - pxamci_enable_irq(host, END_CMD_RES); -} - -static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq) -{ - host->mrq = NULL; - host->cmd = NULL; - host->data = NULL; - mmc_request_done(host->mmc, mrq); -} - -static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat) -{ - struct mmc_command *cmd = host->cmd; - int i; - u32 v; - - if (!cmd) - return 0; - - host->cmd = NULL; - - /* - * Did I mention this is Sick. We always need to - * discard the upper 8 bits of the first 16-bit word. - */ - v = readl(host->base + MMC_RES) & 0xffff; - for (i = 0; i < 4; i++) { - u32 w1 = readl(host->base + MMC_RES) & 0xffff; - u32 w2 = readl(host->base + MMC_RES) & 0xffff; - cmd->resp[i] = v << 24 | w1 << 8 | w2 >> 8; - v = w2; - } - - 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 { - pr_debug("ignoring CRC from command %d - *risky*\n",cmd->opcode); - } -#else - cmd->error = MMC_ERR_BADCRC; -#endif - } - - pxamci_disable_irq(host, END_CMD_RES); - if (host->data && cmd->error == MMC_ERR_NONE) { - pxamci_enable_irq(host, DATA_TRAN_DONE); - } else { - pxamci_finish_request(host, host->mrq); - } - - return 1; -} - -static int pxamci_data_done(struct pxamci_host *host, unsigned int stat) -{ - struct mmc_data *data = host->data; - - if (!data) - return 0; - - DCSR(host->dma) = 0; - 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; - else if (stat & (STAT_CRC_READ_ERROR|STAT_CRC_WRITE_ERROR)) - data->error = MMC_ERR_BADCRC; - - /* - * There appears to be a hardware design bug here. There seems to - * be no way to find out how much data was transferred to the card. - * This means that if there was an error on any block, we mark all - * data blocks as being in error. - */ - if (data->error == MMC_ERR_NONE) - data->bytes_xfered = data->blocks * data->blksz; - else - data->bytes_xfered = 0; - - pxamci_disable_irq(host, DATA_TRAN_DONE); - - host->data = NULL; - if (host->mrq->stop) { - pxamci_stop_clock(host); - pxamci_start_cmd(host, host->mrq->stop, 0); - } else { - pxamci_finish_request(host, host->mrq); - } - - return 1; -} - -static irqreturn_t pxamci_irq(int irq, void *devid) -{ - struct pxamci_host *host = devid; - unsigned int ireg; - int handled = 0; - - ireg = readl(host->base + MMC_I_REG); - - if (ireg) { - unsigned stat = readl(host->base + MMC_STAT); - - pr_debug("PXAMCI: irq %08x stat %08x\n", ireg, stat); - - if (ireg & END_CMD_RES) - handled |= pxamci_cmd_done(host, stat); - if (ireg & DATA_TRAN_DONE) - handled |= pxamci_data_done(host, stat); - } - - return IRQ_RETVAL(handled); -} - -static void pxamci_request(struct mmc_host *mmc, struct mmc_request *mrq) -{ - struct pxamci_host *host = mmc_priv(mmc); - unsigned int cmdat; - - WARN_ON(host->mrq != NULL); - - host->mrq = mrq; - - pxamci_stop_clock(host); - - cmdat = host->cmdat; - host->cmdat &= ~CMDAT_INIT; - - if (mrq->data) { - pxamci_setup_data(host, mrq->data); - - cmdat &= ~CMDAT_BUSY; - cmdat |= CMDAT_DATAEN | CMDAT_DMAEN; - if (mrq->data->flags & MMC_DATA_WRITE) - cmdat |= CMDAT_WRITE; - - if (mrq->data->flags & MMC_DATA_STREAM) - cmdat |= CMDAT_STREAM; - } - - pxamci_start_cmd(host, mrq->cmd, cmdat); -} - -static int pxamci_get_ro(struct mmc_host *mmc) -{ - struct pxamci_host *host = mmc_priv(mmc); - - if (host->pdata && host->pdata->get_ro) - return host->pdata->get_ro(mmc_dev(mmc)); - /* Host doesn't support read only detection so assume writeable */ - return 0; -} - -static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -{ - struct pxamci_host *host = mmc_priv(mmc); - - if (ios->clock) { - unsigned int clk = CLOCKRATE / ios->clock; - 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 { - pxamci_stop_clock(host); - pxa_set_cken(CKEN12_MMC, 0); - } - - if (host->power_mode != ios->power_mode) { - host->power_mode = ios->power_mode; - - if (host->pdata && host->pdata->setpower) - host->pdata->setpower(mmc_dev(mmc), ios->vdd); - - if (ios->power_mode == MMC_POWER_ON) - host->cmdat |= CMDAT_INIT; - } - - pr_debug("PXAMCI: clkrt = %x cmdat = %x\n", - host->clkrt, host->cmdat); -} - -static const struct mmc_host_ops pxamci_ops = { - .request = pxamci_request, - .get_ro = pxamci_get_ro, - .set_ios = pxamci_set_ios, -}; - -static void pxamci_dma_irq(int dma, void *devid) -{ - printk(KERN_ERR "DMA%d: IRQ???\n", dma); - DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; -} - -static irqreturn_t pxamci_detect_irq(int irq, void *devid) -{ - struct pxamci_host *host = mmc_priv(devid); - - mmc_detect_change(devid, host->pdata->detect_delay); - return IRQ_HANDLED; -} - -static int pxamci_probe(struct platform_device *pdev) -{ - struct mmc_host *mmc; - struct pxamci_host *host = NULL; - struct resource *r; - int ret, irq; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq = platform_get_irq(pdev, 0); - if (!r || irq < 0) - return -ENXIO; - - r = request_mem_region(r->start, SZ_4K, DRIVER_NAME); - if (!r) - return -EBUSY; - - mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev); - if (!mmc) { - ret = -ENOMEM; - goto out; - } - - mmc->ops = &pxamci_ops; - 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; - - /* - * Block length register is 10 bits. - */ - mmc->max_blk_size = 1023; - - /* - * Block count register is 16 bits. - */ - mmc->max_blk_count = 65535; - - host = mmc_priv(mmc); - host->mmc = mmc; - host->dma = -1; - host->pdata = pdev->dev.platform_data; - mmc->ocr_avail = host->pdata ? - host->pdata->ocr_mask : - MMC_VDD_32_33|MMC_VDD_33_34; - - host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); - if (!host->sg_cpu) { - ret = -ENOMEM; - goto out; - } - - spin_lock_init(&host->lock); - host->res = r; - host->irq = irq; - host->imask = MMC_I_MASK_ALL; - - host->base = ioremap(r->start, SZ_4K); - if (!host->base) { - ret = -ENOMEM; - goto out; - } - - /* - * Ensure that the host controller is shut down, and setup - * with our defaults. - */ - pxamci_stop_clock(host); - writel(0, host->base + MMC_SPI); - writel(64, host->base + MMC_RESTO); - writel(host->imask, host->base + MMC_I_MASK); - - 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, DRIVER_NAME, host); - if (ret) - goto out; - - platform_set_drvdata(pdev, mmc); - - if (host->pdata && host->pdata->init) - host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc); - - mmc_add_host(mmc); - - return 0; - - out: - if (host) { - if (host->dma >= 0) - pxa_free_dma(host->dma); - if (host->base) - iounmap(host->base); - if (host->sg_cpu) - dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); - } - if (mmc) - mmc_free_host(mmc); - release_resource(r); - return ret; -} - -static int pxamci_remove(struct platform_device *pdev) -{ - struct mmc_host *mmc = platform_get_drvdata(pdev); - - platform_set_drvdata(pdev, NULL); - - if (mmc) { - struct pxamci_host *host = mmc_priv(mmc); - - if (host->pdata && host->pdata->exit) - host->pdata->exit(&pdev->dev, mmc); - - mmc_remove_host(mmc); - - pxamci_stop_clock(host); - writel(TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD| - END_CMD_RES|PRG_DONE|DATA_TRAN_DONE, - host->base + MMC_I_MASK); - - DRCMRRXMMC = 0; - DRCMRTXMMC = 0; - - free_irq(host->irq, host); - pxa_free_dma(host->dma); - iounmap(host->base); - dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); - - release_resource(host->res); - - mmc_free_host(mmc); - } - return 0; -} - -#ifdef CONFIG_PM -static int pxamci_suspend(struct platform_device *dev, pm_message_t state) -{ - struct mmc_host *mmc = platform_get_drvdata(dev); - int ret = 0; - - if (mmc) - ret = mmc_suspend_host(mmc, state); - - return ret; -} - -static int pxamci_resume(struct platform_device *dev) -{ - struct mmc_host *mmc = platform_get_drvdata(dev); - int ret = 0; - - if (mmc) - ret = mmc_resume_host(mmc); - - return ret; -} -#else -#define pxamci_suspend NULL -#define pxamci_resume NULL -#endif - -static struct platform_driver pxamci_driver = { - .probe = pxamci_probe, - .remove = pxamci_remove, - .suspend = pxamci_suspend, - .resume = pxamci_resume, - .driver = { - .name = DRIVER_NAME, - }, -}; - -static int __init pxamci_init(void) -{ - return platform_driver_register(&pxamci_driver); -} - -static void __exit pxamci_exit(void) -{ - platform_driver_unregister(&pxamci_driver); -} - -module_init(pxamci_init); -module_exit(pxamci_exit); - -MODULE_DESCRIPTION("PXA Multimedia Card Interface Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/pxamci.h b/drivers/mmc/pxamci.h deleted file mode 100644 index 1b163220df2..00000000000 --- a/drivers/mmc/pxamci.h +++ /dev/null @@ -1,124 +0,0 @@ -#undef MMC_STRPCL -#undef MMC_STAT -#undef MMC_CLKRT -#undef MMC_SPI -#undef MMC_CMDAT -#undef MMC_RESTO -#undef MMC_RDTO -#undef MMC_BLKLEN -#undef MMC_NOB -#undef MMC_PRTBUF -#undef MMC_I_MASK -#undef END_CMD_RES -#undef PRG_DONE -#undef DATA_TRAN_DONE -#undef MMC_I_REG -#undef MMC_CMD -#undef MMC_ARGH -#undef MMC_ARGL -#undef MMC_RES -#undef MMC_RXFIFO -#undef MMC_TXFIFO - -#define MMC_STRPCL 0x0000 -#define STOP_CLOCK (1 << 0) -#define START_CLOCK (2 << 0) - -#define MMC_STAT 0x0004 -#define STAT_END_CMD_RES (1 << 13) -#define STAT_PRG_DONE (1 << 12) -#define STAT_DATA_TRAN_DONE (1 << 11) -#define STAT_CLK_EN (1 << 8) -#define STAT_RECV_FIFO_FULL (1 << 7) -#define STAT_XMIT_FIFO_EMPTY (1 << 6) -#define STAT_RES_CRC_ERR (1 << 5) -#define STAT_SPI_READ_ERROR_TOKEN (1 << 4) -#define STAT_CRC_READ_ERROR (1 << 3) -#define STAT_CRC_WRITE_ERROR (1 << 2) -#define STAT_TIME_OUT_RESPONSE (1 << 1) -#define STAT_READ_TIME_OUT (1 << 0) - -#define MMC_CLKRT 0x0008 /* 3 bit */ - -#define MMC_SPI 0x000c -#define SPI_CS_ADDRESS (1 << 3) -#define SPI_CS_EN (1 << 2) -#define CRC_ON (1 << 1) -#define SPI_EN (1 << 0) - -#define MMC_CMDAT 0x0010 -#define CMDAT_DMAEN (1 << 7) -#define CMDAT_INIT (1 << 6) -#define CMDAT_BUSY (1 << 5) -#define CMDAT_STREAM (1 << 4) /* 1 = stream */ -#define CMDAT_WRITE (1 << 3) /* 1 = write */ -#define CMDAT_DATAEN (1 << 2) -#define CMDAT_RESP_NONE (0 << 0) -#define CMDAT_RESP_SHORT (1 << 0) -#define CMDAT_RESP_R2 (2 << 0) -#define CMDAT_RESP_R3 (3 << 0) - -#define MMC_RESTO 0x0014 /* 7 bit */ - -#define MMC_RDTO 0x0018 /* 16 bit */ - -#define MMC_BLKLEN 0x001c /* 10 bit */ - -#define MMC_NOB 0x0020 /* 16 bit */ - -#define MMC_PRTBUF 0x0024 -#define BUF_PART_FULL (1 << 0) - -#define MMC_I_MASK 0x0028 - -/*PXA27x MMC interrupts*/ -#define SDIO_SUSPEND_ACK (1 << 12) -#define SDIO_INT (1 << 11) -#define RD_STALLED (1 << 10) -#define RES_ERR (1 << 9) -#define DAT_ERR (1 << 8) -#define TINT (1 << 7) - -/*PXA2xx MMC interrupts*/ -#define TXFIFO_WR_REQ (1 << 6) -#define RXFIFO_RD_REQ (1 << 5) -#define CLK_IS_OFF (1 << 4) -#define STOP_CMD (1 << 3) -#define END_CMD_RES (1 << 2) -#define PRG_DONE (1 << 1) -#define DATA_TRAN_DONE (1 << 0) - -#ifdef CONFIG_PXA27x -#define MMC_I_MASK_ALL 0x00001fff -#else -#define MMC_I_MASK_ALL 0x0000007f -#endif - -#define MMC_I_REG 0x002c -/* same as MMC_I_MASK */ - -#define MMC_CMD 0x0030 - -#define MMC_ARGH 0x0034 /* 16 bit */ - -#define MMC_ARGL 0x0038 /* 16 bit */ - -#define MMC_RES 0x003c /* 16 bit */ - -#define MMC_RXFIFO 0x0040 /* 8 bit */ - -#define MMC_TXFIFO 0x0044 /* 8 bit */ - -/* - * The base MMC clock rate - */ -#ifdef CONFIG_PXA27x -#define CLOCKRATE_MIN 304688 -#define CLOCKRATE_MAX 19500000 -#else -#define CLOCKRATE_MIN 312500 -#define CLOCKRATE_MAX 20000000 -#endif - -#define CLOCKRATE CLOCKRATE_MAX - diff --git a/drivers/mmc/queue.c b/drivers/mmc/queue.c deleted file mode 100644 index aa75ac11a19..00000000000 --- a/drivers/mmc/queue.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * linux/drivers/mmc/queue.c - * - * Copyright (C) 2003 Russell King, All Rights Reserved. - * Copyright 2006-2007 Pierre Ossman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - */ -#include -#include -#include - -#include -#include -#include "queue.h" - -#define MMC_QUEUE_SUSPENDED (1 << 0) - -/* - * Prepare a MMC request. Essentially, this means passing the - * preparation off to the media driver. The media driver will - * create a mmc_io_request in req->special. - */ -static int mmc_prep_request(struct request_queue *q, struct request *req) -{ - struct mmc_queue *mq = q->queuedata; - int ret = BLKPREP_KILL; - - if (blk_special_request(req)) { - /* - * Special commands already have the command - * blocks already setup in req->special. - */ - BUG_ON(!req->special); - - ret = BLKPREP_OK; - } else if (blk_fs_request(req) || blk_pc_request(req)) { - /* - * Block I/O requests need translating according - * to the protocol. - */ - ret = mq->prep_fn(mq, req); - } else { - /* - * Everything else is invalid. - */ - blk_dump_rq_flags(req, "MMC bad request"); - } - - if (ret == BLKPREP_OK) - req->cmd_flags |= REQ_DONTPREP; - - return ret; -} - -static int mmc_queue_thread(void *d) -{ - struct mmc_queue *mq = d; - struct request_queue *q = mq->queue; - - /* - * Set iothread to ensure that we aren't put to sleep by - * the process freezing. We handle suspension ourselves. - */ - current->flags |= PF_MEMALLOC|PF_NOFREEZE; - - down(&mq->thread_sem); - do { - struct request *req = NULL; - - spin_lock_irq(q->queue_lock); - set_current_state(TASK_INTERRUPTIBLE); - if (!blk_queue_plugged(q)) - req = elv_next_request(q); - mq->req = req; - spin_unlock_irq(q->queue_lock); - - if (!req) { - if (kthread_should_stop()) { - set_current_state(TASK_RUNNING); - break; - } - up(&mq->thread_sem); - schedule(); - down(&mq->thread_sem); - continue; - } - set_current_state(TASK_RUNNING); - - mq->issue_fn(mq, req); - } while (1); - up(&mq->thread_sem); - - return 0; -} - -/* - * Generic MMC request handler. This is called for any queue on a - * particular host. When the host is not busy, we look for a request - * on any queue on this host, and attempt to issue it. This may - * not be the queue we were asked to process. - */ -static void mmc_request(request_queue_t *q) -{ - struct mmc_queue *mq = q->queuedata; - struct request *req; - int ret; - - if (!mq) { - printk(KERN_ERR "MMC: killing requests for dead queue\n"); - while ((req = elv_next_request(q)) != NULL) { - do { - ret = end_that_request_chunk(req, 0, - req->current_nr_sectors << 9); - } while (ret); - } - return; - } - - if (!mq->req) - wake_up_process(mq->thread); -} - -/** - * mmc_init_queue - initialise a queue structure. - * @mq: mmc queue - * @card: mmc card to attach this queue - * @lock: queue lock - * - * Initialise a MMC card request queue. - */ -int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock) -{ - struct mmc_host *host = card->host; - u64 limit = BLK_BOUNCE_HIGH; - int ret; - - if (mmc_dev(host)->dma_mask && *mmc_dev(host)->dma_mask) - limit = *mmc_dev(host)->dma_mask; - - mq->card = card; - mq->queue = blk_init_queue(mmc_request, lock); - if (!mq->queue) - return -ENOMEM; - - blk_queue_prep_rq(mq->queue, mmc_prep_request); - blk_queue_bounce_limit(mq->queue, limit); - blk_queue_max_sectors(mq->queue, host->max_req_size / 512); - blk_queue_max_phys_segments(mq->queue, host->max_phys_segs); - blk_queue_max_hw_segments(mq->queue, host->max_hw_segs); - blk_queue_max_segment_size(mq->queue, host->max_seg_size); - - mq->queue->queuedata = mq; - mq->req = NULL; - - mq->sg = kmalloc(sizeof(struct scatterlist) * host->max_phys_segs, - GFP_KERNEL); - if (!mq->sg) { - ret = -ENOMEM; - goto cleanup_queue; - } - - init_MUTEX(&mq->thread_sem); - - mq->thread = kthread_run(mmc_queue_thread, mq, "mmcqd"); - if (IS_ERR(mq->thread)) { - ret = PTR_ERR(mq->thread); - goto free_sg; - } - - return 0; - - free_sg: - kfree(mq->sg); - mq->sg = NULL; - cleanup_queue: - blk_cleanup_queue(mq->queue); - return ret; -} - -void mmc_cleanup_queue(struct mmc_queue *mq) -{ - request_queue_t *q = mq->queue; - unsigned long flags; - - /* Mark that we should start throwing out stragglers */ - spin_lock_irqsave(q->queue_lock, flags); - q->queuedata = NULL; - spin_unlock_irqrestore(q->queue_lock, flags); - - /* Then terminate our worker thread */ - kthread_stop(mq->thread); - - kfree(mq->sg); - mq->sg = NULL; - - blk_cleanup_queue(mq->queue); - - mq->card = NULL; -} -EXPORT_SYMBOL(mmc_cleanup_queue); - -/** - * mmc_queue_suspend - suspend a MMC request queue - * @mq: MMC queue to suspend - * - * Stop the block request queue, and wait for our thread to - * complete any outstanding requests. This ensures that we - * won't suspend while a request is being processed. - */ -void mmc_queue_suspend(struct mmc_queue *mq) -{ - request_queue_t *q = mq->queue; - unsigned long flags; - - if (!(mq->flags & MMC_QUEUE_SUSPENDED)) { - mq->flags |= MMC_QUEUE_SUSPENDED; - - spin_lock_irqsave(q->queue_lock, flags); - blk_stop_queue(q); - spin_unlock_irqrestore(q->queue_lock, flags); - - down(&mq->thread_sem); - } -} - -/** - * mmc_queue_resume - resume a previously suspended MMC request queue - * @mq: MMC queue to resume - */ -void mmc_queue_resume(struct mmc_queue *mq) -{ - request_queue_t *q = mq->queue; - unsigned long flags; - - if (mq->flags & MMC_QUEUE_SUSPENDED) { - mq->flags &= ~MMC_QUEUE_SUSPENDED; - - up(&mq->thread_sem); - - spin_lock_irqsave(q->queue_lock, flags); - blk_start_queue(q); - spin_unlock_irqrestore(q->queue_lock, flags); - } -} - diff --git a/drivers/mmc/queue.h b/drivers/mmc/queue.h deleted file mode 100644 index c9f139e764f..00000000000 --- a/drivers/mmc/queue.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef MMC_QUEUE_H -#define MMC_QUEUE_H - -struct request; -struct task_struct; - -struct mmc_queue { - struct mmc_card *card; - struct task_struct *thread; - struct semaphore thread_sem; - unsigned int flags; - struct request *req; - int (*prep_fn)(struct mmc_queue *, struct request *); - int (*issue_fn)(struct mmc_queue *, struct request *); - void *data; - struct request_queue *queue; - struct scatterlist *sg; -}; - -struct mmc_io_request { - struct request *rq; - int num; - struct mmc_command selcmd; /* mmc_queue private */ - struct mmc_command cmd[4]; /* max 4 commands */ -}; - -extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *); -extern void mmc_cleanup_queue(struct mmc_queue *); -extern void mmc_queue_suspend(struct mmc_queue *); -extern void mmc_queue_resume(struct mmc_queue *); - -#endif diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c deleted file mode 100644 index 579142a7904..00000000000 --- a/drivers/mmc/sdhci.c +++ /dev/null @@ -1,1539 +0,0 @@ -/* - * linux/drivers/mmc/sdhci.c - Secure Digital Host Controller Interface driver - * - * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - */ - -#include -#include -#include -#include - -#include - -#include - -#include "sdhci.h" - -#define DRIVER_NAME "sdhci" - -#define DBG(f, x...) \ - pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x) - -static unsigned int debug_nodma = 0; -static unsigned int debug_forcedma = 0; -static unsigned int debug_quirks = 0; - -#define SDHCI_QUIRK_CLOCK_BEFORE_RESET (1<<0) -#define SDHCI_QUIRK_FORCE_DMA (1<<1) -/* Controller doesn't like some resets when there is no card inserted. */ -#define SDHCI_QUIRK_NO_CARD_NO_RESET (1<<2) -#define SDHCI_QUIRK_SINGLE_POWER_WRITE (1<<3) - -static const struct pci_device_id pci_ids[] __devinitdata = { - { - .vendor = PCI_VENDOR_ID_RICOH, - .device = PCI_DEVICE_ID_RICOH_R5C822, - .subvendor = PCI_VENDOR_ID_IBM, - .subdevice = PCI_ANY_ID, - .driver_data = SDHCI_QUIRK_CLOCK_BEFORE_RESET | - SDHCI_QUIRK_FORCE_DMA, - }, - - { - .vendor = PCI_VENDOR_ID_RICOH, - .device = PCI_DEVICE_ID_RICOH_R5C822, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = SDHCI_QUIRK_FORCE_DMA | - SDHCI_QUIRK_NO_CARD_NO_RESET, - }, - - { - .vendor = PCI_VENDOR_ID_TI, - .device = PCI_DEVICE_ID_TI_XX21_XX11_SD, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = SDHCI_QUIRK_FORCE_DMA, - }, - - { - .vendor = PCI_VENDOR_ID_ENE, - .device = PCI_DEVICE_ID_ENE_CB712_SD, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - .driver_data = SDHCI_QUIRK_SINGLE_POWER_WRITE, - }, - - { /* Generic SD host controller */ - PCI_DEVICE_CLASS((PCI_CLASS_SYSTEM_SDHCI << 8), 0xFFFF00) - }, - - { /* end: all zeroes */ }, -}; - -MODULE_DEVICE_TABLE(pci, pci_ids); - -static void sdhci_prepare_data(struct sdhci_host *, struct mmc_data *); -static void sdhci_finish_data(struct sdhci_host *); - -static void sdhci_send_command(struct sdhci_host *, struct mmc_command *); -static void sdhci_finish_command(struct sdhci_host *); - -static void sdhci_dumpregs(struct sdhci_host *host) -{ - printk(KERN_DEBUG DRIVER_NAME ": ============== REGISTER DUMP ==============\n"); - - printk(KERN_DEBUG DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n", - readl(host->ioaddr + SDHCI_DMA_ADDRESS), - readw(host->ioaddr + SDHCI_HOST_VERSION)); - printk(KERN_DEBUG DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n", - readw(host->ioaddr + SDHCI_BLOCK_SIZE), - readw(host->ioaddr + SDHCI_BLOCK_COUNT)); - printk(KERN_DEBUG DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n", - readl(host->ioaddr + SDHCI_ARGUMENT), - readw(host->ioaddr + SDHCI_TRANSFER_MODE)); - printk(KERN_DEBUG DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n", - readl(host->ioaddr + SDHCI_PRESENT_STATE), - readb(host->ioaddr + SDHCI_HOST_CONTROL)); - printk(KERN_DEBUG DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n", - readb(host->ioaddr + SDHCI_POWER_CONTROL), - readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL)); - printk(KERN_DEBUG DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n", - readb(host->ioaddr + SDHCI_WALK_UP_CONTROL), - readw(host->ioaddr + SDHCI_CLOCK_CONTROL)); - printk(KERN_DEBUG DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n", - readb(host->ioaddr + SDHCI_TIMEOUT_CONTROL), - readl(host->ioaddr + SDHCI_INT_STATUS)); - printk(KERN_DEBUG DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n", - readl(host->ioaddr + SDHCI_INT_ENABLE), - readl(host->ioaddr + SDHCI_SIGNAL_ENABLE)); - printk(KERN_DEBUG DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n", - readw(host->ioaddr + SDHCI_ACMD12_ERR), - readw(host->ioaddr + SDHCI_SLOT_INT_STATUS)); - printk(KERN_DEBUG DRIVER_NAME ": Caps: 0x%08x | Max curr: 0x%08x\n", - readl(host->ioaddr + SDHCI_CAPABILITIES), - readl(host->ioaddr + SDHCI_MAX_CURRENT)); - - printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n"); -} - -/*****************************************************************************\ - * * - * Low level functions * - * * -\*****************************************************************************/ - -static void sdhci_reset(struct sdhci_host *host, u8 mask) -{ - unsigned long timeout; - - if (host->chip->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { - if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & - SDHCI_CARD_PRESENT)) - return; - } - - writeb(mask, host->ioaddr + SDHCI_SOFTWARE_RESET); - - if (mask & SDHCI_RESET_ALL) - host->clock = 0; - - /* Wait max 100 ms */ - timeout = 100; - - /* hw clears the bit when it's done */ - while (readb(host->ioaddr + SDHCI_SOFTWARE_RESET) & mask) { - if (timeout == 0) { - printk(KERN_ERR "%s: Reset 0x%x never completed.\n", - mmc_hostname(host->mmc), (int)mask); - sdhci_dumpregs(host); - return; - } - timeout--; - mdelay(1); - } -} - -static void sdhci_init(struct sdhci_host *host) -{ - u32 intmask; - - sdhci_reset(host, SDHCI_RESET_ALL); - - intmask = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | - SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | - SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | - SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT | - SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | - SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE; - - writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); - writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE); -} - -static void sdhci_activate_led(struct sdhci_host *host) -{ - u8 ctrl; - - ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); - ctrl |= SDHCI_CTRL_LED; - writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); -} - -static void sdhci_deactivate_led(struct sdhci_host *host) -{ - u8 ctrl; - - ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); - ctrl &= ~SDHCI_CTRL_LED; - writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); -} - -/*****************************************************************************\ - * * - * Core functions * - * * -\*****************************************************************************/ - -static inline char* sdhci_sg_to_buffer(struct sdhci_host* host) -{ - return page_address(host->cur_sg->page) + host->cur_sg->offset; -} - -static inline int sdhci_next_sg(struct sdhci_host* host) -{ - /* - * Skip to next SG entry. - */ - host->cur_sg++; - host->num_sg--; - - /* - * Any entries left? - */ - if (host->num_sg > 0) { - host->offset = 0; - host->remain = host->cur_sg->length; - } - - return host->num_sg; -} - -static void sdhci_read_block_pio(struct sdhci_host *host) -{ - int blksize, chunk_remain; - u32 data; - char *buffer; - int size; - - DBG("PIO reading\n"); - - blksize = host->data->blksz; - chunk_remain = 0; - data = 0; - - buffer = sdhci_sg_to_buffer(host) + host->offset; - - while (blksize) { - if (chunk_remain == 0) { - data = readl(host->ioaddr + SDHCI_BUFFER); - chunk_remain = min(blksize, 4); - } - - size = min(host->remain, chunk_remain); - - chunk_remain -= size; - blksize -= size; - host->offset += size; - host->remain -= size; - - while (size) { - *buffer = data & 0xFF; - buffer++; - data >>= 8; - size--; - } - - if (host->remain == 0) { - if (sdhci_next_sg(host) == 0) { - BUG_ON(blksize != 0); - return; - } - buffer = sdhci_sg_to_buffer(host); - } - } -} - -static void sdhci_write_block_pio(struct sdhci_host *host) -{ - int blksize, chunk_remain; - u32 data; - char *buffer; - int bytes, size; - - DBG("PIO writing\n"); - - blksize = host->data->blksz; - chunk_remain = 4; - data = 0; - - bytes = 0; - buffer = sdhci_sg_to_buffer(host) + host->offset; - - while (blksize) { - size = min(host->remain, chunk_remain); - - chunk_remain -= size; - blksize -= size; - host->offset += size; - host->remain -= size; - - while (size) { - data >>= 8; - data |= (u32)*buffer << 24; - buffer++; - size--; - } - - if (chunk_remain == 0) { - writel(data, host->ioaddr + SDHCI_BUFFER); - chunk_remain = min(blksize, 4); - } - - if (host->remain == 0) { - if (sdhci_next_sg(host) == 0) { - BUG_ON(blksize != 0); - return; - } - buffer = sdhci_sg_to_buffer(host); - } - } -} - -static void sdhci_transfer_pio(struct sdhci_host *host) -{ - u32 mask; - - BUG_ON(!host->data); - - if (host->num_sg == 0) - return; - - if (host->data->flags & MMC_DATA_READ) - mask = SDHCI_DATA_AVAILABLE; - else - mask = SDHCI_SPACE_AVAILABLE; - - while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) { - if (host->data->flags & MMC_DATA_READ) - sdhci_read_block_pio(host); - else - sdhci_write_block_pio(host); - - if (host->num_sg == 0) - break; - } - - DBG("PIO transfer complete.\n"); -} - -static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) -{ - u8 count; - unsigned target_timeout, current_timeout; - - WARN_ON(host->data); - - if (data == NULL) - return; - - DBG("blksz %04x blks %04x flags %08x\n", - data->blksz, data->blocks, data->flags); - DBG("tsac %d ms nsac %d clk\n", - data->timeout_ns / 1000000, data->timeout_clks); - - /* Sanity checks */ - BUG_ON(data->blksz * data->blocks > 524288); - BUG_ON(data->blksz > host->mmc->max_blk_size); - BUG_ON(data->blocks > 65535); - - /* timeout in us */ - target_timeout = data->timeout_ns / 1000 + - data->timeout_clks / host->clock; - - /* - * Figure out needed cycles. - * We do this in steps in order to fit inside a 32 bit int. - * The first step is the minimum timeout, which will have a - * minimum resolution of 6 bits: - * (1) 2^13*1000 > 2^22, - * (2) host->timeout_clk < 2^16 - * => - * (1) / (2) > 2^6 - */ - count = 0; - current_timeout = (1 << 13) * 1000 / host->timeout_clk; - while (current_timeout < target_timeout) { - count++; - current_timeout <<= 1; - if (count >= 0xF) - break; - } - - if (count >= 0xF) { - printk(KERN_WARNING "%s: Too large timeout requested!\n", - mmc_hostname(host->mmc)); - count = 0xE; - } - - writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL); - - if (host->flags & SDHCI_USE_DMA) { - int count; - - count = pci_map_sg(host->chip->pdev, data->sg, data->sg_len, - (data->flags & MMC_DATA_READ)?PCI_DMA_FROMDEVICE:PCI_DMA_TODEVICE); - BUG_ON(count != 1); - - writel(sg_dma_address(data->sg), host->ioaddr + SDHCI_DMA_ADDRESS); - } else { - host->cur_sg = data->sg; - host->num_sg = data->sg_len; - - host->offset = 0; - host->remain = host->cur_sg->length; - } - - /* We do not handle DMA boundaries, so set it to max (512 KiB) */ - writew(SDHCI_MAKE_BLKSZ(7, data->blksz), - host->ioaddr + SDHCI_BLOCK_SIZE); - writew(data->blocks, host->ioaddr + SDHCI_BLOCK_COUNT); -} - -static void sdhci_set_transfer_mode(struct sdhci_host *host, - struct mmc_data *data) -{ - u16 mode; - - WARN_ON(host->data); - - if (data == NULL) - return; - - mode = SDHCI_TRNS_BLK_CNT_EN; - if (data->blocks > 1) - mode |= SDHCI_TRNS_MULTI; - if (data->flags & MMC_DATA_READ) - mode |= SDHCI_TRNS_READ; - if (host->flags & SDHCI_USE_DMA) - mode |= SDHCI_TRNS_DMA; - - writew(mode, host->ioaddr + SDHCI_TRANSFER_MODE); -} - -static void sdhci_finish_data(struct sdhci_host *host) -{ - struct mmc_data *data; - u16 blocks; - - BUG_ON(!host->data); - - data = host->data; - host->data = NULL; - - if (host->flags & SDHCI_USE_DMA) { - pci_unmap_sg(host->chip->pdev, data->sg, data->sg_len, - (data->flags & MMC_DATA_READ)?PCI_DMA_FROMDEVICE:PCI_DMA_TODEVICE); - } - - /* - * Controller doesn't count down when in single block mode. - */ - if ((data->blocks == 1) && (data->error == MMC_ERR_NONE)) - blocks = 0; - else - blocks = readw(host->ioaddr + SDHCI_BLOCK_COUNT); - data->bytes_xfered = data->blksz * (data->blocks - blocks); - - if ((data->error == MMC_ERR_NONE) && blocks) { - printk(KERN_ERR "%s: Controller signalled completion even " - "though there were blocks left.\n", - mmc_hostname(host->mmc)); - data->error = MMC_ERR_FAILED; - } - - DBG("Ending data transfer (%d bytes)\n", data->bytes_xfered); - - if (data->stop) { - /* - * The controller needs a reset of internal state machines - * upon error conditions. - */ - if (data->error != MMC_ERR_NONE) { - sdhci_reset(host, SDHCI_RESET_CMD); - sdhci_reset(host, SDHCI_RESET_DATA); - } - - sdhci_send_command(host, data->stop); - } else - tasklet_schedule(&host->finish_tasklet); -} - -static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) -{ - int flags; - u32 mask; - unsigned long timeout; - - WARN_ON(host->cmd); - - DBG("Sending cmd (%x)\n", cmd->opcode); - - /* Wait max 10 ms */ - timeout = 10; - - mask = SDHCI_CMD_INHIBIT; - if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY)) - mask |= SDHCI_DATA_INHIBIT; - - /* We shouldn't wait for data inihibit for stop commands, even - though they might use busy signaling */ - if (host->mrq->data && (cmd == host->mrq->data->stop)) - mask &= ~SDHCI_DATA_INHIBIT; - - while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) { - if (timeout == 0) { - printk(KERN_ERR "%s: Controller never released " - "inhibit bit(s).\n", mmc_hostname(host->mmc)); - sdhci_dumpregs(host); - cmd->error = MMC_ERR_FAILED; - tasklet_schedule(&host->finish_tasklet); - return; - } - timeout--; - mdelay(1); - } - - mod_timer(&host->timer, jiffies + 10 * HZ); - - host->cmd = cmd; - - sdhci_prepare_data(host, cmd->data); - - writel(cmd->arg, host->ioaddr + SDHCI_ARGUMENT); - - sdhci_set_transfer_mode(host, cmd->data); - - if ((cmd->flags & MMC_RSP_136) && (cmd->flags & MMC_RSP_BUSY)) { - printk(KERN_ERR "%s: Unsupported response type!\n", - mmc_hostname(host->mmc)); - cmd->error = MMC_ERR_INVALID; - tasklet_schedule(&host->finish_tasklet); - return; - } - - if (!(cmd->flags & MMC_RSP_PRESENT)) - flags = SDHCI_CMD_RESP_NONE; - else if (cmd->flags & MMC_RSP_136) - flags = SDHCI_CMD_RESP_LONG; - else if (cmd->flags & MMC_RSP_BUSY) - flags = SDHCI_CMD_RESP_SHORT_BUSY; - else - flags = SDHCI_CMD_RESP_SHORT; - - if (cmd->flags & MMC_RSP_CRC) - flags |= SDHCI_CMD_CRC; - if (cmd->flags & MMC_RSP_OPCODE) - flags |= SDHCI_CMD_INDEX; - if (cmd->data) - flags |= SDHCI_CMD_DATA; - - writew(SDHCI_MAKE_CMD(cmd->opcode, flags), - host->ioaddr + SDHCI_COMMAND); -} - -static void sdhci_finish_command(struct sdhci_host *host) -{ - int i; - - BUG_ON(host->cmd == NULL); - - if (host->cmd->flags & MMC_RSP_PRESENT) { - if (host->cmd->flags & MMC_RSP_136) { - /* CRC is stripped so we need to do some shifting. */ - for (i = 0;i < 4;i++) { - host->cmd->resp[i] = readl(host->ioaddr + - SDHCI_RESPONSE + (3-i)*4) << 8; - if (i != 3) - host->cmd->resp[i] |= - readb(host->ioaddr + - SDHCI_RESPONSE + (3-i)*4-1); - } - } else { - host->cmd->resp[0] = readl(host->ioaddr + SDHCI_RESPONSE); - } - } - - host->cmd->error = MMC_ERR_NONE; - - DBG("Ending cmd (%x)\n", host->cmd->opcode); - - if (host->cmd->data) - host->data = host->cmd->data; - else - tasklet_schedule(&host->finish_tasklet); - - host->cmd = NULL; -} - -static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) -{ - int div; - u16 clk; - unsigned long timeout; - - if (clock == host->clock) - return; - - writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL); - - if (clock == 0) - goto out; - - for (div = 1;div < 256;div *= 2) { - if ((host->max_clk / div) <= clock) - break; - } - div >>= 1; - - clk = div << SDHCI_DIVIDER_SHIFT; - clk |= SDHCI_CLOCK_INT_EN; - writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL); - - /* Wait max 10 ms */ - timeout = 10; - while (!((clk = readw(host->ioaddr + SDHCI_CLOCK_CONTROL)) - & SDHCI_CLOCK_INT_STABLE)) { - if (timeout == 0) { - printk(KERN_ERR "%s: Internal clock never " - "stabilised.\n", mmc_hostname(host->mmc)); - sdhci_dumpregs(host); - return; - } - timeout--; - mdelay(1); - } - - clk |= SDHCI_CLOCK_CARD_EN; - writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL); - -out: - host->clock = clock; -} - -static void sdhci_set_power(struct sdhci_host *host, unsigned short power) -{ - u8 pwr; - - if (host->power == power) - return; - - if (power == (unsigned short)-1) { - writeb(0, host->ioaddr + SDHCI_POWER_CONTROL); - goto out; - } - - /* - * Spec says that we should clear the power reg before setting - * a new value. Some controllers don't seem to like this though. - */ - if (!(host->chip->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) - writeb(0, host->ioaddr + SDHCI_POWER_CONTROL); - - pwr = SDHCI_POWER_ON; - - switch (power) { - case MMC_VDD_170: - case MMC_VDD_180: - case MMC_VDD_190: - pwr |= SDHCI_POWER_180; - break; - case MMC_VDD_290: - case MMC_VDD_300: - case MMC_VDD_310: - pwr |= SDHCI_POWER_300; - break; - case MMC_VDD_320: - case MMC_VDD_330: - case MMC_VDD_340: - pwr |= SDHCI_POWER_330; - break; - default: - BUG(); - } - - writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL); - -out: - host->power = power; -} - -/*****************************************************************************\ - * * - * MMC callbacks * - * * -\*****************************************************************************/ - -static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) -{ - struct sdhci_host *host; - unsigned long flags; - - host = mmc_priv(mmc); - - spin_lock_irqsave(&host->lock, flags); - - WARN_ON(host->mrq != NULL); - - sdhci_activate_led(host); - - host->mrq = mrq; - - if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) { - host->mrq->cmd->error = MMC_ERR_TIMEOUT; - tasklet_schedule(&host->finish_tasklet); - } else - sdhci_send_command(host, mrq->cmd); - - mmiowb(); - spin_unlock_irqrestore(&host->lock, flags); -} - -static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -{ - struct sdhci_host *host; - unsigned long flags; - u8 ctrl; - - host = mmc_priv(mmc); - - spin_lock_irqsave(&host->lock, flags); - - /* - * Reset the chip on each power off. - * Should clear out any weird states. - */ - if (ios->power_mode == MMC_POWER_OFF) { - writel(0, host->ioaddr + SDHCI_SIGNAL_ENABLE); - sdhci_init(host); - } - - sdhci_set_clock(host, ios->clock); - - if (ios->power_mode == MMC_POWER_OFF) - sdhci_set_power(host, -1); - else - sdhci_set_power(host, ios->vdd); - - ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); - - if (ios->bus_width == MMC_BUS_WIDTH_4) - ctrl |= SDHCI_CTRL_4BITBUS; - else - ctrl &= ~SDHCI_CTRL_4BITBUS; - - if (ios->timing == MMC_TIMING_SD_HS) - ctrl |= SDHCI_CTRL_HISPD; - else - ctrl &= ~SDHCI_CTRL_HISPD; - - writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); - - mmiowb(); - spin_unlock_irqrestore(&host->lock, flags); -} - -static int sdhci_get_ro(struct mmc_host *mmc) -{ - struct sdhci_host *host; - unsigned long flags; - int present; - - host = mmc_priv(mmc); - - spin_lock_irqsave(&host->lock, flags); - - present = readl(host->ioaddr + SDHCI_PRESENT_STATE); - - spin_unlock_irqrestore(&host->lock, flags); - - return !(present & SDHCI_WRITE_PROTECT); -} - -static const struct mmc_host_ops sdhci_ops = { - .request = sdhci_request, - .set_ios = sdhci_set_ios, - .get_ro = sdhci_get_ro, -}; - -/*****************************************************************************\ - * * - * Tasklets * - * * -\*****************************************************************************/ - -static void sdhci_tasklet_card(unsigned long param) -{ - struct sdhci_host *host; - unsigned long flags; - - host = (struct sdhci_host*)param; - - spin_lock_irqsave(&host->lock, flags); - - if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) { - if (host->mrq) { - printk(KERN_ERR "%s: Card removed during transfer!\n", - mmc_hostname(host->mmc)); - printk(KERN_ERR "%s: Resetting controller.\n", - mmc_hostname(host->mmc)); - - sdhci_reset(host, SDHCI_RESET_CMD); - sdhci_reset(host, SDHCI_RESET_DATA); - - host->mrq->cmd->error = MMC_ERR_FAILED; - tasklet_schedule(&host->finish_tasklet); - } - } - - spin_unlock_irqrestore(&host->lock, flags); - - mmc_detect_change(host->mmc, msecs_to_jiffies(500)); -} - -static void sdhci_tasklet_finish(unsigned long param) -{ - struct sdhci_host *host; - unsigned long flags; - struct mmc_request *mrq; - - host = (struct sdhci_host*)param; - - spin_lock_irqsave(&host->lock, flags); - - del_timer(&host->timer); - - mrq = host->mrq; - - DBG("Ending request, cmd (%x)\n", mrq->cmd->opcode); - - /* - * The controller needs a reset of internal state machines - * upon error conditions. - */ - if ((mrq->cmd->error != MMC_ERR_NONE) || - (mrq->data && ((mrq->data->error != MMC_ERR_NONE) || - (mrq->data->stop && (mrq->data->stop->error != MMC_ERR_NONE))))) { - - /* Some controllers need this kick or reset won't work here */ - if (host->chip->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET) { - unsigned int clock; - - /* This is to force an update */ - clock = host->clock; - host->clock = 0; - sdhci_set_clock(host, clock); - } - - /* Spec says we should do both at the same time, but Ricoh - controllers do not like that. */ - sdhci_reset(host, SDHCI_RESET_CMD); - sdhci_reset(host, SDHCI_RESET_DATA); - } - - host->mrq = NULL; - host->cmd = NULL; - host->data = NULL; - - sdhci_deactivate_led(host); - - mmiowb(); - spin_unlock_irqrestore(&host->lock, flags); - - mmc_request_done(host->mmc, mrq); -} - -static void sdhci_timeout_timer(unsigned long data) -{ - struct sdhci_host *host; - unsigned long flags; - - host = (struct sdhci_host*)data; - - spin_lock_irqsave(&host->lock, flags); - - if (host->mrq) { - printk(KERN_ERR "%s: Timeout waiting for hardware " - "interrupt.\n", mmc_hostname(host->mmc)); - sdhci_dumpregs(host); - - if (host->data) { - host->data->error = MMC_ERR_TIMEOUT; - sdhci_finish_data(host); - } else { - if (host->cmd) - host->cmd->error = MMC_ERR_TIMEOUT; - else - host->mrq->cmd->error = MMC_ERR_TIMEOUT; - - tasklet_schedule(&host->finish_tasklet); - } - } - - mmiowb(); - spin_unlock_irqrestore(&host->lock, flags); -} - -/*****************************************************************************\ - * * - * Interrupt handling * - * * -\*****************************************************************************/ - -static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask) -{ - BUG_ON(intmask == 0); - - if (!host->cmd) { - printk(KERN_ERR "%s: Got command interrupt even though no " - "command operation was in progress.\n", - mmc_hostname(host->mmc)); - sdhci_dumpregs(host); - return; - } - - if (intmask & SDHCI_INT_RESPONSE) - sdhci_finish_command(host); - else { - if (intmask & SDHCI_INT_TIMEOUT) - host->cmd->error = MMC_ERR_TIMEOUT; - else if (intmask & SDHCI_INT_CRC) - host->cmd->error = MMC_ERR_BADCRC; - else if (intmask & (SDHCI_INT_END_BIT | SDHCI_INT_INDEX)) - host->cmd->error = MMC_ERR_FAILED; - else - host->cmd->error = MMC_ERR_INVALID; - - tasklet_schedule(&host->finish_tasklet); - } -} - -static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) -{ - BUG_ON(intmask == 0); - - if (!host->data) { - /* - * A data end interrupt is sent together with the response - * for the stop command. - */ - if (intmask & SDHCI_INT_DATA_END) - return; - - printk(KERN_ERR "%s: Got data interrupt even though no " - "data operation was in progress.\n", - mmc_hostname(host->mmc)); - sdhci_dumpregs(host); - - return; - } - - if (intmask & SDHCI_INT_DATA_TIMEOUT) - host->data->error = MMC_ERR_TIMEOUT; - else if (intmask & SDHCI_INT_DATA_CRC) - host->data->error = MMC_ERR_BADCRC; - else if (intmask & SDHCI_INT_DATA_END_BIT) - host->data->error = MMC_ERR_FAILED; - - if (host->data->error != MMC_ERR_NONE) - sdhci_finish_data(host); - else { - if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) - sdhci_transfer_pio(host); - - if (intmask & SDHCI_INT_DATA_END) - sdhci_finish_data(host); - } -} - -static irqreturn_t sdhci_irq(int irq, void *dev_id) -{ - irqreturn_t result; - struct sdhci_host* host = dev_id; - u32 intmask; - - spin_lock(&host->lock); - - intmask = readl(host->ioaddr + SDHCI_INT_STATUS); - - if (!intmask || intmask == 0xffffffff) { - result = IRQ_NONE; - goto out; - } - - DBG("*** %s got interrupt: 0x%08x\n", host->slot_descr, intmask); - - if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { - writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE), - host->ioaddr + SDHCI_INT_STATUS); - tasklet_schedule(&host->card_tasklet); - } - - intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE); - - if (intmask & SDHCI_INT_CMD_MASK) { - writel(intmask & SDHCI_INT_CMD_MASK, - host->ioaddr + SDHCI_INT_STATUS); - sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); - } - - if (intmask & SDHCI_INT_DATA_MASK) { - writel(intmask & SDHCI_INT_DATA_MASK, - host->ioaddr + SDHCI_INT_STATUS); - sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); - } - - intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK); - - if (intmask & SDHCI_INT_BUS_POWER) { - printk(KERN_ERR "%s: Card is consuming too much power!\n", - mmc_hostname(host->mmc)); - writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS); - } - - intmask &= SDHCI_INT_BUS_POWER; - - if (intmask) { - printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n", - mmc_hostname(host->mmc), intmask); - sdhci_dumpregs(host); - - writel(intmask, host->ioaddr + SDHCI_INT_STATUS); - } - - result = IRQ_HANDLED; - - mmiowb(); -out: - spin_unlock(&host->lock); - - return result; -} - -/*****************************************************************************\ - * * - * Suspend/resume * - * * -\*****************************************************************************/ - -#ifdef CONFIG_PM - -static int sdhci_suspend (struct pci_dev *pdev, pm_message_t state) -{ - struct sdhci_chip *chip; - int i, ret; - - chip = pci_get_drvdata(pdev); - if (!chip) - return 0; - - DBG("Suspending...\n"); - - for (i = 0;i < chip->num_slots;i++) { - if (!chip->hosts[i]) - continue; - ret = mmc_suspend_host(chip->hosts[i]->mmc, state); - if (ret) { - for (i--;i >= 0;i--) - mmc_resume_host(chip->hosts[i]->mmc); - return ret; - } - } - - pci_save_state(pdev); - pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); - - for (i = 0;i < chip->num_slots;i++) { - if (!chip->hosts[i]) - continue; - free_irq(chip->hosts[i]->irq, chip->hosts[i]); - } - - pci_disable_device(pdev); - pci_set_power_state(pdev, pci_choose_state(pdev, state)); - - return 0; -} - -static int sdhci_resume (struct pci_dev *pdev) -{ - struct sdhci_chip *chip; - int i, ret; - - chip = pci_get_drvdata(pdev); - if (!chip) - return 0; - - DBG("Resuming...\n"); - - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - ret = pci_enable_device(pdev); - if (ret) - return ret; - - for (i = 0;i < chip->num_slots;i++) { - if (!chip->hosts[i]) - continue; - if (chip->hosts[i]->flags & SDHCI_USE_DMA) - pci_set_master(pdev); - ret = request_irq(chip->hosts[i]->irq, sdhci_irq, - IRQF_SHARED, chip->hosts[i]->slot_descr, - chip->hosts[i]); - if (ret) - return ret; - sdhci_init(chip->hosts[i]); - mmiowb(); - ret = mmc_resume_host(chip->hosts[i]->mmc); - if (ret) - return ret; - } - - return 0; -} - -#else /* CONFIG_PM */ - -#define sdhci_suspend NULL -#define sdhci_resume NULL - -#endif /* CONFIG_PM */ - -/*****************************************************************************\ - * * - * Device probing/removal * - * * -\*****************************************************************************/ - -static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) -{ - int ret; - unsigned int version; - struct sdhci_chip *chip; - struct mmc_host *mmc; - struct sdhci_host *host; - - u8 first_bar; - unsigned int caps; - - chip = pci_get_drvdata(pdev); - BUG_ON(!chip); - - ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &first_bar); - if (ret) - return ret; - - first_bar &= PCI_SLOT_INFO_FIRST_BAR_MASK; - - if (first_bar > 5) { - printk(KERN_ERR DRIVER_NAME ": Invalid first BAR. Aborting.\n"); - return -ENODEV; - } - - if (!(pci_resource_flags(pdev, first_bar + slot) & IORESOURCE_MEM)) { - printk(KERN_ERR DRIVER_NAME ": BAR is not iomem. Aborting.\n"); - return -ENODEV; - } - - if (pci_resource_len(pdev, first_bar + slot) != 0x100) { - printk(KERN_ERR DRIVER_NAME ": Invalid iomem size. " - "You may experience problems.\n"); - } - - if ((pdev->class & 0x0000FF) == PCI_SDHCI_IFVENDOR) { - printk(KERN_ERR DRIVER_NAME ": Vendor specific interface. Aborting.\n"); - return -ENODEV; - } - - if ((pdev->class & 0x0000FF) > PCI_SDHCI_IFVENDOR) { - printk(KERN_ERR DRIVER_NAME ": Unknown interface. Aborting.\n"); - return -ENODEV; - } - - mmc = mmc_alloc_host(sizeof(struct sdhci_host), &pdev->dev); - if (!mmc) - return -ENOMEM; - - host = mmc_priv(mmc); - host->mmc = mmc; - - host->chip = chip; - chip->hosts[slot] = host; - - host->bar = first_bar + slot; - - host->addr = pci_resource_start(pdev, host->bar); - host->irq = pdev->irq; - - DBG("slot %d at 0x%08lx, irq %d\n", slot, host->addr, host->irq); - - snprintf(host->slot_descr, 20, "sdhci:slot%d", slot); - - ret = pci_request_region(pdev, host->bar, host->slot_descr); - if (ret) - goto free; - - host->ioaddr = ioremap_nocache(host->addr, - pci_resource_len(pdev, host->bar)); - if (!host->ioaddr) { - ret = -ENOMEM; - goto release; - } - - sdhci_reset(host, SDHCI_RESET_ALL); - - version = readw(host->ioaddr + SDHCI_HOST_VERSION); - version = (version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT; - if (version != 0) { - printk(KERN_ERR "%s: Unknown controller version (%d). " - "You may experience problems.\n", host->slot_descr, - version); - } - - caps = readl(host->ioaddr + SDHCI_CAPABILITIES); - - if (debug_nodma) - DBG("DMA forced off\n"); - else if (debug_forcedma) { - DBG("DMA forced on\n"); - host->flags |= SDHCI_USE_DMA; - } else if (chip->quirks & SDHCI_QUIRK_FORCE_DMA) - host->flags |= SDHCI_USE_DMA; - else if ((pdev->class & 0x0000FF) != PCI_SDHCI_IFDMA) - DBG("Controller doesn't have DMA interface\n"); - else if (!(caps & SDHCI_CAN_DO_DMA)) - DBG("Controller doesn't have DMA capability\n"); - else - host->flags |= SDHCI_USE_DMA; - - if (host->flags & SDHCI_USE_DMA) { - if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { - printk(KERN_WARNING "%s: No suitable DMA available. " - "Falling back to PIO.\n", host->slot_descr); - host->flags &= ~SDHCI_USE_DMA; - } - } - - if (host->flags & SDHCI_USE_DMA) - pci_set_master(pdev); - else /* XXX: Hack to get MMC layer to avoid highmem */ - pdev->dma_mask = 0; - - host->max_clk = - (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; - if (host->max_clk == 0) { - printk(KERN_ERR "%s: Hardware doesn't specify base clock " - "frequency.\n", host->slot_descr); - ret = -ENODEV; - goto unmap; - } - host->max_clk *= 1000000; - - host->timeout_clk = - (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT; - if (host->timeout_clk == 0) { - printk(KERN_ERR "%s: Hardware doesn't specify timeout clock " - "frequency.\n", host->slot_descr); - ret = -ENODEV; - goto unmap; - } - if (caps & SDHCI_TIMEOUT_CLK_UNIT) - host->timeout_clk *= 1000; - - /* - * Set host parameters. - */ - mmc->ops = &sdhci_ops; - mmc->f_min = host->max_clk / 256; - mmc->f_max = host->max_clk; - mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; - - if (caps & SDHCI_CAN_DO_HISPD) - mmc->caps |= MMC_CAP_SD_HIGHSPEED; - - mmc->ocr_avail = 0; - if (caps & SDHCI_CAN_VDD_330) - mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34; - if (caps & SDHCI_CAN_VDD_300) - mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31; - if (caps & SDHCI_CAN_VDD_180) - mmc->ocr_avail |= MMC_VDD_17_18|MMC_VDD_18_19; - - if (mmc->ocr_avail == 0) { - printk(KERN_ERR "%s: Hardware doesn't report any " - "support voltages.\n", host->slot_descr); - ret = -ENODEV; - goto unmap; - } - - spin_lock_init(&host->lock); - - /* - * Maximum number of segments. Hardware cannot do scatter lists. - */ - if (host->flags & SDHCI_USE_DMA) - mmc->max_hw_segs = 1; - else - mmc->max_hw_segs = 16; - mmc->max_phys_segs = 16; - - /* - * Maximum number of sectors in one transfer. Limited by DMA boundary - * size (512KiB). - */ - mmc->max_req_size = 524288; - - /* - * Maximum segment size. Could be one segment with the maximum number - * of bytes. - */ - mmc->max_seg_size = mmc->max_req_size; - - /* - * Maximum block size. This varies from controller to controller and - * is specified in the capabilities register. - */ - mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT; - if (mmc->max_blk_size >= 3) { - printk(KERN_ERR "%s: Invalid maximum block size.\n", - host->slot_descr); - ret = -ENODEV; - goto unmap; - } - mmc->max_blk_size = 512 << mmc->max_blk_size; - - /* - * Maximum block count. - */ - mmc->max_blk_count = 65535; - - /* - * Init tasklets. - */ - tasklet_init(&host->card_tasklet, - sdhci_tasklet_card, (unsigned long)host); - tasklet_init(&host->finish_tasklet, - sdhci_tasklet_finish, (unsigned long)host); - - setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host); - - ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED, - host->slot_descr, host); - if (ret) - goto untasklet; - - sdhci_init(host); - -#ifdef CONFIG_MMC_DEBUG - sdhci_dumpregs(host); -#endif - - mmiowb(); - - mmc_add_host(mmc); - - printk(KERN_INFO "%s: SDHCI at 0x%08lx irq %d %s\n", mmc_hostname(mmc), - host->addr, host->irq, - (host->flags & SDHCI_USE_DMA)?"DMA":"PIO"); - - return 0; - -untasklet: - tasklet_kill(&host->card_tasklet); - tasklet_kill(&host->finish_tasklet); -unmap: - iounmap(host->ioaddr); -release: - pci_release_region(pdev, host->bar); -free: - mmc_free_host(mmc); - - return ret; -} - -static void sdhci_remove_slot(struct pci_dev *pdev, int slot) -{ - struct sdhci_chip *chip; - struct mmc_host *mmc; - struct sdhci_host *host; - - chip = pci_get_drvdata(pdev); - host = chip->hosts[slot]; - mmc = host->mmc; - - chip->hosts[slot] = NULL; - - mmc_remove_host(mmc); - - sdhci_reset(host, SDHCI_RESET_ALL); - - free_irq(host->irq, host); - - del_timer_sync(&host->timer); - - tasklet_kill(&host->card_tasklet); - tasklet_kill(&host->finish_tasklet); - - iounmap(host->ioaddr); - - pci_release_region(pdev, host->bar); - - mmc_free_host(mmc); -} - -static int __devinit sdhci_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - int ret, i; - u8 slots, rev; - struct sdhci_chip *chip; - - BUG_ON(pdev == NULL); - BUG_ON(ent == NULL); - - pci_read_config_byte(pdev, PCI_CLASS_REVISION, &rev); - - printk(KERN_INFO DRIVER_NAME - ": SDHCI controller found at %s [%04x:%04x] (rev %x)\n", - pci_name(pdev), (int)pdev->vendor, (int)pdev->device, - (int)rev); - - ret = pci_read_config_byte(pdev, PCI_SLOT_INFO, &slots); - if (ret) - return ret; - - slots = PCI_SLOT_INFO_SLOTS(slots) + 1; - DBG("found %d slot(s)\n", slots); - if (slots == 0) - return -ENODEV; - - ret = pci_enable_device(pdev); - if (ret) - return ret; - - chip = kzalloc(sizeof(struct sdhci_chip) + - sizeof(struct sdhci_host*) * slots, GFP_KERNEL); - if (!chip) { - ret = -ENOMEM; - goto err; - } - - chip->pdev = pdev; - chip->quirks = ent->driver_data; - - if (debug_quirks) - chip->quirks = debug_quirks; - - chip->num_slots = slots; - pci_set_drvdata(pdev, chip); - - for (i = 0;i < slots;i++) { - ret = sdhci_probe_slot(pdev, i); - if (ret) { - for (i--;i >= 0;i--) - sdhci_remove_slot(pdev, i); - goto free; - } - } - - return 0; - -free: - pci_set_drvdata(pdev, NULL); - kfree(chip); - -err: - pci_disable_device(pdev); - return ret; -} - -static void __devexit sdhci_remove(struct pci_dev *pdev) -{ - int i; - struct sdhci_chip *chip; - - chip = pci_get_drvdata(pdev); - - if (chip) { - for (i = 0;i < chip->num_slots;i++) - sdhci_remove_slot(pdev, i); - - pci_set_drvdata(pdev, NULL); - - kfree(chip); - } - - pci_disable_device(pdev); -} - -static struct pci_driver sdhci_driver = { - .name = DRIVER_NAME, - .id_table = pci_ids, - .probe = sdhci_probe, - .remove = __devexit_p(sdhci_remove), - .suspend = sdhci_suspend, - .resume = sdhci_resume, -}; - -/*****************************************************************************\ - * * - * Driver init/exit * - * * -\*****************************************************************************/ - -static int __init sdhci_drv_init(void) -{ - printk(KERN_INFO DRIVER_NAME - ": Secure Digital Host Controller Interface driver\n"); - printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); - - return pci_register_driver(&sdhci_driver); -} - -static void __exit sdhci_drv_exit(void) -{ - DBG("Exiting\n"); - - pci_unregister_driver(&sdhci_driver); -} - -module_init(sdhci_drv_init); -module_exit(sdhci_drv_exit); - -module_param(debug_nodma, uint, 0444); -module_param(debug_forcedma, uint, 0444); -module_param(debug_quirks, uint, 0444); - -MODULE_AUTHOR("Pierre Ossman "); -MODULE_DESCRIPTION("Secure Digital Host Controller Interface driver"); -MODULE_LICENSE("GPL"); - -MODULE_PARM_DESC(debug_nodma, "Forcefully disable DMA transfers. (default 0)"); -MODULE_PARM_DESC(debug_forcedma, "Forcefully enable DMA transfers. (default 0)"); -MODULE_PARM_DESC(debug_quirks, "Force certain quirks."); diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h deleted file mode 100644 index 7400f4bc114..00000000000 --- a/drivers/mmc/sdhci.h +++ /dev/null @@ -1,210 +0,0 @@ -/* - * linux/drivers/mmc/sdhci.h - Secure Digital Host Controller Interface driver - * - * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - */ - -/* - * PCI registers - */ - -#define PCI_SDHCI_IFPIO 0x00 -#define PCI_SDHCI_IFDMA 0x01 -#define PCI_SDHCI_IFVENDOR 0x02 - -#define PCI_SLOT_INFO 0x40 /* 8 bits */ -#define PCI_SLOT_INFO_SLOTS(x) ((x >> 4) & 7) -#define PCI_SLOT_INFO_FIRST_BAR_MASK 0x07 - -/* - * Controller registers - */ - -#define SDHCI_DMA_ADDRESS 0x00 - -#define SDHCI_BLOCK_SIZE 0x04 -#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF)) - -#define SDHCI_BLOCK_COUNT 0x06 - -#define SDHCI_ARGUMENT 0x08 - -#define SDHCI_TRANSFER_MODE 0x0C -#define SDHCI_TRNS_DMA 0x01 -#define SDHCI_TRNS_BLK_CNT_EN 0x02 -#define SDHCI_TRNS_ACMD12 0x04 -#define SDHCI_TRNS_READ 0x10 -#define SDHCI_TRNS_MULTI 0x20 - -#define SDHCI_COMMAND 0x0E -#define SDHCI_CMD_RESP_MASK 0x03 -#define SDHCI_CMD_CRC 0x08 -#define SDHCI_CMD_INDEX 0x10 -#define SDHCI_CMD_DATA 0x20 - -#define SDHCI_CMD_RESP_NONE 0x00 -#define SDHCI_CMD_RESP_LONG 0x01 -#define SDHCI_CMD_RESP_SHORT 0x02 -#define SDHCI_CMD_RESP_SHORT_BUSY 0x03 - -#define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff)) - -#define SDHCI_RESPONSE 0x10 - -#define SDHCI_BUFFER 0x20 - -#define SDHCI_PRESENT_STATE 0x24 -#define SDHCI_CMD_INHIBIT 0x00000001 -#define SDHCI_DATA_INHIBIT 0x00000002 -#define SDHCI_DOING_WRITE 0x00000100 -#define SDHCI_DOING_READ 0x00000200 -#define SDHCI_SPACE_AVAILABLE 0x00000400 -#define SDHCI_DATA_AVAILABLE 0x00000800 -#define SDHCI_CARD_PRESENT 0x00010000 -#define SDHCI_WRITE_PROTECT 0x00080000 - -#define SDHCI_HOST_CONTROL 0x28 -#define SDHCI_CTRL_LED 0x01 -#define SDHCI_CTRL_4BITBUS 0x02 -#define SDHCI_CTRL_HISPD 0x04 - -#define SDHCI_POWER_CONTROL 0x29 -#define SDHCI_POWER_ON 0x01 -#define SDHCI_POWER_180 0x0A -#define SDHCI_POWER_300 0x0C -#define SDHCI_POWER_330 0x0E - -#define SDHCI_BLOCK_GAP_CONTROL 0x2A - -#define SDHCI_WALK_UP_CONTROL 0x2B - -#define SDHCI_CLOCK_CONTROL 0x2C -#define SDHCI_DIVIDER_SHIFT 8 -#define SDHCI_CLOCK_CARD_EN 0x0004 -#define SDHCI_CLOCK_INT_STABLE 0x0002 -#define SDHCI_CLOCK_INT_EN 0x0001 - -#define SDHCI_TIMEOUT_CONTROL 0x2E - -#define SDHCI_SOFTWARE_RESET 0x2F -#define SDHCI_RESET_ALL 0x01 -#define SDHCI_RESET_CMD 0x02 -#define SDHCI_RESET_DATA 0x04 - -#define SDHCI_INT_STATUS 0x30 -#define SDHCI_INT_ENABLE 0x34 -#define SDHCI_SIGNAL_ENABLE 0x38 -#define SDHCI_INT_RESPONSE 0x00000001 -#define SDHCI_INT_DATA_END 0x00000002 -#define SDHCI_INT_DMA_END 0x00000008 -#define SDHCI_INT_SPACE_AVAIL 0x00000010 -#define SDHCI_INT_DATA_AVAIL 0x00000020 -#define SDHCI_INT_CARD_INSERT 0x00000040 -#define SDHCI_INT_CARD_REMOVE 0x00000080 -#define SDHCI_INT_CARD_INT 0x00000100 -#define SDHCI_INT_TIMEOUT 0x00010000 -#define SDHCI_INT_CRC 0x00020000 -#define SDHCI_INT_END_BIT 0x00040000 -#define SDHCI_INT_INDEX 0x00080000 -#define SDHCI_INT_DATA_TIMEOUT 0x00100000 -#define SDHCI_INT_DATA_CRC 0x00200000 -#define SDHCI_INT_DATA_END_BIT 0x00400000 -#define SDHCI_INT_BUS_POWER 0x00800000 -#define SDHCI_INT_ACMD12ERR 0x01000000 - -#define SDHCI_INT_NORMAL_MASK 0x00007FFF -#define SDHCI_INT_ERROR_MASK 0xFFFF8000 - -#define SDHCI_INT_CMD_MASK (SDHCI_INT_RESPONSE | SDHCI_INT_TIMEOUT | \ - SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX) -#define SDHCI_INT_DATA_MASK (SDHCI_INT_DATA_END | SDHCI_INT_DMA_END | \ - SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \ - SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \ - SDHCI_INT_DATA_END_BIT) - -#define SDHCI_ACMD12_ERR 0x3C - -/* 3E-3F reserved */ - -#define SDHCI_CAPABILITIES 0x40 -#define SDHCI_TIMEOUT_CLK_MASK 0x0000003F -#define SDHCI_TIMEOUT_CLK_SHIFT 0 -#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080 -#define SDHCI_CLOCK_BASE_MASK 0x00003F00 -#define SDHCI_CLOCK_BASE_SHIFT 8 -#define SDHCI_MAX_BLOCK_MASK 0x00030000 -#define SDHCI_MAX_BLOCK_SHIFT 16 -#define SDHCI_CAN_DO_HISPD 0x00200000 -#define SDHCI_CAN_DO_DMA 0x00400000 -#define SDHCI_CAN_VDD_330 0x01000000 -#define SDHCI_CAN_VDD_300 0x02000000 -#define SDHCI_CAN_VDD_180 0x04000000 - -/* 44-47 reserved for more caps */ - -#define SDHCI_MAX_CURRENT 0x48 - -/* 4C-4F reserved for more max current */ - -/* 50-FB reserved */ - -#define SDHCI_SLOT_INT_STATUS 0xFC - -#define SDHCI_HOST_VERSION 0xFE -#define SDHCI_VENDOR_VER_MASK 0xFF00 -#define SDHCI_VENDOR_VER_SHIFT 8 -#define SDHCI_SPEC_VER_MASK 0x00FF -#define SDHCI_SPEC_VER_SHIFT 0 - -struct sdhci_chip; - -struct sdhci_host { - struct sdhci_chip *chip; - struct mmc_host *mmc; /* MMC structure */ - - spinlock_t lock; /* Mutex */ - - int flags; /* Host attributes */ -#define SDHCI_USE_DMA (1<<0) - - unsigned int max_clk; /* Max possible freq (MHz) */ - unsigned int timeout_clk; /* Timeout freq (KHz) */ - - unsigned int clock; /* Current clock (MHz) */ - unsigned short power; /* Current voltage */ - - struct mmc_request *mrq; /* Current request */ - struct mmc_command *cmd; /* Current command */ - struct mmc_data *data; /* Current data request */ - - struct scatterlist *cur_sg; /* We're working on this */ - int num_sg; /* Entries left */ - int offset; /* Offset into current sg */ - int remain; /* Bytes left in current */ - - char slot_descr[20]; /* Name for reservations */ - - int irq; /* Device IRQ */ - int bar; /* PCI BAR index */ - unsigned long addr; /* Bus address */ - void __iomem * ioaddr; /* Mapped address */ - - struct tasklet_struct card_tasklet; /* Tasklet structures */ - struct tasklet_struct finish_tasklet; - - struct timer_list timer; /* Timer for timeouts */ -}; - -struct sdhci_chip { - struct pci_dev *pdev; - - unsigned long quirks; - - int num_slots; /* Slots on controller */ - struct sdhci_host *hosts[0]; /* Pointers to hosts */ -}; diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c deleted file mode 100644 index b0d77d29841..00000000000 --- a/drivers/mmc/tifm_sd.c +++ /dev/null @@ -1,1102 +0,0 @@ -/* - * tifm_sd.c - TI FlashMedia driver - * - * Copyright (C) 2006 Alex Dubov - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Special thanks to Brad Campbell for extensive testing of this driver. - * - */ - - -#include -#include -#include -#include -#include - -#define DRIVER_NAME "tifm_sd" -#define DRIVER_VERSION "0.8" - -static int no_dma = 0; -static int fixed_timeout = 0; -module_param(no_dma, bool, 0644); -module_param(fixed_timeout, bool, 0644); - -/* Constants here are mostly from OMAP5912 datasheet */ -#define TIFM_MMCSD_RESET 0x0002 -#define TIFM_MMCSD_CLKMASK 0x03ff -#define TIFM_MMCSD_POWER 0x0800 -#define TIFM_MMCSD_4BBUS 0x8000 -#define TIFM_MMCSD_RXDE 0x8000 /* rx dma enable */ -#define TIFM_MMCSD_TXDE 0x0080 /* tx dma enable */ -#define TIFM_MMCSD_BUFINT 0x0c00 /* set bits: AE, AF */ -#define TIFM_MMCSD_DPE 0x0020 /* data timeout counted in kilocycles */ -#define TIFM_MMCSD_INAB 0x0080 /* abort / initialize command */ -#define TIFM_MMCSD_READ 0x8000 - -#define TIFM_MMCSD_ERRMASK 0x01e0 /* set bits: CCRC, CTO, DCRC, DTO */ -#define TIFM_MMCSD_EOC 0x0001 /* end of command phase */ -#define TIFM_MMCSD_CD 0x0002 /* card detect */ -#define TIFM_MMCSD_CB 0x0004 /* card enter busy state */ -#define TIFM_MMCSD_BRS 0x0008 /* block received/sent */ -#define TIFM_MMCSD_EOFB 0x0010 /* card exit busy state */ -#define TIFM_MMCSD_DTO 0x0020 /* data time-out */ -#define TIFM_MMCSD_DCRC 0x0040 /* data crc error */ -#define TIFM_MMCSD_CTO 0x0080 /* command time-out */ -#define TIFM_MMCSD_CCRC 0x0100 /* command crc error */ -#define TIFM_MMCSD_AF 0x0400 /* fifo almost full */ -#define TIFM_MMCSD_AE 0x0800 /* fifo almost empty */ -#define TIFM_MMCSD_OCRB 0x1000 /* OCR busy */ -#define TIFM_MMCSD_CIRQ 0x2000 /* card irq (cmd40/sdio) */ -#define TIFM_MMCSD_CERR 0x4000 /* card status error */ - -#define TIFM_MMCSD_ODTO 0x0040 /* open drain / extended timeout */ -#define TIFM_MMCSD_CARD_RO 0x0200 /* card is read-only */ - -#define TIFM_MMCSD_FIFO_SIZE 0x0020 - -#define TIFM_MMCSD_RSP_R0 0x0000 -#define TIFM_MMCSD_RSP_R1 0x0100 -#define TIFM_MMCSD_RSP_R2 0x0200 -#define TIFM_MMCSD_RSP_R3 0x0300 -#define TIFM_MMCSD_RSP_R4 0x0400 -#define TIFM_MMCSD_RSP_R5 0x0500 -#define TIFM_MMCSD_RSP_R6 0x0600 - -#define TIFM_MMCSD_RSP_BUSY 0x0800 - -#define TIFM_MMCSD_CMD_BC 0x0000 -#define TIFM_MMCSD_CMD_BCR 0x1000 -#define TIFM_MMCSD_CMD_AC 0x2000 -#define TIFM_MMCSD_CMD_ADTC 0x3000 - -#define TIFM_MMCSD_MAX_BLOCK_SIZE 0x0800UL - -enum { - CMD_READY = 0x0001, - FIFO_READY = 0x0002, - BRS_READY = 0x0004, - SCMD_ACTIVE = 0x0008, - SCMD_READY = 0x0010, - CARD_BUSY = 0x0020, - DATA_CARRY = 0x0040 -}; - -struct tifm_sd { - struct tifm_dev *dev; - - unsigned short eject:1, - open_drain:1, - no_dma:1; - unsigned short cmd_flags; - - unsigned int clk_freq; - unsigned int clk_div; - unsigned long timeout_jiffies; - - struct tasklet_struct finish_tasklet; - struct timer_list timer; - struct mmc_request *req; - - int sg_len; - int sg_pos; - unsigned int block_pos; - struct scatterlist bounce_buf; - unsigned char bounce_buf_data[TIFM_MMCSD_MAX_BLOCK_SIZE]; -}; - -/* for some reason, host won't respond correctly to readw/writew */ -static void tifm_sd_read_fifo(struct tifm_sd *host, struct page *pg, - unsigned int off, unsigned int cnt) -{ - struct tifm_dev *sock = host->dev; - unsigned char *buf; - unsigned int pos = 0, val; - - buf = kmap_atomic(pg, KM_BIO_DST_IRQ) + off; - if (host->cmd_flags & DATA_CARRY) { - buf[pos++] = host->bounce_buf_data[0]; - host->cmd_flags &= ~DATA_CARRY; - } - - while (pos < cnt) { - val = readl(sock->addr + SOCK_MMCSD_DATA); - buf[pos++] = val & 0xff; - if (pos == cnt) { - host->bounce_buf_data[0] = (val >> 8) & 0xff; - host->cmd_flags |= DATA_CARRY; - break; - } - buf[pos++] = (val >> 8) & 0xff; - } - kunmap_atomic(buf - off, KM_BIO_DST_IRQ); -} - -static void tifm_sd_write_fifo(struct tifm_sd *host, struct page *pg, - unsigned int off, unsigned int cnt) -{ - struct tifm_dev *sock = host->dev; - unsigned char *buf; - unsigned int pos = 0, val; - - buf = kmap_atomic(pg, KM_BIO_SRC_IRQ) + off; - if (host->cmd_flags & DATA_CARRY) { - val = host->bounce_buf_data[0] | ((buf[pos++] << 8) & 0xff00); - writel(val, sock->addr + SOCK_MMCSD_DATA); - host->cmd_flags &= ~DATA_CARRY; - } - - while (pos < cnt) { - val = buf[pos++]; - if (pos == cnt) { - host->bounce_buf_data[0] = val & 0xff; - host->cmd_flags |= DATA_CARRY; - break; - } - val |= (buf[pos++] << 8) & 0xff00; - writel(val, sock->addr + SOCK_MMCSD_DATA); - } - kunmap_atomic(buf - off, KM_BIO_SRC_IRQ); -} - -static void tifm_sd_transfer_data(struct tifm_sd *host) -{ - struct mmc_data *r_data = host->req->cmd->data; - struct scatterlist *sg = r_data->sg; - unsigned int off, cnt, t_size = TIFM_MMCSD_FIFO_SIZE * 2; - unsigned int p_off, p_cnt; - struct page *pg; - - if (host->sg_pos == host->sg_len) - return; - while (t_size) { - cnt = sg[host->sg_pos].length - host->block_pos; - if (!cnt) { - host->block_pos = 0; - host->sg_pos++; - if (host->sg_pos == host->sg_len) { - if ((r_data->flags & MMC_DATA_WRITE) - && DATA_CARRY) - writel(host->bounce_buf_data[0], - host->dev->addr - + SOCK_MMCSD_DATA); - - return; - } - cnt = sg[host->sg_pos].length; - } - off = sg[host->sg_pos].offset + host->block_pos; - - pg = nth_page(sg[host->sg_pos].page, off >> PAGE_SHIFT); - p_off = offset_in_page(off); - p_cnt = PAGE_SIZE - p_off; - p_cnt = min(p_cnt, cnt); - p_cnt = min(p_cnt, t_size); - - if (r_data->flags & MMC_DATA_READ) - tifm_sd_read_fifo(host, pg, p_off, p_cnt); - else if (r_data->flags & MMC_DATA_WRITE) - tifm_sd_write_fifo(host, pg, p_off, p_cnt); - - t_size -= p_cnt; - host->block_pos += p_cnt; - } -} - -static void tifm_sd_copy_page(struct page *dst, unsigned int dst_off, - struct page *src, unsigned int src_off, - unsigned int count) -{ - unsigned char *src_buf = kmap_atomic(src, KM_BIO_SRC_IRQ) + src_off; - unsigned char *dst_buf = kmap_atomic(dst, KM_BIO_DST_IRQ) + dst_off; - - memcpy(dst_buf, src_buf, count); - - kunmap_atomic(dst_buf - dst_off, KM_BIO_DST_IRQ); - kunmap_atomic(src_buf - src_off, KM_BIO_SRC_IRQ); -} - -static void tifm_sd_bounce_block(struct tifm_sd *host, struct mmc_data *r_data) -{ - struct scatterlist *sg = r_data->sg; - unsigned int t_size = r_data->blksz; - unsigned int off, cnt; - unsigned int p_off, p_cnt; - struct page *pg; - - dev_dbg(&host->dev->dev, "bouncing block\n"); - while (t_size) { - cnt = sg[host->sg_pos].length - host->block_pos; - if (!cnt) { - host->block_pos = 0; - host->sg_pos++; - if (host->sg_pos == host->sg_len) - return; - cnt = sg[host->sg_pos].length; - } - off = sg[host->sg_pos].offset + host->block_pos; - - pg = nth_page(sg[host->sg_pos].page, off >> PAGE_SHIFT); - p_off = offset_in_page(off); - p_cnt = PAGE_SIZE - p_off; - p_cnt = min(p_cnt, cnt); - p_cnt = min(p_cnt, t_size); - - if (r_data->flags & MMC_DATA_WRITE) - tifm_sd_copy_page(host->bounce_buf.page, - r_data->blksz - t_size, - pg, p_off, p_cnt); - else if (r_data->flags & MMC_DATA_READ) - tifm_sd_copy_page(pg, p_off, host->bounce_buf.page, - r_data->blksz - t_size, p_cnt); - - t_size -= p_cnt; - host->block_pos += p_cnt; - } -} - -int tifm_sd_set_dma_data(struct tifm_sd *host, struct mmc_data *r_data) -{ - struct tifm_dev *sock = host->dev; - unsigned int t_size = TIFM_DMA_TSIZE * r_data->blksz; - unsigned int dma_len, dma_blk_cnt, dma_off; - struct scatterlist *sg = NULL; - unsigned long flags; - - if (host->sg_pos == host->sg_len) - return 1; - - if (host->cmd_flags & DATA_CARRY) { - host->cmd_flags &= ~DATA_CARRY; - local_irq_save(flags); - tifm_sd_bounce_block(host, r_data); - local_irq_restore(flags); - if (host->sg_pos == host->sg_len) - return 1; - } - - dma_len = sg_dma_len(&r_data->sg[host->sg_pos]) - host->block_pos; - if (!dma_len) { - host->block_pos = 0; - host->sg_pos++; - if (host->sg_pos == host->sg_len) - return 1; - dma_len = sg_dma_len(&r_data->sg[host->sg_pos]); - } - - if (dma_len < t_size) { - dma_blk_cnt = dma_len / r_data->blksz; - dma_off = host->block_pos; - host->block_pos += dma_blk_cnt * r_data->blksz; - } else { - dma_blk_cnt = TIFM_DMA_TSIZE; - dma_off = host->block_pos; - host->block_pos += t_size; - } - - if (dma_blk_cnt) - sg = &r_data->sg[host->sg_pos]; - else if (dma_len) { - if (r_data->flags & MMC_DATA_WRITE) { - local_irq_save(flags); - tifm_sd_bounce_block(host, r_data); - local_irq_restore(flags); - } else - host->cmd_flags |= DATA_CARRY; - - sg = &host->bounce_buf; - dma_off = 0; - dma_blk_cnt = 1; - } else - return 1; - - dev_dbg(&sock->dev, "setting dma for %d blocks\n", dma_blk_cnt); - writel(sg_dma_address(sg) + dma_off, sock->addr + SOCK_DMA_ADDRESS); - if (r_data->flags & MMC_DATA_WRITE) - writel((dma_blk_cnt << 8) | TIFM_DMA_TX | TIFM_DMA_EN, - sock->addr + SOCK_DMA_CONTROL); - else - writel((dma_blk_cnt << 8) | TIFM_DMA_EN, - sock->addr + SOCK_DMA_CONTROL); - - return 0; -} - -static unsigned int tifm_sd_op_flags(struct mmc_command *cmd) -{ - unsigned int rc = 0; - - switch (mmc_resp_type(cmd)) { - case MMC_RSP_NONE: - rc |= TIFM_MMCSD_RSP_R0; - break; - case MMC_RSP_R1B: - rc |= TIFM_MMCSD_RSP_BUSY; // deliberate fall-through - case MMC_RSP_R1: - rc |= TIFM_MMCSD_RSP_R1; - break; - case MMC_RSP_R2: - rc |= TIFM_MMCSD_RSP_R2; - break; - case MMC_RSP_R3: - rc |= TIFM_MMCSD_RSP_R3; - break; - default: - BUG(); - } - - switch (mmc_cmd_type(cmd)) { - case MMC_CMD_BC: - rc |= TIFM_MMCSD_CMD_BC; - break; - case MMC_CMD_BCR: - rc |= TIFM_MMCSD_CMD_BCR; - break; - case MMC_CMD_AC: - rc |= TIFM_MMCSD_CMD_AC; - break; - case MMC_CMD_ADTC: - rc |= TIFM_MMCSD_CMD_ADTC; - break; - default: - BUG(); - } - return rc; -} - -static void tifm_sd_exec(struct tifm_sd *host, struct mmc_command *cmd) -{ - struct tifm_dev *sock = host->dev; - unsigned int cmd_mask = tifm_sd_op_flags(cmd); - - if (host->open_drain) - cmd_mask |= TIFM_MMCSD_ODTO; - - if (cmd->data && (cmd->data->flags & MMC_DATA_READ)) - cmd_mask |= TIFM_MMCSD_READ; - - dev_dbg(&sock->dev, "executing opcode 0x%x, arg: 0x%x, mask: 0x%x\n", - cmd->opcode, cmd->arg, cmd_mask); - - writel((cmd->arg >> 16) & 0xffff, sock->addr + SOCK_MMCSD_ARG_HIGH); - writel(cmd->arg & 0xffff, sock->addr + SOCK_MMCSD_ARG_LOW); - writel(cmd->opcode | cmd_mask, sock->addr + SOCK_MMCSD_COMMAND); -} - -static void tifm_sd_fetch_resp(struct mmc_command *cmd, struct tifm_dev *sock) -{ - cmd->resp[0] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x1c) << 16) - | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x18); - cmd->resp[1] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x14) << 16) - | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x10); - cmd->resp[2] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x0c) << 16) - | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x08); - cmd->resp[3] = (readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x04) << 16) - | readl(sock->addr + SOCK_MMCSD_RESPONSE + 0x00); -} - -static void tifm_sd_check_status(struct tifm_sd *host) -{ - struct tifm_dev *sock = host->dev; - struct mmc_command *cmd = host->req->cmd; - - if (cmd->error != MMC_ERR_NONE) - goto finish_request; - - if (!(host->cmd_flags & CMD_READY)) - return; - - if (cmd->data) { - if (cmd->data->error != MMC_ERR_NONE) { - if ((host->cmd_flags & SCMD_ACTIVE) - && !(host->cmd_flags & SCMD_READY)) - return; - - goto finish_request; - } - - if (!(host->cmd_flags & BRS_READY)) - return; - - if (!(host->no_dma || (host->cmd_flags & FIFO_READY))) - return; - - if (cmd->data->flags & MMC_DATA_WRITE) { - if (host->req->stop) { - if (!(host->cmd_flags & SCMD_ACTIVE)) { - host->cmd_flags |= SCMD_ACTIVE; - writel(TIFM_MMCSD_EOFB - | readl(sock->addr - + SOCK_MMCSD_INT_ENABLE), - sock->addr - + SOCK_MMCSD_INT_ENABLE); - tifm_sd_exec(host, host->req->stop); - return; - } else { - if (!(host->cmd_flags & SCMD_READY) - || (host->cmd_flags & CARD_BUSY)) - return; - writel((~TIFM_MMCSD_EOFB) - & readl(sock->addr - + SOCK_MMCSD_INT_ENABLE), - sock->addr - + SOCK_MMCSD_INT_ENABLE); - } - } else { - if (host->cmd_flags & CARD_BUSY) - return; - writel((~TIFM_MMCSD_EOFB) - & readl(sock->addr - + SOCK_MMCSD_INT_ENABLE), - sock->addr + SOCK_MMCSD_INT_ENABLE); - } - } else { - if (host->req->stop) { - if (!(host->cmd_flags & SCMD_ACTIVE)) { - host->cmd_flags |= SCMD_ACTIVE; - tifm_sd_exec(host, host->req->stop); - return; - } else { - if (!(host->cmd_flags & SCMD_READY)) - return; - } - } - } - } -finish_request: - tasklet_schedule(&host->finish_tasklet); -} - -/* Called from interrupt handler */ -static void tifm_sd_data_event(struct tifm_dev *sock) -{ - struct tifm_sd *host; - unsigned int fifo_status = 0; - struct mmc_data *r_data = NULL; - - spin_lock(&sock->lock); - host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); - fifo_status = readl(sock->addr + SOCK_DMA_FIFO_STATUS); - dev_dbg(&sock->dev, "data event: fifo_status %x, flags %x\n", - fifo_status, host->cmd_flags); - - if (host->req) { - r_data = host->req->cmd->data; - - if (r_data && (fifo_status & TIFM_FIFO_READY)) { - if (tifm_sd_set_dma_data(host, r_data)) { - host->cmd_flags |= FIFO_READY; - tifm_sd_check_status(host); - } - } - } - - writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS); - spin_unlock(&sock->lock); -} - -/* Called from interrupt handler */ -static void tifm_sd_card_event(struct tifm_dev *sock) -{ - struct tifm_sd *host; - unsigned int host_status = 0; - int cmd_error = MMC_ERR_NONE; - struct mmc_command *cmd = NULL; - unsigned long flags; - - spin_lock(&sock->lock); - host = mmc_priv((struct mmc_host*)tifm_get_drvdata(sock)); - host_status = readl(sock->addr + SOCK_MMCSD_STATUS); - dev_dbg(&sock->dev, "host event: host_status %x, flags %x\n", - host_status, host->cmd_flags); - - if (host->req) { - cmd = host->req->cmd; - - if (host_status & TIFM_MMCSD_ERRMASK) { - writel(host_status & TIFM_MMCSD_ERRMASK, - sock->addr + SOCK_MMCSD_STATUS); - if (host_status & TIFM_MMCSD_CTO) - cmd_error = MMC_ERR_TIMEOUT; - else if (host_status & TIFM_MMCSD_CCRC) - cmd_error = MMC_ERR_BADCRC; - - if (cmd->data) { - if (host_status & TIFM_MMCSD_DTO) - cmd->data->error = MMC_ERR_TIMEOUT; - else if (host_status & TIFM_MMCSD_DCRC) - cmd->data->error = MMC_ERR_BADCRC; - } - - writel(TIFM_FIFO_INT_SETALL, - sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); - writel(TIFM_DMA_RESET, sock->addr + SOCK_DMA_CONTROL); - - if (host->req->stop) { - if (host->cmd_flags & SCMD_ACTIVE) { - host->req->stop->error = cmd_error; - host->cmd_flags |= SCMD_READY; - } else { - cmd->error = cmd_error; - host->cmd_flags |= SCMD_ACTIVE; - tifm_sd_exec(host, host->req->stop); - goto done; - } - } else - cmd->error = cmd_error; - } else { - if (host_status & (TIFM_MMCSD_EOC | TIFM_MMCSD_CERR)) { - if (!(host->cmd_flags & CMD_READY)) { - host->cmd_flags |= CMD_READY; - tifm_sd_fetch_resp(cmd, sock); - } else if (host->cmd_flags & SCMD_ACTIVE) { - host->cmd_flags |= SCMD_READY; - tifm_sd_fetch_resp(host->req->stop, - sock); - } - } - if (host_status & TIFM_MMCSD_BRS) - host->cmd_flags |= BRS_READY; - } - - if (host->no_dma && cmd->data) { - if (host_status & TIFM_MMCSD_AE) - writel(host_status & TIFM_MMCSD_AE, - sock->addr + SOCK_MMCSD_STATUS); - - if (host_status & (TIFM_MMCSD_AE | TIFM_MMCSD_AF - | TIFM_MMCSD_BRS)) { - local_irq_save(flags); - tifm_sd_transfer_data(host); - local_irq_restore(flags); - host_status &= ~TIFM_MMCSD_AE; - } - } - - if (host_status & TIFM_MMCSD_EOFB) - host->cmd_flags &= ~CARD_BUSY; - else if (host_status & TIFM_MMCSD_CB) - host->cmd_flags |= CARD_BUSY; - - tifm_sd_check_status(host); - } -done: - writel(host_status, sock->addr + SOCK_MMCSD_STATUS); - spin_unlock(&sock->lock); -} - -static void tifm_sd_set_data_timeout(struct tifm_sd *host, - struct mmc_data *data) -{ - struct tifm_dev *sock = host->dev; - unsigned int data_timeout = data->timeout_clks; - - if (fixed_timeout) - return; - - data_timeout += data->timeout_ns / - ((1000000000UL / host->clk_freq) * host->clk_div); - - if (data_timeout < 0xffff) { - writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); - writel((~TIFM_MMCSD_DPE) - & readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), - sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); - } else { - data_timeout = (data_timeout >> 10) + 1; - if (data_timeout > 0xffff) - data_timeout = 0; /* set to unlimited */ - writel(data_timeout, sock->addr + SOCK_MMCSD_DATA_TO); - writel(TIFM_MMCSD_DPE - | readl(sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG), - sock->addr + SOCK_MMCSD_SDIO_MODE_CONFIG); - } -} - -static void tifm_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) -{ - struct tifm_sd *host = mmc_priv(mmc); - struct tifm_dev *sock = host->dev; - unsigned long flags; - struct mmc_data *r_data = mrq->cmd->data; - - spin_lock_irqsave(&sock->lock, flags); - if (host->eject) { - spin_unlock_irqrestore(&sock->lock, flags); - goto err_out; - } - - if (host->req) { - printk(KERN_ERR "%s : unfinished request detected\n", - sock->dev.bus_id); - spin_unlock_irqrestore(&sock->lock, flags); - goto err_out; - } - - host->cmd_flags = 0; - host->block_pos = 0; - host->sg_pos = 0; - - if (r_data) { - tifm_sd_set_data_timeout(host, r_data); - - if ((r_data->flags & MMC_DATA_WRITE) && !mrq->stop) - writel(TIFM_MMCSD_EOFB - | readl(sock->addr + SOCK_MMCSD_INT_ENABLE), - sock->addr + SOCK_MMCSD_INT_ENABLE); - - if (host->no_dma) { - writel(TIFM_MMCSD_BUFINT - | readl(sock->addr + SOCK_MMCSD_INT_ENABLE), - sock->addr + SOCK_MMCSD_INT_ENABLE); - writel(((TIFM_MMCSD_FIFO_SIZE - 1) << 8) - | (TIFM_MMCSD_FIFO_SIZE - 1), - sock->addr + SOCK_MMCSD_BUFFER_CONFIG); - - host->sg_len = r_data->sg_len; - } else { - sg_init_one(&host->bounce_buf, host->bounce_buf_data, - r_data->blksz); - - if(1 != tifm_map_sg(sock, &host->bounce_buf, 1, - r_data->flags & MMC_DATA_WRITE - ? PCI_DMA_TODEVICE - : PCI_DMA_FROMDEVICE)) { - printk(KERN_ERR "%s : scatterlist map failed\n", - sock->dev.bus_id); - spin_unlock_irqrestore(&sock->lock, flags); - goto err_out; - } - host->sg_len = tifm_map_sg(sock, r_data->sg, - r_data->sg_len, - r_data->flags - & MMC_DATA_WRITE - ? PCI_DMA_TODEVICE - : PCI_DMA_FROMDEVICE); - if (host->sg_len < 1) { - printk(KERN_ERR "%s : scatterlist map failed\n", - sock->dev.bus_id); - tifm_unmap_sg(sock, &host->bounce_buf, 1, - r_data->flags & MMC_DATA_WRITE - ? PCI_DMA_TODEVICE - : PCI_DMA_FROMDEVICE); - spin_unlock_irqrestore(&sock->lock, flags); - goto err_out; - } - - writel(TIFM_FIFO_INT_SETALL, - sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); - writel(ilog2(r_data->blksz) - 2, - sock->addr + SOCK_FIFO_PAGE_SIZE); - writel(TIFM_FIFO_ENABLE, - sock->addr + SOCK_FIFO_CONTROL); - writel(TIFM_FIFO_INTMASK, - sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); - - if (r_data->flags & MMC_DATA_WRITE) - writel(TIFM_MMCSD_TXDE, - sock->addr + SOCK_MMCSD_BUFFER_CONFIG); - else - writel(TIFM_MMCSD_RXDE, - sock->addr + SOCK_MMCSD_BUFFER_CONFIG); - - tifm_sd_set_dma_data(host, r_data); - } - - writel(r_data->blocks - 1, - sock->addr + SOCK_MMCSD_NUM_BLOCKS); - writel(r_data->blksz - 1, - sock->addr + SOCK_MMCSD_BLOCK_LEN); - } - - host->req = mrq; - mod_timer(&host->timer, jiffies + host->timeout_jiffies); - writel(TIFM_CTRL_LED | readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); - tifm_sd_exec(host, mrq->cmd); - spin_unlock_irqrestore(&sock->lock, flags); - return; - -err_out: - mrq->cmd->error = MMC_ERR_TIMEOUT; - mmc_request_done(mmc, mrq); -} - -static void tifm_sd_end_cmd(unsigned long data) -{ - struct tifm_sd *host = (struct tifm_sd*)data; - struct tifm_dev *sock = host->dev; - struct mmc_host *mmc = tifm_get_drvdata(sock); - struct mmc_request *mrq; - struct mmc_data *r_data = NULL; - unsigned long flags; - - spin_lock_irqsave(&sock->lock, flags); - - del_timer(&host->timer); - mrq = host->req; - host->req = NULL; - - if (!mrq) { - printk(KERN_ERR " %s : no request to complete?\n", - sock->dev.bus_id); - spin_unlock_irqrestore(&sock->lock, flags); - return; - } - - r_data = mrq->cmd->data; - if (r_data) { - if (host->no_dma) { - writel((~TIFM_MMCSD_BUFINT) - & readl(sock->addr + SOCK_MMCSD_INT_ENABLE), - sock->addr + SOCK_MMCSD_INT_ENABLE); - } else { - tifm_unmap_sg(sock, &host->bounce_buf, 1, - (r_data->flags & MMC_DATA_WRITE) - ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - tifm_unmap_sg(sock, r_data->sg, r_data->sg_len, - (r_data->flags & MMC_DATA_WRITE) - ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - } - - r_data->bytes_xfered = r_data->blocks - - readl(sock->addr + SOCK_MMCSD_NUM_BLOCKS) - 1; - r_data->bytes_xfered *= r_data->blksz; - r_data->bytes_xfered += r_data->blksz - - readl(sock->addr + SOCK_MMCSD_BLOCK_LEN) + 1; - } - - writel((~TIFM_CTRL_LED) & readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); - - spin_unlock_irqrestore(&sock->lock, flags); - mmc_request_done(mmc, mrq); -} - -static void tifm_sd_abort(unsigned long data) -{ - struct tifm_sd *host = (struct tifm_sd*)data; - - printk(KERN_ERR - "%s : card failed to respond for a long period of time " - "(%x, %x)\n", - host->dev->dev.bus_id, host->req->cmd->opcode, host->cmd_flags); - - tifm_eject(host->dev); -} - -static void tifm_sd_ios(struct mmc_host *mmc, struct mmc_ios *ios) -{ - struct tifm_sd *host = mmc_priv(mmc); - struct tifm_dev *sock = host->dev; - unsigned int clk_div1, clk_div2; - unsigned long flags; - - spin_lock_irqsave(&sock->lock, flags); - - dev_dbg(&sock->dev, "ios: clock = %u, vdd = %x, bus_mode = %x, " - "chip_select = %x, power_mode = %x, bus_width = %x\n", - ios->clock, ios->vdd, ios->bus_mode, ios->chip_select, - ios->power_mode, ios->bus_width); - - if (ios->bus_width == MMC_BUS_WIDTH_4) { - writel(TIFM_MMCSD_4BBUS | readl(sock->addr + SOCK_MMCSD_CONFIG), - sock->addr + SOCK_MMCSD_CONFIG); - } else { - writel((~TIFM_MMCSD_4BBUS) - & readl(sock->addr + SOCK_MMCSD_CONFIG), - sock->addr + SOCK_MMCSD_CONFIG); - } - - if (ios->clock) { - clk_div1 = 20000000 / ios->clock; - if (!clk_div1) - clk_div1 = 1; - - clk_div2 = 24000000 / ios->clock; - if (!clk_div2) - clk_div2 = 1; - - if ((20000000 / clk_div1) > ios->clock) - clk_div1++; - if ((24000000 / clk_div2) > ios->clock) - clk_div2++; - if ((20000000 / clk_div1) > (24000000 / clk_div2)) { - host->clk_freq = 20000000; - host->clk_div = clk_div1; - writel((~TIFM_CTRL_FAST_CLK) - & readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); - } else { - host->clk_freq = 24000000; - host->clk_div = clk_div2; - writel(TIFM_CTRL_FAST_CLK - | readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); - } - } else { - host->clk_div = 0; - } - host->clk_div &= TIFM_MMCSD_CLKMASK; - writel(host->clk_div - | ((~TIFM_MMCSD_CLKMASK) - & readl(sock->addr + SOCK_MMCSD_CONFIG)), - sock->addr + SOCK_MMCSD_CONFIG); - - host->open_drain = (ios->bus_mode == MMC_BUSMODE_OPENDRAIN); - - /* chip_select : maybe later */ - //vdd - //power is set before probe / after remove - - spin_unlock_irqrestore(&sock->lock, flags); -} - -static int tifm_sd_ro(struct mmc_host *mmc) -{ - int rc = 0; - struct tifm_sd *host = mmc_priv(mmc); - struct tifm_dev *sock = host->dev; - unsigned long flags; - - spin_lock_irqsave(&sock->lock, flags); - if (TIFM_MMCSD_CARD_RO & readl(sock->addr + SOCK_PRESENT_STATE)) - rc = 1; - spin_unlock_irqrestore(&sock->lock, flags); - return rc; -} - -static const struct mmc_host_ops tifm_sd_ops = { - .request = tifm_sd_request, - .set_ios = tifm_sd_ios, - .get_ro = tifm_sd_ro -}; - -static int tifm_sd_initialize_host(struct tifm_sd *host) -{ - int rc; - unsigned int host_status = 0; - struct tifm_dev *sock = host->dev; - - writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); - mmiowb(); - host->clk_div = 61; - host->clk_freq = 20000000; - writel(TIFM_MMCSD_RESET, sock->addr + SOCK_MMCSD_SYSTEM_CONTROL); - writel(host->clk_div | TIFM_MMCSD_POWER, - sock->addr + SOCK_MMCSD_CONFIG); - - /* wait up to 0.51 sec for reset */ - for (rc = 32; rc <= 256; rc <<= 1) { - if (1 & readl(sock->addr + SOCK_MMCSD_SYSTEM_STATUS)) { - rc = 0; - break; - } - msleep(rc); - } - - if (rc) { - printk(KERN_ERR "%s : controller failed to reset\n", - sock->dev.bus_id); - return -ENODEV; - } - - writel(0, sock->addr + SOCK_MMCSD_NUM_BLOCKS); - writel(host->clk_div | TIFM_MMCSD_POWER, - sock->addr + SOCK_MMCSD_CONFIG); - writel(TIFM_MMCSD_RXDE, sock->addr + SOCK_MMCSD_BUFFER_CONFIG); - - // command timeout fixed to 64 clocks for now - writel(64, sock->addr + SOCK_MMCSD_COMMAND_TO); - writel(TIFM_MMCSD_INAB, sock->addr + SOCK_MMCSD_COMMAND); - - for (rc = 16; rc <= 64; rc <<= 1) { - host_status = readl(sock->addr + SOCK_MMCSD_STATUS); - writel(host_status, sock->addr + SOCK_MMCSD_STATUS); - if (!(host_status & TIFM_MMCSD_ERRMASK) - && (host_status & TIFM_MMCSD_EOC)) { - rc = 0; - break; - } - msleep(rc); - } - - if (rc) { - printk(KERN_ERR - "%s : card not ready - probe failed on initialization\n", - sock->dev.bus_id); - return -ENODEV; - } - - writel(TIFM_MMCSD_CERR | TIFM_MMCSD_BRS | TIFM_MMCSD_EOC - | TIFM_MMCSD_ERRMASK, - sock->addr + SOCK_MMCSD_INT_ENABLE); - mmiowb(); - - return 0; -} - -static int tifm_sd_probe(struct tifm_dev *sock) -{ - struct mmc_host *mmc; - struct tifm_sd *host; - int rc = -EIO; - - if (!(TIFM_SOCK_STATE_OCCUPIED - & readl(sock->addr + SOCK_PRESENT_STATE))) { - printk(KERN_WARNING "%s : card gone, unexpectedly\n", - sock->dev.bus_id); - return rc; - } - - mmc = mmc_alloc_host(sizeof(struct tifm_sd), &sock->dev); - if (!mmc) - return -ENOMEM; - - host = mmc_priv(mmc); - host->no_dma = no_dma; - tifm_set_drvdata(sock, mmc); - host->dev = sock; - host->timeout_jiffies = msecs_to_jiffies(1000); - - tasklet_init(&host->finish_tasklet, tifm_sd_end_cmd, - (unsigned long)host); - setup_timer(&host->timer, tifm_sd_abort, (unsigned long)host); - - mmc->ops = &tifm_sd_ops; - mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE; - mmc->f_min = 20000000 / 60; - mmc->f_max = 24000000; - - mmc->max_blk_count = 2048; - mmc->max_hw_segs = mmc->max_blk_count; - mmc->max_blk_size = min(TIFM_MMCSD_MAX_BLOCK_SIZE, PAGE_SIZE); - mmc->max_seg_size = mmc->max_blk_count * mmc->max_blk_size; - mmc->max_req_size = mmc->max_seg_size; - mmc->max_phys_segs = mmc->max_hw_segs; - - sock->card_event = tifm_sd_card_event; - sock->data_event = tifm_sd_data_event; - rc = tifm_sd_initialize_host(host); - - if (!rc) - rc = mmc_add_host(mmc); - if (!rc) - return 0; - - mmc_free_host(mmc); - return rc; -} - -static void tifm_sd_remove(struct tifm_dev *sock) -{ - struct mmc_host *mmc = tifm_get_drvdata(sock); - struct tifm_sd *host = mmc_priv(mmc); - unsigned long flags; - - spin_lock_irqsave(&sock->lock, flags); - host->eject = 1; - writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); - mmiowb(); - spin_unlock_irqrestore(&sock->lock, flags); - - tasklet_kill(&host->finish_tasklet); - - spin_lock_irqsave(&sock->lock, flags); - if (host->req) { - writel(TIFM_FIFO_INT_SETALL, - sock->addr + SOCK_DMA_FIFO_INT_ENABLE_CLEAR); - writel(0, sock->addr + SOCK_DMA_FIFO_INT_ENABLE_SET); - host->req->cmd->error = MMC_ERR_TIMEOUT; - if (host->req->stop) - host->req->stop->error = MMC_ERR_TIMEOUT; - tasklet_schedule(&host->finish_tasklet); - } - spin_unlock_irqrestore(&sock->lock, flags); - mmc_remove_host(mmc); - dev_dbg(&sock->dev, "after remove\n"); - - /* The meaning of the bit majority in this constant is unknown. */ - writel(0xfff8 & readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); - - mmc_free_host(mmc); -} - -#ifdef CONFIG_PM - -static int tifm_sd_suspend(struct tifm_dev *sock, pm_message_t state) -{ - struct mmc_host *mmc = tifm_get_drvdata(sock); - int rc; - - rc = mmc_suspend_host(mmc, state); - /* The meaning of the bit majority in this constant is unknown. */ - writel(0xfff8 & readl(sock->addr + SOCK_CONTROL), - sock->addr + SOCK_CONTROL); - return rc; -} - -static int tifm_sd_resume(struct tifm_dev *sock) -{ - struct mmc_host *mmc = tifm_get_drvdata(sock); - struct tifm_sd *host = mmc_priv(mmc); - int rc; - - rc = tifm_sd_initialize_host(host); - dev_dbg(&sock->dev, "resume initialize %d\n", rc); - - if (rc) - host->eject = 1; - else - rc = mmc_resume_host(mmc); - - return rc; -} - -#else - -#define tifm_sd_suspend NULL -#define tifm_sd_resume NULL - -#endif /* CONFIG_PM */ - -static struct tifm_device_id tifm_sd_id_tbl[] = { - { TIFM_TYPE_SD }, { } -}; - -static struct tifm_driver tifm_sd_driver = { - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE - }, - .id_table = tifm_sd_id_tbl, - .probe = tifm_sd_probe, - .remove = tifm_sd_remove, - .suspend = tifm_sd_suspend, - .resume = tifm_sd_resume -}; - -static int __init tifm_sd_init(void) -{ - return tifm_register_driver(&tifm_sd_driver); -} - -static void __exit tifm_sd_exit(void) -{ - tifm_unregister_driver(&tifm_sd_driver); -} - -MODULE_AUTHOR("Alex Dubov"); -MODULE_DESCRIPTION("TI FlashMedia SD driver"); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(tifm, tifm_sd_id_tbl); -MODULE_VERSION(DRIVER_VERSION); - -module_init(tifm_sd_init); -module_exit(tifm_sd_exit); diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c deleted file mode 100644 index 9f7518b37c3..00000000000 --- a/drivers/mmc/wbsd.c +++ /dev/null @@ -1,2062 +0,0 @@ -/* - * linux/drivers/mmc/wbsd.c - Winbond W83L51xD SD/MMC driver - * - * Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * - * Warning! - * - * Changes to the FIFO system should be done with extreme care since - * the hardware is full of bugs related to the FIFO. Known issues are: - * - * - FIFO size field in FSR is always zero. - * - * - FIFO interrupts tend not to work as they should. Interrupts are - * triggered only for full/empty events, not for threshold values. - * - * - On APIC systems the FIFO empty interrupt is sometimes lost. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "wbsd.h" - -#define DRIVER_NAME "wbsd" - -#define DBG(x...) \ - pr_debug(DRIVER_NAME ": " x) -#define DBGF(f, x...) \ - pr_debug(DRIVER_NAME " [%s()]: " f, __func__ , ##x) - -/* - * Device resources - */ - -#ifdef CONFIG_PNP - -static const struct pnp_device_id pnp_dev_table[] = { - { "WEC0517", 0 }, - { "WEC0518", 0 }, - { "", 0 }, -}; - -MODULE_DEVICE_TABLE(pnp, pnp_dev_table); - -#endif /* CONFIG_PNP */ - -static const int config_ports[] = { 0x2E, 0x4E }; -static const int unlock_codes[] = { 0x83, 0x87 }; - -static const int valid_ids[] = { - 0x7112, - }; - -#ifdef CONFIG_PNP -static unsigned int nopnp = 0; -#else -static const unsigned int nopnp = 1; -#endif -static unsigned int io = 0x248; -static unsigned int irq = 6; -static int dma = 2; - -/* - * Basic functions - */ - -static inline void wbsd_unlock_config(struct wbsd_host *host) -{ - BUG_ON(host->config == 0); - - outb(host->unlock_code, host->config); - outb(host->unlock_code, host->config); -} - -static inline void wbsd_lock_config(struct wbsd_host *host) -{ - BUG_ON(host->config == 0); - - outb(LOCK_CODE, host->config); -} - -static inline void wbsd_write_config(struct wbsd_host *host, u8 reg, u8 value) -{ - BUG_ON(host->config == 0); - - outb(reg, host->config); - outb(value, host->config + 1); -} - -static inline u8 wbsd_read_config(struct wbsd_host *host, u8 reg) -{ - BUG_ON(host->config == 0); - - outb(reg, host->config); - return inb(host->config + 1); -} - -static inline void wbsd_write_index(struct wbsd_host *host, u8 index, u8 value) -{ - outb(index, host->base + WBSD_IDXR); - outb(value, host->base + WBSD_DATAR); -} - -static inline u8 wbsd_read_index(struct wbsd_host *host, u8 index) -{ - outb(index, host->base + WBSD_IDXR); - return inb(host->base + WBSD_DATAR); -} - -/* - * Common routines - */ - -static void wbsd_init_device(struct wbsd_host *host) -{ - u8 setup, ier; - - /* - * Reset chip (SD/MMC part) and fifo. - */ - setup = wbsd_read_index(host, WBSD_IDX_SETUP); - setup |= WBSD_FIFO_RESET | WBSD_SOFT_RESET; - wbsd_write_index(host, WBSD_IDX_SETUP, setup); - - /* - * Set DAT3 to input - */ - setup &= ~WBSD_DAT3_H; - wbsd_write_index(host, WBSD_IDX_SETUP, setup); - host->flags &= ~WBSD_FIGNORE_DETECT; - - /* - * Read back default clock. - */ - host->clk = wbsd_read_index(host, WBSD_IDX_CLK); - - /* - * Power down port. - */ - outb(WBSD_POWER_N, host->base + WBSD_CSR); - - /* - * Set maximum timeout. - */ - wbsd_write_index(host, WBSD_IDX_TAAC, 0x7F); - - /* - * Test for card presence - */ - if (inb(host->base + WBSD_CSR) & WBSD_CARDPRESENT) - host->flags |= WBSD_FCARD_PRESENT; - else - host->flags &= ~WBSD_FCARD_PRESENT; - - /* - * Enable interesting interrupts. - */ - ier = 0; - ier |= WBSD_EINT_CARD; - ier |= WBSD_EINT_FIFO_THRE; - ier |= WBSD_EINT_CRC; - ier |= WBSD_EINT_TIMEOUT; - ier |= WBSD_EINT_TC; - - outb(ier, host->base + WBSD_EIR); - - /* - * Clear interrupts. - */ - inb(host->base + WBSD_ISR); -} - -static void wbsd_reset(struct wbsd_host *host) -{ - u8 setup; - - printk(KERN_ERR "%s: Resetting chip\n", mmc_hostname(host->mmc)); - - /* - * Soft reset of chip (SD/MMC part). - */ - setup = wbsd_read_index(host, WBSD_IDX_SETUP); - setup |= WBSD_SOFT_RESET; - wbsd_write_index(host, WBSD_IDX_SETUP, setup); -} - -static void wbsd_request_end(struct wbsd_host *host, struct mmc_request *mrq) -{ - unsigned long dmaflags; - - DBGF("Ending request, cmd (%x)\n", mrq->cmd->opcode); - - if (host->dma >= 0) { - /* - * Release ISA DMA controller. - */ - dmaflags = claim_dma_lock(); - disable_dma(host->dma); - clear_dma_ff(host->dma); - release_dma_lock(dmaflags); - - /* - * Disable DMA on host. - */ - wbsd_write_index(host, WBSD_IDX_DMA, 0); - } - - host->mrq = NULL; - - /* - * MMC layer might call back into the driver so first unlock. - */ - spin_unlock(&host->lock); - mmc_request_done(host->mmc, mrq); - spin_lock(&host->lock); -} - -/* - * Scatter/gather functions - */ - -static inline void wbsd_init_sg(struct wbsd_host *host, struct mmc_data *data) -{ - /* - * Get info. about SG list from data structure. - */ - host->cur_sg = data->sg; - host->num_sg = data->sg_len; - - host->offset = 0; - host->remain = host->cur_sg->length; -} - -static inline int wbsd_next_sg(struct wbsd_host *host) -{ - /* - * Skip to next SG entry. - */ - host->cur_sg++; - host->num_sg--; - - /* - * Any entries left? - */ - if (host->num_sg > 0) { - host->offset = 0; - host->remain = host->cur_sg->length; - } - - return host->num_sg; -} - -static inline char *wbsd_sg_to_buffer(struct wbsd_host *host) -{ - return page_address(host->cur_sg->page) + host->cur_sg->offset; -} - -static inline void wbsd_sg_to_dma(struct wbsd_host *host, struct mmc_data *data) -{ - unsigned int len, i; - struct scatterlist *sg; - char *dmabuf = host->dma_buffer; - char *sgbuf; - - sg = data->sg; - len = data->sg_len; - - for (i = 0; i < len; i++) { - sgbuf = page_address(sg[i].page) + sg[i].offset; - memcpy(dmabuf, sgbuf, sg[i].length); - dmabuf += sg[i].length; - } -} - -static inline void wbsd_dma_to_sg(struct wbsd_host *host, struct mmc_data *data) -{ - unsigned int len, i; - struct scatterlist *sg; - char *dmabuf = host->dma_buffer; - char *sgbuf; - - sg = data->sg; - len = data->sg_len; - - for (i = 0; i < len; i++) { - sgbuf = page_address(sg[i].page) + sg[i].offset; - memcpy(sgbuf, dmabuf, sg[i].length); - dmabuf += sg[i].length; - } -} - -/* - * Command handling - */ - -static inline void wbsd_get_short_reply(struct wbsd_host *host, - struct mmc_command *cmd) -{ - /* - * Correct response type? - */ - if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_SHORT) { - cmd->error = MMC_ERR_INVALID; - return; - } - - cmd->resp[0] = wbsd_read_index(host, WBSD_IDX_RESP12) << 24; - cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP13) << 16; - cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP14) << 8; - cmd->resp[0] |= wbsd_read_index(host, WBSD_IDX_RESP15) << 0; - cmd->resp[1] = wbsd_read_index(host, WBSD_IDX_RESP16) << 24; -} - -static inline void wbsd_get_long_reply(struct wbsd_host *host, - struct mmc_command *cmd) -{ - int i; - - /* - * Correct response type? - */ - if (wbsd_read_index(host, WBSD_IDX_RSPLEN) != WBSD_RSP_LONG) { - cmd->error = MMC_ERR_INVALID; - return; - } - - for (i = 0; i < 4; i++) { - cmd->resp[i] = - wbsd_read_index(host, WBSD_IDX_RESP1 + i * 4) << 24; - cmd->resp[i] |= - wbsd_read_index(host, WBSD_IDX_RESP2 + i * 4) << 16; - cmd->resp[i] |= - wbsd_read_index(host, WBSD_IDX_RESP3 + i * 4) << 8; - cmd->resp[i] |= - wbsd_read_index(host, WBSD_IDX_RESP4 + i * 4) << 0; - } -} - -static void wbsd_send_command(struct wbsd_host *host, struct mmc_command *cmd) -{ - int i; - u8 status, isr; - - DBGF("Sending cmd (%x)\n", cmd->opcode); - - /* - * Clear accumulated ISR. The interrupt routine - * will fill this one with events that occur during - * transfer. - */ - host->isr = 0; - - /* - * Send the command (CRC calculated by host). - */ - outb(cmd->opcode, host->base + WBSD_CMDR); - for (i = 3; i >= 0; i--) - outb((cmd->arg >> (i * 8)) & 0xff, host->base + WBSD_CMDR); - - cmd->error = MMC_ERR_NONE; - - /* - * Wait for the request to complete. - */ - do { - status = wbsd_read_index(host, WBSD_IDX_STATUS); - } while (status & WBSD_CARDTRAFFIC); - - /* - * Do we expect a reply? - */ - if (cmd->flags & MMC_RSP_PRESENT) { - /* - * Read back status. - */ - isr = host->isr; - - /* Card removed? */ - if (isr & WBSD_INT_CARD) - cmd->error = MMC_ERR_TIMEOUT; - /* Timeout? */ - else if (isr & WBSD_INT_TIMEOUT) - cmd->error = MMC_ERR_TIMEOUT; - /* CRC? */ - else if ((cmd->flags & MMC_RSP_CRC) && (isr & WBSD_INT_CRC)) - cmd->error = MMC_ERR_BADCRC; - /* All ok */ - else { - if (cmd->flags & MMC_RSP_136) - wbsd_get_long_reply(host, cmd); - else - wbsd_get_short_reply(host, cmd); - } - } - - DBGF("Sent cmd (%x), res %d\n", cmd->opcode, cmd->error); -} - -/* - * Data functions - */ - -static void wbsd_empty_fifo(struct wbsd_host *host) -{ - struct mmc_data *data = host->mrq->cmd->data; - char *buffer; - int i, fsr, fifo; - - /* - * Handle excessive data. - */ - if (host->num_sg == 0) - return; - - buffer = wbsd_sg_to_buffer(host) + host->offset; - - /* - * Drain the fifo. This has a tendency to loop longer - * than the FIFO length (usually one block). - */ - while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_EMPTY)) { - /* - * The size field in the FSR is broken so we have to - * do some guessing. - */ - if (fsr & WBSD_FIFO_FULL) - fifo = 16; - else if (fsr & WBSD_FIFO_FUTHRE) - fifo = 8; - else - fifo = 1; - - for (i = 0; i < fifo; i++) { - *buffer = inb(host->base + WBSD_DFR); - buffer++; - host->offset++; - host->remain--; - - data->bytes_xfered++; - - /* - * End of scatter list entry? - */ - if (host->remain == 0) { - /* - * Get next entry. Check if last. - */ - if (!wbsd_next_sg(host)) - return; - - buffer = wbsd_sg_to_buffer(host); - } - } - } - - /* - * This is a very dirty hack to solve a - * hardware problem. The chip doesn't trigger - * FIFO threshold interrupts properly. - */ - if ((data->blocks * data->blksz - data->bytes_xfered) < 16) - tasklet_schedule(&host->fifo_tasklet); -} - -static void wbsd_fill_fifo(struct wbsd_host *host) -{ - struct mmc_data *data = host->mrq->cmd->data; - char *buffer; - int i, fsr, fifo; - - /* - * Check that we aren't being called after the - * entire buffer has been transfered. - */ - if (host->num_sg == 0) - return; - - buffer = wbsd_sg_to_buffer(host) + host->offset; - - /* - * Fill the fifo. This has a tendency to loop longer - * than the FIFO length (usually one block). - */ - while (!((fsr = inb(host->base + WBSD_FSR)) & WBSD_FIFO_FULL)) { - /* - * The size field in the FSR is broken so we have to - * do some guessing. - */ - if (fsr & WBSD_FIFO_EMPTY) - fifo = 0; - else if (fsr & WBSD_FIFO_EMTHRE) - fifo = 8; - else - fifo = 15; - - for (i = 16; i > fifo; i--) { - outb(*buffer, host->base + WBSD_DFR); - buffer++; - host->offset++; - host->remain--; - - data->bytes_xfered++; - - /* - * End of scatter list entry? - */ - if (host->remain == 0) { - /* - * Get next entry. Check if last. - */ - if (!wbsd_next_sg(host)) - return; - - buffer = wbsd_sg_to_buffer(host); - } - } - } - - /* - * The controller stops sending interrupts for - * 'FIFO empty' under certain conditions. So we - * need to be a bit more pro-active. - */ - tasklet_schedule(&host->fifo_tasklet); -} - -static void wbsd_prepare_data(struct wbsd_host *host, struct mmc_data *data) -{ - u16 blksize; - u8 setup; - unsigned long dmaflags; - unsigned int size; - - DBGF("blksz %04x blks %04x flags %08x\n", - data->blksz, data->blocks, data->flags); - DBGF("tsac %d ms nsac %d clk\n", - data->timeout_ns / 1000000, data->timeout_clks); - - /* - * Calculate size. - */ - size = data->blocks * data->blksz; - - /* - * Check timeout values for overflow. - * (Yes, some cards cause this value to overflow). - */ - if (data->timeout_ns > 127000000) - wbsd_write_index(host, WBSD_IDX_TAAC, 127); - else { - wbsd_write_index(host, WBSD_IDX_TAAC, - data->timeout_ns / 1000000); - } - - if (data->timeout_clks > 255) - wbsd_write_index(host, WBSD_IDX_NSAC, 255); - else - wbsd_write_index(host, WBSD_IDX_NSAC, data->timeout_clks); - - /* - * Inform the chip of how large blocks will be - * sent. It needs this to determine when to - * calculate CRC. - * - * Space for CRC must be included in the size. - * Two bytes are needed for each data line. - */ - if (host->bus_width == MMC_BUS_WIDTH_1) { - blksize = data->blksz + 2; - - wbsd_write_index(host, WBSD_IDX_PBSMSB, (blksize >> 4) & 0xF0); - wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); - } else if (host->bus_width == MMC_BUS_WIDTH_4) { - blksize = data->blksz + 2 * 4; - - wbsd_write_index(host, WBSD_IDX_PBSMSB, - ((blksize >> 4) & 0xF0) | WBSD_DATA_WIDTH); - wbsd_write_index(host, WBSD_IDX_PBSLSB, blksize & 0xFF); - } else { - data->error = MMC_ERR_INVALID; - return; - } - - /* - * Clear the FIFO. This is needed even for DMA - * transfers since the chip still uses the FIFO - * internally. - */ - setup = wbsd_read_index(host, WBSD_IDX_SETUP); - setup |= WBSD_FIFO_RESET; - wbsd_write_index(host, WBSD_IDX_SETUP, setup); - - /* - * DMA transfer? - */ - if (host->dma >= 0) { - /* - * The buffer for DMA is only 64 kB. - */ - BUG_ON(size > 0x10000); - if (size > 0x10000) { - data->error = MMC_ERR_INVALID; - return; - } - - /* - * Transfer data from the SG list to - * the DMA buffer. - */ - if (data->flags & MMC_DATA_WRITE) - wbsd_sg_to_dma(host, data); - - /* - * Initialise the ISA DMA controller. - */ - dmaflags = claim_dma_lock(); - disable_dma(host->dma); - clear_dma_ff(host->dma); - if (data->flags & MMC_DATA_READ) - set_dma_mode(host->dma, DMA_MODE_READ & ~0x40); - else - set_dma_mode(host->dma, DMA_MODE_WRITE & ~0x40); - set_dma_addr(host->dma, host->dma_addr); - set_dma_count(host->dma, size); - - enable_dma(host->dma); - release_dma_lock(dmaflags); - - /* - * Enable DMA on the host. - */ - wbsd_write_index(host, WBSD_IDX_DMA, WBSD_DMA_ENABLE); - } else { - /* - * This flag is used to keep printk - * output to a minimum. - */ - host->firsterr = 1; - - /* - * Initialise the SG list. - */ - wbsd_init_sg(host, data); - - /* - * Turn off DMA. - */ - wbsd_write_index(host, WBSD_IDX_DMA, 0); - - /* - * Set up FIFO threshold levels (and fill - * buffer if doing a write). - */ - if (data->flags & MMC_DATA_READ) { - wbsd_write_index(host, WBSD_IDX_FIFOEN, - WBSD_FIFOEN_FULL | 8); - } else { - wbsd_write_index(host, WBSD_IDX_FIFOEN, - WBSD_FIFOEN_EMPTY | 8); - wbsd_fill_fifo(host); - } - } - - data->error = MMC_ERR_NONE; -} - -static void wbsd_finish_data(struct wbsd_host *host, struct mmc_data *data) -{ - unsigned long dmaflags; - int count; - u8 status; - - WARN_ON(host->mrq == NULL); - - /* - * Send a stop command if needed. - */ - if (data->stop) - wbsd_send_command(host, data->stop); - - /* - * Wait for the controller to leave data - * transfer state. - */ - do { - status = wbsd_read_index(host, WBSD_IDX_STATUS); - } while (status & (WBSD_BLOCK_READ | WBSD_BLOCK_WRITE)); - - /* - * DMA transfer? - */ - if (host->dma >= 0) { - /* - * Disable DMA on the host. - */ - wbsd_write_index(host, WBSD_IDX_DMA, 0); - - /* - * Turn of ISA DMA controller. - */ - dmaflags = claim_dma_lock(); - disable_dma(host->dma); - clear_dma_ff(host->dma); - count = get_dma_residue(host->dma); - release_dma_lock(dmaflags); - - data->bytes_xfered = host->mrq->data->blocks * - host->mrq->data->blksz - count; - data->bytes_xfered -= data->bytes_xfered % data->blksz; - - /* - * Any leftover data? - */ - if (count) { - printk(KERN_ERR "%s: Incomplete DMA transfer. " - "%d bytes left.\n", - mmc_hostname(host->mmc), count); - - if (data->error == MMC_ERR_NONE) - data->error = MMC_ERR_FAILED; - } else { - /* - * Transfer data from DMA buffer to - * SG list. - */ - if (data->flags & MMC_DATA_READ) - wbsd_dma_to_sg(host, data); - } - - if (data->error != MMC_ERR_NONE) { - if (data->bytes_xfered) - data->bytes_xfered -= data->blksz; - } - } - - DBGF("Ending data transfer (%d bytes)\n", data->bytes_xfered); - - wbsd_request_end(host, host->mrq); -} - -/*****************************************************************************\ - * * - * MMC layer callbacks * - * * -\*****************************************************************************/ - -static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq) -{ - struct wbsd_host *host = mmc_priv(mmc); - struct mmc_command *cmd; - - /* - * Disable tasklets to avoid a deadlock. - */ - spin_lock_bh(&host->lock); - - BUG_ON(host->mrq != NULL); - - cmd = mrq->cmd; - - host->mrq = mrq; - - /* - * If there is no card in the slot then - * timeout immediatly. - */ - if (!(host->flags & WBSD_FCARD_PRESENT)) { - cmd->error = MMC_ERR_TIMEOUT; - goto done; - } - - /* - * Does the request include data? - */ - if (cmd->data) { - wbsd_prepare_data(host, cmd->data); - - if (cmd->data->error != MMC_ERR_NONE) - goto done; - } - - wbsd_send_command(host, cmd); - - /* - * If this is a data transfer the request - * will be finished after the data has - * transfered. - */ - if (cmd->data && (cmd->error == MMC_ERR_NONE)) { - /* - * The hardware is so delightfully stupid that it has a list - * of "data" commands. If a command isn't on this list, it'll - * just go back to the idle state and won't send any data - * interrupts. - */ - switch (cmd->opcode) { - case 11: - case 17: - case 18: - case 20: - case 24: - case 25: - case 26: - case 27: - case 30: - case 42: - case 56: - break; - - /* ACMDs. We don't keep track of state, so we just treat them - * like any other command. */ - case 51: - break; - - default: -#ifdef CONFIG_MMC_DEBUG - printk(KERN_WARNING "%s: Data command %d is not " - "supported by this controller.\n", - mmc_hostname(host->mmc), cmd->opcode); -#endif - cmd->data->error = MMC_ERR_INVALID; - - if (cmd->data->stop) - wbsd_send_command(host, cmd->data->stop); - - goto done; - }; - - /* - * Dirty fix for hardware bug. - */ - if (host->dma == -1) - tasklet_schedule(&host->fifo_tasklet); - - spin_unlock_bh(&host->lock); - - return; - } - -done: - wbsd_request_end(host, mrq); - - spin_unlock_bh(&host->lock); -} - -static void wbsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -{ - struct wbsd_host *host = mmc_priv(mmc); - u8 clk, setup, pwr; - - spin_lock_bh(&host->lock); - - /* - * Reset the chip on each power off. - * Should clear out any weird states. - */ - if (ios->power_mode == MMC_POWER_OFF) - wbsd_init_device(host); - - if (ios->clock >= 24000000) - clk = WBSD_CLK_24M; - else if (ios->clock >= 16000000) - clk = WBSD_CLK_16M; - else if (ios->clock >= 12000000) - clk = WBSD_CLK_12M; - else - clk = WBSD_CLK_375K; - - /* - * Only write to the clock register when - * there is an actual change. - */ - if (clk != host->clk) { - wbsd_write_index(host, WBSD_IDX_CLK, clk); - host->clk = clk; - } - - /* - * Power up card. - */ - if (ios->power_mode != MMC_POWER_OFF) { - pwr = inb(host->base + WBSD_CSR); - pwr &= ~WBSD_POWER_N; - outb(pwr, host->base + WBSD_CSR); - } - - /* - * MMC cards need to have pin 1 high during init. - * It wreaks havoc with the card detection though so - * that needs to be disabled. - */ - setup = wbsd_read_index(host, WBSD_IDX_SETUP); - if (ios->chip_select == MMC_CS_HIGH) { - BUG_ON(ios->bus_width != MMC_BUS_WIDTH_1); - setup |= WBSD_DAT3_H; - host->flags |= WBSD_FIGNORE_DETECT; - } else { - if (setup & WBSD_DAT3_H) { - setup &= ~WBSD_DAT3_H; - - /* - * We cannot resume card detection immediatly - * because of capacitance and delays in the chip. - */ - mod_timer(&host->ignore_timer, jiffies + HZ / 100); - } - } - wbsd_write_index(host, WBSD_IDX_SETUP, setup); - - /* - * Store bus width for later. Will be used when - * setting up the data transfer. - */ - host->bus_width = ios->bus_width; - - spin_unlock_bh(&host->lock); -} - -static int wbsd_get_ro(struct mmc_host *mmc) -{ - struct wbsd_host *host = mmc_priv(mmc); - u8 csr; - - spin_lock_bh(&host->lock); - - csr = inb(host->base + WBSD_CSR); - csr |= WBSD_MSLED; - outb(csr, host->base + WBSD_CSR); - - mdelay(1); - - csr = inb(host->base + WBSD_CSR); - csr &= ~WBSD_MSLED; - outb(csr, host->base + WBSD_CSR); - - spin_unlock_bh(&host->lock); - - return csr & WBSD_WRPT; -} - -static const struct mmc_host_ops wbsd_ops = { - .request = wbsd_request, - .set_ios = wbsd_set_ios, - .get_ro = wbsd_get_ro, -}; - -/*****************************************************************************\ - * * - * Interrupt handling * - * * -\*****************************************************************************/ - -/* - * Helper function to reset detection ignore - */ - -static void wbsd_reset_ignore(unsigned long data) -{ - struct wbsd_host *host = (struct wbsd_host *)data; - - BUG_ON(host == NULL); - - DBG("Resetting card detection ignore\n"); - - spin_lock_bh(&host->lock); - - host->flags &= ~WBSD_FIGNORE_DETECT; - - /* - * Card status might have changed during the - * blackout. - */ - tasklet_schedule(&host->card_tasklet); - - spin_unlock_bh(&host->lock); -} - -/* - * Tasklets - */ - -static inline struct mmc_data *wbsd_get_data(struct wbsd_host *host) -{ - WARN_ON(!host->mrq); - if (!host->mrq) - return NULL; - - WARN_ON(!host->mrq->cmd); - if (!host->mrq->cmd) - return NULL; - - WARN_ON(!host->mrq->cmd->data); - if (!host->mrq->cmd->data) - return NULL; - - return host->mrq->cmd->data; -} - -static void wbsd_tasklet_card(unsigned long param) -{ - struct wbsd_host *host = (struct wbsd_host *)param; - u8 csr; - int delay = -1; - - spin_lock(&host->lock); - - if (host->flags & WBSD_FIGNORE_DETECT) { - spin_unlock(&host->lock); - return; - } - - csr = inb(host->base + WBSD_CSR); - WARN_ON(csr == 0xff); - - if (csr & WBSD_CARDPRESENT) { - if (!(host->flags & WBSD_FCARD_PRESENT)) { - DBG("Card inserted\n"); - host->flags |= WBSD_FCARD_PRESENT; - - delay = 500; - } - } else if (host->flags & WBSD_FCARD_PRESENT) { - DBG("Card removed\n"); - host->flags &= ~WBSD_FCARD_PRESENT; - - if (host->mrq) { - printk(KERN_ERR "%s: Card removed during transfer!\n", - mmc_hostname(host->mmc)); - wbsd_reset(host); - - host->mrq->cmd->error = MMC_ERR_FAILED; - tasklet_schedule(&host->finish_tasklet); - } - - delay = 0; - } - - /* - * Unlock first since we might get a call back. - */ - - spin_unlock(&host->lock); - - if (delay != -1) - mmc_detect_change(host->mmc, msecs_to_jiffies(delay)); -} - -static void wbsd_tasklet_fifo(unsigned long param) -{ - struct wbsd_host *host = (struct wbsd_host *)param; - struct mmc_data *data; - - spin_lock(&host->lock); - - if (!host->mrq) - goto end; - - data = wbsd_get_data(host); - if (!data) - goto end; - - if (data->flags & MMC_DATA_WRITE) - wbsd_fill_fifo(host); - else - wbsd_empty_fifo(host); - - /* - * Done? - */ - if (host->num_sg == 0) { - wbsd_write_index(host, WBSD_IDX_FIFOEN, 0); - tasklet_schedule(&host->finish_tasklet); - } - -end: - spin_unlock(&host->lock); -} - -static void wbsd_tasklet_crc(unsigned long param) -{ - struct wbsd_host *host = (struct wbsd_host *)param; - struct mmc_data *data; - - spin_lock(&host->lock); - - if (!host->mrq) - goto end; - - data = wbsd_get_data(host); - if (!data) - goto end; - - DBGF("CRC error\n"); - - data->error = MMC_ERR_BADCRC; - - tasklet_schedule(&host->finish_tasklet); - -end: - spin_unlock(&host->lock); -} - -static void wbsd_tasklet_timeout(unsigned long param) -{ - struct wbsd_host *host = (struct wbsd_host *)param; - struct mmc_data *data; - - spin_lock(&host->lock); - - if (!host->mrq) - goto end; - - data = wbsd_get_data(host); - if (!data) - goto end; - - DBGF("Timeout\n"); - - data->error = MMC_ERR_TIMEOUT; - - tasklet_schedule(&host->finish_tasklet); - -end: - spin_unlock(&host->lock); -} - -static void wbsd_tasklet_finish(unsigned long param) -{ - struct wbsd_host *host = (struct wbsd_host *)param; - struct mmc_data *data; - - spin_lock(&host->lock); - - WARN_ON(!host->mrq); - if (!host->mrq) - goto end; - - data = wbsd_get_data(host); - if (!data) - goto end; - - wbsd_finish_data(host, data); - -end: - spin_unlock(&host->lock); -} - -/* - * Interrupt handling - */ - -static irqreturn_t wbsd_irq(int irq, void *dev_id) -{ - struct wbsd_host *host = dev_id; - int isr; - - isr = inb(host->base + WBSD_ISR); - - /* - * Was it actually our hardware that caused the interrupt? - */ - if (isr == 0xff || isr == 0x00) - return IRQ_NONE; - - host->isr |= isr; - - /* - * Schedule tasklets as needed. - */ - if (isr & WBSD_INT_CARD) - tasklet_schedule(&host->card_tasklet); - if (isr & WBSD_INT_FIFO_THRE) - tasklet_schedule(&host->fifo_tasklet); - if (isr & WBSD_INT_CRC) - tasklet_hi_schedule(&host->crc_tasklet); - if (isr & WBSD_INT_TIMEOUT) - tasklet_hi_schedule(&host->timeout_tasklet); - if (isr & WBSD_INT_TC) - tasklet_schedule(&host->finish_tasklet); - - return IRQ_HANDLED; -} - -/*****************************************************************************\ - * * - * Device initialisation and shutdown * - * * -\*****************************************************************************/ - -/* - * Allocate/free MMC structure. - */ - -static int __devinit wbsd_alloc_mmc(struct device *dev) -{ - struct mmc_host *mmc; - struct wbsd_host *host; - - /* - * Allocate MMC structure. - */ - mmc = mmc_alloc_host(sizeof(struct wbsd_host), dev); - if (!mmc) - return -ENOMEM; - - host = mmc_priv(mmc); - host->mmc = mmc; - - host->dma = -1; - - /* - * Set host parameters. - */ - mmc->ops = &wbsd_ops; - mmc->f_min = 375000; - mmc->f_max = 24000000; - mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; - - spin_lock_init(&host->lock); - - /* - * Set up timers - */ - init_timer(&host->ignore_timer); - host->ignore_timer.data = (unsigned long)host; - host->ignore_timer.function = wbsd_reset_ignore; - - /* - * Maximum number of segments. Worst case is one sector per segment - * so this will be 64kB/512. - */ - mmc->max_hw_segs = 128; - mmc->max_phys_segs = 128; - - /* - * Maximum request size. Also limited by 64KiB buffer. - */ - mmc->max_req_size = 65536; - - /* - * Maximum segment size. Could be one segment with the maximum number - * of bytes. - */ - mmc->max_seg_size = mmc->max_req_size; - - /* - * Maximum block size. We have 12 bits (= 4095) but have to subtract - * space for CRC. So the maximum is 4095 - 4*2 = 4087. - */ - mmc->max_blk_size = 4087; - - /* - * Maximum block count. There is no real limit so the maximum - * request size will be the only restriction. - */ - mmc->max_blk_count = mmc->max_req_size; - - dev_set_drvdata(dev, mmc); - - return 0; -} - -static void __devexit wbsd_free_mmc(struct device *dev) -{ - struct mmc_host *mmc; - struct wbsd_host *host; - - mmc = dev_get_drvdata(dev); - if (!mmc) - return; - - host = mmc_priv(mmc); - BUG_ON(host == NULL); - - del_timer_sync(&host->ignore_timer); - - mmc_free_host(mmc); - - dev_set_drvdata(dev, NULL); -} - -/* - * Scan for known chip id:s - */ - -static int __devinit wbsd_scan(struct wbsd_host *host) -{ - int i, j, k; - int id; - - /* - * Iterate through all ports, all codes to - * find hardware that is in our known list. - */ - for (i = 0; i < ARRAY_SIZE(config_ports); i++) { - if (!request_region(config_ports[i], 2, DRIVER_NAME)) - continue; - - for (j = 0; j < ARRAY_SIZE(unlock_codes); j++) { - id = 0xFFFF; - - host->config = config_ports[i]; - host->unlock_code = unlock_codes[j]; - - wbsd_unlock_config(host); - - outb(WBSD_CONF_ID_HI, config_ports[i]); - id = inb(config_ports[i] + 1) << 8; - - outb(WBSD_CONF_ID_LO, config_ports[i]); - id |= inb(config_ports[i] + 1); - - wbsd_lock_config(host); - - for (k = 0; k < ARRAY_SIZE(valid_ids); k++) { - if (id == valid_ids[k]) { - host->chip_id = id; - - return 0; - } - } - - if (id != 0xFFFF) { - DBG("Unknown hardware (id %x) found at %x\n", - id, config_ports[i]); - } - } - - release_region(config_ports[i], 2); - } - - host->config = 0; - host->unlock_code = 0; - - return -ENODEV; -} - -/* - * Allocate/free io port ranges - */ - -static int __devinit wbsd_request_region(struct wbsd_host *host, int base) -{ - if (base & 0x7) - return -EINVAL; - - if (!request_region(base, 8, DRIVER_NAME)) - return -EIO; - - host->base = base; - - return 0; -} - -static void __devexit wbsd_release_regions(struct wbsd_host *host) -{ - if (host->base) - release_region(host->base, 8); - - host->base = 0; - - if (host->config) - release_region(host->config, 2); - - host->config = 0; -} - -/* - * Allocate/free DMA port and buffer - */ - -static void __devinit wbsd_request_dma(struct wbsd_host *host, int dma) -{ - if (dma < 0) - return; - - if (request_dma(dma, DRIVER_NAME)) - goto err; - - /* - * We need to allocate a special buffer in - * order for ISA to be able to DMA to it. - */ - host->dma_buffer = kmalloc(WBSD_DMA_SIZE, - GFP_NOIO | GFP_DMA | __GFP_REPEAT | __GFP_NOWARN); - if (!host->dma_buffer) - goto free; - - /* - * Translate the address to a physical address. - */ - host->dma_addr = dma_map_single(mmc_dev(host->mmc), host->dma_buffer, - WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); - - /* - * ISA DMA must be aligned on a 64k basis. - */ - if ((host->dma_addr & 0xffff) != 0) - goto kfree; - /* - * ISA cannot access memory above 16 MB. - */ - else if (host->dma_addr >= 0x1000000) - goto kfree; - - host->dma = dma; - - return; - -kfree: - /* - * If we've gotten here then there is some kind of alignment bug - */ - BUG_ON(1); - - dma_unmap_single(mmc_dev(host->mmc), host->dma_addr, - WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); - host->dma_addr = (dma_addr_t)NULL; - - kfree(host->dma_buffer); - host->dma_buffer = NULL; - -free: - free_dma(dma); - -err: - printk(KERN_WARNING DRIVER_NAME ": Unable to allocate DMA %d. " - "Falling back on FIFO.\n", dma); -} - -static void __devexit wbsd_release_dma(struct wbsd_host *host) -{ - if (host->dma_addr) { - dma_unmap_single(mmc_dev(host->mmc), host->dma_addr, - WBSD_DMA_SIZE, DMA_BIDIRECTIONAL); - } - kfree(host->dma_buffer); - if (host->dma >= 0) - free_dma(host->dma); - - host->dma = -1; - host->dma_buffer = NULL; - host->dma_addr = (dma_addr_t)NULL; -} - -/* - * Allocate/free IRQ. - */ - -static int __devinit wbsd_request_irq(struct wbsd_host *host, int irq) -{ - int ret; - - /* - * Allocate interrupt. - */ - - ret = request_irq(irq, wbsd_irq, IRQF_SHARED, DRIVER_NAME, host); - if (ret) - return ret; - - host->irq = irq; - - /* - * Set up tasklets. - */ - tasklet_init(&host->card_tasklet, wbsd_tasklet_card, - (unsigned long)host); - tasklet_init(&host->fifo_tasklet, wbsd_tasklet_fifo, - (unsigned long)host); - tasklet_init(&host->crc_tasklet, wbsd_tasklet_crc, - (unsigned long)host); - tasklet_init(&host->timeout_tasklet, wbsd_tasklet_timeout, - (unsigned long)host); - tasklet_init(&host->finish_tasklet, wbsd_tasklet_finish, - (unsigned long)host); - - return 0; -} - -static void __devexit wbsd_release_irq(struct wbsd_host *host) -{ - if (!host->irq) - return; - - free_irq(host->irq, host); - - host->irq = 0; - - tasklet_kill(&host->card_tasklet); - tasklet_kill(&host->fifo_tasklet); - tasklet_kill(&host->crc_tasklet); - tasklet_kill(&host->timeout_tasklet); - tasklet_kill(&host->finish_tasklet); -} - -/* - * Allocate all resources for the host. - */ - -static int __devinit wbsd_request_resources(struct wbsd_host *host, - int base, int irq, int dma) -{ - int ret; - - /* - * Allocate I/O ports. - */ - ret = wbsd_request_region(host, base); - if (ret) - return ret; - - /* - * Allocate interrupt. - */ - ret = wbsd_request_irq(host, irq); - if (ret) - return ret; - - /* - * Allocate DMA. - */ - wbsd_request_dma(host, dma); - - return 0; -} - -/* - * Release all resources for the host. - */ - -static void __devexit wbsd_release_resources(struct wbsd_host *host) -{ - wbsd_release_dma(host); - wbsd_release_irq(host); - wbsd_release_regions(host); -} - -/* - * Configure the resources the chip should use. - */ - -static void wbsd_chip_config(struct wbsd_host *host) -{ - wbsd_unlock_config(host); - - /* - * Reset the chip. - */ - wbsd_write_config(host, WBSD_CONF_SWRST, 1); - wbsd_write_config(host, WBSD_CONF_SWRST, 0); - - /* - * Select SD/MMC function. - */ - wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); - - /* - * Set up card detection. - */ - wbsd_write_config(host, WBSD_CONF_PINS, WBSD_PINS_DETECT_GP11); - - /* - * Configure chip - */ - wbsd_write_config(host, WBSD_CONF_PORT_HI, host->base >> 8); - wbsd_write_config(host, WBSD_CONF_PORT_LO, host->base & 0xff); - - wbsd_write_config(host, WBSD_CONF_IRQ, host->irq); - - if (host->dma >= 0) - wbsd_write_config(host, WBSD_CONF_DRQ, host->dma); - - /* - * Enable and power up chip. - */ - wbsd_write_config(host, WBSD_CONF_ENABLE, 1); - wbsd_write_config(host, WBSD_CONF_POWER, 0x20); - - wbsd_lock_config(host); -} - -/* - * Check that configured resources are correct. - */ - -static int wbsd_chip_validate(struct wbsd_host *host) -{ - int base, irq, dma; - - wbsd_unlock_config(host); - - /* - * Select SD/MMC function. - */ - wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); - - /* - * Read configuration. - */ - base = wbsd_read_config(host, WBSD_CONF_PORT_HI) << 8; - base |= wbsd_read_config(host, WBSD_CONF_PORT_LO); - - irq = wbsd_read_config(host, WBSD_CONF_IRQ); - - dma = wbsd_read_config(host, WBSD_CONF_DRQ); - - wbsd_lock_config(host); - - /* - * Validate against given configuration. - */ - if (base != host->base) - return 0; - if (irq != host->irq) - return 0; - if ((dma != host->dma) && (host->dma != -1)) - return 0; - - return 1; -} - -/* - * Powers down the SD function - */ - -static void wbsd_chip_poweroff(struct wbsd_host *host) -{ - wbsd_unlock_config(host); - - wbsd_write_config(host, WBSD_CONF_DEVICE, DEVICE_SD); - wbsd_write_config(host, WBSD_CONF_ENABLE, 0); - - wbsd_lock_config(host); -} - -/*****************************************************************************\ - * * - * Devices setup and shutdown * - * * -\*****************************************************************************/ - -static int __devinit wbsd_init(struct device *dev, int base, int irq, int dma, - int pnp) -{ - struct wbsd_host *host = NULL; - struct mmc_host *mmc = NULL; - int ret; - - ret = wbsd_alloc_mmc(dev); - if (ret) - return ret; - - mmc = dev_get_drvdata(dev); - host = mmc_priv(mmc); - - /* - * Scan for hardware. - */ - ret = wbsd_scan(host); - if (ret) { - if (pnp && (ret == -ENODEV)) { - printk(KERN_WARNING DRIVER_NAME - ": Unable to confirm device presence. You may " - "experience lock-ups.\n"); - } else { - wbsd_free_mmc(dev); - return ret; - } - } - - /* - * Request resources. - */ - ret = wbsd_request_resources(host, base, irq, dma); - if (ret) { - wbsd_release_resources(host); - wbsd_free_mmc(dev); - return ret; - } - - /* - * See if chip needs to be configured. - */ - if (pnp) { - if ((host->config != 0) && !wbsd_chip_validate(host)) { - printk(KERN_WARNING DRIVER_NAME - ": PnP active but chip not configured! " - "You probably have a buggy BIOS. " - "Configuring chip manually.\n"); - wbsd_chip_config(host); - } - } else - wbsd_chip_config(host); - - /* - * Power Management stuff. No idea how this works. - * Not tested. - */ -#ifdef CONFIG_PM - if (host->config) { - wbsd_unlock_config(host); - wbsd_write_config(host, WBSD_CONF_PME, 0xA0); - wbsd_lock_config(host); - } -#endif - /* - * Allow device to initialise itself properly. - */ - mdelay(5); - - /* - * Reset the chip into a known state. - */ - wbsd_init_device(host); - - mmc_add_host(mmc); - - printk(KERN_INFO "%s: W83L51xD", mmc_hostname(mmc)); - if (host->chip_id != 0) - printk(" id %x", (int)host->chip_id); - printk(" at 0x%x irq %d", (int)host->base, (int)host->irq); - if (host->dma >= 0) - printk(" dma %d", (int)host->dma); - else - printk(" FIFO"); - if (pnp) - printk(" PnP"); - printk("\n"); - - return 0; -} - -static void __devexit wbsd_shutdown(struct device *dev, int pnp) -{ - struct mmc_host *mmc = dev_get_drvdata(dev); - struct wbsd_host *host; - - if (!mmc) - return; - - host = mmc_priv(mmc); - - mmc_remove_host(mmc); - - /* - * Power down the SD/MMC function. - */ - if (!pnp) - wbsd_chip_poweroff(host); - - wbsd_release_resources(host); - - wbsd_free_mmc(dev); -} - -/* - * Non-PnP - */ - -static int __devinit wbsd_probe(struct platform_device *dev) -{ - /* Use the module parameters for resources */ - return wbsd_init(&dev->dev, io, irq, dma, 0); -} - -static int __devexit wbsd_remove(struct platform_device *dev) -{ - wbsd_shutdown(&dev->dev, 0); - - return 0; -} - -/* - * PnP - */ - -#ifdef CONFIG_PNP - -static int __devinit -wbsd_pnp_probe(struct pnp_dev *pnpdev, const struct pnp_device_id *dev_id) -{ - int io, irq, dma; - - /* - * Get resources from PnP layer. - */ - io = pnp_port_start(pnpdev, 0); - irq = pnp_irq(pnpdev, 0); - if (pnp_dma_valid(pnpdev, 0)) - dma = pnp_dma(pnpdev, 0); - else - dma = -1; - - DBGF("PnP resources: port %3x irq %d dma %d\n", io, irq, dma); - - return wbsd_init(&pnpdev->dev, io, irq, dma, 1); -} - -static void __devexit wbsd_pnp_remove(struct pnp_dev *dev) -{ - wbsd_shutdown(&dev->dev, 1); -} - -#endif /* CONFIG_PNP */ - -/* - * Power management - */ - -#ifdef CONFIG_PM - -static int wbsd_suspend(struct wbsd_host *host, pm_message_t state) -{ - BUG_ON(host == NULL); - - return mmc_suspend_host(host->mmc, state); -} - -static int wbsd_resume(struct wbsd_host *host) -{ - BUG_ON(host == NULL); - - wbsd_init_device(host); - - return mmc_resume_host(host->mmc); -} - -static int wbsd_platform_suspend(struct platform_device *dev, - pm_message_t state) -{ - struct mmc_host *mmc = platform_get_drvdata(dev); - struct wbsd_host *host; - int ret; - - if (mmc == NULL) - return 0; - - DBGF("Suspending...\n"); - - host = mmc_priv(mmc); - - ret = wbsd_suspend(host, state); - if (ret) - return ret; - - wbsd_chip_poweroff(host); - - return 0; -} - -static int wbsd_platform_resume(struct platform_device *dev) -{ - struct mmc_host *mmc = platform_get_drvdata(dev); - struct wbsd_host *host; - - if (mmc == NULL) - return 0; - - DBGF("Resuming...\n"); - - host = mmc_priv(mmc); - - wbsd_chip_config(host); - - /* - * Allow device to initialise itself properly. - */ - mdelay(5); - - return wbsd_resume(host); -} - -#ifdef CONFIG_PNP - -static int wbsd_pnp_suspend(struct pnp_dev *pnp_dev, pm_message_t state) -{ - struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev); - struct wbsd_host *host; - - if (mmc == NULL) - return 0; - - DBGF("Suspending...\n"); - - host = mmc_priv(mmc); - - return wbsd_suspend(host, state); -} - -static int wbsd_pnp_resume(struct pnp_dev *pnp_dev) -{ - struct mmc_host *mmc = dev_get_drvdata(&pnp_dev->dev); - struct wbsd_host *host; - - if (mmc == NULL) - return 0; - - DBGF("Resuming...\n"); - - host = mmc_priv(mmc); - - /* - * See if chip needs to be configured. - */ - if (host->config != 0) { - if (!wbsd_chip_validate(host)) { - printk(KERN_WARNING DRIVER_NAME - ": PnP active but chip not configured! " - "You probably have a buggy BIOS. " - "Configuring chip manually.\n"); - wbsd_chip_config(host); - } - } - - /* - * Allow device to initialise itself properly. - */ - mdelay(5); - - return wbsd_resume(host); -} - -#endif /* CONFIG_PNP */ - -#else /* CONFIG_PM */ - -#define wbsd_platform_suspend NULL -#define wbsd_platform_resume NULL - -#define wbsd_pnp_suspend NULL -#define wbsd_pnp_resume NULL - -#endif /* CONFIG_PM */ - -static struct platform_device *wbsd_device; - -static struct platform_driver wbsd_driver = { - .probe = wbsd_probe, - .remove = __devexit_p(wbsd_remove), - - .suspend = wbsd_platform_suspend, - .resume = wbsd_platform_resume, - .driver = { - .name = DRIVER_NAME, - }, -}; - -#ifdef CONFIG_PNP - -static struct pnp_driver wbsd_pnp_driver = { - .name = DRIVER_NAME, - .id_table = pnp_dev_table, - .probe = wbsd_pnp_probe, - .remove = __devexit_p(wbsd_pnp_remove), - - .suspend = wbsd_pnp_suspend, - .resume = wbsd_pnp_resume, -}; - -#endif /* CONFIG_PNP */ - -/* - * Module loading/unloading - */ - -static int __init wbsd_drv_init(void) -{ - int result; - - printk(KERN_INFO DRIVER_NAME - ": Winbond W83L51xD SD/MMC card interface driver\n"); - printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n"); - -#ifdef CONFIG_PNP - - if (!nopnp) { - result = pnp_register_driver(&wbsd_pnp_driver); - if (result < 0) - return result; - } -#endif /* CONFIG_PNP */ - - if (nopnp) { - result = platform_driver_register(&wbsd_driver); - if (result < 0) - return result; - - wbsd_device = platform_device_alloc(DRIVER_NAME, -1); - if (!wbsd_device) { - platform_driver_unregister(&wbsd_driver); - return -ENOMEM; - } - - result = platform_device_add(wbsd_device); - if (result) { - platform_device_put(wbsd_device); - platform_driver_unregister(&wbsd_driver); - return result; - } - } - - return 0; -} - -static void __exit wbsd_drv_exit(void) -{ -#ifdef CONFIG_PNP - - if (!nopnp) - pnp_unregister_driver(&wbsd_pnp_driver); - -#endif /* CONFIG_PNP */ - - if (nopnp) { - platform_device_unregister(wbsd_device); - - platform_driver_unregister(&wbsd_driver); - } - - DBG("unloaded\n"); -} - -module_init(wbsd_drv_init); -module_exit(wbsd_drv_exit); -#ifdef CONFIG_PNP -module_param(nopnp, uint, 0444); -#endif -module_param(io, uint, 0444); -module_param(irq, uint, 0444); -module_param(dma, int, 0444); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Pierre Ossman "); -MODULE_DESCRIPTION("Winbond W83L51xD SD/MMC card interface driver"); - -#ifdef CONFIG_PNP -MODULE_PARM_DESC(nopnp, "Scan for device instead of relying on PNP. (default 0)"); -#endif -MODULE_PARM_DESC(io, "I/O base to allocate. Must be 8 byte aligned. (default 0x248)"); -MODULE_PARM_DESC(irq, "IRQ to allocate. (default 6)"); -MODULE_PARM_DESC(dma, "DMA channel to allocate. -1 for no DMA. (default 2)"); diff --git a/drivers/mmc/wbsd.h b/drivers/mmc/wbsd.h deleted file mode 100644 index 873bda1e59b..00000000000 --- a/drivers/mmc/wbsd.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * linux/drivers/mmc/wbsd.h - Winbond W83L51xD SD/MMC driver - * - * Copyright (C) 2004-2007 Pierre Ossman, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - */ - -#define LOCK_CODE 0xAA - -#define WBSD_CONF_SWRST 0x02 -#define WBSD_CONF_DEVICE 0x07 -#define WBSD_CONF_ID_HI 0x20 -#define WBSD_CONF_ID_LO 0x21 -#define WBSD_CONF_POWER 0x22 -#define WBSD_CONF_PME 0x23 -#define WBSD_CONF_PMES 0x24 - -#define WBSD_CONF_ENABLE 0x30 -#define WBSD_CONF_PORT_HI 0x60 -#define WBSD_CONF_PORT_LO 0x61 -#define WBSD_CONF_IRQ 0x70 -#define WBSD_CONF_DRQ 0x74 - -#define WBSD_CONF_PINS 0xF0 - -#define DEVICE_SD 0x03 - -#define WBSD_PINS_DAT3_HI 0x20 -#define WBSD_PINS_DAT3_OUT 0x10 -#define WBSD_PINS_GP11_HI 0x04 -#define WBSD_PINS_DETECT_GP11 0x02 -#define WBSD_PINS_DETECT_DAT3 0x01 - -#define WBSD_CMDR 0x00 -#define WBSD_DFR 0x01 -#define WBSD_EIR 0x02 -#define WBSD_ISR 0x03 -#define WBSD_FSR 0x04 -#define WBSD_IDXR 0x05 -#define WBSD_DATAR 0x06 -#define WBSD_CSR 0x07 - -#define WBSD_EINT_CARD 0x40 -#define WBSD_EINT_FIFO_THRE 0x20 -#define WBSD_EINT_CRC 0x10 -#define WBSD_EINT_TIMEOUT 0x08 -#define WBSD_EINT_PROGEND 0x04 -#define WBSD_EINT_BUSYEND 0x02 -#define WBSD_EINT_TC 0x01 - -#define WBSD_INT_PENDING 0x80 -#define WBSD_INT_CARD 0x40 -#define WBSD_INT_FIFO_THRE 0x20 -#define WBSD_INT_CRC 0x10 -#define WBSD_INT_TIMEOUT 0x08 -#define WBSD_INT_PROGEND 0x04 -#define WBSD_INT_BUSYEND 0x02 -#define WBSD_INT_TC 0x01 - -#define WBSD_FIFO_EMPTY 0x80 -#define WBSD_FIFO_FULL 0x40 -#define WBSD_FIFO_EMTHRE 0x20 -#define WBSD_FIFO_FUTHRE 0x10 -#define WBSD_FIFO_SZMASK 0x0F - -#define WBSD_MSLED 0x20 -#define WBSD_POWER_N 0x10 -#define WBSD_WRPT 0x04 -#define WBSD_CARDPRESENT 0x01 - -#define WBSD_IDX_CLK 0x01 -#define WBSD_IDX_PBSMSB 0x02 -#define WBSD_IDX_TAAC 0x03 -#define WBSD_IDX_NSAC 0x04 -#define WBSD_IDX_PBSLSB 0x05 -#define WBSD_IDX_SETUP 0x06 -#define WBSD_IDX_DMA 0x07 -#define WBSD_IDX_FIFOEN 0x08 -#define WBSD_IDX_STATUS 0x10 -#define WBSD_IDX_RSPLEN 0x1E -#define WBSD_IDX_RESP0 0x1F -#define WBSD_IDX_RESP1 0x20 -#define WBSD_IDX_RESP2 0x21 -#define WBSD_IDX_RESP3 0x22 -#define WBSD_IDX_RESP4 0x23 -#define WBSD_IDX_RESP5 0x24 -#define WBSD_IDX_RESP6 0x25 -#define WBSD_IDX_RESP7 0x26 -#define WBSD_IDX_RESP8 0x27 -#define WBSD_IDX_RESP9 0x28 -#define WBSD_IDX_RESP10 0x29 -#define WBSD_IDX_RESP11 0x2A -#define WBSD_IDX_RESP12 0x2B -#define WBSD_IDX_RESP13 0x2C -#define WBSD_IDX_RESP14 0x2D -#define WBSD_IDX_RESP15 0x2E -#define WBSD_IDX_RESP16 0x2F -#define WBSD_IDX_CRCSTATUS 0x30 -#define WBSD_IDX_ISR 0x3F - -#define WBSD_CLK_375K 0x00 -#define WBSD_CLK_12M 0x01 -#define WBSD_CLK_16M 0x02 -#define WBSD_CLK_24M 0x03 - -#define WBSD_DATA_WIDTH 0x01 - -#define WBSD_DAT3_H 0x08 -#define WBSD_FIFO_RESET 0x04 -#define WBSD_SOFT_RESET 0x02 -#define WBSD_INC_INDEX 0x01 - -#define WBSD_DMA_SINGLE 0x02 -#define WBSD_DMA_ENABLE 0x01 - -#define WBSD_FIFOEN_EMPTY 0x20 -#define WBSD_FIFOEN_FULL 0x10 -#define WBSD_FIFO_THREMASK 0x0F - -#define WBSD_BLOCK_READ 0x80 -#define WBSD_BLOCK_WRITE 0x40 -#define WBSD_BUSY 0x20 -#define WBSD_CARDTRAFFIC 0x04 -#define WBSD_SENDCMD 0x02 -#define WBSD_RECVRES 0x01 - -#define WBSD_RSP_SHORT 0x00 -#define WBSD_RSP_LONG 0x01 - -#define WBSD_CRC_MASK 0x1F -#define WBSD_CRC_OK 0x05 /* S010E (00101) */ -#define WBSD_CRC_FAIL 0x0B /* S101E (01011) */ - -#define WBSD_DMA_SIZE 65536 - -struct wbsd_host -{ - struct mmc_host* mmc; /* MMC structure */ - - spinlock_t lock; /* Mutex */ - - int flags; /* Driver states */ - -#define WBSD_FCARD_PRESENT (1<<0) /* Card is present */ -#define WBSD_FIGNORE_DETECT (1<<1) /* Ignore card detection */ - - struct mmc_request* mrq; /* Current request */ - - u8 isr; /* Accumulated ISR */ - - struct scatterlist* cur_sg; /* Current SG entry */ - unsigned int num_sg; /* Number of entries left */ - - unsigned int offset; /* Offset into current entry */ - unsigned int remain; /* Data left in curren entry */ - - char* dma_buffer; /* ISA DMA buffer */ - dma_addr_t dma_addr; /* Physical address for same */ - - int firsterr; /* See fifo functions */ - - u8 clk; /* Current clock speed */ - unsigned char bus_width; /* Current bus width */ - - int config; /* Config port */ - u8 unlock_code; /* Code to unlock config */ - - int chip_id; /* ID of controller */ - - int base; /* I/O port base */ - int irq; /* Interrupt */ - int dma; /* DMA channel */ - - struct tasklet_struct card_tasklet; /* Tasklet structures */ - struct tasklet_struct fifo_tasklet; - struct tasklet_struct crc_tasklet; - struct tasklet_struct timeout_tasklet; - struct tasklet_struct finish_tasklet; - - struct timer_list ignore_timer; /* Ignore detection timer */ -}; -- cgit v1.2.3 From 3b91e5507cddaca53bccf1524ff11a0ac5c85531 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 11 Feb 2007 20:43:19 +0100 Subject: mmc: Flush pending detects on host removal Make sure we kill of any pending detection runs when the host is removed instead of when it is freed. Also add some debugging to make sure the driver doesn't queue up more detection after it has removed the host. Signed-off-by: Pierre Ossman --- drivers/mmc/mmc.c | 15 ++++++++++++++- include/linux/mmc/host.h | 3 +++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 9ffeeb2cba4..0242c6a21da 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1550,6 +1550,12 @@ static void mmc_setup(struct mmc_host *host) */ void mmc_detect_change(struct mmc_host *host, unsigned long delay) { +#ifdef CONFIG_MMC_DEBUG + mmc_claim_host(host); + BUG_ON(host->removed); + mmc_release_host(host); +#endif + mmc_schedule_delayed_work(&host->detect, delay); } @@ -1690,6 +1696,14 @@ void mmc_remove_host(struct mmc_host *host) { struct list_head *l, *n; +#ifdef CONFIG_MMC_DEBUG + mmc_claim_host(host); + host->removed = 1; + mmc_release_host(host); +#endif + + mmc_flush_scheduled_work(); + list_for_each_safe(l, n, &host->cards) { struct mmc_card *card = mmc_list_to_card(l); @@ -1710,7 +1724,6 @@ EXPORT_SYMBOL(mmc_remove_host); */ void mmc_free_host(struct mmc_host *host) { - mmc_flush_scheduled_work(); mmc_free_host_sysfs(host); } diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index c89f4109130..92efe8e5be7 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -147,6 +147,9 @@ struct mmc_host { struct mmc_card *card_selected; /* the selected MMC card */ struct delayed_work detect; +#ifdef CONFIG_MMC_DEBUG + unsigned int removed:1; /* host is being removed */ +#endif unsigned long private[0] ____cacheline_aligned; }; -- cgit v1.2.3 From d2b46f66b4b342be07a4194bd5e82384d07e470d Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 28 Apr 2007 16:52:12 +0200 Subject: mmc: allow suspended block driver to be removed Make sure we don't deadlock when removing a suspended block queue, something that might happen if the card is removed during suspend. Signed-off-by: Pierre Ossman --- drivers/mmc/card/queue.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c index aa75ac11a19..2e77963db33 100644 --- a/drivers/mmc/card/queue.c +++ b/drivers/mmc/card/queue.c @@ -191,6 +191,9 @@ void mmc_cleanup_queue(struct mmc_queue *mq) q->queuedata = NULL; spin_unlock_irqrestore(q->queue_lock, flags); + /* Make sure the queue isn't suspended, as that will deadlock */ + mmc_queue_resume(mq); + /* Then terminate our worker thread */ kthread_stop(mq->thread); -- cgit v1.2.3 From b5af25bee2de2f6cd1ac74ba737cbc4f3d303e5d Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 28 Apr 2007 17:30:50 +0200 Subject: mmc: remove card upon suspend Suspending MMC/SD cards (versus removing and readding them) is an inherently unsafe operation and has even been broken for some time. Signed-off-by: Pierre Ossman --- drivers/mmc/mmc.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 0242c6a21da..94c04725726 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -1738,10 +1738,17 @@ EXPORT_SYMBOL(mmc_free_host); */ int mmc_suspend_host(struct mmc_host *host, pm_message_t state) { - mmc_claim_host(host); - mmc_deselect_cards(host); + struct list_head *l, *n; + + mmc_flush_scheduled_work(); + + list_for_each_safe(l, n, &host->cards) { + struct mmc_card *card = mmc_list_to_card(l); + + mmc_remove_card(card); + } + mmc_power_off(host); - mmc_release_host(host); return 0; } -- cgit v1.2.3 From b855885e3b60cf6f9452848712a62517b94583eb Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Wed, 3 Jan 2007 19:47:29 +0100 Subject: mmc: deprecate mmc bus topology The classic MMC bus was defined as multi card bus system, which is reflected in the design in the MMC layer. When SD showed up, the bus topology was abandoned and a star topology (one card per host) was mandated. MMC version 4 has followed this, officially deprecating the bus topology. As we do not have any known users of the bus topology we can remove support for it. This will simplify the code and rectify some incorrect assumptions in the newer additions. Signed-off-by: Pierre Ossman --- drivers/mmc/card/block.c | 13 +- drivers/mmc/mmc.c | 921 ++++++++++++++++++++--------------------------- include/linux/mmc/card.h | 8 - include/linux/mmc/host.h | 4 +- include/linux/mmc/mmc.h | 9 +- 5 files changed, 400 insertions(+), 555 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 8eba037a18e..8a84e4dc1b2 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -226,8 +226,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) struct mmc_blk_request brq; int ret = 1, sg_pos, data_size; - if (mmc_card_claim_host(card)) - goto flush_queue; + mmc_claim_host(card->host); do { struct mmc_command cmd; @@ -357,7 +356,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) spin_unlock_irq(&md->lock); } while (ret); - mmc_card_release_host(card); + mmc_release_host(card->host); return 1; @@ -393,9 +392,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) spin_unlock_irq(&md->lock); } -flush_queue: - - mmc_card_release_host(card); + mmc_release_host(card->host); spin_lock_irq(&md->lock); while (ret) { @@ -526,12 +523,12 @@ mmc_blk_set_blksize(struct mmc_blk_data *md, struct mmc_card *card) if (mmc_card_blockaddr(card)) return 0; - mmc_card_claim_host(card); + mmc_claim_host(card->host); cmd.opcode = MMC_SET_BLOCKLEN; cmd.arg = 1 << md->block_bits; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; err = mmc_wait_for_cmd(card->host, &cmd, 5); - mmc_card_release_host(card); + mmc_release_host(card->host); if (err) { printk(KERN_ERR "%s: unable to set block size to %d: %d\n", diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 94c04725726..3f50b8882c8 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -3,7 +3,7 @@ * * Copyright (C) 2003-2004 Russell King, All Rights Reserved. * SD support Copyright (C) 2004 Ian Molton, All Rights Reserved. - * SD support Copyright (C) 2005 Pierre Ossman, All Rights Reserved. + * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. * MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved. * * This program is free software; you can redistribute it and/or modify @@ -316,8 +316,6 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card, } EXPORT_SYMBOL(mmc_set_data_timeout); -static int mmc_select_card(struct mmc_host *host, struct mmc_card *card); - /** * __mmc_claim_host - exclusively claim a host * @host: mmc host to claim @@ -329,11 +327,10 @@ static int mmc_select_card(struct mmc_host *host, struct mmc_card *card); * * Note: you should use mmc_card_claim_host or mmc_claim_host. */ -int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card) +void mmc_claim_host(struct mmc_host *host) { DECLARE_WAITQUEUE(wait, current); unsigned long flags; - int err = 0; add_wait_queue(&host->wq, &wait); spin_lock_irqsave(&host->lock, flags); @@ -349,17 +346,9 @@ int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card) host->claimed = 1; spin_unlock_irqrestore(&host->lock, flags); remove_wait_queue(&host->wq, &wait); - - if (card != (void *)-1) { - err = mmc_select_card(host, card); - if (err != MMC_ERR_NONE) - return err; - } - - return err; } -EXPORT_SYMBOL(__mmc_claim_host); +EXPORT_SYMBOL(mmc_claim_host); /** * mmc_release_host - release a host @@ -396,23 +385,18 @@ static inline void mmc_set_ios(struct mmc_host *host) host->ops->set_ios(host, ios); } -static int mmc_select_card(struct mmc_host *host, struct mmc_card *card) +static int mmc_select_card(struct mmc_card *card) { int err; struct mmc_command cmd; - BUG_ON(!host->claimed); - - if (host->card_selected == card) - return MMC_ERR_NONE; - - host->card_selected = card; + BUG_ON(!card->host->claimed); cmd.opcode = MMC_SELECT_CARD; cmd.arg = card->rca << 16; cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + err = mmc_wait_for_cmd(card->host, &cmd, CMD_RETRIES); if (err != MMC_ERR_NONE) return err; @@ -426,49 +410,24 @@ static int mmc_select_card(struct mmc_host *host, struct mmc_card *card) * wider version. */ if (mmc_card_sd(card) && - (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { + (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4) && + (card->host->caps & MMC_CAP_4_BIT_DATA)) { - /* - * Default bus width is 1 bit. - */ - host->ios.bus_width = MMC_BUS_WIDTH_1; - - if (host->caps & MMC_CAP_4_BIT_DATA) { - struct mmc_command cmd; - cmd.opcode = SD_APP_SET_BUS_WIDTH; - cmd.arg = SD_BUS_WIDTH_4; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - - err = mmc_wait_for_app_cmd(host, card->rca, &cmd, - CMD_RETRIES); - if (err != MMC_ERR_NONE) - return err; - - host->ios.bus_width = MMC_BUS_WIDTH_4; - } - } - - mmc_set_ios(host); - - return MMC_ERR_NONE; -} - -/* - * Ensure that no card is selected. - */ -static void mmc_deselect_cards(struct mmc_host *host) -{ - struct mmc_command cmd; - - if (host->card_selected) { - host->card_selected = NULL; + struct mmc_command cmd; + cmd.opcode = SD_APP_SET_BUS_WIDTH; + cmd.arg = SD_BUS_WIDTH_4; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - cmd.opcode = MMC_SELECT_CARD; - cmd.arg = 0; - cmd.flags = MMC_RSP_NONE | MMC_CMD_AC; + err = mmc_wait_for_app_cmd(card->host, card->rca, + &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + return err; - mmc_wait_for_cmd(host, &cmd, 0); + card->host->ios.bus_width = MMC_BUS_WIDTH_4; + mmc_set_ios(card->host); } + + return MMC_ERR_NONE; } @@ -732,27 +691,12 @@ static void mmc_decode_scr(struct mmc_card *card) } /* - * Locate a MMC card on this MMC host given a raw CID. - */ -static struct mmc_card *mmc_find_card(struct mmc_host *host, u32 *raw_cid) -{ - struct mmc_card *card; - - list_for_each_entry(card, &host->cards, node) { - if (memcmp(card->raw_cid, raw_cid, sizeof(card->raw_cid)) == 0) - return card; - } - return NULL; -} - -/* - * Allocate a new MMC card, and assign a unique RCA. + * Allocate a new MMC card */ static struct mmc_card * -mmc_alloc_card(struct mmc_host *host, u32 *raw_cid, unsigned int *frca) +mmc_alloc_card(struct mmc_host *host, u32 *raw_cid) { - struct mmc_card *card, *c; - unsigned int rca = *frca; + struct mmc_card *card; card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL); if (!card) @@ -761,17 +705,6 @@ mmc_alloc_card(struct mmc_host *host, u32 *raw_cid, unsigned int *frca) mmc_init_card(card, host); memcpy(card->raw_cid, raw_cid, sizeof(card->raw_cid)); - again: - list_for_each_entry(c, &host->cards, node) - if (c->rca == rca) { - rca++; - goto again; - } - - card->rca = rca; - - *frca = rca; - return card; } @@ -937,128 +870,128 @@ static int mmc_send_if_cond(struct mmc_host *host, u32 ocr, int *rsd2) } /* - * Discover cards by requesting their CID. If this command - * times out, it is not an error; there are no further cards - * to be discovered. Add new cards to the list. + * Discover the card by requesting its CID. * - * Create a mmc_card entry for each discovered card, assigning + * Create a mmc_card entry for the discovered card, assigning * it an RCA, and save the raw CID for decoding later. */ -static void mmc_discover_cards(struct mmc_host *host) +static void mmc_discover_card(struct mmc_host *host) { - struct mmc_card *card; - unsigned int first_rca = 1, err; + unsigned int err; - while (1) { - struct mmc_command cmd; + struct mmc_command cmd; - cmd.opcode = MMC_ALL_SEND_CID; + BUG_ON(host->card); + + cmd.opcode = MMC_ALL_SEND_CID; + cmd.arg = 0; + cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err == MMC_ERR_TIMEOUT) { + err = MMC_ERR_NONE; + return; + } + if (err != MMC_ERR_NONE) { + printk(KERN_ERR "%s: error requesting CID: %d\n", + mmc_hostname(host), err); + return; + } + + host->card = mmc_alloc_card(host, cmd.resp); + if (IS_ERR(host->card)) { + err = PTR_ERR(host->card); + host->card = NULL; + return; + } + + if (host->mode == MMC_MODE_SD) { + host->card->type = MMC_TYPE_SD; + + cmd.opcode = SD_SEND_RELATIVE_ADDR; cmd.arg = 0; - cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; + cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err == MMC_ERR_TIMEOUT) { - err = MMC_ERR_NONE; - break; - } - if (err != MMC_ERR_NONE) { - printk(KERN_ERR "%s: error requesting CID: %d\n", - mmc_hostname(host), err); - break; - } - - card = mmc_find_card(host, cmd.resp); - if (!card) { - card = mmc_alloc_card(host, cmd.resp, &first_rca); - if (IS_ERR(card)) { - err = PTR_ERR(card); - break; + if (err != MMC_ERR_NONE) + mmc_card_set_dead(host->card); + else { + host->card->rca = cmd.resp[0] >> 16; + + if (!host->ops->get_ro) { + printk(KERN_WARNING "%s: host does not " + "support reading read-only " + "switch. assuming write-enable.\n", + mmc_hostname(host)); + } else { + if (host->ops->get_ro(host)) + mmc_card_set_readonly(host->card); } - list_add(&card->node, &host->cards); } + } else { + host->card->type = MMC_TYPE_MMC; + host->card->rca = 1; - card->state &= ~MMC_STATE_DEAD; - - if (host->mode == MMC_MODE_SD) { - card->type = MMC_TYPE_SD; - - cmd.opcode = SD_SEND_RELATIVE_ADDR; - cmd.arg = 0; - cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) - mmc_card_set_dead(card); - else { - card->rca = cmd.resp[0] >> 16; - - if (!host->ops->get_ro) { - printk(KERN_WARNING "%s: host does not " - "support reading read-only " - "switch. assuming write-enable.\n", - mmc_hostname(host)); - } else { - if (host->ops->get_ro(host)) - mmc_card_set_readonly(card); - } - } - } else { - card->type = MMC_TYPE_MMC; - cmd.opcode = MMC_SET_RELATIVE_ADDR; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) - mmc_card_set_dead(card); - } + cmd.opcode = MMC_SET_RELATIVE_ADDR; + cmd.arg = host->card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + mmc_card_set_dead(host->card); } } -static void mmc_read_csds(struct mmc_host *host) +static void mmc_read_csd(struct mmc_host *host) { - struct mmc_card *card; - - list_for_each_entry(card, &host->cards, node) { - struct mmc_command cmd; - int err; + struct mmc_command cmd; + int err; - if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT)) - continue; + if (!host->card) + return; + if (mmc_card_dead(host->card)) + return; - cmd.opcode = MMC_SEND_CSD; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R2 | MMC_CMD_AC; + cmd.opcode = MMC_SEND_CSD; + cmd.arg = host->card->rca << 16; + cmd.flags = MMC_RSP_R2 | MMC_CMD_AC; - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) { - mmc_card_set_dead(card); - continue; - } + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) { + mmc_card_set_dead(host->card); + return; + } - memcpy(card->raw_csd, cmd.resp, sizeof(card->raw_csd)); + memcpy(host->card->raw_csd, cmd.resp, sizeof(host->card->raw_csd)); - mmc_decode_csd(card); - mmc_decode_cid(card); - } + mmc_decode_csd(host->card); + mmc_decode_cid(host->card); } -static void mmc_process_ext_csds(struct mmc_host *host) +static void mmc_process_ext_csd(struct mmc_host *host) { int err; - struct mmc_card *card; struct mmc_request mrq; struct mmc_command cmd; struct mmc_data data; + u8 *ext_csd; struct scatterlist sg; + if (!host->card) + return; + if (mmc_card_dead(host->card)) + return; + if (mmc_card_sd(host->card)) + return; + if (host->card->csd.mmca_vsn < CSD_SPEC_VER_4) + return; + /* * As the ext_csd is so large and mostly unused, we don't store the * raw block in mmc_card. */ - u8 *ext_csd; ext_csd = kmalloc(512, GFP_KERNEL); if (!ext_csd) { printk("%s: could not allocate a buffer to receive the ext_csd." @@ -1067,211 +1000,184 @@ static void mmc_process_ext_csds(struct mmc_host *host) return; } - list_for_each_entry(card, &host->cards, node) { - if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT)) - continue; - if (mmc_card_sd(card)) - continue; - if (card->csd.mmca_vsn < CSD_SPEC_VER_4) - continue; - - err = mmc_select_card(host, card); - if (err != MMC_ERR_NONE) { - mmc_card_set_dead(card); - continue; - } - - memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = MMC_SEND_EXT_CSD; - cmd.arg = 0; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + cmd.opcode = MMC_SEND_EXT_CSD; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - memset(&data, 0, sizeof(struct mmc_data)); + memset(&data, 0, sizeof(struct mmc_data)); - mmc_set_data_timeout(&data, card, 0); + mmc_set_data_timeout(&data, host->card, 0); - data.blksz = 512; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = &sg; - data.sg_len = 1; + data.blksz = 512; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; - memset(&mrq, 0, sizeof(struct mmc_request)); + memset(&mrq, 0, sizeof(struct mmc_request)); - mrq.cmd = &cmd; - mrq.data = &data; + mrq.cmd = &cmd; + mrq.data = &data; - sg_init_one(&sg, ext_csd, 512); + sg_init_one(&sg, ext_csd, 512); - mmc_wait_for_req(host, &mrq); + mmc_wait_for_req(host, &mrq); - if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { - if (card->csd.capacity == (4096 * 512)) { - printk(KERN_ERR "%s: unable to read EXT_CSD " - "on a possible high capacity card. " - "Card will be ignored.\n", - mmc_hostname(card->host)); - mmc_card_set_dead(card); - } else { - printk(KERN_WARNING "%s: unable to read " - "EXT_CSD, performance might " - "suffer.\n", - mmc_hostname(card->host)); - } - continue; + if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { + if (host->card->csd.capacity == (4096 * 512)) { + printk(KERN_ERR "%s: unable to read EXT_CSD " + "on a possible high capacity card. " + "Card will be ignored.\n", + mmc_hostname(host)); + mmc_card_set_dead(host->card); + } else { + printk(KERN_WARNING "%s: unable to read " + "EXT_CSD, performance might " + "suffer.\n", + mmc_hostname(host)); } + goto out; + } - card->ext_csd.sectors = - ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | - ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | - ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | - ext_csd[EXT_CSD_SEC_CNT + 3] << 24; - if (card->ext_csd.sectors) - mmc_card_set_blockaddr(card); + host->card->ext_csd.sectors = + ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | + ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | + ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | + ext_csd[EXT_CSD_SEC_CNT + 3] << 24; + if (host->card->ext_csd.sectors) + mmc_card_set_blockaddr(host->card); + + switch (ext_csd[EXT_CSD_CARD_TYPE]) { + case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26: + host->card->ext_csd.hs_max_dtr = 52000000; + break; + case EXT_CSD_CARD_TYPE_26: + host->card->ext_csd.hs_max_dtr = 26000000; + break; + default: + /* MMC v4 spec says this cannot happen */ + printk("%s: card is mmc v4 but doesn't support " + "any high-speed modes.\n", + mmc_hostname(host)); + goto out; + } - switch (ext_csd[EXT_CSD_CARD_TYPE]) { - case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26: - card->ext_csd.hs_max_dtr = 52000000; - break; - case EXT_CSD_CARD_TYPE_26: - card->ext_csd.hs_max_dtr = 26000000; - break; - default: - /* MMC v4 spec says this cannot happen */ - printk("%s: card is mmc v4 but doesn't support " - "any high-speed modes.\n", - mmc_hostname(card->host)); - continue; - } + if (host->caps & MMC_CAP_MMC_HIGHSPEED) { + /* Activate highspeed support. */ + cmd.opcode = MMC_SWITCH; + cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_HS_TIMING << 16) | + (1 << 8) | + EXT_CSD_CMD_SET_NORMAL; + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; - if (host->caps & MMC_CAP_MMC_HIGHSPEED) { - /* Activate highspeed support. */ - cmd.opcode = MMC_SWITCH; - cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | - (EXT_CSD_HS_TIMING << 16) | - (1 << 8) | - EXT_CSD_CMD_SET_NORMAL; - cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) { - printk("%s: failed to switch card to mmc v4 " - "high-speed mode.\n", - mmc_hostname(card->host)); - continue; - } + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) { + printk("%s: failed to switch card to mmc v4 " + "high-speed mode.\n", + mmc_hostname(host)); + goto out; + } - mmc_card_set_highspeed(card); + mmc_card_set_highspeed(host->card); - host->ios.timing = MMC_TIMING_MMC_HS; - mmc_set_ios(host); - } + host->ios.timing = MMC_TIMING_MMC_HS; + mmc_set_ios(host); + } - /* Check for host support for wide-bus modes. */ - if (host->caps & MMC_CAP_4_BIT_DATA) { - /* Activate 4-bit support. */ - cmd.opcode = MMC_SWITCH; - cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | - (EXT_CSD_BUS_WIDTH << 16) | - (EXT_CSD_BUS_WIDTH_4 << 8) | - EXT_CSD_CMD_SET_NORMAL; - cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) { - printk("%s: failed to switch card to " - "mmc v4 4-bit bus mode.\n", - mmc_hostname(card->host)); - continue; - } + /* Check for host support for wide-bus modes. */ + if (host->caps & MMC_CAP_4_BIT_DATA) { + /* Activate 4-bit support. */ + cmd.opcode = MMC_SWITCH; + cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_BUS_WIDTH << 16) | + (EXT_CSD_BUS_WIDTH_4 << 8) | + EXT_CSD_CMD_SET_NORMAL; + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; - host->ios.bus_width = MMC_BUS_WIDTH_4; - mmc_set_ios(host); + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) { + printk("%s: failed to switch card to " + "mmc v4 4-bit bus mode.\n", + mmc_hostname(host)); + goto out; } + + host->ios.bus_width = MMC_BUS_WIDTH_4; + mmc_set_ios(host); } +out: kfree(ext_csd); - - mmc_deselect_cards(host); } -static void mmc_read_scrs(struct mmc_host *host) +static void mmc_read_scr(struct mmc_host *host) { int err; - struct mmc_card *card; struct mmc_request mrq; struct mmc_command cmd; struct mmc_data data; struct scatterlist sg; - list_for_each_entry(card, &host->cards, node) { - if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT)) - continue; - if (!mmc_card_sd(card)) - continue; - - err = mmc_select_card(host, card); - if (err != MMC_ERR_NONE) { - mmc_card_set_dead(card); - continue; - } - - memset(&cmd, 0, sizeof(struct mmc_command)); - - cmd.opcode = MMC_APP_CMD; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + if (!host->card) + return; + if (mmc_card_dead(host->card)) + return; + if (!mmc_card_sd(host->card)) + return; - err = mmc_wait_for_cmd(host, &cmd, 0); - if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) { - mmc_card_set_dead(card); - continue; - } + memset(&cmd, 0, sizeof(struct mmc_command)); - memset(&cmd, 0, sizeof(struct mmc_command)); + cmd.opcode = MMC_APP_CMD; + cmd.arg = host->card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - cmd.opcode = SD_APP_SEND_SCR; - cmd.arg = 0; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + err = mmc_wait_for_cmd(host, &cmd, 0); + if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) { + mmc_card_set_dead(host->card); + return; + } - memset(&data, 0, sizeof(struct mmc_data)); + memset(&cmd, 0, sizeof(struct mmc_command)); - mmc_set_data_timeout(&data, card, 0); + cmd.opcode = SD_APP_SEND_SCR; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - data.blksz = 1 << 3; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = &sg; - data.sg_len = 1; + memset(&data, 0, sizeof(struct mmc_data)); - memset(&mrq, 0, sizeof(struct mmc_request)); + mmc_set_data_timeout(&data, host->card, 0); - mrq.cmd = &cmd; - mrq.data = &data; + data.blksz = 1 << 3; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; - sg_init_one(&sg, (u8*)card->raw_scr, 8); + memset(&mrq, 0, sizeof(struct mmc_request)); - mmc_wait_for_req(host, &mrq); + mrq.cmd = &cmd; + mrq.data = &data; - if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { - mmc_card_set_dead(card); - continue; - } + sg_init_one(&sg, (u8*)host->card->raw_scr, 8); - card->raw_scr[0] = ntohl(card->raw_scr[0]); - card->raw_scr[1] = ntohl(card->raw_scr[1]); + mmc_wait_for_req(host, &mrq); - mmc_decode_scr(card); + if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { + mmc_card_set_dead(host->card); + return; } - mmc_deselect_cards(host); + host->card->raw_scr[0] = ntohl(host->card->raw_scr[0]); + host->card->raw_scr[1] = ntohl(host->card->raw_scr[1]); + + mmc_decode_scr(host->card); } static void mmc_read_switch_caps(struct mmc_host *host) { - int err; - struct mmc_card *card; struct mmc_request mrq; struct mmc_command cmd; struct mmc_data data; @@ -1281,6 +1187,15 @@ static void mmc_read_switch_caps(struct mmc_host *host) if (!(host->caps & MMC_CAP_SD_HIGHSPEED)) return; + if (!host->card) + return; + if (mmc_card_dead(host->card)) + return; + if (!mmc_card_sd(host->card)) + return; + if (host->card->scr.sda_vsn < SCR_SPEC_VER_1) + return; + status = kmalloc(64, GFP_KERNEL); if (!status) { printk(KERN_WARNING "%s: Unable to allocate buffer for " @@ -1289,116 +1204,98 @@ static void mmc_read_switch_caps(struct mmc_host *host) return; } - list_for_each_entry(card, &host->cards, node) { - if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT)) - continue; - if (!mmc_card_sd(card)) - continue; - if (card->scr.sda_vsn < SCR_SPEC_VER_1) - continue; - - err = mmc_select_card(host, card); - if (err != MMC_ERR_NONE) { - mmc_card_set_dead(card); - continue; - } - - memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = SD_SWITCH; - cmd.arg = 0x00FFFFF1; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + cmd.opcode = SD_SWITCH; + cmd.arg = 0x00FFFFF1; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - memset(&data, 0, sizeof(struct mmc_data)); + memset(&data, 0, sizeof(struct mmc_data)); - mmc_set_data_timeout(&data, card, 0); + mmc_set_data_timeout(&data, host->card, 0); - data.blksz = 64; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = &sg; - data.sg_len = 1; + data.blksz = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; - memset(&mrq, 0, sizeof(struct mmc_request)); + memset(&mrq, 0, sizeof(struct mmc_request)); - mrq.cmd = &cmd; - mrq.data = &data; + mrq.cmd = &cmd; + mrq.data = &data; - sg_init_one(&sg, status, 64); + sg_init_one(&sg, status, 64); - mmc_wait_for_req(host, &mrq); + mmc_wait_for_req(host, &mrq); - if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { - printk("%s: unable to read switch capabilities, " - "performance might suffer.\n", - mmc_hostname(card->host)); - continue; - } + if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { + printk("%s: unable to read switch capabilities, " + "performance might suffer.\n", + mmc_hostname(host)); + goto out; + } - if (status[13] & 0x02) - card->sw_caps.hs_max_dtr = 50000000; + if (status[13] & 0x02) + host->card->sw_caps.hs_max_dtr = 50000000; - memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&cmd, 0, sizeof(struct mmc_command)); - cmd.opcode = SD_SWITCH; - cmd.arg = 0x80FFFFF1; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + cmd.opcode = SD_SWITCH; + cmd.arg = 0x80FFFFF1; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - memset(&data, 0, sizeof(struct mmc_data)); + memset(&data, 0, sizeof(struct mmc_data)); - mmc_set_data_timeout(&data, card, 0); + mmc_set_data_timeout(&data, host->card, 0); - data.blksz = 64; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = &sg; - data.sg_len = 1; + data.blksz = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; - memset(&mrq, 0, sizeof(struct mmc_request)); + memset(&mrq, 0, sizeof(struct mmc_request)); - mrq.cmd = &cmd; - mrq.data = &data; + mrq.cmd = &cmd; + mrq.data = &data; - sg_init_one(&sg, status, 64); + sg_init_one(&sg, status, 64); - mmc_wait_for_req(host, &mrq); + mmc_wait_for_req(host, &mrq); - if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE || - (status[16] & 0xF) != 1) { - printk(KERN_WARNING "%s: Problem switching card " - "into high-speed mode!\n", - mmc_hostname(host)); - continue; - } + if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE || + (status[16] & 0xF) != 1) { + printk(KERN_WARNING "%s: Problem switching card " + "into high-speed mode!\n", + mmc_hostname(host)); + goto out; + } - mmc_card_set_highspeed(card); + mmc_card_set_highspeed(host->card); - host->ios.timing = MMC_TIMING_SD_HS; - mmc_set_ios(host); - } + host->ios.timing = MMC_TIMING_SD_HS; + mmc_set_ios(host); +out: kfree(status); - - mmc_deselect_cards(host); } static unsigned int mmc_calculate_clock(struct mmc_host *host) { - struct mmc_card *card; unsigned int max_dtr = host->f_max; - list_for_each_entry(card, &host->cards, node) - if (!mmc_card_dead(card)) { - if (mmc_card_highspeed(card) && mmc_card_sd(card)) { - if (max_dtr > card->sw_caps.hs_max_dtr) - max_dtr = card->sw_caps.hs_max_dtr; - } else if (mmc_card_highspeed(card) && !mmc_card_sd(card)) { - if (max_dtr > card->ext_csd.hs_max_dtr) - max_dtr = card->ext_csd.hs_max_dtr; - } else if (max_dtr > card->csd.max_dtr) { - max_dtr = card->csd.max_dtr; - } + if (host->card && !mmc_card_dead(host->card)) { + if (mmc_card_highspeed(host->card) && mmc_card_sd(host->card)) { + if (max_dtr > host->card->sw_caps.hs_max_dtr) + max_dtr = host->card->sw_caps.hs_max_dtr; + } else if (mmc_card_highspeed(host->card) && !mmc_card_sd(host->card)) { + if (max_dtr > host->card->ext_csd.hs_max_dtr) + max_dtr = host->card->ext_csd.hs_max_dtr; + } else if (max_dtr > host->card->csd.max_dtr) { + max_dtr = host->card->csd.max_dtr; } + } pr_debug("%s: selected %d.%03dMHz transfer rate\n", mmc_hostname(host), @@ -1415,91 +1312,65 @@ static unsigned int mmc_calculate_clock(struct mmc_host *host) * A request for status does not cause a state change in data * transfer mode. */ -static void mmc_check_cards(struct mmc_host *host) +static void mmc_check_card(struct mmc_card *card) { - struct list_head *l, *n; - - mmc_deselect_cards(host); + struct mmc_command cmd; + int err; - list_for_each_safe(l, n, &host->cards) { - struct mmc_card *card = mmc_list_to_card(l); - struct mmc_command cmd; - int err; + BUG_ON(!card); - cmd.opcode = MMC_SEND_STATUS; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err == MMC_ERR_NONE) - continue; + err = mmc_wait_for_cmd(card->host, &cmd, CMD_RETRIES); + if (err == MMC_ERR_NONE) + return; - mmc_card_set_dead(card); - } + mmc_card_set_dead(card); } static void mmc_setup(struct mmc_host *host) { - if (host->ios.power_mode != MMC_POWER_ON) { - int err; - u32 ocr; - - host->mode = MMC_MODE_SD; - - mmc_power_up(host); - mmc_idle_cards(host); + int err; + u32 ocr; - err = mmc_send_if_cond(host, host->ocr_avail, NULL); - if (err != MMC_ERR_NONE) { - return; - } - err = mmc_send_app_op_cond(host, 0, &ocr); + host->mode = MMC_MODE_SD; - /* - * If we fail to detect any SD cards then try - * searching for MMC cards. - */ - if (err != MMC_ERR_NONE) { - host->mode = MMC_MODE_MMC; - - err = mmc_send_op_cond(host, 0, &ocr); - if (err != MMC_ERR_NONE) - return; - } + mmc_power_up(host); + mmc_idle_cards(host); - host->ocr = mmc_select_voltage(host, ocr); + err = mmc_send_if_cond(host, host->ocr_avail, NULL); + if (err != MMC_ERR_NONE) { + return; + } + err = mmc_send_app_op_cond(host, 0, &ocr); - /* - * Since we're changing the OCR value, we seem to - * need to tell some cards to go back to the idle - * state. We wait 1ms to give cards time to - * respond. - */ - if (host->ocr) - mmc_idle_cards(host); - } else { - host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; - host->ios.clock = host->f_min; - mmc_set_ios(host); + /* + * If we fail to detect any SD cards then try + * searching for MMC cards. + */ + if (err != MMC_ERR_NONE) { + host->mode = MMC_MODE_MMC; - /* - * We should remember the OCR mask from the existing - * cards, and detect the new cards OCR mask, combine - * the two and re-select the VDD. However, if we do - * change VDD, we should do an idle, and then do a - * full re-initialisation. We would need to notify - * drivers so that they can re-setup the cards as - * well, while keeping their queues at bay. - * - * For the moment, we take the easy way out - if the - * new cards don't like our currently selected VDD, - * they drop off the bus. - */ + err = mmc_send_op_cond(host, 0, &ocr); + if (err != MMC_ERR_NONE) + return; } + host->ocr = mmc_select_voltage(host, ocr); + if (host->ocr == 0) return; + /* + * Since we're changing the OCR value, we seem to + * need to tell some cards to go back to the idle + * state. We wait 1ms to give cards time to + * respond. + */ + mmc_idle_cards(host); + /* * Send the selected OCR multiple times... until the cards * all get the idea that they should be ready for CMD2. @@ -1522,7 +1393,7 @@ static void mmc_setup(struct mmc_host *host) mmc_send_op_cond(host, host->ocr | (1 << 30), NULL); } - mmc_discover_cards(host); + mmc_discover_card(host); /* * Ok, now switch to push-pull mode. @@ -1530,13 +1401,19 @@ static void mmc_setup(struct mmc_host *host) host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; mmc_set_ios(host); - mmc_read_csds(host); + mmc_read_csd(host); + + if (host->card && !mmc_card_dead(host->card)) { + err = mmc_select_card(host->card); + if (err != MMC_ERR_NONE) + mmc_card_set_dead(host->card); + } if (host->mode == MMC_MODE_SD) { - mmc_read_scrs(host); + mmc_read_scr(host); mmc_read_switch_caps(host); } else - mmc_process_ext_csds(host); + mmc_process_ext_csd(host); } @@ -1566,31 +1443,29 @@ static void mmc_rescan(struct work_struct *work) { struct mmc_host *host = container_of(work, struct mmc_host, detect.work); - struct list_head *l, *n; - unsigned char power_mode; mmc_claim_host(host); /* - * Check for removed cards and newly inserted ones. We check for + * Check for removed card and newly inserted ones. We check for * removed cards first so we can intelligently re-select the VDD. */ - power_mode = host->ios.power_mode; - if (power_mode == MMC_POWER_ON) - mmc_check_cards(host); + if (host->card) { + mmc_check_card(host->card); - mmc_setup(host); + mmc_release_host(host); - /* - * Some broken cards process CMD1 even in stand-by state. There is - * no reply, but an ILLEGAL_COMMAND error is cached and returned - * after next command. We poll for card status here to clear any - * possibly pending error. - */ - if (power_mode == MMC_POWER_ON) - mmc_check_cards(host); + if (mmc_card_dead(host->card)) { + mmc_remove_card(host->card); + host->card = NULL; + } + + goto out; + } - if (!list_empty(&host->cards)) { + mmc_setup(host); + + if (host->card && !mmc_card_dead(host->card)) { /* * (Re-)calculate the fastest clock rate which the * attached cards and the host support. @@ -1601,31 +1476,28 @@ static void mmc_rescan(struct work_struct *work) mmc_release_host(host); - list_for_each_safe(l, n, &host->cards) { - struct mmc_card *card = mmc_list_to_card(l); - - /* - * If this is a new and good card, register it. - */ - if (!mmc_card_present(card) && !mmc_card_dead(card)) { - if (mmc_register_card(card)) - mmc_card_set_dead(card); - } + /* + * If this is a new and good card, register it. + */ + if (host->card && !mmc_card_dead(host->card)) { + if (mmc_register_card(host->card)) + mmc_card_set_dead(host->card); + } - /* - * If this card is dead, destroy it. - */ - if (mmc_card_dead(card)) { - list_del(&card->node); - mmc_remove_card(card); - } + /* + * If this card is dead, destroy it. + */ + if (host->card && mmc_card_dead(host->card)) { + mmc_remove_card(host->card); + host->card = NULL; } +out: /* * If we discover that there are no cards on the * bus, turn off the clock and power down. */ - if (list_empty(&host->cards)) + if (!host->card) mmc_power_off(host); } @@ -1645,7 +1517,6 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) if (host) { spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); - INIT_LIST_HEAD(&host->cards); INIT_DELAYED_WORK(&host->detect, mmc_rescan); /* @@ -1694,8 +1565,6 @@ EXPORT_SYMBOL(mmc_add_host); */ void mmc_remove_host(struct mmc_host *host) { - struct list_head *l, *n; - #ifdef CONFIG_MMC_DEBUG mmc_claim_host(host); host->removed = 1; @@ -1704,10 +1573,9 @@ void mmc_remove_host(struct mmc_host *host) mmc_flush_scheduled_work(); - list_for_each_safe(l, n, &host->cards) { - struct mmc_card *card = mmc_list_to_card(l); - - mmc_remove_card(card); + if (host->card) { + mmc_remove_card(host->card); + host->card = NULL; } mmc_power_off(host); @@ -1738,14 +1606,11 @@ EXPORT_SYMBOL(mmc_free_host); */ int mmc_suspend_host(struct mmc_host *host, pm_message_t state) { - struct list_head *l, *n; - mmc_flush_scheduled_work(); - list_for_each_safe(l, n, &host->cards) { - struct mmc_card *card = mmc_list_to_card(l); - - mmc_remove_card(card); + if (host->card) { + mmc_remove_card(host->card); + host->card = NULL; } mmc_power_off(host); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 1ca50542ce1..7d98990ac94 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -61,7 +61,6 @@ struct mmc_host; * MMC device */ struct mmc_card { - struct list_head node; /* node in hosts devices list */ struct mmc_host *host; /* the host this device belongs to */ struct device dev; /* the device */ unsigned int rca; /* relative card address of device */ @@ -123,11 +122,4 @@ struct mmc_driver { extern int mmc_register_driver(struct mmc_driver *); extern void mmc_unregister_driver(struct mmc_driver *); -static inline int mmc_card_claim_host(struct mmc_card *card) -{ - return __mmc_claim_host(card->host, card); -} - -#define mmc_card_release_host(c) mmc_release_host((c)->host) - #endif diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 92efe8e5be7..6ea3c0ea3e1 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -138,14 +138,12 @@ struct mmc_host { #define MMC_MODE_MMC 0 #define MMC_MODE_SD 1 - struct list_head cards; /* devices attached to this host */ + struct mmc_card *card; /* device attached to this host */ wait_queue_head_t wq; spinlock_t lock; /* claimed lock */ unsigned int claimed:1; /* host exclusively claimed */ - struct mmc_card *card_selected; /* the selected MMC card */ - struct delayed_work detect; #ifdef CONFIG_MMC_DEBUG unsigned int removed:1; /* host is being removed */ diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index cdc54be804f..b3d80efc643 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -8,7 +8,6 @@ #ifndef MMC_H #define MMC_H -#include #include #include @@ -107,13 +106,7 @@ extern int mmc_wait_for_app_cmd(struct mmc_host *, unsigned int, extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *, int); -extern int __mmc_claim_host(struct mmc_host *host, struct mmc_card *card); - -static inline void mmc_claim_host(struct mmc_host *host) -{ - __mmc_claim_host(host, (struct mmc_card *)-1); -} - +extern void mmc_claim_host(struct mmc_host *host); extern void mmc_release_host(struct mmc_host *host); #endif -- cgit v1.2.3 From aaac1b470bd0dccb30912356617069dc6199cc80 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Wed, 28 Feb 2007 15:33:10 +0100 Subject: mmc: Move core functions to subdir Create a "core" subdirectory to house the central bus handling functions. Signed-off-by: Pierre Ossman --- drivers/mmc/Makefile | 7 +- drivers/mmc/core/Makefile | 11 + drivers/mmc/core/core.c | 1638 +++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/core/core.h | 25 + drivers/mmc/core/sysfs.c | 361 ++++++++++ drivers/mmc/mmc.c | 1638 --------------------------------------------- drivers/mmc/mmc.h | 25 - drivers/mmc/mmc_sysfs.c | 361 ---------- include/linux/mmc/card.h | 2 +- include/linux/mmc/core.h | 112 ++++ include/linux/mmc/host.h | 2 +- include/linux/mmc/mmc.h | 112 ---- 12 files changed, 2150 insertions(+), 2144 deletions(-) create mode 100644 drivers/mmc/core/Makefile create mode 100644 drivers/mmc/core/core.c create mode 100644 drivers/mmc/core/core.h create mode 100644 drivers/mmc/core/sysfs.c delete mode 100644 drivers/mmc/mmc.c delete mode 100644 drivers/mmc/mmc.h delete mode 100644 drivers/mmc/mmc_sysfs.c create mode 100644 include/linux/mmc/core.h delete mode 100644 include/linux/mmc/mmc.h diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 4d2bdfeb8d7..9979f5e9765 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -6,12 +6,7 @@ ifeq ($(CONFIG_MMC_DEBUG),y) EXTRA_CFLAGS += -DDEBUG endif -# -# Core -# -obj-$(CONFIG_MMC) += mmc_core.o -mmc_core-y := mmc.o mmc_sysfs.o - +obj-$(CONFIG_MMC) += core/ obj-$(CONFIG_MMC) += card/ obj-$(CONFIG_MMC) += host/ diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile new file mode 100644 index 00000000000..f911fbd2845 --- /dev/null +++ b/drivers/mmc/core/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the kernel mmc core. +# + +ifeq ($(CONFIG_MMC_DEBUG),y) + EXTRA_CFLAGS += -DDEBUG +endif + +obj-$(CONFIG_MMC) += mmc_core.o +mmc_core-y := core.o sysfs.o + diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c new file mode 100644 index 00000000000..334e663e465 --- /dev/null +++ b/drivers/mmc/core/core.c @@ -0,0 +1,1638 @@ +/* + * linux/drivers/mmc/core/core.c + * + * Copyright (C) 2003-2004 Russell King, All Rights Reserved. + * SD support Copyright (C) 2004 Ian Molton, All Rights Reserved. + * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. + * MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "core.h" + +#define CMD_RETRIES 3 + +/* + * OCR Bit positions to 10s of Vdd mV. + */ +static const unsigned short mmc_ocr_bit_to_vdd[] = { + 150, 155, 160, 165, 170, 180, 190, 200, + 210, 220, 230, 240, 250, 260, 270, 280, + 290, 300, 310, 320, 330, 340, 350, 360 +}; + +static const unsigned int tran_exp[] = { + 10000, 100000, 1000000, 10000000, + 0, 0, 0, 0 +}; + +static const unsigned char tran_mant[] = { + 0, 10, 12, 13, 15, 20, 25, 30, + 35, 40, 45, 50, 55, 60, 70, 80, +}; + +static const unsigned int tacc_exp[] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, +}; + +static const unsigned int tacc_mant[] = { + 0, 10, 12, 13, 15, 20, 25, 30, + 35, 40, 45, 50, 55, 60, 70, 80, +}; + + +/** + * mmc_request_done - finish processing an MMC request + * @host: MMC host which completed request + * @mrq: MMC request which request + * + * MMC drivers should call this function when they have completed + * their processing of a request. + */ +void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) +{ + struct mmc_command *cmd = mrq->cmd; + int err = cmd->error; + + pr_debug("%s: req done (CMD%u): %d/%d/%d: %08x %08x %08x %08x\n", + mmc_hostname(host), cmd->opcode, err, + mrq->data ? mrq->data->error : 0, + mrq->stop ? mrq->stop->error : 0, + cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); + + if (err && cmd->retries) { + cmd->retries--; + cmd->error = 0; + host->ops->request(host, mrq); + } else if (mrq->done) { + mrq->done(mrq); + } +} + +EXPORT_SYMBOL(mmc_request_done); + +/** + * mmc_start_request - start a command on a host + * @host: MMC host to start command on + * @mrq: MMC request to start + * + * Queue a command on the specified host. We expect the + * caller to be holding the host lock with interrupts disabled. + */ +void +mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) +{ +#ifdef CONFIG_MMC_DEBUG + unsigned int i, sz; +#endif + + pr_debug("%s: starting CMD%u arg %08x flags %08x\n", + mmc_hostname(host), mrq->cmd->opcode, + mrq->cmd->arg, mrq->cmd->flags); + + WARN_ON(!host->claimed); + + mrq->cmd->error = 0; + mrq->cmd->mrq = mrq; + if (mrq->data) { + BUG_ON(mrq->data->blksz > host->max_blk_size); + BUG_ON(mrq->data->blocks > host->max_blk_count); + BUG_ON(mrq->data->blocks * mrq->data->blksz > + host->max_req_size); + +#ifdef CONFIG_MMC_DEBUG + sz = 0; + for (i = 0;i < mrq->data->sg_len;i++) + sz += mrq->data->sg[i].length; + BUG_ON(sz != mrq->data->blocks * mrq->data->blksz); +#endif + + mrq->cmd->data = mrq->data; + mrq->data->error = 0; + mrq->data->mrq = mrq; + if (mrq->stop) { + mrq->data->stop = mrq->stop; + mrq->stop->error = 0; + mrq->stop->mrq = mrq; + } + } + host->ops->request(host, mrq); +} + +EXPORT_SYMBOL(mmc_start_request); + +static void mmc_wait_done(struct mmc_request *mrq) +{ + complete(mrq->done_data); +} + +int mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) +{ + DECLARE_COMPLETION_ONSTACK(complete); + + mrq->done_data = &complete; + mrq->done = mmc_wait_done; + + mmc_start_request(host, mrq); + + wait_for_completion(&complete); + + return 0; +} + +EXPORT_SYMBOL(mmc_wait_for_req); + +/** + * mmc_wait_for_cmd - start a command and wait for completion + * @host: MMC host to start command + * @cmd: MMC command to start + * @retries: maximum number of retries + * + * Start a new MMC command for a host, and wait for the command + * to complete. Return any error that occurred while the command + * was executing. Do not attempt to parse the response. + */ +int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries) +{ + struct mmc_request mrq; + + BUG_ON(!host->claimed); + + memset(&mrq, 0, sizeof(struct mmc_request)); + + memset(cmd->resp, 0, sizeof(cmd->resp)); + cmd->retries = retries; + + mrq.cmd = cmd; + cmd->data = NULL; + + mmc_wait_for_req(host, &mrq); + + return cmd->error; +} + +EXPORT_SYMBOL(mmc_wait_for_cmd); + +/** + * mmc_wait_for_app_cmd - start an application command and wait for + completion + * @host: MMC host to start command + * @rca: RCA to send MMC_APP_CMD to + * @cmd: MMC command to start + * @retries: maximum number of retries + * + * Sends a MMC_APP_CMD, checks the card response, sends the command + * in the parameter and waits for it to complete. Return any error + * that occurred while the command was executing. Do not attempt to + * parse the response. + */ +int mmc_wait_for_app_cmd(struct mmc_host *host, unsigned int rca, + struct mmc_command *cmd, int retries) +{ + struct mmc_request mrq; + struct mmc_command appcmd; + + int i, err; + + BUG_ON(!host->claimed); + BUG_ON(retries < 0); + + err = MMC_ERR_INVALID; + + /* + * We have to resend MMC_APP_CMD for each attempt so + * we cannot use the retries field in mmc_command. + */ + for (i = 0;i <= retries;i++) { + memset(&mrq, 0, sizeof(struct mmc_request)); + + appcmd.opcode = MMC_APP_CMD; + appcmd.arg = rca << 16; + appcmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + appcmd.retries = 0; + memset(appcmd.resp, 0, sizeof(appcmd.resp)); + appcmd.data = NULL; + + mrq.cmd = &appcmd; + appcmd.data = NULL; + + mmc_wait_for_req(host, &mrq); + + if (appcmd.error) { + err = appcmd.error; + continue; + } + + /* Check that card supported application commands */ + if (!(appcmd.resp[0] & R1_APP_CMD)) + return MMC_ERR_FAILED; + + memset(&mrq, 0, sizeof(struct mmc_request)); + + memset(cmd->resp, 0, sizeof(cmd->resp)); + cmd->retries = 0; + + mrq.cmd = cmd; + cmd->data = NULL; + + mmc_wait_for_req(host, &mrq); + + err = cmd->error; + if (cmd->error == MMC_ERR_NONE) + break; + } + + return err; +} + +EXPORT_SYMBOL(mmc_wait_for_app_cmd); + +/** + * mmc_set_data_timeout - set the timeout for a data command + * @data: data phase for command + * @card: the MMC card associated with the data transfer + * @write: flag to differentiate reads from writes + */ +void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card, + int write) +{ + unsigned int mult; + + /* + * SD cards use a 100 multiplier rather than 10 + */ + mult = mmc_card_sd(card) ? 100 : 10; + + /* + * Scale up the multiplier (and therefore the timeout) by + * the r2w factor for writes. + */ + if (write) + mult <<= card->csd.r2w_factor; + + data->timeout_ns = card->csd.tacc_ns * mult; + data->timeout_clks = card->csd.tacc_clks * mult; + + /* + * SD cards also have an upper limit on the timeout. + */ + if (mmc_card_sd(card)) { + unsigned int timeout_us, limit_us; + + timeout_us = data->timeout_ns / 1000; + timeout_us += data->timeout_clks * 1000 / + (card->host->ios.clock / 1000); + + if (write) + limit_us = 250000; + else + limit_us = 100000; + + /* + * SDHC cards always use these fixed values. + */ + if (timeout_us > limit_us || mmc_card_blockaddr(card)) { + data->timeout_ns = limit_us * 1000; + data->timeout_clks = 0; + } + } +} +EXPORT_SYMBOL(mmc_set_data_timeout); + +/** + * __mmc_claim_host - exclusively claim a host + * @host: mmc host to claim + * @card: mmc card to claim host for + * + * Claim a host for a set of operations. If a valid card + * is passed and this wasn't the last card selected, select + * the card before returning. + * + * Note: you should use mmc_card_claim_host or mmc_claim_host. + */ +void mmc_claim_host(struct mmc_host *host) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + + add_wait_queue(&host->wq, &wait); + spin_lock_irqsave(&host->lock, flags); + while (1) { + set_current_state(TASK_UNINTERRUPTIBLE); + if (!host->claimed) + break; + spin_unlock_irqrestore(&host->lock, flags); + schedule(); + spin_lock_irqsave(&host->lock, flags); + } + set_current_state(TASK_RUNNING); + host->claimed = 1; + spin_unlock_irqrestore(&host->lock, flags); + remove_wait_queue(&host->wq, &wait); +} + +EXPORT_SYMBOL(mmc_claim_host); + +/** + * mmc_release_host - release a host + * @host: mmc host to release + * + * Release a MMC host, allowing others to claim the host + * for their operations. + */ +void mmc_release_host(struct mmc_host *host) +{ + unsigned long flags; + + BUG_ON(!host->claimed); + + spin_lock_irqsave(&host->lock, flags); + host->claimed = 0; + spin_unlock_irqrestore(&host->lock, flags); + + wake_up(&host->wq); +} + +EXPORT_SYMBOL(mmc_release_host); + +static inline void mmc_set_ios(struct mmc_host *host) +{ + struct mmc_ios *ios = &host->ios; + + pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u " + "width %u timing %u\n", + mmc_hostname(host), ios->clock, ios->bus_mode, + ios->power_mode, ios->chip_select, ios->vdd, + ios->bus_width, ios->timing); + + host->ops->set_ios(host, ios); +} + +static int mmc_select_card(struct mmc_card *card) +{ + int err; + struct mmc_command cmd; + + BUG_ON(!card->host->claimed); + + cmd.opcode = MMC_SELECT_CARD; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + return err; + + /* + * We can only change the bus width of SD cards when + * they are selected so we have to put the handling + * here. + * + * The card is in 1 bit mode by default so + * we only need to change if it supports the + * wider version. + */ + if (mmc_card_sd(card) && + (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4) && + (card->host->caps & MMC_CAP_4_BIT_DATA)) { + + struct mmc_command cmd; + cmd.opcode = SD_APP_SET_BUS_WIDTH; + cmd.arg = SD_BUS_WIDTH_4; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_app_cmd(card->host, card->rca, + &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + return err; + + card->host->ios.bus_width = MMC_BUS_WIDTH_4; + mmc_set_ios(card->host); + } + + return MMC_ERR_NONE; +} + + +static inline void mmc_delay(unsigned int ms) +{ + if (ms < 1000 / HZ) { + cond_resched(); + mdelay(ms); + } else { + msleep(ms); + } +} + +/* + * Mask off any voltages we don't support and select + * the lowest voltage + */ +static u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) +{ + int bit; + + ocr &= host->ocr_avail; + + bit = ffs(ocr); + if (bit) { + bit -= 1; + + ocr &= 3 << bit; + + host->ios.vdd = bit; + mmc_set_ios(host); + } else { + ocr = 0; + } + + return ocr; +} + +#define UNSTUFF_BITS(resp,start,size) \ + ({ \ + const int __size = size; \ + const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; \ + const int __off = 3 - ((start) / 32); \ + const int __shft = (start) & 31; \ + u32 __res; \ + \ + __res = resp[__off] >> __shft; \ + if (__size + __shft > 32) \ + __res |= resp[__off-1] << ((32 - __shft) % 32); \ + __res & __mask; \ + }) + +/* + * Given the decoded CSD structure, decode the raw CID to our CID structure. + */ +static void mmc_decode_cid(struct mmc_card *card) +{ + u32 *resp = card->raw_cid; + + memset(&card->cid, 0, sizeof(struct mmc_cid)); + + if (mmc_card_sd(card)) { + /* + * SD doesn't currently have a version field so we will + * have to assume we can parse this. + */ + card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); + card->cid.oemid = UNSTUFF_BITS(resp, 104, 16); + card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); + card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); + card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); + card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); + card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); + card->cid.hwrev = UNSTUFF_BITS(resp, 60, 4); + card->cid.fwrev = UNSTUFF_BITS(resp, 56, 4); + card->cid.serial = UNSTUFF_BITS(resp, 24, 32); + card->cid.year = UNSTUFF_BITS(resp, 12, 8); + card->cid.month = UNSTUFF_BITS(resp, 8, 4); + + card->cid.year += 2000; /* SD cards year offset */ + } else { + /* + * The selection of the format here is based upon published + * specs from sandisk and from what people have reported. + */ + switch (card->csd.mmca_vsn) { + case 0: /* MMC v1.0 - v1.2 */ + case 1: /* MMC v1.4 */ + card->cid.manfid = UNSTUFF_BITS(resp, 104, 24); + card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); + card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); + card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); + card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); + card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); + card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); + card->cid.prod_name[6] = UNSTUFF_BITS(resp, 48, 8); + card->cid.hwrev = UNSTUFF_BITS(resp, 44, 4); + card->cid.fwrev = UNSTUFF_BITS(resp, 40, 4); + card->cid.serial = UNSTUFF_BITS(resp, 16, 24); + card->cid.month = UNSTUFF_BITS(resp, 12, 4); + card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; + break; + + case 2: /* MMC v2.0 - v2.2 */ + case 3: /* MMC v3.1 - v3.3 */ + case 4: /* MMC v4 */ + card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); + card->cid.oemid = UNSTUFF_BITS(resp, 104, 16); + card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); + card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); + card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); + card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); + card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); + card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); + card->cid.serial = UNSTUFF_BITS(resp, 16, 32); + card->cid.month = UNSTUFF_BITS(resp, 12, 4); + card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; + break; + + default: + printk("%s: card has unknown MMCA version %d\n", + mmc_hostname(card->host), card->csd.mmca_vsn); + mmc_card_set_bad(card); + break; + } + } +} + +/* + * Given a 128-bit response, decode to our card CSD structure. + */ +static void mmc_decode_csd(struct mmc_card *card) +{ + struct mmc_csd *csd = &card->csd; + unsigned int e, m, csd_struct; + u32 *resp = card->raw_csd; + + if (mmc_card_sd(card)) { + csd_struct = UNSTUFF_BITS(resp, 126, 2); + + switch (csd_struct) { + case 0: + m = UNSTUFF_BITS(resp, 115, 4); + e = UNSTUFF_BITS(resp, 112, 3); + csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10; + csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100; + + m = UNSTUFF_BITS(resp, 99, 4); + e = UNSTUFF_BITS(resp, 96, 3); + csd->max_dtr = tran_exp[e] * tran_mant[m]; + csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); + + e = UNSTUFF_BITS(resp, 47, 3); + m = UNSTUFF_BITS(resp, 62, 12); + csd->capacity = (1 + m) << (e + 2); + + csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); + csd->read_partial = UNSTUFF_BITS(resp, 79, 1); + csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); + csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); + csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); + csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); + csd->write_partial = UNSTUFF_BITS(resp, 21, 1); + break; + case 1: + /* + * This is a block-addressed SDHC card. Most + * interesting fields are unused and have fixed + * values. To avoid getting tripped by buggy cards, + * we assume those fixed values ourselves. + */ + mmc_card_set_blockaddr(card); + + csd->tacc_ns = 0; /* Unused */ + csd->tacc_clks = 0; /* Unused */ + + m = UNSTUFF_BITS(resp, 99, 4); + e = UNSTUFF_BITS(resp, 96, 3); + csd->max_dtr = tran_exp[e] * tran_mant[m]; + csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); + + m = UNSTUFF_BITS(resp, 48, 22); + csd->capacity = (1 + m) << 10; + + csd->read_blkbits = 9; + csd->read_partial = 0; + csd->write_misalign = 0; + csd->read_misalign = 0; + csd->r2w_factor = 4; /* Unused */ + csd->write_blkbits = 9; + csd->write_partial = 0; + break; + default: + printk("%s: unrecognised CSD structure version %d\n", + mmc_hostname(card->host), csd_struct); + mmc_card_set_bad(card); + return; + } + } else { + /* + * We only understand CSD structure v1.1 and v1.2. + * v1.2 has extra information in bits 15, 11 and 10. + */ + csd_struct = UNSTUFF_BITS(resp, 126, 2); + if (csd_struct != 1 && csd_struct != 2) { + printk("%s: unrecognised CSD structure version %d\n", + mmc_hostname(card->host), csd_struct); + mmc_card_set_bad(card); + return; + } + + csd->mmca_vsn = UNSTUFF_BITS(resp, 122, 4); + m = UNSTUFF_BITS(resp, 115, 4); + e = UNSTUFF_BITS(resp, 112, 3); + csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10; + csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100; + + m = UNSTUFF_BITS(resp, 99, 4); + e = UNSTUFF_BITS(resp, 96, 3); + csd->max_dtr = tran_exp[e] * tran_mant[m]; + csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); + + e = UNSTUFF_BITS(resp, 47, 3); + m = UNSTUFF_BITS(resp, 62, 12); + csd->capacity = (1 + m) << (e + 2); + + csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); + csd->read_partial = UNSTUFF_BITS(resp, 79, 1); + csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); + csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); + csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); + csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); + csd->write_partial = UNSTUFF_BITS(resp, 21, 1); + } +} + +/* + * Given a 64-bit response, decode to our card SCR structure. + */ +static void mmc_decode_scr(struct mmc_card *card) +{ + struct sd_scr *scr = &card->scr; + unsigned int scr_struct; + u32 resp[4]; + + BUG_ON(!mmc_card_sd(card)); + + resp[3] = card->raw_scr[1]; + resp[2] = card->raw_scr[0]; + + scr_struct = UNSTUFF_BITS(resp, 60, 4); + if (scr_struct != 0) { + printk("%s: unrecognised SCR structure version %d\n", + mmc_hostname(card->host), scr_struct); + mmc_card_set_bad(card); + return; + } + + scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4); + scr->bus_widths = UNSTUFF_BITS(resp, 48, 4); +} + +/* + * Allocate a new MMC card + */ +static struct mmc_card * +mmc_alloc_card(struct mmc_host *host, u32 *raw_cid) +{ + struct mmc_card *card; + + card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL); + if (!card) + return ERR_PTR(-ENOMEM); + + mmc_init_card(card, host); + memcpy(card->raw_cid, raw_cid, sizeof(card->raw_cid)); + + return card; +} + +/* + * Tell attached cards to go to IDLE state + */ +static void mmc_idle_cards(struct mmc_host *host) +{ + struct mmc_command cmd; + + host->ios.chip_select = MMC_CS_HIGH; + mmc_set_ios(host); + + mmc_delay(1); + + cmd.opcode = MMC_GO_IDLE_STATE; + cmd.arg = 0; + cmd.flags = MMC_RSP_NONE | MMC_CMD_BC; + + mmc_wait_for_cmd(host, &cmd, 0); + + mmc_delay(1); + + host->ios.chip_select = MMC_CS_DONTCARE; + mmc_set_ios(host); + + mmc_delay(1); +} + +/* + * Apply power to the MMC stack. This is a two-stage process. + * First, we enable power to the card without the clock running. + * We then wait a bit for the power to stabilise. Finally, + * enable the bus drivers and clock to the card. + * + * We must _NOT_ enable the clock prior to power stablising. + * + * If a host does all the power sequencing itself, ignore the + * initial MMC_POWER_UP stage. + */ +static void mmc_power_up(struct mmc_host *host) +{ + int bit = fls(host->ocr_avail) - 1; + + host->ios.vdd = bit; + host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; + host->ios.chip_select = MMC_CS_DONTCARE; + host->ios.power_mode = MMC_POWER_UP; + host->ios.bus_width = MMC_BUS_WIDTH_1; + host->ios.timing = MMC_TIMING_LEGACY; + mmc_set_ios(host); + + mmc_delay(1); + + host->ios.clock = host->f_min; + host->ios.power_mode = MMC_POWER_ON; + mmc_set_ios(host); + + mmc_delay(2); +} + +static void mmc_power_off(struct mmc_host *host) +{ + host->ios.clock = 0; + host->ios.vdd = 0; + host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; + host->ios.chip_select = MMC_CS_DONTCARE; + host->ios.power_mode = MMC_POWER_OFF; + host->ios.bus_width = MMC_BUS_WIDTH_1; + host->ios.timing = MMC_TIMING_LEGACY; + mmc_set_ios(host); +} + +static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) +{ + struct mmc_command cmd; + int i, err = 0; + + cmd.opcode = MMC_SEND_OP_COND; + cmd.arg = ocr; + cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; + + for (i = 100; i; i--) { + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err != MMC_ERR_NONE) + break; + + if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) + break; + + err = MMC_ERR_TIMEOUT; + + mmc_delay(10); + } + + if (rocr) + *rocr = cmd.resp[0]; + + return err; +} + +static int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) +{ + struct mmc_command cmd; + int i, err = 0; + + cmd.opcode = SD_APP_OP_COND; + cmd.arg = ocr; + cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; + + for (i = 100; i; i--) { + err = mmc_wait_for_app_cmd(host, 0, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + break; + + if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) + break; + + err = MMC_ERR_TIMEOUT; + + mmc_delay(10); + } + + if (rocr) + *rocr = cmd.resp[0]; + + return err; +} + +static int mmc_send_if_cond(struct mmc_host *host, u32 ocr, int *rsd2) +{ + struct mmc_command cmd; + int err, sd2; + static const u8 test_pattern = 0xAA; + + /* + * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND + * before SD_APP_OP_COND. This command will harmlessly fail for + * SD 1.0 cards. + */ + cmd.opcode = SD_SEND_IF_COND; + cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; + cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR; + + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err == MMC_ERR_NONE) { + if ((cmd.resp[0] & 0xFF) == test_pattern) { + sd2 = 1; + } else { + sd2 = 0; + err = MMC_ERR_FAILED; + } + } else { + /* + * Treat errors as SD 1.0 card. + */ + sd2 = 0; + err = MMC_ERR_NONE; + } + if (rsd2) + *rsd2 = sd2; + return err; +} + +/* + * Discover the card by requesting its CID. + * + * Create a mmc_card entry for the discovered card, assigning + * it an RCA, and save the raw CID for decoding later. + */ +static void mmc_discover_card(struct mmc_host *host) +{ + unsigned int err; + + struct mmc_command cmd; + + BUG_ON(host->card); + + cmd.opcode = MMC_ALL_SEND_CID; + cmd.arg = 0; + cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err == MMC_ERR_TIMEOUT) { + err = MMC_ERR_NONE; + return; + } + if (err != MMC_ERR_NONE) { + printk(KERN_ERR "%s: error requesting CID: %d\n", + mmc_hostname(host), err); + return; + } + + host->card = mmc_alloc_card(host, cmd.resp); + if (IS_ERR(host->card)) { + err = PTR_ERR(host->card); + host->card = NULL; + return; + } + + if (host->mode == MMC_MODE_SD) { + host->card->type = MMC_TYPE_SD; + + cmd.opcode = SD_SEND_RELATIVE_ADDR; + cmd.arg = 0; + cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + mmc_card_set_dead(host->card); + else { + host->card->rca = cmd.resp[0] >> 16; + + if (!host->ops->get_ro) { + printk(KERN_WARNING "%s: host does not " + "support reading read-only " + "switch. assuming write-enable.\n", + mmc_hostname(host)); + } else { + if (host->ops->get_ro(host)) + mmc_card_set_readonly(host->card); + } + } + } else { + host->card->type = MMC_TYPE_MMC; + host->card->rca = 1; + + cmd.opcode = MMC_SET_RELATIVE_ADDR; + cmd.arg = host->card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + mmc_card_set_dead(host->card); + } +} + +static void mmc_read_csd(struct mmc_host *host) +{ + struct mmc_command cmd; + int err; + + if (!host->card) + return; + if (mmc_card_dead(host->card)) + return; + + cmd.opcode = MMC_SEND_CSD; + cmd.arg = host->card->rca << 16; + cmd.flags = MMC_RSP_R2 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) { + mmc_card_set_dead(host->card); + return; + } + + memcpy(host->card->raw_csd, cmd.resp, sizeof(host->card->raw_csd)); + + mmc_decode_csd(host->card); + mmc_decode_cid(host->card); +} + +static void mmc_process_ext_csd(struct mmc_host *host) +{ + int err; + + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + + u8 *ext_csd; + struct scatterlist sg; + + if (!host->card) + return; + if (mmc_card_dead(host->card)) + return; + if (mmc_card_sd(host->card)) + return; + if (host->card->csd.mmca_vsn < CSD_SPEC_VER_4) + return; + + /* + * As the ext_csd is so large and mostly unused, we don't store the + * raw block in mmc_card. + */ + ext_csd = kmalloc(512, GFP_KERNEL); + if (!ext_csd) { + printk("%s: could not allocate a buffer to receive the ext_csd." + "mmc v4 cards will be treated as v3.\n", + mmc_hostname(host)); + return; + } + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_SEND_EXT_CSD; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + memset(&data, 0, sizeof(struct mmc_data)); + + mmc_set_data_timeout(&data, host->card, 0); + + data.blksz = 512; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + memset(&mrq, 0, sizeof(struct mmc_request)); + + mrq.cmd = &cmd; + mrq.data = &data; + + sg_init_one(&sg, ext_csd, 512); + + mmc_wait_for_req(host, &mrq); + + if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { + if (host->card->csd.capacity == (4096 * 512)) { + printk(KERN_ERR "%s: unable to read EXT_CSD " + "on a possible high capacity card. " + "Card will be ignored.\n", + mmc_hostname(host)); + mmc_card_set_dead(host->card); + } else { + printk(KERN_WARNING "%s: unable to read " + "EXT_CSD, performance might " + "suffer.\n", + mmc_hostname(host)); + } + goto out; + } + + host->card->ext_csd.sectors = + ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | + ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | + ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | + ext_csd[EXT_CSD_SEC_CNT + 3] << 24; + if (host->card->ext_csd.sectors) + mmc_card_set_blockaddr(host->card); + + switch (ext_csd[EXT_CSD_CARD_TYPE]) { + case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26: + host->card->ext_csd.hs_max_dtr = 52000000; + break; + case EXT_CSD_CARD_TYPE_26: + host->card->ext_csd.hs_max_dtr = 26000000; + break; + default: + /* MMC v4 spec says this cannot happen */ + printk("%s: card is mmc v4 but doesn't support " + "any high-speed modes.\n", + mmc_hostname(host)); + goto out; + } + + if (host->caps & MMC_CAP_MMC_HIGHSPEED) { + /* Activate highspeed support. */ + cmd.opcode = MMC_SWITCH; + cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_HS_TIMING << 16) | + (1 << 8) | + EXT_CSD_CMD_SET_NORMAL; + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) { + printk("%s: failed to switch card to mmc v4 " + "high-speed mode.\n", + mmc_hostname(host)); + goto out; + } + + mmc_card_set_highspeed(host->card); + + host->ios.timing = MMC_TIMING_MMC_HS; + mmc_set_ios(host); + } + + /* Check for host support for wide-bus modes. */ + if (host->caps & MMC_CAP_4_BIT_DATA) { + /* Activate 4-bit support. */ + cmd.opcode = MMC_SWITCH; + cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_BUS_WIDTH << 16) | + (EXT_CSD_BUS_WIDTH_4 << 8) | + EXT_CSD_CMD_SET_NORMAL; + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) { + printk("%s: failed to switch card to " + "mmc v4 4-bit bus mode.\n", + mmc_hostname(host)); + goto out; + } + + host->ios.bus_width = MMC_BUS_WIDTH_4; + mmc_set_ios(host); + } + +out: + kfree(ext_csd); +} + +static void mmc_read_scr(struct mmc_host *host) +{ + int err; + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + struct scatterlist sg; + + if (!host->card) + return; + if (mmc_card_dead(host->card)) + return; + if (!mmc_card_sd(host->card)) + return; + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_APP_CMD; + cmd.arg = host->card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(host, &cmd, 0); + if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) { + mmc_card_set_dead(host->card); + return; + } + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_APP_SEND_SCR; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + memset(&data, 0, sizeof(struct mmc_data)); + + mmc_set_data_timeout(&data, host->card, 0); + + data.blksz = 1 << 3; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + memset(&mrq, 0, sizeof(struct mmc_request)); + + mrq.cmd = &cmd; + mrq.data = &data; + + sg_init_one(&sg, (u8*)host->card->raw_scr, 8); + + mmc_wait_for_req(host, &mrq); + + if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { + mmc_card_set_dead(host->card); + return; + } + + host->card->raw_scr[0] = ntohl(host->card->raw_scr[0]); + host->card->raw_scr[1] = ntohl(host->card->raw_scr[1]); + + mmc_decode_scr(host->card); +} + +static void mmc_read_switch_caps(struct mmc_host *host) +{ + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + unsigned char *status; + struct scatterlist sg; + + if (!(host->caps & MMC_CAP_SD_HIGHSPEED)) + return; + + if (!host->card) + return; + if (mmc_card_dead(host->card)) + return; + if (!mmc_card_sd(host->card)) + return; + if (host->card->scr.sda_vsn < SCR_SPEC_VER_1) + return; + + status = kmalloc(64, GFP_KERNEL); + if (!status) { + printk(KERN_WARNING "%s: Unable to allocate buffer for " + "reading switch capabilities.\n", + mmc_hostname(host)); + return; + } + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_SWITCH; + cmd.arg = 0x00FFFFF1; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + memset(&data, 0, sizeof(struct mmc_data)); + + mmc_set_data_timeout(&data, host->card, 0); + + data.blksz = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + memset(&mrq, 0, sizeof(struct mmc_request)); + + mrq.cmd = &cmd; + mrq.data = &data; + + sg_init_one(&sg, status, 64); + + mmc_wait_for_req(host, &mrq); + + if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { + printk("%s: unable to read switch capabilities, " + "performance might suffer.\n", + mmc_hostname(host)); + goto out; + } + + if (status[13] & 0x02) + host->card->sw_caps.hs_max_dtr = 50000000; + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_SWITCH; + cmd.arg = 0x80FFFFF1; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + memset(&data, 0, sizeof(struct mmc_data)); + + mmc_set_data_timeout(&data, host->card, 0); + + data.blksz = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + memset(&mrq, 0, sizeof(struct mmc_request)); + + mrq.cmd = &cmd; + mrq.data = &data; + + sg_init_one(&sg, status, 64); + + mmc_wait_for_req(host, &mrq); + + if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE || + (status[16] & 0xF) != 1) { + printk(KERN_WARNING "%s: Problem switching card " + "into high-speed mode!\n", + mmc_hostname(host)); + goto out; + } + + mmc_card_set_highspeed(host->card); + + host->ios.timing = MMC_TIMING_SD_HS; + mmc_set_ios(host); + +out: + kfree(status); +} + +static unsigned int mmc_calculate_clock(struct mmc_host *host) +{ + unsigned int max_dtr = host->f_max; + + if (host->card && !mmc_card_dead(host->card)) { + if (mmc_card_highspeed(host->card) && mmc_card_sd(host->card)) { + if (max_dtr > host->card->sw_caps.hs_max_dtr) + max_dtr = host->card->sw_caps.hs_max_dtr; + } else if (mmc_card_highspeed(host->card) && !mmc_card_sd(host->card)) { + if (max_dtr > host->card->ext_csd.hs_max_dtr) + max_dtr = host->card->ext_csd.hs_max_dtr; + } else if (max_dtr > host->card->csd.max_dtr) { + max_dtr = host->card->csd.max_dtr; + } + } + + pr_debug("%s: selected %d.%03dMHz transfer rate\n", + mmc_hostname(host), + max_dtr / 1000000, (max_dtr / 1000) % 1000); + + return max_dtr; +} + +/* + * Check whether cards we already know about are still present. + * We do this by requesting status, and checking whether a card + * responds. + * + * A request for status does not cause a state change in data + * transfer mode. + */ +static void mmc_check_card(struct mmc_card *card) +{ + struct mmc_command cmd; + int err; + + BUG_ON(!card); + + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, CMD_RETRIES); + if (err == MMC_ERR_NONE) + return; + + mmc_card_set_dead(card); +} + +static void mmc_setup(struct mmc_host *host) +{ + int err; + u32 ocr; + + host->mode = MMC_MODE_SD; + + mmc_power_up(host); + mmc_idle_cards(host); + + err = mmc_send_if_cond(host, host->ocr_avail, NULL); + if (err != MMC_ERR_NONE) { + return; + } + err = mmc_send_app_op_cond(host, 0, &ocr); + + /* + * If we fail to detect any SD cards then try + * searching for MMC cards. + */ + if (err != MMC_ERR_NONE) { + host->mode = MMC_MODE_MMC; + + err = mmc_send_op_cond(host, 0, &ocr); + if (err != MMC_ERR_NONE) + return; + } + + host->ocr = mmc_select_voltage(host, ocr); + + if (host->ocr == 0) + return; + + /* + * Since we're changing the OCR value, we seem to + * need to tell some cards to go back to the idle + * state. We wait 1ms to give cards time to + * respond. + */ + mmc_idle_cards(host); + + /* + * Send the selected OCR multiple times... until the cards + * all get the idea that they should be ready for CMD2. + * (My SanDisk card seems to need this.) + */ + if (host->mode == MMC_MODE_SD) { + int err, sd2; + err = mmc_send_if_cond(host, host->ocr, &sd2); + if (err == MMC_ERR_NONE) { + /* + * If SD_SEND_IF_COND indicates an SD 2.0 + * compliant card and we should set bit 30 + * of the ocr to indicate that we can handle + * block-addressed SDHC cards. + */ + mmc_send_app_op_cond(host, host->ocr | (sd2 << 30), NULL); + } + } else { + /* The extra bit indicates that we support high capacity */ + mmc_send_op_cond(host, host->ocr | (1 << 30), NULL); + } + + mmc_discover_card(host); + + /* + * Ok, now switch to push-pull mode. + */ + host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; + mmc_set_ios(host); + + mmc_read_csd(host); + + if (host->card && !mmc_card_dead(host->card)) { + err = mmc_select_card(host->card); + if (err != MMC_ERR_NONE) + mmc_card_set_dead(host->card); + } + + if (host->mode == MMC_MODE_SD) { + mmc_read_scr(host); + mmc_read_switch_caps(host); + } else + mmc_process_ext_csd(host); +} + + +/** + * mmc_detect_change - process change of state on a MMC socket + * @host: host which changed state. + * @delay: optional delay to wait before detection (jiffies) + * + * All we know is that card(s) have been inserted or removed + * from the socket(s). We don't know which socket or cards. + */ +void mmc_detect_change(struct mmc_host *host, unsigned long delay) +{ +#ifdef CONFIG_MMC_DEBUG + mmc_claim_host(host); + BUG_ON(host->removed); + mmc_release_host(host); +#endif + + mmc_schedule_delayed_work(&host->detect, delay); +} + +EXPORT_SYMBOL(mmc_detect_change); + + +static void mmc_rescan(struct work_struct *work) +{ + struct mmc_host *host = + container_of(work, struct mmc_host, detect.work); + + mmc_claim_host(host); + + /* + * Check for removed card and newly inserted ones. We check for + * removed cards first so we can intelligently re-select the VDD. + */ + if (host->card) { + mmc_check_card(host->card); + + mmc_release_host(host); + + if (mmc_card_dead(host->card)) { + mmc_remove_card(host->card); + host->card = NULL; + } + + goto out; + } + + mmc_setup(host); + + if (host->card && !mmc_card_dead(host->card)) { + /* + * (Re-)calculate the fastest clock rate which the + * attached cards and the host support. + */ + host->ios.clock = mmc_calculate_clock(host); + mmc_set_ios(host); + } + + mmc_release_host(host); + + /* + * If this is a new and good card, register it. + */ + if (host->card && !mmc_card_dead(host->card)) { + if (mmc_register_card(host->card)) + mmc_card_set_dead(host->card); + } + + /* + * If this card is dead, destroy it. + */ + if (host->card && mmc_card_dead(host->card)) { + mmc_remove_card(host->card); + host->card = NULL; + } + +out: + /* + * If we discover that there are no cards on the + * bus, turn off the clock and power down. + */ + if (!host->card) + mmc_power_off(host); +} + + +/** + * mmc_alloc_host - initialise the per-host structure. + * @extra: sizeof private data structure + * @dev: pointer to host device model structure + * + * Initialise the per-host structure. + */ +struct mmc_host *mmc_alloc_host(int extra, struct device *dev) +{ + struct mmc_host *host; + + host = mmc_alloc_host_sysfs(extra, dev); + if (host) { + spin_lock_init(&host->lock); + init_waitqueue_head(&host->wq); + INIT_DELAYED_WORK(&host->detect, mmc_rescan); + + /* + * By default, hosts do not support SGIO or large requests. + * They have to set these according to their abilities. + */ + host->max_hw_segs = 1; + host->max_phys_segs = 1; + host->max_seg_size = PAGE_CACHE_SIZE; + + host->max_req_size = PAGE_CACHE_SIZE; + host->max_blk_size = 512; + host->max_blk_count = PAGE_CACHE_SIZE / 512; + } + + return host; +} + +EXPORT_SYMBOL(mmc_alloc_host); + +/** + * mmc_add_host - initialise host hardware + * @host: mmc host + */ +int mmc_add_host(struct mmc_host *host) +{ + int ret; + + ret = mmc_add_host_sysfs(host); + if (ret == 0) { + mmc_power_off(host); + mmc_detect_change(host, 0); + } + + return ret; +} + +EXPORT_SYMBOL(mmc_add_host); + +/** + * mmc_remove_host - remove host hardware + * @host: mmc host + * + * Unregister and remove all cards associated with this host, + * and power down the MMC bus. + */ +void mmc_remove_host(struct mmc_host *host) +{ +#ifdef CONFIG_MMC_DEBUG + mmc_claim_host(host); + host->removed = 1; + mmc_release_host(host); +#endif + + mmc_flush_scheduled_work(); + + if (host->card) { + mmc_remove_card(host->card); + host->card = NULL; + } + + mmc_power_off(host); + mmc_remove_host_sysfs(host); +} + +EXPORT_SYMBOL(mmc_remove_host); + +/** + * mmc_free_host - free the host structure + * @host: mmc host + * + * Free the host once all references to it have been dropped. + */ +void mmc_free_host(struct mmc_host *host) +{ + mmc_free_host_sysfs(host); +} + +EXPORT_SYMBOL(mmc_free_host); + +#ifdef CONFIG_PM + +/** + * mmc_suspend_host - suspend a host + * @host: mmc host + * @state: suspend mode (PM_SUSPEND_xxx) + */ +int mmc_suspend_host(struct mmc_host *host, pm_message_t state) +{ + mmc_flush_scheduled_work(); + + if (host->card) { + mmc_remove_card(host->card); + host->card = NULL; + } + + mmc_power_off(host); + + return 0; +} + +EXPORT_SYMBOL(mmc_suspend_host); + +/** + * mmc_resume_host - resume a previously suspended host + * @host: mmc host + */ +int mmc_resume_host(struct mmc_host *host) +{ + mmc_rescan(&host->detect.work); + + return 0; +} + +EXPORT_SYMBOL(mmc_resume_host); + +#endif + +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h new file mode 100644 index 00000000000..076cb2f49a0 --- /dev/null +++ b/drivers/mmc/core/core.h @@ -0,0 +1,25 @@ +/* + * linux/drivers/mmc/core/core.h + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _MMC_CORE_H +#define _MMC_CORE_H +/* core-internal functions */ +void mmc_init_card(struct mmc_card *card, struct mmc_host *host); +int mmc_register_card(struct mmc_card *card); +void mmc_remove_card(struct mmc_card *card); + +struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev); +int mmc_add_host_sysfs(struct mmc_host *host); +void mmc_remove_host_sysfs(struct mmc_host *host); +void mmc_free_host_sysfs(struct mmc_host *host); + +int mmc_schedule_work(struct work_struct *work); +int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay); +void mmc_flush_scheduled_work(void); +#endif diff --git a/drivers/mmc/core/sysfs.c b/drivers/mmc/core/sysfs.c new file mode 100644 index 00000000000..bf9a5f8beb8 --- /dev/null +++ b/drivers/mmc/core/sysfs.c @@ -0,0 +1,361 @@ +/* + * linux/drivers/mmc/core/sysfs.c + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * MMC sysfs/driver model support. + */ +#include +#include +#include +#include +#include + +#include +#include + +#include "core.h" + +#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) +#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) +#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) + +#define MMC_ATTR(name, fmt, args...) \ +static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct mmc_card *card = dev_to_mmc_card(dev); \ + return sprintf(buf, fmt, args); \ +} + +MMC_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], + card->raw_cid[2], card->raw_cid[3]); +MMC_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], + card->raw_csd[2], card->raw_csd[3]); +MMC_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]); +MMC_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); +MMC_ATTR(fwrev, "0x%x\n", card->cid.fwrev); +MMC_ATTR(hwrev, "0x%x\n", card->cid.hwrev); +MMC_ATTR(manfid, "0x%06x\n", card->cid.manfid); +MMC_ATTR(name, "%s\n", card->cid.prod_name); +MMC_ATTR(oemid, "0x%04x\n", card->cid.oemid); +MMC_ATTR(serial, "0x%08x\n", card->cid.serial); + +#define MMC_ATTR_RO(name) __ATTR(name, S_IRUGO, mmc_##name##_show, NULL) + +static struct device_attribute mmc_dev_attrs[] = { + MMC_ATTR_RO(cid), + MMC_ATTR_RO(csd), + MMC_ATTR_RO(date), + MMC_ATTR_RO(fwrev), + MMC_ATTR_RO(hwrev), + MMC_ATTR_RO(manfid), + MMC_ATTR_RO(name), + MMC_ATTR_RO(oemid), + MMC_ATTR_RO(serial), + __ATTR_NULL +}; + +static struct device_attribute mmc_dev_attr_scr = MMC_ATTR_RO(scr); + + +static void mmc_release_card(struct device *dev) +{ + struct mmc_card *card = dev_to_mmc_card(dev); + + kfree(card); +} + +/* + * This currently matches any MMC driver to any MMC card - drivers + * themselves make the decision whether to drive this card in their + * probe method. However, we force "bad" cards to fail. + */ +static int mmc_bus_match(struct device *dev, struct device_driver *drv) +{ + struct mmc_card *card = dev_to_mmc_card(dev); + return !mmc_card_bad(card); +} + +static int +mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf, + int buf_size) +{ + struct mmc_card *card = dev_to_mmc_card(dev); + char ccc[13]; + int retval = 0, i = 0, length = 0; + +#define add_env(fmt,val) do { \ + retval = add_uevent_var(envp, num_envp, &i, \ + buf, buf_size, &length, \ + fmt, val); \ + if (retval) \ + return retval; \ +} while (0); + + for (i = 0; i < 12; i++) + ccc[i] = card->csd.cmdclass & (1 << i) ? '1' : '0'; + ccc[12] = '\0'; + + add_env("MMC_CCC=%s", ccc); + add_env("MMC_MANFID=%06x", card->cid.manfid); + add_env("MMC_NAME=%s", mmc_card_name(card)); + add_env("MMC_OEMID=%04x", card->cid.oemid); +#undef add_env + envp[i] = NULL; + + return 0; +} + +static int mmc_bus_suspend(struct device *dev, pm_message_t state) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = dev_to_mmc_card(dev); + int ret = 0; + + if (dev->driver && drv->suspend) + ret = drv->suspend(card, state); + return ret; +} + +static int mmc_bus_resume(struct device *dev) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = dev_to_mmc_card(dev); + int ret = 0; + + if (dev->driver && drv->resume) + ret = drv->resume(card); + return ret; +} + +static int mmc_bus_probe(struct device *dev) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = dev_to_mmc_card(dev); + + return drv->probe(card); +} + +static int mmc_bus_remove(struct device *dev) +{ + struct mmc_driver *drv = to_mmc_driver(dev->driver); + struct mmc_card *card = dev_to_mmc_card(dev); + + drv->remove(card); + + return 0; +} + +static struct bus_type mmc_bus_type = { + .name = "mmc", + .dev_attrs = mmc_dev_attrs, + .match = mmc_bus_match, + .uevent = mmc_bus_uevent, + .probe = mmc_bus_probe, + .remove = mmc_bus_remove, + .suspend = mmc_bus_suspend, + .resume = mmc_bus_resume, +}; + +/** + * mmc_register_driver - register a media driver + * @drv: MMC media driver + */ +int mmc_register_driver(struct mmc_driver *drv) +{ + drv->drv.bus = &mmc_bus_type; + return driver_register(&drv->drv); +} + +EXPORT_SYMBOL(mmc_register_driver); + +/** + * mmc_unregister_driver - unregister a media driver + * @drv: MMC media driver + */ +void mmc_unregister_driver(struct mmc_driver *drv) +{ + drv->drv.bus = &mmc_bus_type; + driver_unregister(&drv->drv); +} + +EXPORT_SYMBOL(mmc_unregister_driver); + + +/* + * Internal function. Initialise a MMC card structure. + */ +void mmc_init_card(struct mmc_card *card, struct mmc_host *host) +{ + memset(card, 0, sizeof(struct mmc_card)); + card->host = host; + device_initialize(&card->dev); + card->dev.parent = mmc_classdev(host); + card->dev.bus = &mmc_bus_type; + card->dev.release = mmc_release_card; +} + +/* + * Internal function. Register a new MMC card with the driver model. + */ +int mmc_register_card(struct mmc_card *card) +{ + int ret; + + snprintf(card->dev.bus_id, sizeof(card->dev.bus_id), + "%s:%04x", mmc_hostname(card->host), card->rca); + + ret = device_add(&card->dev); + if (ret == 0) { + if (mmc_card_sd(card)) { + ret = device_create_file(&card->dev, &mmc_dev_attr_scr); + if (ret) + device_del(&card->dev); + } + } + if (ret == 0) + mmc_card_set_present(card); + return ret; +} + +/* + * Internal function. Unregister a new MMC card with the + * driver model, and (eventually) free it. + */ +void mmc_remove_card(struct mmc_card *card) +{ + if (mmc_card_present(card)) { + if (mmc_card_sd(card)) + device_remove_file(&card->dev, &mmc_dev_attr_scr); + + device_del(&card->dev); + } + + put_device(&card->dev); +} + + +static void mmc_host_classdev_release(struct device *dev) +{ + struct mmc_host *host = cls_dev_to_mmc_host(dev); + kfree(host); +} + +static struct class mmc_host_class = { + .name = "mmc_host", + .dev_release = mmc_host_classdev_release, +}; + +static DEFINE_IDR(mmc_host_idr); +static DEFINE_SPINLOCK(mmc_host_lock); + +/* + * Internal function. Allocate a new MMC host. + */ +struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev) +{ + struct mmc_host *host; + + host = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); + if (host) { + memset(host, 0, sizeof(struct mmc_host) + extra); + + host->parent = dev; + host->class_dev.parent = dev; + host->class_dev.class = &mmc_host_class; + device_initialize(&host->class_dev); + } + + return host; +} + +/* + * Internal function. Register a new MMC host with the MMC class. + */ +int mmc_add_host_sysfs(struct mmc_host *host) +{ + int err; + + if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL)) + return -ENOMEM; + + spin_lock(&mmc_host_lock); + err = idr_get_new(&mmc_host_idr, host, &host->index); + spin_unlock(&mmc_host_lock); + if (err) + return err; + + snprintf(host->class_dev.bus_id, BUS_ID_SIZE, + "mmc%d", host->index); + + return device_add(&host->class_dev); +} + +/* + * Internal function. Unregister a MMC host with the MMC class. + */ +void mmc_remove_host_sysfs(struct mmc_host *host) +{ + device_del(&host->class_dev); + + spin_lock(&mmc_host_lock); + idr_remove(&mmc_host_idr, host->index); + spin_unlock(&mmc_host_lock); +} + +/* + * Internal function. Free a MMC host. + */ +void mmc_free_host_sysfs(struct mmc_host *host) +{ + put_device(&host->class_dev); +} + +static struct workqueue_struct *workqueue; + +/* + * Internal function. Schedule delayed work in the MMC work queue. + */ +int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay) +{ + return queue_delayed_work(workqueue, work, delay); +} + +/* + * Internal function. Flush all scheduled work from the MMC work queue. + */ +void mmc_flush_scheduled_work(void) +{ + flush_workqueue(workqueue); +} + +static int __init mmc_init(void) +{ + int ret; + + workqueue = create_singlethread_workqueue("kmmcd"); + if (!workqueue) + return -ENOMEM; + + ret = bus_register(&mmc_bus_type); + if (ret == 0) { + ret = class_register(&mmc_host_class); + if (ret) + bus_unregister(&mmc_bus_type); + } + return ret; +} + +static void __exit mmc_exit(void) +{ + class_unregister(&mmc_host_class); + bus_unregister(&mmc_bus_type); + destroy_workqueue(workqueue); +} + +module_init(mmc_init); +module_exit(mmc_exit); diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c deleted file mode 100644 index 3f50b8882c8..00000000000 --- a/drivers/mmc/mmc.c +++ /dev/null @@ -1,1638 +0,0 @@ -/* - * linux/drivers/mmc/mmc.c - * - * Copyright (C) 2003-2004 Russell King, All Rights Reserved. - * SD support Copyright (C) 2004 Ian Molton, All Rights Reserved. - * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. - * MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "mmc.h" - -#define CMD_RETRIES 3 - -/* - * OCR Bit positions to 10s of Vdd mV. - */ -static const unsigned short mmc_ocr_bit_to_vdd[] = { - 150, 155, 160, 165, 170, 180, 190, 200, - 210, 220, 230, 240, 250, 260, 270, 280, - 290, 300, 310, 320, 330, 340, 350, 360 -}; - -static const unsigned int tran_exp[] = { - 10000, 100000, 1000000, 10000000, - 0, 0, 0, 0 -}; - -static const unsigned char tran_mant[] = { - 0, 10, 12, 13, 15, 20, 25, 30, - 35, 40, 45, 50, 55, 60, 70, 80, -}; - -static const unsigned int tacc_exp[] = { - 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, -}; - -static const unsigned int tacc_mant[] = { - 0, 10, 12, 13, 15, 20, 25, 30, - 35, 40, 45, 50, 55, 60, 70, 80, -}; - - -/** - * mmc_request_done - finish processing an MMC request - * @host: MMC host which completed request - * @mrq: MMC request which request - * - * MMC drivers should call this function when they have completed - * their processing of a request. - */ -void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq) -{ - struct mmc_command *cmd = mrq->cmd; - int err = cmd->error; - - pr_debug("%s: req done (CMD%u): %d/%d/%d: %08x %08x %08x %08x\n", - mmc_hostname(host), cmd->opcode, err, - mrq->data ? mrq->data->error : 0, - mrq->stop ? mrq->stop->error : 0, - cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); - - if (err && cmd->retries) { - cmd->retries--; - cmd->error = 0; - host->ops->request(host, mrq); - } else if (mrq->done) { - mrq->done(mrq); - } -} - -EXPORT_SYMBOL(mmc_request_done); - -/** - * mmc_start_request - start a command on a host - * @host: MMC host to start command on - * @mrq: MMC request to start - * - * Queue a command on the specified host. We expect the - * caller to be holding the host lock with interrupts disabled. - */ -void -mmc_start_request(struct mmc_host *host, struct mmc_request *mrq) -{ -#ifdef CONFIG_MMC_DEBUG - unsigned int i, sz; -#endif - - pr_debug("%s: starting CMD%u arg %08x flags %08x\n", - mmc_hostname(host), mrq->cmd->opcode, - mrq->cmd->arg, mrq->cmd->flags); - - WARN_ON(!host->claimed); - - mrq->cmd->error = 0; - mrq->cmd->mrq = mrq; - if (mrq->data) { - BUG_ON(mrq->data->blksz > host->max_blk_size); - BUG_ON(mrq->data->blocks > host->max_blk_count); - BUG_ON(mrq->data->blocks * mrq->data->blksz > - host->max_req_size); - -#ifdef CONFIG_MMC_DEBUG - sz = 0; - for (i = 0;i < mrq->data->sg_len;i++) - sz += mrq->data->sg[i].length; - BUG_ON(sz != mrq->data->blocks * mrq->data->blksz); -#endif - - mrq->cmd->data = mrq->data; - mrq->data->error = 0; - mrq->data->mrq = mrq; - if (mrq->stop) { - mrq->data->stop = mrq->stop; - mrq->stop->error = 0; - mrq->stop->mrq = mrq; - } - } - host->ops->request(host, mrq); -} - -EXPORT_SYMBOL(mmc_start_request); - -static void mmc_wait_done(struct mmc_request *mrq) -{ - complete(mrq->done_data); -} - -int mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) -{ - DECLARE_COMPLETION_ONSTACK(complete); - - mrq->done_data = &complete; - mrq->done = mmc_wait_done; - - mmc_start_request(host, mrq); - - wait_for_completion(&complete); - - return 0; -} - -EXPORT_SYMBOL(mmc_wait_for_req); - -/** - * mmc_wait_for_cmd - start a command and wait for completion - * @host: MMC host to start command - * @cmd: MMC command to start - * @retries: maximum number of retries - * - * Start a new MMC command for a host, and wait for the command - * to complete. Return any error that occurred while the command - * was executing. Do not attempt to parse the response. - */ -int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries) -{ - struct mmc_request mrq; - - BUG_ON(!host->claimed); - - memset(&mrq, 0, sizeof(struct mmc_request)); - - memset(cmd->resp, 0, sizeof(cmd->resp)); - cmd->retries = retries; - - mrq.cmd = cmd; - cmd->data = NULL; - - mmc_wait_for_req(host, &mrq); - - return cmd->error; -} - -EXPORT_SYMBOL(mmc_wait_for_cmd); - -/** - * mmc_wait_for_app_cmd - start an application command and wait for - completion - * @host: MMC host to start command - * @rca: RCA to send MMC_APP_CMD to - * @cmd: MMC command to start - * @retries: maximum number of retries - * - * Sends a MMC_APP_CMD, checks the card response, sends the command - * in the parameter and waits for it to complete. Return any error - * that occurred while the command was executing. Do not attempt to - * parse the response. - */ -int mmc_wait_for_app_cmd(struct mmc_host *host, unsigned int rca, - struct mmc_command *cmd, int retries) -{ - struct mmc_request mrq; - struct mmc_command appcmd; - - int i, err; - - BUG_ON(!host->claimed); - BUG_ON(retries < 0); - - err = MMC_ERR_INVALID; - - /* - * We have to resend MMC_APP_CMD for each attempt so - * we cannot use the retries field in mmc_command. - */ - for (i = 0;i <= retries;i++) { - memset(&mrq, 0, sizeof(struct mmc_request)); - - appcmd.opcode = MMC_APP_CMD; - appcmd.arg = rca << 16; - appcmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - appcmd.retries = 0; - memset(appcmd.resp, 0, sizeof(appcmd.resp)); - appcmd.data = NULL; - - mrq.cmd = &appcmd; - appcmd.data = NULL; - - mmc_wait_for_req(host, &mrq); - - if (appcmd.error) { - err = appcmd.error; - continue; - } - - /* Check that card supported application commands */ - if (!(appcmd.resp[0] & R1_APP_CMD)) - return MMC_ERR_FAILED; - - memset(&mrq, 0, sizeof(struct mmc_request)); - - memset(cmd->resp, 0, sizeof(cmd->resp)); - cmd->retries = 0; - - mrq.cmd = cmd; - cmd->data = NULL; - - mmc_wait_for_req(host, &mrq); - - err = cmd->error; - if (cmd->error == MMC_ERR_NONE) - break; - } - - return err; -} - -EXPORT_SYMBOL(mmc_wait_for_app_cmd); - -/** - * mmc_set_data_timeout - set the timeout for a data command - * @data: data phase for command - * @card: the MMC card associated with the data transfer - * @write: flag to differentiate reads from writes - */ -void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card, - int write) -{ - unsigned int mult; - - /* - * SD cards use a 100 multiplier rather than 10 - */ - mult = mmc_card_sd(card) ? 100 : 10; - - /* - * Scale up the multiplier (and therefore the timeout) by - * the r2w factor for writes. - */ - if (write) - mult <<= card->csd.r2w_factor; - - data->timeout_ns = card->csd.tacc_ns * mult; - data->timeout_clks = card->csd.tacc_clks * mult; - - /* - * SD cards also have an upper limit on the timeout. - */ - if (mmc_card_sd(card)) { - unsigned int timeout_us, limit_us; - - timeout_us = data->timeout_ns / 1000; - timeout_us += data->timeout_clks * 1000 / - (card->host->ios.clock / 1000); - - if (write) - limit_us = 250000; - else - limit_us = 100000; - - /* - * SDHC cards always use these fixed values. - */ - if (timeout_us > limit_us || mmc_card_blockaddr(card)) { - data->timeout_ns = limit_us * 1000; - data->timeout_clks = 0; - } - } -} -EXPORT_SYMBOL(mmc_set_data_timeout); - -/** - * __mmc_claim_host - exclusively claim a host - * @host: mmc host to claim - * @card: mmc card to claim host for - * - * Claim a host for a set of operations. If a valid card - * is passed and this wasn't the last card selected, select - * the card before returning. - * - * Note: you should use mmc_card_claim_host or mmc_claim_host. - */ -void mmc_claim_host(struct mmc_host *host) -{ - DECLARE_WAITQUEUE(wait, current); - unsigned long flags; - - add_wait_queue(&host->wq, &wait); - spin_lock_irqsave(&host->lock, flags); - while (1) { - set_current_state(TASK_UNINTERRUPTIBLE); - if (!host->claimed) - break; - spin_unlock_irqrestore(&host->lock, flags); - schedule(); - spin_lock_irqsave(&host->lock, flags); - } - set_current_state(TASK_RUNNING); - host->claimed = 1; - spin_unlock_irqrestore(&host->lock, flags); - remove_wait_queue(&host->wq, &wait); -} - -EXPORT_SYMBOL(mmc_claim_host); - -/** - * mmc_release_host - release a host - * @host: mmc host to release - * - * Release a MMC host, allowing others to claim the host - * for their operations. - */ -void mmc_release_host(struct mmc_host *host) -{ - unsigned long flags; - - BUG_ON(!host->claimed); - - spin_lock_irqsave(&host->lock, flags); - host->claimed = 0; - spin_unlock_irqrestore(&host->lock, flags); - - wake_up(&host->wq); -} - -EXPORT_SYMBOL(mmc_release_host); - -static inline void mmc_set_ios(struct mmc_host *host) -{ - struct mmc_ios *ios = &host->ios; - - pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u " - "width %u timing %u\n", - mmc_hostname(host), ios->clock, ios->bus_mode, - ios->power_mode, ios->chip_select, ios->vdd, - ios->bus_width, ios->timing); - - host->ops->set_ios(host, ios); -} - -static int mmc_select_card(struct mmc_card *card) -{ - int err; - struct mmc_command cmd; - - BUG_ON(!card->host->claimed); - - cmd.opcode = MMC_SELECT_CARD; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - - err = mmc_wait_for_cmd(card->host, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) - return err; - - /* - * We can only change the bus width of SD cards when - * they are selected so we have to put the handling - * here. - * - * The card is in 1 bit mode by default so - * we only need to change if it supports the - * wider version. - */ - if (mmc_card_sd(card) && - (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4) && - (card->host->caps & MMC_CAP_4_BIT_DATA)) { - - struct mmc_command cmd; - cmd.opcode = SD_APP_SET_BUS_WIDTH; - cmd.arg = SD_BUS_WIDTH_4; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - - err = mmc_wait_for_app_cmd(card->host, card->rca, - &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) - return err; - - card->host->ios.bus_width = MMC_BUS_WIDTH_4; - mmc_set_ios(card->host); - } - - return MMC_ERR_NONE; -} - - -static inline void mmc_delay(unsigned int ms) -{ - if (ms < 1000 / HZ) { - cond_resched(); - mdelay(ms); - } else { - msleep(ms); - } -} - -/* - * Mask off any voltages we don't support and select - * the lowest voltage - */ -static u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) -{ - int bit; - - ocr &= host->ocr_avail; - - bit = ffs(ocr); - if (bit) { - bit -= 1; - - ocr &= 3 << bit; - - host->ios.vdd = bit; - mmc_set_ios(host); - } else { - ocr = 0; - } - - return ocr; -} - -#define UNSTUFF_BITS(resp,start,size) \ - ({ \ - const int __size = size; \ - const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; \ - const int __off = 3 - ((start) / 32); \ - const int __shft = (start) & 31; \ - u32 __res; \ - \ - __res = resp[__off] >> __shft; \ - if (__size + __shft > 32) \ - __res |= resp[__off-1] << ((32 - __shft) % 32); \ - __res & __mask; \ - }) - -/* - * Given the decoded CSD structure, decode the raw CID to our CID structure. - */ -static void mmc_decode_cid(struct mmc_card *card) -{ - u32 *resp = card->raw_cid; - - memset(&card->cid, 0, sizeof(struct mmc_cid)); - - if (mmc_card_sd(card)) { - /* - * SD doesn't currently have a version field so we will - * have to assume we can parse this. - */ - card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); - card->cid.oemid = UNSTUFF_BITS(resp, 104, 16); - card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); - card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); - card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); - card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); - card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); - card->cid.hwrev = UNSTUFF_BITS(resp, 60, 4); - card->cid.fwrev = UNSTUFF_BITS(resp, 56, 4); - card->cid.serial = UNSTUFF_BITS(resp, 24, 32); - card->cid.year = UNSTUFF_BITS(resp, 12, 8); - card->cid.month = UNSTUFF_BITS(resp, 8, 4); - - card->cid.year += 2000; /* SD cards year offset */ - } else { - /* - * The selection of the format here is based upon published - * specs from sandisk and from what people have reported. - */ - switch (card->csd.mmca_vsn) { - case 0: /* MMC v1.0 - v1.2 */ - case 1: /* MMC v1.4 */ - card->cid.manfid = UNSTUFF_BITS(resp, 104, 24); - card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); - card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); - card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); - card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); - card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); - card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); - card->cid.prod_name[6] = UNSTUFF_BITS(resp, 48, 8); - card->cid.hwrev = UNSTUFF_BITS(resp, 44, 4); - card->cid.fwrev = UNSTUFF_BITS(resp, 40, 4); - card->cid.serial = UNSTUFF_BITS(resp, 16, 24); - card->cid.month = UNSTUFF_BITS(resp, 12, 4); - card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; - break; - - case 2: /* MMC v2.0 - v2.2 */ - case 3: /* MMC v3.1 - v3.3 */ - case 4: /* MMC v4 */ - card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); - card->cid.oemid = UNSTUFF_BITS(resp, 104, 16); - card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); - card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); - card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); - card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); - card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); - card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); - card->cid.serial = UNSTUFF_BITS(resp, 16, 32); - card->cid.month = UNSTUFF_BITS(resp, 12, 4); - card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; - break; - - default: - printk("%s: card has unknown MMCA version %d\n", - mmc_hostname(card->host), card->csd.mmca_vsn); - mmc_card_set_bad(card); - break; - } - } -} - -/* - * Given a 128-bit response, decode to our card CSD structure. - */ -static void mmc_decode_csd(struct mmc_card *card) -{ - struct mmc_csd *csd = &card->csd; - unsigned int e, m, csd_struct; - u32 *resp = card->raw_csd; - - if (mmc_card_sd(card)) { - csd_struct = UNSTUFF_BITS(resp, 126, 2); - - switch (csd_struct) { - case 0: - m = UNSTUFF_BITS(resp, 115, 4); - e = UNSTUFF_BITS(resp, 112, 3); - csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10; - csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100; - - m = UNSTUFF_BITS(resp, 99, 4); - e = UNSTUFF_BITS(resp, 96, 3); - csd->max_dtr = tran_exp[e] * tran_mant[m]; - csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); - - e = UNSTUFF_BITS(resp, 47, 3); - m = UNSTUFF_BITS(resp, 62, 12); - csd->capacity = (1 + m) << (e + 2); - - csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); - csd->read_partial = UNSTUFF_BITS(resp, 79, 1); - csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); - csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); - csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); - csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); - csd->write_partial = UNSTUFF_BITS(resp, 21, 1); - break; - case 1: - /* - * This is a block-addressed SDHC card. Most - * interesting fields are unused and have fixed - * values. To avoid getting tripped by buggy cards, - * we assume those fixed values ourselves. - */ - mmc_card_set_blockaddr(card); - - csd->tacc_ns = 0; /* Unused */ - csd->tacc_clks = 0; /* Unused */ - - m = UNSTUFF_BITS(resp, 99, 4); - e = UNSTUFF_BITS(resp, 96, 3); - csd->max_dtr = tran_exp[e] * tran_mant[m]; - csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); - - m = UNSTUFF_BITS(resp, 48, 22); - csd->capacity = (1 + m) << 10; - - csd->read_blkbits = 9; - csd->read_partial = 0; - csd->write_misalign = 0; - csd->read_misalign = 0; - csd->r2w_factor = 4; /* Unused */ - csd->write_blkbits = 9; - csd->write_partial = 0; - break; - default: - printk("%s: unrecognised CSD structure version %d\n", - mmc_hostname(card->host), csd_struct); - mmc_card_set_bad(card); - return; - } - } else { - /* - * We only understand CSD structure v1.1 and v1.2. - * v1.2 has extra information in bits 15, 11 and 10. - */ - csd_struct = UNSTUFF_BITS(resp, 126, 2); - if (csd_struct != 1 && csd_struct != 2) { - printk("%s: unrecognised CSD structure version %d\n", - mmc_hostname(card->host), csd_struct); - mmc_card_set_bad(card); - return; - } - - csd->mmca_vsn = UNSTUFF_BITS(resp, 122, 4); - m = UNSTUFF_BITS(resp, 115, 4); - e = UNSTUFF_BITS(resp, 112, 3); - csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10; - csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100; - - m = UNSTUFF_BITS(resp, 99, 4); - e = UNSTUFF_BITS(resp, 96, 3); - csd->max_dtr = tran_exp[e] * tran_mant[m]; - csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); - - e = UNSTUFF_BITS(resp, 47, 3); - m = UNSTUFF_BITS(resp, 62, 12); - csd->capacity = (1 + m) << (e + 2); - - csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); - csd->read_partial = UNSTUFF_BITS(resp, 79, 1); - csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); - csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); - csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); - csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); - csd->write_partial = UNSTUFF_BITS(resp, 21, 1); - } -} - -/* - * Given a 64-bit response, decode to our card SCR structure. - */ -static void mmc_decode_scr(struct mmc_card *card) -{ - struct sd_scr *scr = &card->scr; - unsigned int scr_struct; - u32 resp[4]; - - BUG_ON(!mmc_card_sd(card)); - - resp[3] = card->raw_scr[1]; - resp[2] = card->raw_scr[0]; - - scr_struct = UNSTUFF_BITS(resp, 60, 4); - if (scr_struct != 0) { - printk("%s: unrecognised SCR structure version %d\n", - mmc_hostname(card->host), scr_struct); - mmc_card_set_bad(card); - return; - } - - scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4); - scr->bus_widths = UNSTUFF_BITS(resp, 48, 4); -} - -/* - * Allocate a new MMC card - */ -static struct mmc_card * -mmc_alloc_card(struct mmc_host *host, u32 *raw_cid) -{ - struct mmc_card *card; - - card = kmalloc(sizeof(struct mmc_card), GFP_KERNEL); - if (!card) - return ERR_PTR(-ENOMEM); - - mmc_init_card(card, host); - memcpy(card->raw_cid, raw_cid, sizeof(card->raw_cid)); - - return card; -} - -/* - * Tell attached cards to go to IDLE state - */ -static void mmc_idle_cards(struct mmc_host *host) -{ - struct mmc_command cmd; - - host->ios.chip_select = MMC_CS_HIGH; - mmc_set_ios(host); - - mmc_delay(1); - - cmd.opcode = MMC_GO_IDLE_STATE; - cmd.arg = 0; - cmd.flags = MMC_RSP_NONE | MMC_CMD_BC; - - mmc_wait_for_cmd(host, &cmd, 0); - - mmc_delay(1); - - host->ios.chip_select = MMC_CS_DONTCARE; - mmc_set_ios(host); - - mmc_delay(1); -} - -/* - * Apply power to the MMC stack. This is a two-stage process. - * First, we enable power to the card without the clock running. - * We then wait a bit for the power to stabilise. Finally, - * enable the bus drivers and clock to the card. - * - * We must _NOT_ enable the clock prior to power stablising. - * - * If a host does all the power sequencing itself, ignore the - * initial MMC_POWER_UP stage. - */ -static void mmc_power_up(struct mmc_host *host) -{ - int bit = fls(host->ocr_avail) - 1; - - host->ios.vdd = bit; - host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; - host->ios.chip_select = MMC_CS_DONTCARE; - host->ios.power_mode = MMC_POWER_UP; - host->ios.bus_width = MMC_BUS_WIDTH_1; - host->ios.timing = MMC_TIMING_LEGACY; - mmc_set_ios(host); - - mmc_delay(1); - - host->ios.clock = host->f_min; - host->ios.power_mode = MMC_POWER_ON; - mmc_set_ios(host); - - mmc_delay(2); -} - -static void mmc_power_off(struct mmc_host *host) -{ - host->ios.clock = 0; - host->ios.vdd = 0; - host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; - host->ios.chip_select = MMC_CS_DONTCARE; - host->ios.power_mode = MMC_POWER_OFF; - host->ios.bus_width = MMC_BUS_WIDTH_1; - host->ios.timing = MMC_TIMING_LEGACY; - mmc_set_ios(host); -} - -static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) -{ - struct mmc_command cmd; - int i, err = 0; - - cmd.opcode = MMC_SEND_OP_COND; - cmd.arg = ocr; - cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; - - for (i = 100; i; i--) { - err = mmc_wait_for_cmd(host, &cmd, 0); - if (err != MMC_ERR_NONE) - break; - - if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) - break; - - err = MMC_ERR_TIMEOUT; - - mmc_delay(10); - } - - if (rocr) - *rocr = cmd.resp[0]; - - return err; -} - -static int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) -{ - struct mmc_command cmd; - int i, err = 0; - - cmd.opcode = SD_APP_OP_COND; - cmd.arg = ocr; - cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; - - for (i = 100; i; i--) { - err = mmc_wait_for_app_cmd(host, 0, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) - break; - - if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) - break; - - err = MMC_ERR_TIMEOUT; - - mmc_delay(10); - } - - if (rocr) - *rocr = cmd.resp[0]; - - return err; -} - -static int mmc_send_if_cond(struct mmc_host *host, u32 ocr, int *rsd2) -{ - struct mmc_command cmd; - int err, sd2; - static const u8 test_pattern = 0xAA; - - /* - * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND - * before SD_APP_OP_COND. This command will harmlessly fail for - * SD 1.0 cards. - */ - cmd.opcode = SD_SEND_IF_COND; - cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; - cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR; - - err = mmc_wait_for_cmd(host, &cmd, 0); - if (err == MMC_ERR_NONE) { - if ((cmd.resp[0] & 0xFF) == test_pattern) { - sd2 = 1; - } else { - sd2 = 0; - err = MMC_ERR_FAILED; - } - } else { - /* - * Treat errors as SD 1.0 card. - */ - sd2 = 0; - err = MMC_ERR_NONE; - } - if (rsd2) - *rsd2 = sd2; - return err; -} - -/* - * Discover the card by requesting its CID. - * - * Create a mmc_card entry for the discovered card, assigning - * it an RCA, and save the raw CID for decoding later. - */ -static void mmc_discover_card(struct mmc_host *host) -{ - unsigned int err; - - struct mmc_command cmd; - - BUG_ON(host->card); - - cmd.opcode = MMC_ALL_SEND_CID; - cmd.arg = 0; - cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err == MMC_ERR_TIMEOUT) { - err = MMC_ERR_NONE; - return; - } - if (err != MMC_ERR_NONE) { - printk(KERN_ERR "%s: error requesting CID: %d\n", - mmc_hostname(host), err); - return; - } - - host->card = mmc_alloc_card(host, cmd.resp); - if (IS_ERR(host->card)) { - err = PTR_ERR(host->card); - host->card = NULL; - return; - } - - if (host->mode == MMC_MODE_SD) { - host->card->type = MMC_TYPE_SD; - - cmd.opcode = SD_SEND_RELATIVE_ADDR; - cmd.arg = 0; - cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) - mmc_card_set_dead(host->card); - else { - host->card->rca = cmd.resp[0] >> 16; - - if (!host->ops->get_ro) { - printk(KERN_WARNING "%s: host does not " - "support reading read-only " - "switch. assuming write-enable.\n", - mmc_hostname(host)); - } else { - if (host->ops->get_ro(host)) - mmc_card_set_readonly(host->card); - } - } - } else { - host->card->type = MMC_TYPE_MMC; - host->card->rca = 1; - - cmd.opcode = MMC_SET_RELATIVE_ADDR; - cmd.arg = host->card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) - mmc_card_set_dead(host->card); - } -} - -static void mmc_read_csd(struct mmc_host *host) -{ - struct mmc_command cmd; - int err; - - if (!host->card) - return; - if (mmc_card_dead(host->card)) - return; - - cmd.opcode = MMC_SEND_CSD; - cmd.arg = host->card->rca << 16; - cmd.flags = MMC_RSP_R2 | MMC_CMD_AC; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) { - mmc_card_set_dead(host->card); - return; - } - - memcpy(host->card->raw_csd, cmd.resp, sizeof(host->card->raw_csd)); - - mmc_decode_csd(host->card); - mmc_decode_cid(host->card); -} - -static void mmc_process_ext_csd(struct mmc_host *host) -{ - int err; - - struct mmc_request mrq; - struct mmc_command cmd; - struct mmc_data data; - - u8 *ext_csd; - struct scatterlist sg; - - if (!host->card) - return; - if (mmc_card_dead(host->card)) - return; - if (mmc_card_sd(host->card)) - return; - if (host->card->csd.mmca_vsn < CSD_SPEC_VER_4) - return; - - /* - * As the ext_csd is so large and mostly unused, we don't store the - * raw block in mmc_card. - */ - ext_csd = kmalloc(512, GFP_KERNEL); - if (!ext_csd) { - printk("%s: could not allocate a buffer to receive the ext_csd." - "mmc v4 cards will be treated as v3.\n", - mmc_hostname(host)); - return; - } - - memset(&cmd, 0, sizeof(struct mmc_command)); - - cmd.opcode = MMC_SEND_EXT_CSD; - cmd.arg = 0; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - - memset(&data, 0, sizeof(struct mmc_data)); - - mmc_set_data_timeout(&data, host->card, 0); - - data.blksz = 512; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = &sg; - data.sg_len = 1; - - memset(&mrq, 0, sizeof(struct mmc_request)); - - mrq.cmd = &cmd; - mrq.data = &data; - - sg_init_one(&sg, ext_csd, 512); - - mmc_wait_for_req(host, &mrq); - - if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { - if (host->card->csd.capacity == (4096 * 512)) { - printk(KERN_ERR "%s: unable to read EXT_CSD " - "on a possible high capacity card. " - "Card will be ignored.\n", - mmc_hostname(host)); - mmc_card_set_dead(host->card); - } else { - printk(KERN_WARNING "%s: unable to read " - "EXT_CSD, performance might " - "suffer.\n", - mmc_hostname(host)); - } - goto out; - } - - host->card->ext_csd.sectors = - ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | - ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | - ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | - ext_csd[EXT_CSD_SEC_CNT + 3] << 24; - if (host->card->ext_csd.sectors) - mmc_card_set_blockaddr(host->card); - - switch (ext_csd[EXT_CSD_CARD_TYPE]) { - case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26: - host->card->ext_csd.hs_max_dtr = 52000000; - break; - case EXT_CSD_CARD_TYPE_26: - host->card->ext_csd.hs_max_dtr = 26000000; - break; - default: - /* MMC v4 spec says this cannot happen */ - printk("%s: card is mmc v4 but doesn't support " - "any high-speed modes.\n", - mmc_hostname(host)); - goto out; - } - - if (host->caps & MMC_CAP_MMC_HIGHSPEED) { - /* Activate highspeed support. */ - cmd.opcode = MMC_SWITCH; - cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | - (EXT_CSD_HS_TIMING << 16) | - (1 << 8) | - EXT_CSD_CMD_SET_NORMAL; - cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) { - printk("%s: failed to switch card to mmc v4 " - "high-speed mode.\n", - mmc_hostname(host)); - goto out; - } - - mmc_card_set_highspeed(host->card); - - host->ios.timing = MMC_TIMING_MMC_HS; - mmc_set_ios(host); - } - - /* Check for host support for wide-bus modes. */ - if (host->caps & MMC_CAP_4_BIT_DATA) { - /* Activate 4-bit support. */ - cmd.opcode = MMC_SWITCH; - cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | - (EXT_CSD_BUS_WIDTH << 16) | - (EXT_CSD_BUS_WIDTH_4 << 8) | - EXT_CSD_CMD_SET_NORMAL; - cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) { - printk("%s: failed to switch card to " - "mmc v4 4-bit bus mode.\n", - mmc_hostname(host)); - goto out; - } - - host->ios.bus_width = MMC_BUS_WIDTH_4; - mmc_set_ios(host); - } - -out: - kfree(ext_csd); -} - -static void mmc_read_scr(struct mmc_host *host) -{ - int err; - struct mmc_request mrq; - struct mmc_command cmd; - struct mmc_data data; - struct scatterlist sg; - - if (!host->card) - return; - if (mmc_card_dead(host->card)) - return; - if (!mmc_card_sd(host->card)) - return; - - memset(&cmd, 0, sizeof(struct mmc_command)); - - cmd.opcode = MMC_APP_CMD; - cmd.arg = host->card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - - err = mmc_wait_for_cmd(host, &cmd, 0); - if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) { - mmc_card_set_dead(host->card); - return; - } - - memset(&cmd, 0, sizeof(struct mmc_command)); - - cmd.opcode = SD_APP_SEND_SCR; - cmd.arg = 0; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - - memset(&data, 0, sizeof(struct mmc_data)); - - mmc_set_data_timeout(&data, host->card, 0); - - data.blksz = 1 << 3; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = &sg; - data.sg_len = 1; - - memset(&mrq, 0, sizeof(struct mmc_request)); - - mrq.cmd = &cmd; - mrq.data = &data; - - sg_init_one(&sg, (u8*)host->card->raw_scr, 8); - - mmc_wait_for_req(host, &mrq); - - if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { - mmc_card_set_dead(host->card); - return; - } - - host->card->raw_scr[0] = ntohl(host->card->raw_scr[0]); - host->card->raw_scr[1] = ntohl(host->card->raw_scr[1]); - - mmc_decode_scr(host->card); -} - -static void mmc_read_switch_caps(struct mmc_host *host) -{ - struct mmc_request mrq; - struct mmc_command cmd; - struct mmc_data data; - unsigned char *status; - struct scatterlist sg; - - if (!(host->caps & MMC_CAP_SD_HIGHSPEED)) - return; - - if (!host->card) - return; - if (mmc_card_dead(host->card)) - return; - if (!mmc_card_sd(host->card)) - return; - if (host->card->scr.sda_vsn < SCR_SPEC_VER_1) - return; - - status = kmalloc(64, GFP_KERNEL); - if (!status) { - printk(KERN_WARNING "%s: Unable to allocate buffer for " - "reading switch capabilities.\n", - mmc_hostname(host)); - return; - } - - memset(&cmd, 0, sizeof(struct mmc_command)); - - cmd.opcode = SD_SWITCH; - cmd.arg = 0x00FFFFF1; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - - memset(&data, 0, sizeof(struct mmc_data)); - - mmc_set_data_timeout(&data, host->card, 0); - - data.blksz = 64; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = &sg; - data.sg_len = 1; - - memset(&mrq, 0, sizeof(struct mmc_request)); - - mrq.cmd = &cmd; - mrq.data = &data; - - sg_init_one(&sg, status, 64); - - mmc_wait_for_req(host, &mrq); - - if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { - printk("%s: unable to read switch capabilities, " - "performance might suffer.\n", - mmc_hostname(host)); - goto out; - } - - if (status[13] & 0x02) - host->card->sw_caps.hs_max_dtr = 50000000; - - memset(&cmd, 0, sizeof(struct mmc_command)); - - cmd.opcode = SD_SWITCH; - cmd.arg = 0x80FFFFF1; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - - memset(&data, 0, sizeof(struct mmc_data)); - - mmc_set_data_timeout(&data, host->card, 0); - - data.blksz = 64; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = &sg; - data.sg_len = 1; - - memset(&mrq, 0, sizeof(struct mmc_request)); - - mrq.cmd = &cmd; - mrq.data = &data; - - sg_init_one(&sg, status, 64); - - mmc_wait_for_req(host, &mrq); - - if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE || - (status[16] & 0xF) != 1) { - printk(KERN_WARNING "%s: Problem switching card " - "into high-speed mode!\n", - mmc_hostname(host)); - goto out; - } - - mmc_card_set_highspeed(host->card); - - host->ios.timing = MMC_TIMING_SD_HS; - mmc_set_ios(host); - -out: - kfree(status); -} - -static unsigned int mmc_calculate_clock(struct mmc_host *host) -{ - unsigned int max_dtr = host->f_max; - - if (host->card && !mmc_card_dead(host->card)) { - if (mmc_card_highspeed(host->card) && mmc_card_sd(host->card)) { - if (max_dtr > host->card->sw_caps.hs_max_dtr) - max_dtr = host->card->sw_caps.hs_max_dtr; - } else if (mmc_card_highspeed(host->card) && !mmc_card_sd(host->card)) { - if (max_dtr > host->card->ext_csd.hs_max_dtr) - max_dtr = host->card->ext_csd.hs_max_dtr; - } else if (max_dtr > host->card->csd.max_dtr) { - max_dtr = host->card->csd.max_dtr; - } - } - - pr_debug("%s: selected %d.%03dMHz transfer rate\n", - mmc_hostname(host), - max_dtr / 1000000, (max_dtr / 1000) % 1000); - - return max_dtr; -} - -/* - * Check whether cards we already know about are still present. - * We do this by requesting status, and checking whether a card - * responds. - * - * A request for status does not cause a state change in data - * transfer mode. - */ -static void mmc_check_card(struct mmc_card *card) -{ - struct mmc_command cmd; - int err; - - BUG_ON(!card); - - cmd.opcode = MMC_SEND_STATUS; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - - err = mmc_wait_for_cmd(card->host, &cmd, CMD_RETRIES); - if (err == MMC_ERR_NONE) - return; - - mmc_card_set_dead(card); -} - -static void mmc_setup(struct mmc_host *host) -{ - int err; - u32 ocr; - - host->mode = MMC_MODE_SD; - - mmc_power_up(host); - mmc_idle_cards(host); - - err = mmc_send_if_cond(host, host->ocr_avail, NULL); - if (err != MMC_ERR_NONE) { - return; - } - err = mmc_send_app_op_cond(host, 0, &ocr); - - /* - * If we fail to detect any SD cards then try - * searching for MMC cards. - */ - if (err != MMC_ERR_NONE) { - host->mode = MMC_MODE_MMC; - - err = mmc_send_op_cond(host, 0, &ocr); - if (err != MMC_ERR_NONE) - return; - } - - host->ocr = mmc_select_voltage(host, ocr); - - if (host->ocr == 0) - return; - - /* - * Since we're changing the OCR value, we seem to - * need to tell some cards to go back to the idle - * state. We wait 1ms to give cards time to - * respond. - */ - mmc_idle_cards(host); - - /* - * Send the selected OCR multiple times... until the cards - * all get the idea that they should be ready for CMD2. - * (My SanDisk card seems to need this.) - */ - if (host->mode == MMC_MODE_SD) { - int err, sd2; - err = mmc_send_if_cond(host, host->ocr, &sd2); - if (err == MMC_ERR_NONE) { - /* - * If SD_SEND_IF_COND indicates an SD 2.0 - * compliant card and we should set bit 30 - * of the ocr to indicate that we can handle - * block-addressed SDHC cards. - */ - mmc_send_app_op_cond(host, host->ocr | (sd2 << 30), NULL); - } - } else { - /* The extra bit indicates that we support high capacity */ - mmc_send_op_cond(host, host->ocr | (1 << 30), NULL); - } - - mmc_discover_card(host); - - /* - * Ok, now switch to push-pull mode. - */ - host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; - mmc_set_ios(host); - - mmc_read_csd(host); - - if (host->card && !mmc_card_dead(host->card)) { - err = mmc_select_card(host->card); - if (err != MMC_ERR_NONE) - mmc_card_set_dead(host->card); - } - - if (host->mode == MMC_MODE_SD) { - mmc_read_scr(host); - mmc_read_switch_caps(host); - } else - mmc_process_ext_csd(host); -} - - -/** - * mmc_detect_change - process change of state on a MMC socket - * @host: host which changed state. - * @delay: optional delay to wait before detection (jiffies) - * - * All we know is that card(s) have been inserted or removed - * from the socket(s). We don't know which socket or cards. - */ -void mmc_detect_change(struct mmc_host *host, unsigned long delay) -{ -#ifdef CONFIG_MMC_DEBUG - mmc_claim_host(host); - BUG_ON(host->removed); - mmc_release_host(host); -#endif - - mmc_schedule_delayed_work(&host->detect, delay); -} - -EXPORT_SYMBOL(mmc_detect_change); - - -static void mmc_rescan(struct work_struct *work) -{ - struct mmc_host *host = - container_of(work, struct mmc_host, detect.work); - - mmc_claim_host(host); - - /* - * Check for removed card and newly inserted ones. We check for - * removed cards first so we can intelligently re-select the VDD. - */ - if (host->card) { - mmc_check_card(host->card); - - mmc_release_host(host); - - if (mmc_card_dead(host->card)) { - mmc_remove_card(host->card); - host->card = NULL; - } - - goto out; - } - - mmc_setup(host); - - if (host->card && !mmc_card_dead(host->card)) { - /* - * (Re-)calculate the fastest clock rate which the - * attached cards and the host support. - */ - host->ios.clock = mmc_calculate_clock(host); - mmc_set_ios(host); - } - - mmc_release_host(host); - - /* - * If this is a new and good card, register it. - */ - if (host->card && !mmc_card_dead(host->card)) { - if (mmc_register_card(host->card)) - mmc_card_set_dead(host->card); - } - - /* - * If this card is dead, destroy it. - */ - if (host->card && mmc_card_dead(host->card)) { - mmc_remove_card(host->card); - host->card = NULL; - } - -out: - /* - * If we discover that there are no cards on the - * bus, turn off the clock and power down. - */ - if (!host->card) - mmc_power_off(host); -} - - -/** - * mmc_alloc_host - initialise the per-host structure. - * @extra: sizeof private data structure - * @dev: pointer to host device model structure - * - * Initialise the per-host structure. - */ -struct mmc_host *mmc_alloc_host(int extra, struct device *dev) -{ - struct mmc_host *host; - - host = mmc_alloc_host_sysfs(extra, dev); - if (host) { - spin_lock_init(&host->lock); - init_waitqueue_head(&host->wq); - INIT_DELAYED_WORK(&host->detect, mmc_rescan); - - /* - * By default, hosts do not support SGIO or large requests. - * They have to set these according to their abilities. - */ - host->max_hw_segs = 1; - host->max_phys_segs = 1; - host->max_seg_size = PAGE_CACHE_SIZE; - - host->max_req_size = PAGE_CACHE_SIZE; - host->max_blk_size = 512; - host->max_blk_count = PAGE_CACHE_SIZE / 512; - } - - return host; -} - -EXPORT_SYMBOL(mmc_alloc_host); - -/** - * mmc_add_host - initialise host hardware - * @host: mmc host - */ -int mmc_add_host(struct mmc_host *host) -{ - int ret; - - ret = mmc_add_host_sysfs(host); - if (ret == 0) { - mmc_power_off(host); - mmc_detect_change(host, 0); - } - - return ret; -} - -EXPORT_SYMBOL(mmc_add_host); - -/** - * mmc_remove_host - remove host hardware - * @host: mmc host - * - * Unregister and remove all cards associated with this host, - * and power down the MMC bus. - */ -void mmc_remove_host(struct mmc_host *host) -{ -#ifdef CONFIG_MMC_DEBUG - mmc_claim_host(host); - host->removed = 1; - mmc_release_host(host); -#endif - - mmc_flush_scheduled_work(); - - if (host->card) { - mmc_remove_card(host->card); - host->card = NULL; - } - - mmc_power_off(host); - mmc_remove_host_sysfs(host); -} - -EXPORT_SYMBOL(mmc_remove_host); - -/** - * mmc_free_host - free the host structure - * @host: mmc host - * - * Free the host once all references to it have been dropped. - */ -void mmc_free_host(struct mmc_host *host) -{ - mmc_free_host_sysfs(host); -} - -EXPORT_SYMBOL(mmc_free_host); - -#ifdef CONFIG_PM - -/** - * mmc_suspend_host - suspend a host - * @host: mmc host - * @state: suspend mode (PM_SUSPEND_xxx) - */ -int mmc_suspend_host(struct mmc_host *host, pm_message_t state) -{ - mmc_flush_scheduled_work(); - - if (host->card) { - mmc_remove_card(host->card); - host->card = NULL; - } - - mmc_power_off(host); - - return 0; -} - -EXPORT_SYMBOL(mmc_suspend_host); - -/** - * mmc_resume_host - resume a previously suspended host - * @host: mmc host - */ -int mmc_resume_host(struct mmc_host *host) -{ - mmc_rescan(&host->detect.work); - - return 0; -} - -EXPORT_SYMBOL(mmc_resume_host); - -#endif - -MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/mmc.h b/drivers/mmc/mmc.h deleted file mode 100644 index 149affe0b68..00000000000 --- a/drivers/mmc/mmc.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * linux/drivers/mmc/mmc.h - * - * Copyright (C) 2003 Russell King, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef _MMC_H -#define _MMC_H -/* core-internal functions */ -void mmc_init_card(struct mmc_card *card, struct mmc_host *host); -int mmc_register_card(struct mmc_card *card); -void mmc_remove_card(struct mmc_card *card); - -struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev); -int mmc_add_host_sysfs(struct mmc_host *host); -void mmc_remove_host_sysfs(struct mmc_host *host); -void mmc_free_host_sysfs(struct mmc_host *host); - -int mmc_schedule_work(struct work_struct *work); -int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay); -void mmc_flush_scheduled_work(void); -#endif diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c deleted file mode 100644 index 06f264b2f79..00000000000 --- a/drivers/mmc/mmc_sysfs.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - * linux/drivers/mmc/mmc_sysfs.c - * - * Copyright (C) 2003 Russell King, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * MMC sysfs/driver model support. - */ -#include -#include -#include -#include -#include - -#include -#include - -#include "mmc.h" - -#define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) -#define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) -#define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev) - -#define MMC_ATTR(name, fmt, args...) \ -static ssize_t mmc_##name##_show (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct mmc_card *card = dev_to_mmc_card(dev); \ - return sprintf(buf, fmt, args); \ -} - -MMC_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], - card->raw_cid[2], card->raw_cid[3]); -MMC_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], - card->raw_csd[2], card->raw_csd[3]); -MMC_ATTR(scr, "%08x%08x\n", card->raw_scr[0], card->raw_scr[1]); -MMC_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); -MMC_ATTR(fwrev, "0x%x\n", card->cid.fwrev); -MMC_ATTR(hwrev, "0x%x\n", card->cid.hwrev); -MMC_ATTR(manfid, "0x%06x\n", card->cid.manfid); -MMC_ATTR(name, "%s\n", card->cid.prod_name); -MMC_ATTR(oemid, "0x%04x\n", card->cid.oemid); -MMC_ATTR(serial, "0x%08x\n", card->cid.serial); - -#define MMC_ATTR_RO(name) __ATTR(name, S_IRUGO, mmc_##name##_show, NULL) - -static struct device_attribute mmc_dev_attrs[] = { - MMC_ATTR_RO(cid), - MMC_ATTR_RO(csd), - MMC_ATTR_RO(date), - MMC_ATTR_RO(fwrev), - MMC_ATTR_RO(hwrev), - MMC_ATTR_RO(manfid), - MMC_ATTR_RO(name), - MMC_ATTR_RO(oemid), - MMC_ATTR_RO(serial), - __ATTR_NULL -}; - -static struct device_attribute mmc_dev_attr_scr = MMC_ATTR_RO(scr); - - -static void mmc_release_card(struct device *dev) -{ - struct mmc_card *card = dev_to_mmc_card(dev); - - kfree(card); -} - -/* - * This currently matches any MMC driver to any MMC card - drivers - * themselves make the decision whether to drive this card in their - * probe method. However, we force "bad" cards to fail. - */ -static int mmc_bus_match(struct device *dev, struct device_driver *drv) -{ - struct mmc_card *card = dev_to_mmc_card(dev); - return !mmc_card_bad(card); -} - -static int -mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf, - int buf_size) -{ - struct mmc_card *card = dev_to_mmc_card(dev); - char ccc[13]; - int retval = 0, i = 0, length = 0; - -#define add_env(fmt,val) do { \ - retval = add_uevent_var(envp, num_envp, &i, \ - buf, buf_size, &length, \ - fmt, val); \ - if (retval) \ - return retval; \ -} while (0); - - for (i = 0; i < 12; i++) - ccc[i] = card->csd.cmdclass & (1 << i) ? '1' : '0'; - ccc[12] = '\0'; - - add_env("MMC_CCC=%s", ccc); - add_env("MMC_MANFID=%06x", card->cid.manfid); - add_env("MMC_NAME=%s", mmc_card_name(card)); - add_env("MMC_OEMID=%04x", card->cid.oemid); -#undef add_env - envp[i] = NULL; - - return 0; -} - -static int mmc_bus_suspend(struct device *dev, pm_message_t state) -{ - struct mmc_driver *drv = to_mmc_driver(dev->driver); - struct mmc_card *card = dev_to_mmc_card(dev); - int ret = 0; - - if (dev->driver && drv->suspend) - ret = drv->suspend(card, state); - return ret; -} - -static int mmc_bus_resume(struct device *dev) -{ - struct mmc_driver *drv = to_mmc_driver(dev->driver); - struct mmc_card *card = dev_to_mmc_card(dev); - int ret = 0; - - if (dev->driver && drv->resume) - ret = drv->resume(card); - return ret; -} - -static int mmc_bus_probe(struct device *dev) -{ - struct mmc_driver *drv = to_mmc_driver(dev->driver); - struct mmc_card *card = dev_to_mmc_card(dev); - - return drv->probe(card); -} - -static int mmc_bus_remove(struct device *dev) -{ - struct mmc_driver *drv = to_mmc_driver(dev->driver); - struct mmc_card *card = dev_to_mmc_card(dev); - - drv->remove(card); - - return 0; -} - -static struct bus_type mmc_bus_type = { - .name = "mmc", - .dev_attrs = mmc_dev_attrs, - .match = mmc_bus_match, - .uevent = mmc_bus_uevent, - .probe = mmc_bus_probe, - .remove = mmc_bus_remove, - .suspend = mmc_bus_suspend, - .resume = mmc_bus_resume, -}; - -/** - * mmc_register_driver - register a media driver - * @drv: MMC media driver - */ -int mmc_register_driver(struct mmc_driver *drv) -{ - drv->drv.bus = &mmc_bus_type; - return driver_register(&drv->drv); -} - -EXPORT_SYMBOL(mmc_register_driver); - -/** - * mmc_unregister_driver - unregister a media driver - * @drv: MMC media driver - */ -void mmc_unregister_driver(struct mmc_driver *drv) -{ - drv->drv.bus = &mmc_bus_type; - driver_unregister(&drv->drv); -} - -EXPORT_SYMBOL(mmc_unregister_driver); - - -/* - * Internal function. Initialise a MMC card structure. - */ -void mmc_init_card(struct mmc_card *card, struct mmc_host *host) -{ - memset(card, 0, sizeof(struct mmc_card)); - card->host = host; - device_initialize(&card->dev); - card->dev.parent = mmc_classdev(host); - card->dev.bus = &mmc_bus_type; - card->dev.release = mmc_release_card; -} - -/* - * Internal function. Register a new MMC card with the driver model. - */ -int mmc_register_card(struct mmc_card *card) -{ - int ret; - - snprintf(card->dev.bus_id, sizeof(card->dev.bus_id), - "%s:%04x", mmc_hostname(card->host), card->rca); - - ret = device_add(&card->dev); - if (ret == 0) { - if (mmc_card_sd(card)) { - ret = device_create_file(&card->dev, &mmc_dev_attr_scr); - if (ret) - device_del(&card->dev); - } - } - if (ret == 0) - mmc_card_set_present(card); - return ret; -} - -/* - * Internal function. Unregister a new MMC card with the - * driver model, and (eventually) free it. - */ -void mmc_remove_card(struct mmc_card *card) -{ - if (mmc_card_present(card)) { - if (mmc_card_sd(card)) - device_remove_file(&card->dev, &mmc_dev_attr_scr); - - device_del(&card->dev); - } - - put_device(&card->dev); -} - - -static void mmc_host_classdev_release(struct device *dev) -{ - struct mmc_host *host = cls_dev_to_mmc_host(dev); - kfree(host); -} - -static struct class mmc_host_class = { - .name = "mmc_host", - .dev_release = mmc_host_classdev_release, -}; - -static DEFINE_IDR(mmc_host_idr); -static DEFINE_SPINLOCK(mmc_host_lock); - -/* - * Internal function. Allocate a new MMC host. - */ -struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev) -{ - struct mmc_host *host; - - host = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); - if (host) { - memset(host, 0, sizeof(struct mmc_host) + extra); - - host->parent = dev; - host->class_dev.parent = dev; - host->class_dev.class = &mmc_host_class; - device_initialize(&host->class_dev); - } - - return host; -} - -/* - * Internal function. Register a new MMC host with the MMC class. - */ -int mmc_add_host_sysfs(struct mmc_host *host) -{ - int err; - - if (!idr_pre_get(&mmc_host_idr, GFP_KERNEL)) - return -ENOMEM; - - spin_lock(&mmc_host_lock); - err = idr_get_new(&mmc_host_idr, host, &host->index); - spin_unlock(&mmc_host_lock); - if (err) - return err; - - snprintf(host->class_dev.bus_id, BUS_ID_SIZE, - "mmc%d", host->index); - - return device_add(&host->class_dev); -} - -/* - * Internal function. Unregister a MMC host with the MMC class. - */ -void mmc_remove_host_sysfs(struct mmc_host *host) -{ - device_del(&host->class_dev); - - spin_lock(&mmc_host_lock); - idr_remove(&mmc_host_idr, host->index); - spin_unlock(&mmc_host_lock); -} - -/* - * Internal function. Free a MMC host. - */ -void mmc_free_host_sysfs(struct mmc_host *host) -{ - put_device(&host->class_dev); -} - -static struct workqueue_struct *workqueue; - -/* - * Internal function. Schedule delayed work in the MMC work queue. - */ -int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay) -{ - return queue_delayed_work(workqueue, work, delay); -} - -/* - * Internal function. Flush all scheduled work from the MMC work queue. - */ -void mmc_flush_scheduled_work(void) -{ - flush_workqueue(workqueue); -} - -static int __init mmc_init(void) -{ - int ret; - - workqueue = create_singlethread_workqueue("kmmcd"); - if (!workqueue) - return -ENOMEM; - - ret = bus_register(&mmc_bus_type); - if (ret == 0) { - ret = class_register(&mmc_host_class); - if (ret) - bus_unregister(&mmc_bus_type); - } - return ret; -} - -static void __exit mmc_exit(void) -{ - class_unregister(&mmc_host_class); - bus_unregister(&mmc_bus_type); - destroy_workqueue(workqueue); -} - -module_init(mmc_init); -module_exit(mmc_exit); diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 7d98990ac94..800425e0516 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -10,7 +10,7 @@ #ifndef LINUX_MMC_CARD_H #define LINUX_MMC_CARD_H -#include +#include struct mmc_cid { unsigned int manfid; diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h new file mode 100644 index 00000000000..d8fd66cf28b --- /dev/null +++ b/include/linux/mmc/core.h @@ -0,0 +1,112 @@ +/* + * linux/include/linux/mmc/core.h + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef LINUX_MMC_CORE_H +#define LINUX_MMC_CORE_H + +#include +#include + +struct request; +struct mmc_data; +struct mmc_request; + +struct mmc_command { + u32 opcode; + u32 arg; + u32 resp[4]; + unsigned int flags; /* expected response type */ +#define MMC_RSP_PRESENT (1 << 0) +#define MMC_RSP_136 (1 << 1) /* 136 bit response */ +#define MMC_RSP_CRC (1 << 2) /* expect valid crc */ +#define MMC_RSP_BUSY (1 << 3) /* card may send busy */ +#define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */ +#define MMC_CMD_MASK (3 << 5) /* command type */ +#define MMC_CMD_AC (0 << 5) +#define MMC_CMD_ADTC (1 << 5) +#define MMC_CMD_BC (2 << 5) +#define MMC_CMD_BCR (3 << 5) + +/* + * These are the response types, and correspond to valid bit + * patterns of the above flags. One additional valid pattern + * is all zeros, which means we don't expect a response. + */ +#define MMC_RSP_NONE (0) +#define MMC_RSP_R1 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) +#define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY) +#define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC) +#define MMC_RSP_R3 (MMC_RSP_PRESENT) +#define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) +#define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) + +#define mmc_resp_type(cmd) ((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE)) + +/* + * These are the command types. + */ +#define mmc_cmd_type(cmd) ((cmd)->flags & MMC_CMD_MASK) + + unsigned int retries; /* max number of retries */ + unsigned int error; /* command error */ + +#define MMC_ERR_NONE 0 +#define MMC_ERR_TIMEOUT 1 +#define MMC_ERR_BADCRC 2 +#define MMC_ERR_FIFO 3 +#define MMC_ERR_FAILED 4 +#define MMC_ERR_INVALID 5 + + struct mmc_data *data; /* data segment associated with cmd */ + struct mmc_request *mrq; /* associated request */ +}; + +struct mmc_data { + unsigned int timeout_ns; /* data timeout (in ns, max 80ms) */ + unsigned int timeout_clks; /* data timeout (in clocks) */ + unsigned int blksz; /* data block size */ + unsigned int blocks; /* number of blocks */ + unsigned int error; /* data error */ + unsigned int flags; + +#define MMC_DATA_WRITE (1 << 8) +#define MMC_DATA_READ (1 << 9) +#define MMC_DATA_STREAM (1 << 10) +#define MMC_DATA_MULTI (1 << 11) + + unsigned int bytes_xfered; + + struct mmc_command *stop; /* stop command */ + struct mmc_request *mrq; /* associated request */ + + unsigned int sg_len; /* size of scatter list */ + struct scatterlist *sg; /* I/O scatter list */ +}; + +struct mmc_request { + struct mmc_command *cmd; + struct mmc_data *data; + struct mmc_command *stop; + + void *done_data; /* completion data */ + void (*done)(struct mmc_request *);/* completion function */ +}; + +struct mmc_host; +struct mmc_card; + +extern int mmc_wait_for_req(struct mmc_host *, struct mmc_request *); +extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); +extern int mmc_wait_for_app_cmd(struct mmc_host *, unsigned int, + struct mmc_command *, int); + +extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *, int); + +extern void mmc_claim_host(struct mmc_host *host); +extern void mmc_release_host(struct mmc_host *host); + +#endif diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 6ea3c0ea3e1..43bf6a5c398 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -10,7 +10,7 @@ #ifndef LINUX_MMC_HOST_H #define LINUX_MMC_HOST_H -#include +#include struct mmc_ios { unsigned int clock; /* clock rate */ diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h deleted file mode 100644 index b3d80efc643..00000000000 --- a/include/linux/mmc/mmc.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * linux/include/linux/mmc/mmc.h - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef MMC_H -#define MMC_H - -#include -#include - -struct request; -struct mmc_data; -struct mmc_request; - -struct mmc_command { - u32 opcode; - u32 arg; - u32 resp[4]; - unsigned int flags; /* expected response type */ -#define MMC_RSP_PRESENT (1 << 0) -#define MMC_RSP_136 (1 << 1) /* 136 bit response */ -#define MMC_RSP_CRC (1 << 2) /* expect valid crc */ -#define MMC_RSP_BUSY (1 << 3) /* card may send busy */ -#define MMC_RSP_OPCODE (1 << 4) /* response contains opcode */ -#define MMC_CMD_MASK (3 << 5) /* command type */ -#define MMC_CMD_AC (0 << 5) -#define MMC_CMD_ADTC (1 << 5) -#define MMC_CMD_BC (2 << 5) -#define MMC_CMD_BCR (3 << 5) - -/* - * These are the response types, and correspond to valid bit - * patterns of the above flags. One additional valid pattern - * is all zeros, which means we don't expect a response. - */ -#define MMC_RSP_NONE (0) -#define MMC_RSP_R1 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) -#define MMC_RSP_R1B (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE|MMC_RSP_BUSY) -#define MMC_RSP_R2 (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC) -#define MMC_RSP_R3 (MMC_RSP_PRESENT) -#define MMC_RSP_R6 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) -#define MMC_RSP_R7 (MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE) - -#define mmc_resp_type(cmd) ((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE)) - -/* - * These are the command types. - */ -#define mmc_cmd_type(cmd) ((cmd)->flags & MMC_CMD_MASK) - - unsigned int retries; /* max number of retries */ - unsigned int error; /* command error */ - -#define MMC_ERR_NONE 0 -#define MMC_ERR_TIMEOUT 1 -#define MMC_ERR_BADCRC 2 -#define MMC_ERR_FIFO 3 -#define MMC_ERR_FAILED 4 -#define MMC_ERR_INVALID 5 - - struct mmc_data *data; /* data segment associated with cmd */ - struct mmc_request *mrq; /* associated request */ -}; - -struct mmc_data { - unsigned int timeout_ns; /* data timeout (in ns, max 80ms) */ - unsigned int timeout_clks; /* data timeout (in clocks) */ - unsigned int blksz; /* data block size */ - unsigned int blocks; /* number of blocks */ - unsigned int error; /* data error */ - unsigned int flags; - -#define MMC_DATA_WRITE (1 << 8) -#define MMC_DATA_READ (1 << 9) -#define MMC_DATA_STREAM (1 << 10) -#define MMC_DATA_MULTI (1 << 11) - - unsigned int bytes_xfered; - - struct mmc_command *stop; /* stop command */ - struct mmc_request *mrq; /* associated request */ - - unsigned int sg_len; /* size of scatter list */ - struct scatterlist *sg; /* I/O scatter list */ -}; - -struct mmc_request { - struct mmc_command *cmd; - struct mmc_data *data; - struct mmc_command *stop; - - void *done_data; /* completion data */ - void (*done)(struct mmc_request *);/* completion function */ -}; - -struct mmc_host; -struct mmc_card; - -extern int mmc_wait_for_req(struct mmc_host *, struct mmc_request *); -extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); -extern int mmc_wait_for_app_cmd(struct mmc_host *, unsigned int, - struct mmc_command *, int); - -extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *, int); - -extern void mmc_claim_host(struct mmc_host *host); -extern void mmc_release_host(struct mmc_host *host); - -#endif -- cgit v1.2.3 From da7fbe58d2d347e95af699ddf04d885be6362bbe Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 24 Dec 2006 22:46:55 +0100 Subject: mmc: Separate out protocol ops Move protocol operations and definitions into their own files in an effort to separate protocol handling and bus arbitration more clearly. Signed-off-by: Pierre Ossman --- drivers/mmc/card/block.c | 4 +- drivers/mmc/core/Makefile | 2 +- drivers/mmc/core/core.c | 511 ++++++------------------------------------- drivers/mmc/core/core.h | 32 +-- drivers/mmc/core/mmc_ops.c | 276 +++++++++++++++++++++++ drivers/mmc/core/mmc_ops.h | 27 +++ drivers/mmc/core/sd_ops.c | 316 ++++++++++++++++++++++++++ drivers/mmc/core/sd_ops.h | 25 +++ drivers/mmc/core/sysfs.c | 2 +- drivers/mmc/core/sysfs.h | 27 +++ include/linux/mmc/core.h | 2 +- include/linux/mmc/mmc.h | 257 ++++++++++++++++++++++ include/linux/mmc/protocol.h | 307 -------------------------- include/linux/mmc/sd.h | 83 +++++++ 14 files changed, 1099 insertions(+), 772 deletions(-) create mode 100644 drivers/mmc/core/mmc_ops.c create mode 100644 drivers/mmc/core/mmc_ops.h create mode 100644 drivers/mmc/core/sd_ops.c create mode 100644 drivers/mmc/core/sd_ops.h create mode 100644 drivers/mmc/core/sysfs.h create mode 100644 include/linux/mmc/mmc.h delete mode 100644 include/linux/mmc/protocol.h create mode 100644 include/linux/mmc/sd.h diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 8a84e4dc1b2..d24ab234394 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -32,8 +32,8 @@ #include #include -#include -#include +#include +#include #include #include diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index f911fbd2845..5977abf3e41 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -7,5 +7,5 @@ ifeq ($(CONFIG_MMC_DEBUG),y) endif obj-$(CONFIG_MMC) += mmc_core.o -mmc_core-y := core.o sysfs.o +mmc_core-y := core.o sysfs.o mmc_ops.o sd_ops.o diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 334e663e465..310be2fe194 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -23,9 +23,14 @@ #include #include -#include +#include +#include #include "core.h" +#include "sysfs.h" + +#include "mmc_ops.h" +#include "sd_ops.h" #define CMD_RETRIES 3 @@ -190,80 +195,6 @@ int mmc_wait_for_cmd(struct mmc_host *host, struct mmc_command *cmd, int retries EXPORT_SYMBOL(mmc_wait_for_cmd); -/** - * mmc_wait_for_app_cmd - start an application command and wait for - completion - * @host: MMC host to start command - * @rca: RCA to send MMC_APP_CMD to - * @cmd: MMC command to start - * @retries: maximum number of retries - * - * Sends a MMC_APP_CMD, checks the card response, sends the command - * in the parameter and waits for it to complete. Return any error - * that occurred while the command was executing. Do not attempt to - * parse the response. - */ -int mmc_wait_for_app_cmd(struct mmc_host *host, unsigned int rca, - struct mmc_command *cmd, int retries) -{ - struct mmc_request mrq; - struct mmc_command appcmd; - - int i, err; - - BUG_ON(!host->claimed); - BUG_ON(retries < 0); - - err = MMC_ERR_INVALID; - - /* - * We have to resend MMC_APP_CMD for each attempt so - * we cannot use the retries field in mmc_command. - */ - for (i = 0;i <= retries;i++) { - memset(&mrq, 0, sizeof(struct mmc_request)); - - appcmd.opcode = MMC_APP_CMD; - appcmd.arg = rca << 16; - appcmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - appcmd.retries = 0; - memset(appcmd.resp, 0, sizeof(appcmd.resp)); - appcmd.data = NULL; - - mrq.cmd = &appcmd; - appcmd.data = NULL; - - mmc_wait_for_req(host, &mrq); - - if (appcmd.error) { - err = appcmd.error; - continue; - } - - /* Check that card supported application commands */ - if (!(appcmd.resp[0] & R1_APP_CMD)) - return MMC_ERR_FAILED; - - memset(&mrq, 0, sizeof(struct mmc_request)); - - memset(cmd->resp, 0, sizeof(cmd->resp)); - cmd->retries = 0; - - mrq.cmd = cmd; - cmd->data = NULL; - - mmc_wait_for_req(host, &mrq); - - err = cmd->error; - if (cmd->error == MMC_ERR_NONE) - break; - } - - return err; -} - -EXPORT_SYMBOL(mmc_wait_for_app_cmd); - /** * mmc_set_data_timeout - set the timeout for a data command * @data: data phase for command @@ -385,60 +316,10 @@ static inline void mmc_set_ios(struct mmc_host *host) host->ops->set_ios(host, ios); } -static int mmc_select_card(struct mmc_card *card) -{ - int err; - struct mmc_command cmd; - - BUG_ON(!card->host->claimed); - - cmd.opcode = MMC_SELECT_CARD; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - - err = mmc_wait_for_cmd(card->host, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) - return err; - - /* - * We can only change the bus width of SD cards when - * they are selected so we have to put the handling - * here. - * - * The card is in 1 bit mode by default so - * we only need to change if it supports the - * wider version. - */ - if (mmc_card_sd(card) && - (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4) && - (card->host->caps & MMC_CAP_4_BIT_DATA)) { - - struct mmc_command cmd; - cmd.opcode = SD_APP_SET_BUS_WIDTH; - cmd.arg = SD_BUS_WIDTH_4; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - - err = mmc_wait_for_app_cmd(card->host, card->rca, - &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) - return err; - - card->host->ios.bus_width = MMC_BUS_WIDTH_4; - mmc_set_ios(card->host); - } - - return MMC_ERR_NONE; -} - - -static inline void mmc_delay(unsigned int ms) +void mmc_set_chip_select(struct mmc_host *host, int mode) { - if (ms < 1000 / HZ) { - cond_resched(); - mdelay(ms); - } else { - msleep(ms); - } + host->ios.chip_select = mode; + mmc_set_ios(host); } /* @@ -708,32 +589,6 @@ mmc_alloc_card(struct mmc_host *host, u32 *raw_cid) return card; } -/* - * Tell attached cards to go to IDLE state - */ -static void mmc_idle_cards(struct mmc_host *host) -{ - struct mmc_command cmd; - - host->ios.chip_select = MMC_CS_HIGH; - mmc_set_ios(host); - - mmc_delay(1); - - cmd.opcode = MMC_GO_IDLE_STATE; - cmd.arg = 0; - cmd.flags = MMC_RSP_NONE | MMC_CMD_BC; - - mmc_wait_for_cmd(host, &cmd, 0); - - mmc_delay(1); - - host->ios.chip_select = MMC_CS_DONTCARE; - mmc_set_ios(host); - - mmc_delay(1); -} - /* * Apply power to the MMC stack. This is a two-stage process. * First, we enable power to the card without the clock running. @@ -778,97 +633,6 @@ static void mmc_power_off(struct mmc_host *host) mmc_set_ios(host); } -static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) -{ - struct mmc_command cmd; - int i, err = 0; - - cmd.opcode = MMC_SEND_OP_COND; - cmd.arg = ocr; - cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; - - for (i = 100; i; i--) { - err = mmc_wait_for_cmd(host, &cmd, 0); - if (err != MMC_ERR_NONE) - break; - - if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) - break; - - err = MMC_ERR_TIMEOUT; - - mmc_delay(10); - } - - if (rocr) - *rocr = cmd.resp[0]; - - return err; -} - -static int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) -{ - struct mmc_command cmd; - int i, err = 0; - - cmd.opcode = SD_APP_OP_COND; - cmd.arg = ocr; - cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; - - for (i = 100; i; i--) { - err = mmc_wait_for_app_cmd(host, 0, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) - break; - - if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) - break; - - err = MMC_ERR_TIMEOUT; - - mmc_delay(10); - } - - if (rocr) - *rocr = cmd.resp[0]; - - return err; -} - -static int mmc_send_if_cond(struct mmc_host *host, u32 ocr, int *rsd2) -{ - struct mmc_command cmd; - int err, sd2; - static const u8 test_pattern = 0xAA; - - /* - * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND - * before SD_APP_OP_COND. This command will harmlessly fail for - * SD 1.0 cards. - */ - cmd.opcode = SD_SEND_IF_COND; - cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; - cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR; - - err = mmc_wait_for_cmd(host, &cmd, 0); - if (err == MMC_ERR_NONE) { - if ((cmd.resp[0] & 0xFF) == test_pattern) { - sd2 = 1; - } else { - sd2 = 0; - err = MMC_ERR_FAILED; - } - } else { - /* - * Treat errors as SD 1.0 card. - */ - sd2 = 0; - err = MMC_ERR_NONE; - } - if (rsd2) - *rsd2 = sd2; - return err; -} - /* * Discover the card by requesting its CID. * @@ -878,27 +642,18 @@ static int mmc_send_if_cond(struct mmc_host *host, u32 ocr, int *rsd2) static void mmc_discover_card(struct mmc_host *host) { unsigned int err; - - struct mmc_command cmd; + u32 cid[4]; BUG_ON(host->card); - cmd.opcode = MMC_ALL_SEND_CID; - cmd.arg = 0; - cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err == MMC_ERR_TIMEOUT) { - err = MMC_ERR_NONE; - return; - } + err = mmc_all_send_cid(host, cid); if (err != MMC_ERR_NONE) { printk(KERN_ERR "%s: error requesting CID: %d\n", mmc_hostname(host), err); return; } - host->card = mmc_alloc_card(host, cmd.resp); + host->card = mmc_alloc_card(host, cid); if (IS_ERR(host->card)) { err = PTR_ERR(host->card); host->card = NULL; @@ -908,16 +663,10 @@ static void mmc_discover_card(struct mmc_host *host) if (host->mode == MMC_MODE_SD) { host->card->type = MMC_TYPE_SD; - cmd.opcode = SD_SEND_RELATIVE_ADDR; - cmd.arg = 0; - cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + err = mmc_send_relative_addr(host, &host->card->rca); if (err != MMC_ERR_NONE) mmc_card_set_dead(host->card); else { - host->card->rca = cmd.resp[0] >> 16; - if (!host->ops->get_ro) { printk(KERN_WARNING "%s: host does not " "support reading read-only " @@ -932,11 +681,7 @@ static void mmc_discover_card(struct mmc_host *host) host->card->type = MMC_TYPE_MMC; host->card->rca = 1; - cmd.opcode = MMC_SET_RELATIVE_ADDR; - cmd.arg = host->card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + err = mmc_set_relative_addr(host->card); if (err != MMC_ERR_NONE) mmc_card_set_dead(host->card); } @@ -944,7 +689,6 @@ static void mmc_discover_card(struct mmc_host *host) static void mmc_read_csd(struct mmc_host *host) { - struct mmc_command cmd; int err; if (!host->card) @@ -952,18 +696,12 @@ static void mmc_read_csd(struct mmc_host *host) if (mmc_card_dead(host->card)) return; - cmd.opcode = MMC_SEND_CSD; - cmd.arg = host->card->rca << 16; - cmd.flags = MMC_RSP_R2 | MMC_CMD_AC; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + err = mmc_send_csd(host->card, host->card->raw_csd); if (err != MMC_ERR_NONE) { mmc_card_set_dead(host->card); return; } - memcpy(host->card->raw_csd, cmd.resp, sizeof(host->card->raw_csd)); - mmc_decode_csd(host->card); mmc_decode_cid(host->card); } @@ -971,13 +709,7 @@ static void mmc_read_csd(struct mmc_host *host) static void mmc_process_ext_csd(struct mmc_host *host) { int err; - - struct mmc_request mrq; - struct mmc_command cmd; - struct mmc_data data; - u8 *ext_csd; - struct scatterlist sg; if (!host->card) return; @@ -1000,32 +732,8 @@ static void mmc_process_ext_csd(struct mmc_host *host) return; } - memset(&cmd, 0, sizeof(struct mmc_command)); - - cmd.opcode = MMC_SEND_EXT_CSD; - cmd.arg = 0; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - - memset(&data, 0, sizeof(struct mmc_data)); - - mmc_set_data_timeout(&data, host->card, 0); - - data.blksz = 512; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = &sg; - data.sg_len = 1; - - memset(&mrq, 0, sizeof(struct mmc_request)); - - mrq.cmd = &cmd; - mrq.data = &data; - - sg_init_one(&sg, ext_csd, 512); - - mmc_wait_for_req(host, &mrq); - - if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { + err = mmc_send_ext_csd(host->card, ext_csd); + if (err != MMC_ERR_NONE) { if (host->card->csd.capacity == (4096 * 512)) { printk(KERN_ERR "%s: unable to read EXT_CSD " "on a possible high capacity card. " @@ -1066,14 +774,8 @@ static void mmc_process_ext_csd(struct mmc_host *host) if (host->caps & MMC_CAP_MMC_HIGHSPEED) { /* Activate highspeed support. */ - cmd.opcode = MMC_SWITCH; - cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | - (EXT_CSD_HS_TIMING << 16) | - (1 << 8) | - EXT_CSD_CMD_SET_NORMAL; - cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + err = mmc_switch(host->card, MMC_SWITCH_MODE_WRITE_BYTE, + EXT_CSD_HS_TIMING, 1); if (err != MMC_ERR_NONE) { printk("%s: failed to switch card to mmc v4 " "high-speed mode.\n", @@ -1090,14 +792,9 @@ static void mmc_process_ext_csd(struct mmc_host *host) /* Check for host support for wide-bus modes. */ if (host->caps & MMC_CAP_4_BIT_DATA) { /* Activate 4-bit support. */ - cmd.opcode = MMC_SWITCH; - cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | - (EXT_CSD_BUS_WIDTH << 16) | - (EXT_CSD_BUS_WIDTH_4 << 8) | - EXT_CSD_CMD_SET_NORMAL; - cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; - - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + err = mmc_switch(host->card, MMC_SWITCH_MODE_WRITE_BYTE, + EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4 | + EXT_CSD_CMD_SET_NORMAL); if (err != MMC_ERR_NONE) { printk("%s: failed to switch card to " "mmc v4 4-bit bus mode.\n", @@ -1116,10 +813,6 @@ out: static void mmc_read_scr(struct mmc_host *host) { int err; - struct mmc_request mrq; - struct mmc_command cmd; - struct mmc_data data; - struct scatterlist sg; if (!host->card) return; @@ -1128,61 +821,19 @@ static void mmc_read_scr(struct mmc_host *host) if (!mmc_card_sd(host->card)) return; - memset(&cmd, 0, sizeof(struct mmc_command)); - - cmd.opcode = MMC_APP_CMD; - cmd.arg = host->card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - - err = mmc_wait_for_cmd(host, &cmd, 0); - if ((err != MMC_ERR_NONE) || !(cmd.resp[0] & R1_APP_CMD)) { - mmc_card_set_dead(host->card); - return; - } - - memset(&cmd, 0, sizeof(struct mmc_command)); - - cmd.opcode = SD_APP_SEND_SCR; - cmd.arg = 0; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - - memset(&data, 0, sizeof(struct mmc_data)); - - mmc_set_data_timeout(&data, host->card, 0); - - data.blksz = 1 << 3; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = &sg; - data.sg_len = 1; - - memset(&mrq, 0, sizeof(struct mmc_request)); - - mrq.cmd = &cmd; - mrq.data = &data; - - sg_init_one(&sg, (u8*)host->card->raw_scr, 8); - - mmc_wait_for_req(host, &mrq); - - if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { + err = mmc_app_send_scr(host->card, host->card->raw_scr); + if (err != MMC_ERR_NONE) { mmc_card_set_dead(host->card); return; } - host->card->raw_scr[0] = ntohl(host->card->raw_scr[0]); - host->card->raw_scr[1] = ntohl(host->card->raw_scr[1]); - mmc_decode_scr(host->card); } static void mmc_read_switch_caps(struct mmc_host *host) { - struct mmc_request mrq; - struct mmc_command cmd; - struct mmc_data data; + int err; unsigned char *status; - struct scatterlist sg; if (!(host->caps & MMC_CAP_SD_HIGHSPEED)) return; @@ -1204,32 +855,9 @@ static void mmc_read_switch_caps(struct mmc_host *host) return; } - memset(&cmd, 0, sizeof(struct mmc_command)); - - cmd.opcode = SD_SWITCH; - cmd.arg = 0x00FFFFF1; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - - memset(&data, 0, sizeof(struct mmc_data)); - - mmc_set_data_timeout(&data, host->card, 0); - - data.blksz = 64; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = &sg; - data.sg_len = 1; - - memset(&mrq, 0, sizeof(struct mmc_request)); - - mrq.cmd = &cmd; - mrq.data = &data; - - sg_init_one(&sg, status, 64); - - mmc_wait_for_req(host, &mrq); - - if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE) { + err = mmc_sd_switch(host->card, SD_SWITCH_CHECK, + SD_SWITCH_GRP_ACCESS, SD_SWITCH_ACCESS_HS, status); + if (err != MMC_ERR_NONE) { printk("%s: unable to read switch capabilities, " "performance might suffer.\n", mmc_hostname(host)); @@ -1239,33 +867,9 @@ static void mmc_read_switch_caps(struct mmc_host *host) if (status[13] & 0x02) host->card->sw_caps.hs_max_dtr = 50000000; - memset(&cmd, 0, sizeof(struct mmc_command)); - - cmd.opcode = SD_SWITCH; - cmd.arg = 0x80FFFFF1; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - - memset(&data, 0, sizeof(struct mmc_data)); - - mmc_set_data_timeout(&data, host->card, 0); - - data.blksz = 64; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = &sg; - data.sg_len = 1; - - memset(&mrq, 0, sizeof(struct mmc_request)); - - mrq.cmd = &cmd; - mrq.data = &data; - - sg_init_one(&sg, status, 64); - - mmc_wait_for_req(host, &mrq); - - if (cmd.error != MMC_ERR_NONE || data.error != MMC_ERR_NONE || - (status[16] & 0xF) != 1) { + err = mmc_sd_switch(host->card, SD_SWITCH_SET, + SD_SWITCH_GRP_ACCESS, SD_SWITCH_ACCESS_HS, status); + if (err != MMC_ERR_NONE || (status[16] & 0xF) != 1) { printk(KERN_WARNING "%s: Problem switching card " "into high-speed mode!\n", mmc_hostname(host)); @@ -1314,16 +918,11 @@ static unsigned int mmc_calculate_clock(struct mmc_host *host) */ static void mmc_check_card(struct mmc_card *card) { - struct mmc_command cmd; int err; BUG_ON(!card); - cmd.opcode = MMC_SEND_STATUS; - cmd.arg = card->rca << 16; - cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - - err = mmc_wait_for_cmd(card->host, &cmd, CMD_RETRIES); + err = mmc_send_status(card, NULL); if (err == MMC_ERR_NONE) return; @@ -1338,9 +937,9 @@ static void mmc_setup(struct mmc_host *host) host->mode = MMC_MODE_SD; mmc_power_up(host); - mmc_idle_cards(host); + mmc_go_idle(host); - err = mmc_send_if_cond(host, host->ocr_avail, NULL); + err = mmc_send_if_cond(host, host->ocr_avail); if (err != MMC_ERR_NONE) { return; } @@ -1369,7 +968,7 @@ static void mmc_setup(struct mmc_host *host) * state. We wait 1ms to give cards time to * respond. */ - mmc_idle_cards(host); + mmc_go_idle(host); /* * Send the selected OCR multiple times... until the cards @@ -1377,17 +976,17 @@ static void mmc_setup(struct mmc_host *host) * (My SanDisk card seems to need this.) */ if (host->mode == MMC_MODE_SD) { - int err, sd2; - err = mmc_send_if_cond(host, host->ocr, &sd2); - if (err == MMC_ERR_NONE) { - /* - * If SD_SEND_IF_COND indicates an SD 2.0 - * compliant card and we should set bit 30 - * of the ocr to indicate that we can handle - * block-addressed SDHC cards. - */ - mmc_send_app_op_cond(host, host->ocr | (sd2 << 30), NULL); - } + /* + * If SD_SEND_IF_COND indicates an SD 2.0 + * compliant card and we should set bit 30 + * of the ocr to indicate that we can handle + * block-addressed SDHC cards. + */ + err = mmc_send_if_cond(host, host->ocr); + if (err == MMC_ERR_NONE) + ocr = host->ocr | (1 << 30); + + mmc_send_app_op_cond(host, ocr, NULL); } else { /* The extra bit indicates that we support high capacity */ mmc_send_op_cond(host, host->ocr | (1 << 30), NULL); @@ -1409,6 +1008,24 @@ static void mmc_setup(struct mmc_host *host) mmc_card_set_dead(host->card); } + /* + * The card is in 1 bit mode by default so + * we only need to change if it supports the + * wider version. + */ + if (host->card && !mmc_card_dead(host->card) && + mmc_card_sd(host->card) && + (host->card->scr.bus_widths & SD_SCR_BUS_WIDTH_4) && + (host->card->host->caps & MMC_CAP_4_BIT_DATA)) { + err = mmc_app_set_bus_width(host->card, SD_BUS_WIDTH_4); + if (err != MMC_ERR_NONE) + mmc_card_set_dead(host->card); + else { + host->ios.bus_width = MMC_BUS_WIDTH_4; + mmc_set_ios(host); + } + } + if (host->mode == MMC_MODE_SD) { mmc_read_scr(host); mmc_read_switch_caps(host); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 076cb2f49a0..1c1066342fb 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -2,24 +2,30 @@ * linux/drivers/mmc/core/core.h * * Copyright (C) 2003 Russell King, All Rights Reserved. + * Copyright 2007 Pierre Ossman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#ifndef _MMC_CORE_H -#define _MMC_CORE_H -/* core-internal functions */ -void mmc_init_card(struct mmc_card *card, struct mmc_host *host); -int mmc_register_card(struct mmc_card *card); -void mmc_remove_card(struct mmc_card *card); +#ifndef _MMC_CORE_CORE_H +#define _MMC_CORE_CORE_H -struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev); -int mmc_add_host_sysfs(struct mmc_host *host); -void mmc_remove_host_sysfs(struct mmc_host *host); -void mmc_free_host_sysfs(struct mmc_host *host); +#include + +#define MMC_CMD_RETRIES 3 + +void mmc_set_chip_select(struct mmc_host *host, int mode); + +static inline void mmc_delay(unsigned int ms) +{ + if (ms < 1000 / HZ) { + cond_resched(); + mdelay(ms); + } else { + msleep(ms); + } +} -int mmc_schedule_work(struct work_struct *work); -int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay); -void mmc_flush_scheduled_work(void); #endif + diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c new file mode 100644 index 00000000000..7dd720fa589 --- /dev/null +++ b/drivers/mmc/core/mmc_ops.c @@ -0,0 +1,276 @@ +/* + * linux/drivers/mmc/mmc_ops.h + * + * Copyright 2006-2007 Pierre Ossman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#include +#include +#include + +#include +#include +#include + +#include "core.h" +#include "mmc_ops.h" + +static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card) +{ + int err; + struct mmc_command cmd; + + BUG_ON(!host); + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_SELECT_CARD; + + if (card) { + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + } else { + cmd.arg = 0; + cmd.flags = MMC_RSP_NONE | MMC_CMD_AC; + } + + err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); + if (err != MMC_ERR_NONE) + return err; + + return MMC_ERR_NONE; +} + +int mmc_select_card(struct mmc_card *card) +{ + BUG_ON(!card); + + return _mmc_select_card(card->host, card); +} + +int mmc_deselect_cards(struct mmc_host *host) +{ + return _mmc_select_card(host, NULL); +} + +int mmc_go_idle(struct mmc_host *host) +{ + int err; + struct mmc_command cmd; + + mmc_set_chip_select(host, MMC_CS_HIGH); + + mmc_delay(1); + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_GO_IDLE_STATE; + cmd.arg = 0; + cmd.flags = MMC_RSP_NONE | MMC_CMD_BC; + + err = mmc_wait_for_cmd(host, &cmd, 0); + + mmc_delay(1); + + mmc_set_chip_select(host, MMC_CS_DONTCARE); + + mmc_delay(1); + + return err; +} + +int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) +{ + struct mmc_command cmd; + int i, err = 0; + + BUG_ON(!host); + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_SEND_OP_COND; + cmd.arg = ocr; + cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; + + for (i = 100; i; i--) { + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err != MMC_ERR_NONE) + break; + + if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) + break; + + err = MMC_ERR_TIMEOUT; + + mmc_delay(10); + } + + if (rocr) + *rocr = cmd.resp[0]; + + return err; +} + +int mmc_all_send_cid(struct mmc_host *host, u32 *cid) +{ + int err; + struct mmc_command cmd; + + BUG_ON(!host); + BUG_ON(!cid); + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_ALL_SEND_CID; + cmd.arg = 0; + cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; + + err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); + if (err != MMC_ERR_NONE) + return err; + + memcpy(cid, cmd.resp, sizeof(u32) * 4); + + return MMC_ERR_NONE; +} + +int mmc_set_relative_addr(struct mmc_card *card) +{ + int err; + struct mmc_command cmd; + + BUG_ON(!card); + BUG_ON(!card->host); + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_SET_RELATIVE_ADDR; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); + if (err != MMC_ERR_NONE) + return err; + + return MMC_ERR_NONE; +} + +int mmc_send_csd(struct mmc_card *card, u32 *csd) +{ + int err; + struct mmc_command cmd; + + BUG_ON(!card); + BUG_ON(!card->host); + BUG_ON(!csd); + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_SEND_CSD; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R2 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); + if (err != MMC_ERR_NONE) + return err; + + memcpy(csd, cmd.resp, sizeof(u32) * 4); + + return MMC_ERR_NONE; +} + +int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd) +{ + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + struct scatterlist sg; + + BUG_ON(!card); + BUG_ON(!card->host); + BUG_ON(!ext_csd); + + memset(&mrq, 0, sizeof(struct mmc_request)); + memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&data, 0, sizeof(struct mmc_data)); + + mrq.cmd = &cmd; + mrq.data = &data; + + cmd.opcode = MMC_SEND_EXT_CSD; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = 512; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg_init_one(&sg, ext_csd, 512); + + mmc_set_data_timeout(&data, card, 0); + + mmc_wait_for_req(card->host, &mrq); + + if (cmd.error != MMC_ERR_NONE) + return cmd.error; + if (data.error != MMC_ERR_NONE) + return data.error; + + return MMC_ERR_NONE; +} + +int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value) +{ + int err; + struct mmc_command cmd; + + BUG_ON(!card); + BUG_ON(!card->host); + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_SWITCH; + cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (index << 16) | + (value << 8) | + set; + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); + if (err != MMC_ERR_NONE) + return err; + + return MMC_ERR_NONE; +} + +int mmc_send_status(struct mmc_card *card, u32 *status) +{ + int err; + struct mmc_command cmd; + + BUG_ON(!card); + BUG_ON(!card->host); + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = MMC_SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES); + if (err != MMC_ERR_NONE) + return err; + + if (status) + *status = cmd.resp[0]; + + return MMC_ERR_NONE; +} + diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h new file mode 100644 index 00000000000..7a481e8ca5e --- /dev/null +++ b/drivers/mmc/core/mmc_ops.h @@ -0,0 +1,27 @@ +/* + * linux/drivers/mmc/mmc_ops.h + * + * Copyright 2006-2007 Pierre Ossman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#ifndef _MMC_MMC_OPS_H +#define _MMC_MMC_OPS_H + +int mmc_select_card(struct mmc_card *card); +int mmc_deselect_cards(struct mmc_host *host); +int mmc_go_idle(struct mmc_host *host); +int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr); +int mmc_all_send_cid(struct mmc_host *host, u32 *cid); +int mmc_set_relative_addr(struct mmc_card *card); +int mmc_send_csd(struct mmc_card *card, u32 *csd); +int mmc_send_ext_csd(struct mmc_card *card, u8 *ext_csd); +int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value); +int mmc_send_status(struct mmc_card *card, u32 *status); + +#endif + diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c new file mode 100644 index 00000000000..9697ce58110 --- /dev/null +++ b/drivers/mmc/core/sd_ops.c @@ -0,0 +1,316 @@ +/* + * linux/drivers/mmc/sd_ops.h + * + * Copyright 2006-2007 Pierre Ossman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include "core.h" +#include "sd_ops.h" + +/** + * mmc_wait_for_app_cmd - start an application command and wait for + completion + * @host: MMC host to start command + * @rca: RCA to send MMC_APP_CMD to + * @cmd: MMC command to start + * @retries: maximum number of retries + * + * Sends a MMC_APP_CMD, checks the card response, sends the command + * in the parameter and waits for it to complete. Return any error + * that occurred while the command was executing. Do not attempt to + * parse the response. + */ +int mmc_wait_for_app_cmd(struct mmc_host *host, struct mmc_card *card, + struct mmc_command *cmd, int retries) +{ + struct mmc_request mrq; + + int i, err; + + BUG_ON(!cmd); + BUG_ON(retries < 0); + + err = MMC_ERR_INVALID; + + /* + * We have to resend MMC_APP_CMD for each attempt so + * we cannot use the retries field in mmc_command. + */ + for (i = 0;i <= retries;i++) { + memset(&mrq, 0, sizeof(struct mmc_request)); + + err = mmc_app_cmd(host, card); + if (err != MMC_ERR_NONE) + continue; + + memset(&mrq, 0, sizeof(struct mmc_request)); + + memset(cmd->resp, 0, sizeof(cmd->resp)); + cmd->retries = 0; + + mrq.cmd = cmd; + cmd->data = NULL; + + mmc_wait_for_req(host, &mrq); + + err = cmd->error; + if (cmd->error == MMC_ERR_NONE) + break; + } + + return err; +} + +EXPORT_SYMBOL(mmc_wait_for_app_cmd); + +int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card) +{ + int err; + struct mmc_command cmd; + + BUG_ON(!host); + BUG_ON(card && (card->host != host)); + + cmd.opcode = MMC_APP_CMD; + + if (card) { + cmd.arg = card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + } else { + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_BCR; + } + + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err != MMC_ERR_NONE) + return err; + + /* Check that card supported application commands */ + if (!(cmd.resp[0] & R1_APP_CMD)) + return MMC_ERR_FAILED; + + return MMC_ERR_NONE; +} + +int mmc_app_set_bus_width(struct mmc_card *card, int width) +{ + int err; + struct mmc_command cmd; + + BUG_ON(!card); + BUG_ON(!card->host); + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_APP_SET_BUS_WIDTH; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + switch (width) { + case MMC_BUS_WIDTH_1: + cmd.arg = SD_BUS_WIDTH_1; + break; + case MMC_BUS_WIDTH_4: + cmd.arg = SD_BUS_WIDTH_4; + break; + default: + return MMC_ERR_INVALID; + } + + err = mmc_wait_for_app_cmd(card->host, card, &cmd, MMC_CMD_RETRIES); + if (err != MMC_ERR_NONE) + return err; + + return MMC_ERR_NONE; +} + +int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) +{ + struct mmc_command cmd; + int i, err = 0; + + BUG_ON(!host); + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_APP_OP_COND; + cmd.arg = ocr; + cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; + + for (i = 100; i; i--) { + err = mmc_wait_for_app_cmd(host, NULL, &cmd, MMC_CMD_RETRIES); + if (err != MMC_ERR_NONE) + break; + + if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) + break; + + err = MMC_ERR_TIMEOUT; + + mmc_delay(10); + } + + if (rocr) + *rocr = cmd.resp[0]; + + return err; +} + +int mmc_send_if_cond(struct mmc_host *host, u32 ocr) +{ + struct mmc_command cmd; + int err; + static const u8 test_pattern = 0xAA; + + /* + * To support SD 2.0 cards, we must always invoke SD_SEND_IF_COND + * before SD_APP_OP_COND. This command will harmlessly fail for + * SD 1.0 cards. + */ + cmd.opcode = SD_SEND_IF_COND; + cmd.arg = ((ocr & 0xFF8000) != 0) << 8 | test_pattern; + cmd.flags = MMC_RSP_R7 | MMC_CMD_BCR; + + err = mmc_wait_for_cmd(host, &cmd, 0); + if (err != MMC_ERR_NONE) + return err; + + if ((cmd.resp[0] & 0xFF) != test_pattern) + return MMC_ERR_FAILED; + + return MMC_ERR_NONE; +} + +int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca) +{ + int err; + struct mmc_command cmd; + + BUG_ON(!host); + BUG_ON(!rca); + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = SD_SEND_RELATIVE_ADDR; + cmd.arg = 0; + cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; + + err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES); + if (err != MMC_ERR_NONE) + return err; + + *rca = cmd.resp[0] >> 16; + + return MMC_ERR_NONE; +} + +int mmc_app_send_scr(struct mmc_card *card, u32 *scr) +{ + int err; + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + struct scatterlist sg; + + BUG_ON(!card); + BUG_ON(!card->host); + BUG_ON(!scr); + + err = mmc_app_cmd(card->host, card); + if (err != MMC_ERR_NONE) + return err; + + memset(&mrq, 0, sizeof(struct mmc_request)); + memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&data, 0, sizeof(struct mmc_data)); + + mrq.cmd = &cmd; + mrq.data = &data; + + cmd.opcode = SD_APP_SEND_SCR; + cmd.arg = 0; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = 8; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg_init_one(&sg, scr, 8); + + mmc_set_data_timeout(&data, card, 0); + + mmc_wait_for_req(card->host, &mrq); + + if (cmd.error != MMC_ERR_NONE) + return cmd.error; + if (data.error != MMC_ERR_NONE) + return data.error; + + scr[0] = ntohl(scr[0]); + scr[1] = ntohl(scr[1]); + + return MMC_ERR_NONE; +} + +int mmc_sd_switch(struct mmc_card *card, int mode, int group, + u8 value, u8 *resp) +{ + struct mmc_request mrq; + struct mmc_command cmd; + struct mmc_data data; + struct scatterlist sg; + + BUG_ON(!card); + BUG_ON(!card->host); + + mode = !!mode; + value &= 0xF; + + memset(&mrq, 0, sizeof(struct mmc_request)); + memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&data, 0, sizeof(struct mmc_data)); + + mrq.cmd = &cmd; + mrq.data = &data; + + cmd.opcode = SD_SWITCH; + cmd.arg = mode << 31 | 0x00FFFFFF; + cmd.arg &= ~(0xF << (group * 4)); + cmd.arg |= value << (group * 4); + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + + data.blksz = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; + + sg_init_one(&sg, resp, 64); + + mmc_set_data_timeout(&data, card, 0); + + mmc_wait_for_req(card->host, &mrq); + + if (cmd.error != MMC_ERR_NONE) + return cmd.error; + if (data.error != MMC_ERR_NONE) + return data.error; + + return MMC_ERR_NONE; +} + diff --git a/drivers/mmc/core/sd_ops.h b/drivers/mmc/core/sd_ops.h new file mode 100644 index 00000000000..1240fddba5e --- /dev/null +++ b/drivers/mmc/core/sd_ops.h @@ -0,0 +1,25 @@ +/* + * linux/drivers/mmc/sd_ops.h + * + * Copyright 2006-2007 Pierre Ossman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#ifndef _MMC_SD_OPS_H +#define _MMC_SD_OPS_H + +int mmc_app_cmd(struct mmc_host *host, struct mmc_card *card); +int mmc_app_set_bus_width(struct mmc_card *card, int width); +int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr); +int mmc_send_if_cond(struct mmc_host *host, u32 ocr); +int mmc_send_relative_addr(struct mmc_host *host, unsigned int *rca); +int mmc_app_send_scr(struct mmc_card *card, u32 *scr); +int mmc_sd_switch(struct mmc_card *card, int mode, int group, + u8 value, u8 *resp); + +#endif + diff --git a/drivers/mmc/core/sysfs.c b/drivers/mmc/core/sysfs.c index bf9a5f8beb8..5c9ce02e7e5 100644 --- a/drivers/mmc/core/sysfs.c +++ b/drivers/mmc/core/sysfs.c @@ -18,7 +18,7 @@ #include #include -#include "core.h" +#include "sysfs.h" #define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev) #define to_mmc_driver(d) container_of(d, struct mmc_driver, drv) diff --git a/drivers/mmc/core/sysfs.h b/drivers/mmc/core/sysfs.h new file mode 100644 index 00000000000..80e29b35828 --- /dev/null +++ b/drivers/mmc/core/sysfs.h @@ -0,0 +1,27 @@ +/* + * linux/drivers/mmc/core/sysfs.h + * + * Copyright (C) 2003 Russell King, All Rights Reserved. + * Copyright 2007 Pierre Ossman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _MMC_CORE_SYSFS_H +#define _MMC_CORE_SYSFS_H + +void mmc_init_card(struct mmc_card *card, struct mmc_host *host); +int mmc_register_card(struct mmc_card *card); +void mmc_remove_card(struct mmc_card *card); + +struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev); +int mmc_add_host_sysfs(struct mmc_host *host); +void mmc_remove_host_sysfs(struct mmc_host *host); +void mmc_free_host_sysfs(struct mmc_host *host); + +int mmc_schedule_work(struct work_struct *work); +int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay); +void mmc_flush_scheduled_work(void); + +#endif diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h index d8fd66cf28b..04bbe12fae8 100644 --- a/include/linux/mmc/core.h +++ b/include/linux/mmc/core.h @@ -101,7 +101,7 @@ struct mmc_card; extern int mmc_wait_for_req(struct mmc_host *, struct mmc_request *); extern int mmc_wait_for_cmd(struct mmc_host *, struct mmc_command *, int); -extern int mmc_wait_for_app_cmd(struct mmc_host *, unsigned int, +extern int mmc_wait_for_app_cmd(struct mmc_host *, struct mmc_card *, struct mmc_command *, int); extern void mmc_set_data_timeout(struct mmc_data *, const struct mmc_card *, int); diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h new file mode 100644 index 00000000000..e3ed9b95040 --- /dev/null +++ b/include/linux/mmc/mmc.h @@ -0,0 +1,257 @@ +/* + * Header for MultiMediaCard (MMC) + * + * Copyright 2002 Hewlett-Packard Company + * + * Use consistent with the GNU GPL is permitted, + * provided that this copyright notice is + * preserved in its entirety in all copies and derived works. + * + * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, + * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS + * FITNESS FOR ANY PARTICULAR PURPOSE. + * + * Many thanks to Alessandro Rubini and Jonathan Corbet! + * + * Based strongly on code by: + * + * Author: Yong-iL Joh + * Date : $Date: 2002/06/18 12:37:30 $ + * + * Author: Andrew Christian + * 15 May 2002 + */ + +#ifndef MMC_MMC_H +#define MMC_MMC_H + +/* Standard MMC commands (4.1) type argument response */ + /* class 1 */ +#define MMC_GO_IDLE_STATE 0 /* bc */ +#define MMC_SEND_OP_COND 1 /* bcr [31:0] OCR R3 */ +#define MMC_ALL_SEND_CID 2 /* bcr R2 */ +#define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */ +#define MMC_SET_DSR 4 /* bc [31:16] RCA */ +#define MMC_SWITCH 6 /* ac [31:0] See below R1b */ +#define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */ +#define MMC_SEND_EXT_CSD 8 /* adtc R1 */ +#define MMC_SEND_CSD 9 /* ac [31:16] RCA R2 */ +#define MMC_SEND_CID 10 /* ac [31:16] RCA R2 */ +#define MMC_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */ +#define MMC_STOP_TRANSMISSION 12 /* ac R1b */ +#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */ +#define MMC_GO_INACTIVE_STATE 15 /* ac [31:16] RCA */ + + /* class 2 */ +#define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */ +#define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */ +#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */ + + /* class 3 */ +#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */ + + /* class 4 */ +#define MMC_SET_BLOCK_COUNT 23 /* adtc [31:0] data addr R1 */ +#define MMC_WRITE_BLOCK 24 /* adtc [31:0] data addr R1 */ +#define MMC_WRITE_MULTIPLE_BLOCK 25 /* adtc R1 */ +#define MMC_PROGRAM_CID 26 /* adtc R1 */ +#define MMC_PROGRAM_CSD 27 /* adtc R1 */ + + /* class 6 */ +#define MMC_SET_WRITE_PROT 28 /* ac [31:0] data addr R1b */ +#define MMC_CLR_WRITE_PROT 29 /* ac [31:0] data addr R1b */ +#define MMC_SEND_WRITE_PROT 30 /* adtc [31:0] wpdata addr R1 */ + + /* class 5 */ +#define MMC_ERASE_GROUP_START 35 /* ac [31:0] data addr R1 */ +#define MMC_ERASE_GROUP_END 36 /* ac [31:0] data addr R1 */ +#define MMC_ERASE 38 /* ac R1b */ + + /* class 9 */ +#define MMC_FAST_IO 39 /* ac R4 */ +#define MMC_GO_IRQ_STATE 40 /* bcr R5 */ + + /* class 7 */ +#define MMC_LOCK_UNLOCK 42 /* adtc R1b */ + + /* class 8 */ +#define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */ +#define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */ + +/* + * MMC_SWITCH argument format: + * + * [31:26] Always 0 + * [25:24] Access Mode + * [23:16] Location of target Byte in EXT_CSD + * [15:08] Value Byte + * [07:03] Always 0 + * [02:00] Command Set + */ + +/* + MMC status in R1 + Type + e : error bit + s : status bit + r : detected and set for the actual command response + x : detected and set during command execution. the host must poll + the card by sending status command in order to read these bits. + Clear condition + a : according to the card state + b : always related to the previous command. Reception of + a valid command will clear it (with a delay of one command) + c : clear by read + */ + +#define R1_OUT_OF_RANGE (1 << 31) /* er, c */ +#define R1_ADDRESS_ERROR (1 << 30) /* erx, c */ +#define R1_BLOCK_LEN_ERROR (1 << 29) /* er, c */ +#define R1_ERASE_SEQ_ERROR (1 << 28) /* er, c */ +#define R1_ERASE_PARAM (1 << 27) /* ex, c */ +#define R1_WP_VIOLATION (1 << 26) /* erx, c */ +#define R1_CARD_IS_LOCKED (1 << 25) /* sx, a */ +#define R1_LOCK_UNLOCK_FAILED (1 << 24) /* erx, c */ +#define R1_COM_CRC_ERROR (1 << 23) /* er, b */ +#define R1_ILLEGAL_COMMAND (1 << 22) /* er, b */ +#define R1_CARD_ECC_FAILED (1 << 21) /* ex, c */ +#define R1_CC_ERROR (1 << 20) /* erx, c */ +#define R1_ERROR (1 << 19) /* erx, c */ +#define R1_UNDERRUN (1 << 18) /* ex, c */ +#define R1_OVERRUN (1 << 17) /* ex, c */ +#define R1_CID_CSD_OVERWRITE (1 << 16) /* erx, c, CID/CSD overwrite */ +#define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */ +#define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */ +#define R1_ERASE_RESET (1 << 13) /* sr, c */ +#define R1_STATUS(x) (x & 0xFFFFE000) +#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ +#define R1_READY_FOR_DATA (1 << 8) /* sx, a */ +#define R1_APP_CMD (1 << 5) /* sr, c */ + +/* These are unpacked versions of the actual responses */ + +struct _mmc_csd { + u8 csd_structure; + u8 spec_vers; + u8 taac; + u8 nsac; + u8 tran_speed; + u16 ccc; + u8 read_bl_len; + u8 read_bl_partial; + u8 write_blk_misalign; + u8 read_blk_misalign; + u8 dsr_imp; + u16 c_size; + u8 vdd_r_curr_min; + u8 vdd_r_curr_max; + u8 vdd_w_curr_min; + u8 vdd_w_curr_max; + u8 c_size_mult; + union { + struct { /* MMC system specification version 3.1 */ + u8 erase_grp_size; + u8 erase_grp_mult; + } v31; + struct { /* MMC system specification version 2.2 */ + u8 sector_size; + u8 erase_grp_size; + } v22; + } erase; + u8 wp_grp_size; + u8 wp_grp_enable; + u8 default_ecc; + u8 r2w_factor; + u8 write_bl_len; + u8 write_bl_partial; + u8 file_format_grp; + u8 copy; + u8 perm_write_protect; + u8 tmp_write_protect; + u8 file_format; + u8 ecc; +}; + +/* + * OCR bits are mostly in host.h + */ +#define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */ + +/* + * Card Command Classes (CCC) + */ +#define CCC_BASIC (1<<0) /* (0) Basic protocol functions */ + /* (CMD0,1,2,3,4,7,9,10,12,13,15) */ +#define CCC_STREAM_READ (1<<1) /* (1) Stream read commands */ + /* (CMD11) */ +#define CCC_BLOCK_READ (1<<2) /* (2) Block read commands */ + /* (CMD16,17,18) */ +#define CCC_STREAM_WRITE (1<<3) /* (3) Stream write commands */ + /* (CMD20) */ +#define CCC_BLOCK_WRITE (1<<4) /* (4) Block write commands */ + /* (CMD16,24,25,26,27) */ +#define CCC_ERASE (1<<5) /* (5) Ability to erase blocks */ + /* (CMD32,33,34,35,36,37,38,39) */ +#define CCC_WRITE_PROT (1<<6) /* (6) Able to write protect blocks */ + /* (CMD28,29,30) */ +#define CCC_LOCK_CARD (1<<7) /* (7) Able to lock down card */ + /* (CMD16,CMD42) */ +#define CCC_APP_SPEC (1<<8) /* (8) Application specific */ + /* (CMD55,56,57,ACMD*) */ +#define CCC_IO_MODE (1<<9) /* (9) I/O mode */ + /* (CMD5,39,40,52,53) */ +#define CCC_SWITCH (1<<10) /* (10) High speed switch */ + /* (CMD6,34,35,36,37,50) */ + /* (11) Reserved */ + /* (CMD?) */ + +/* + * CSD field definitions + */ + +#define CSD_STRUCT_VER_1_0 0 /* Valid for system specification 1.0 - 1.2 */ +#define CSD_STRUCT_VER_1_1 1 /* Valid for system specification 1.4 - 2.2 */ +#define CSD_STRUCT_VER_1_2 2 /* Valid for system specification 3.1 - 3.2 - 3.31 - 4.0 - 4.1 */ +#define CSD_STRUCT_EXT_CSD 3 /* Version is coded in CSD_STRUCTURE in EXT_CSD */ + +#define CSD_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.2 */ +#define CSD_SPEC_VER_1 1 /* Implements system specification 1.4 */ +#define CSD_SPEC_VER_2 2 /* Implements system specification 2.0 - 2.2 */ +#define CSD_SPEC_VER_3 3 /* Implements system specification 3.1 - 3.2 - 3.31 */ +#define CSD_SPEC_VER_4 4 /* Implements system specification 4.0 - 4.1 */ + +/* + * EXT_CSD fields + */ + +#define EXT_CSD_BUS_WIDTH 183 /* R/W */ +#define EXT_CSD_HS_TIMING 185 /* R/W */ +#define EXT_CSD_CARD_TYPE 196 /* RO */ +#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ + +/* + * EXT_CSD field definitions + */ + +#define EXT_CSD_CMD_SET_NORMAL (1<<0) +#define EXT_CSD_CMD_SET_SECURE (1<<1) +#define EXT_CSD_CMD_SET_CPSECURE (1<<2) + +#define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */ +#define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */ + +#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */ +#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */ +#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */ + +/* + * MMC_SWITCH access modes + */ + +#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */ +#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits which are 1 in value */ +#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */ +#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ + +#endif /* MMC_MMC_PROTOCOL_H */ + diff --git a/include/linux/mmc/protocol.h b/include/linux/mmc/protocol.h deleted file mode 100644 index 3ca91a6fc23..00000000000 --- a/include/linux/mmc/protocol.h +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Header for MultiMediaCard (MMC) - * - * Copyright 2002 Hewlett-Packard Company - * - * Use consistent with the GNU GPL is permitted, - * provided that this copyright notice is - * preserved in its entirety in all copies and derived works. - * - * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, - * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS - * FITNESS FOR ANY PARTICULAR PURPOSE. - * - * Many thanks to Alessandro Rubini and Jonathan Corbet! - * - * Based strongly on code by: - * - * Author: Yong-iL Joh - * Date : $Date: 2002/06/18 12:37:30 $ - * - * Author: Andrew Christian - * 15 May 2002 - */ - -#ifndef MMC_MMC_PROTOCOL_H -#define MMC_MMC_PROTOCOL_H - -/* Standard MMC commands (4.1) type argument response */ - /* class 1 */ -#define MMC_GO_IDLE_STATE 0 /* bc */ -#define MMC_SEND_OP_COND 1 /* bcr [31:0] OCR R3 */ -#define MMC_ALL_SEND_CID 2 /* bcr R2 */ -#define MMC_SET_RELATIVE_ADDR 3 /* ac [31:16] RCA R1 */ -#define MMC_SET_DSR 4 /* bc [31:16] RCA */ -#define MMC_SWITCH 6 /* ac [31:0] See below R1b */ -#define MMC_SELECT_CARD 7 /* ac [31:16] RCA R1 */ -#define MMC_SEND_EXT_CSD 8 /* adtc R1 */ -#define MMC_SEND_CSD 9 /* ac [31:16] RCA R2 */ -#define MMC_SEND_CID 10 /* ac [31:16] RCA R2 */ -#define MMC_READ_DAT_UNTIL_STOP 11 /* adtc [31:0] dadr R1 */ -#define MMC_STOP_TRANSMISSION 12 /* ac R1b */ -#define MMC_SEND_STATUS 13 /* ac [31:16] RCA R1 */ -#define MMC_GO_INACTIVE_STATE 15 /* ac [31:16] RCA */ - - /* class 2 */ -#define MMC_SET_BLOCKLEN 16 /* ac [31:0] block len R1 */ -#define MMC_READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */ -#define MMC_READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */ - - /* class 3 */ -#define MMC_WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */ - - /* class 4 */ -#define MMC_SET_BLOCK_COUNT 23 /* adtc [31:0] data addr R1 */ -#define MMC_WRITE_BLOCK 24 /* adtc [31:0] data addr R1 */ -#define MMC_WRITE_MULTIPLE_BLOCK 25 /* adtc R1 */ -#define MMC_PROGRAM_CID 26 /* adtc R1 */ -#define MMC_PROGRAM_CSD 27 /* adtc R1 */ - - /* class 6 */ -#define MMC_SET_WRITE_PROT 28 /* ac [31:0] data addr R1b */ -#define MMC_CLR_WRITE_PROT 29 /* ac [31:0] data addr R1b */ -#define MMC_SEND_WRITE_PROT 30 /* adtc [31:0] wpdata addr R1 */ - - /* class 5 */ -#define MMC_ERASE_GROUP_START 35 /* ac [31:0] data addr R1 */ -#define MMC_ERASE_GROUP_END 36 /* ac [31:0] data addr R1 */ -#define MMC_ERASE 38 /* ac R1b */ - - /* class 9 */ -#define MMC_FAST_IO 39 /* ac R4 */ -#define MMC_GO_IRQ_STATE 40 /* bcr R5 */ - - /* class 7 */ -#define MMC_LOCK_UNLOCK 42 /* adtc R1b */ - - /* class 8 */ -#define MMC_APP_CMD 55 /* ac [31:16] RCA R1 */ -#define MMC_GEN_CMD 56 /* adtc [0] RD/WR R1 */ - -/* SD commands type argument response */ - /* class 0 */ -/* This is basically the same command as for MMC with some quirks. */ -#define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */ -#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */ - - /* class 10 */ -#define SD_SWITCH 6 /* adtc [31:0] See below R1 */ - - /* Application commands */ -#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */ -#define SD_APP_SEND_NUM_WR_BLKS 22 /* adtc R1 */ -#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */ -#define SD_APP_SEND_SCR 51 /* adtc R1 */ - -/* - * MMC_SWITCH argument format: - * - * [31:26] Always 0 - * [25:24] Access Mode - * [23:16] Location of target Byte in EXT_CSD - * [15:08] Value Byte - * [07:03] Always 0 - * [02:00] Command Set - */ - -/* - * SD_SWITCH argument format: - * - * [31] Check (0) or switch (1) - * [30:24] Reserved (0) - * [23:20] Function group 6 - * [19:16] Function group 5 - * [15:12] Function group 4 - * [11:8] Function group 3 - * [7:4] Function group 2 - * [3:0] Function group 1 - */ - -/* - * SD_SEND_IF_COND argument format: - * - * [31:12] Reserved (0) - * [11:8] Host Voltage Supply Flags - * [7:0] Check Pattern (0xAA) - */ - -/* - MMC status in R1 - Type - e : error bit - s : status bit - r : detected and set for the actual command response - x : detected and set during command execution. the host must poll - the card by sending status command in order to read these bits. - Clear condition - a : according to the card state - b : always related to the previous command. Reception of - a valid command will clear it (with a delay of one command) - c : clear by read - */ - -#define R1_OUT_OF_RANGE (1 << 31) /* er, c */ -#define R1_ADDRESS_ERROR (1 << 30) /* erx, c */ -#define R1_BLOCK_LEN_ERROR (1 << 29) /* er, c */ -#define R1_ERASE_SEQ_ERROR (1 << 28) /* er, c */ -#define R1_ERASE_PARAM (1 << 27) /* ex, c */ -#define R1_WP_VIOLATION (1 << 26) /* erx, c */ -#define R1_CARD_IS_LOCKED (1 << 25) /* sx, a */ -#define R1_LOCK_UNLOCK_FAILED (1 << 24) /* erx, c */ -#define R1_COM_CRC_ERROR (1 << 23) /* er, b */ -#define R1_ILLEGAL_COMMAND (1 << 22) /* er, b */ -#define R1_CARD_ECC_FAILED (1 << 21) /* ex, c */ -#define R1_CC_ERROR (1 << 20) /* erx, c */ -#define R1_ERROR (1 << 19) /* erx, c */ -#define R1_UNDERRUN (1 << 18) /* ex, c */ -#define R1_OVERRUN (1 << 17) /* ex, c */ -#define R1_CID_CSD_OVERWRITE (1 << 16) /* erx, c, CID/CSD overwrite */ -#define R1_WP_ERASE_SKIP (1 << 15) /* sx, c */ -#define R1_CARD_ECC_DISABLED (1 << 14) /* sx, a */ -#define R1_ERASE_RESET (1 << 13) /* sr, c */ -#define R1_STATUS(x) (x & 0xFFFFE000) -#define R1_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ -#define R1_READY_FOR_DATA (1 << 8) /* sx, a */ -#define R1_APP_CMD (1 << 5) /* sr, c */ - -/* These are unpacked versions of the actual responses */ - -struct _mmc_csd { - u8 csd_structure; - u8 spec_vers; - u8 taac; - u8 nsac; - u8 tran_speed; - u16 ccc; - u8 read_bl_len; - u8 read_bl_partial; - u8 write_blk_misalign; - u8 read_blk_misalign; - u8 dsr_imp; - u16 c_size; - u8 vdd_r_curr_min; - u8 vdd_r_curr_max; - u8 vdd_w_curr_min; - u8 vdd_w_curr_max; - u8 c_size_mult; - union { - struct { /* MMC system specification version 3.1 */ - u8 erase_grp_size; - u8 erase_grp_mult; - } v31; - struct { /* MMC system specification version 2.2 */ - u8 sector_size; - u8 erase_grp_size; - } v22; - } erase; - u8 wp_grp_size; - u8 wp_grp_enable; - u8 default_ecc; - u8 r2w_factor; - u8 write_bl_len; - u8 write_bl_partial; - u8 file_format_grp; - u8 copy; - u8 perm_write_protect; - u8 tmp_write_protect; - u8 file_format; - u8 ecc; -}; - -/* - * OCR bits are mostly in host.h - */ -#define MMC_CARD_BUSY 0x80000000 /* Card Power up status bit */ - -/* - * Card Command Classes (CCC) - */ -#define CCC_BASIC (1<<0) /* (0) Basic protocol functions */ - /* (CMD0,1,2,3,4,7,9,10,12,13,15) */ -#define CCC_STREAM_READ (1<<1) /* (1) Stream read commands */ - /* (CMD11) */ -#define CCC_BLOCK_READ (1<<2) /* (2) Block read commands */ - /* (CMD16,17,18) */ -#define CCC_STREAM_WRITE (1<<3) /* (3) Stream write commands */ - /* (CMD20) */ -#define CCC_BLOCK_WRITE (1<<4) /* (4) Block write commands */ - /* (CMD16,24,25,26,27) */ -#define CCC_ERASE (1<<5) /* (5) Ability to erase blocks */ - /* (CMD32,33,34,35,36,37,38,39) */ -#define CCC_WRITE_PROT (1<<6) /* (6) Able to write protect blocks */ - /* (CMD28,29,30) */ -#define CCC_LOCK_CARD (1<<7) /* (7) Able to lock down card */ - /* (CMD16,CMD42) */ -#define CCC_APP_SPEC (1<<8) /* (8) Application specific */ - /* (CMD55,56,57,ACMD*) */ -#define CCC_IO_MODE (1<<9) /* (9) I/O mode */ - /* (CMD5,39,40,52,53) */ -#define CCC_SWITCH (1<<10) /* (10) High speed switch */ - /* (CMD6,34,35,36,37,50) */ - /* (11) Reserved */ - /* (CMD?) */ - -/* - * CSD field definitions - */ - -#define CSD_STRUCT_VER_1_0 0 /* Valid for system specification 1.0 - 1.2 */ -#define CSD_STRUCT_VER_1_1 1 /* Valid for system specification 1.4 - 2.2 */ -#define CSD_STRUCT_VER_1_2 2 /* Valid for system specification 3.1 - 3.2 - 3.31 - 4.0 - 4.1 */ -#define CSD_STRUCT_EXT_CSD 3 /* Version is coded in CSD_STRUCTURE in EXT_CSD */ - -#define CSD_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.2 */ -#define CSD_SPEC_VER_1 1 /* Implements system specification 1.4 */ -#define CSD_SPEC_VER_2 2 /* Implements system specification 2.0 - 2.2 */ -#define CSD_SPEC_VER_3 3 /* Implements system specification 3.1 - 3.2 - 3.31 */ -#define CSD_SPEC_VER_4 4 /* Implements system specification 4.0 - 4.1 */ - -/* - * EXT_CSD fields - */ - -#define EXT_CSD_BUS_WIDTH 183 /* R/W */ -#define EXT_CSD_HS_TIMING 185 /* R/W */ -#define EXT_CSD_CARD_TYPE 196 /* RO */ -#define EXT_CSD_SEC_CNT 212 /* RO, 4 bytes */ - -/* - * EXT_CSD field definitions - */ - -#define EXT_CSD_CMD_SET_NORMAL (1<<0) -#define EXT_CSD_CMD_SET_SECURE (1<<1) -#define EXT_CSD_CMD_SET_CPSECURE (1<<2) - -#define EXT_CSD_CARD_TYPE_26 (1<<0) /* Card can run at 26MHz */ -#define EXT_CSD_CARD_TYPE_52 (1<<1) /* Card can run at 52MHz */ - -#define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */ -#define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */ -#define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */ - -/* - * MMC_SWITCH access modes - */ - -#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */ -#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits which are 1 in value */ -#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits which are 1 in value */ -#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ - -/* - * SCR field definitions - */ - -#define SCR_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.01 */ -#define SCR_SPEC_VER_1 1 /* Implements system specification 1.10 */ -#define SCR_SPEC_VER_2 2 /* Implements system specification 2.00 */ - -/* - * SD bus widths - */ -#define SD_BUS_WIDTH_1 0 -#define SD_BUS_WIDTH_4 2 - -#endif /* MMC_MMC_PROTOCOL_H */ - diff --git a/include/linux/mmc/sd.h b/include/linux/mmc/sd.h new file mode 100644 index 00000000000..f310062cffb --- /dev/null +++ b/include/linux/mmc/sd.h @@ -0,0 +1,83 @@ +/* + * include/linux/mmc/sd.h + * + * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#ifndef MMC_SD_H +#define MMC_SD_H + +/* SD commands type argument response */ + /* class 0 */ +/* This is basically the same command as for MMC with some quirks. */ +#define SD_SEND_RELATIVE_ADDR 3 /* bcr R6 */ +#define SD_SEND_IF_COND 8 /* bcr [11:0] See below R7 */ + + /* class 10 */ +#define SD_SWITCH 6 /* adtc [31:0] See below R1 */ + + /* Application commands */ +#define SD_APP_SET_BUS_WIDTH 6 /* ac [1:0] bus width R1 */ +#define SD_APP_SEND_NUM_WR_BLKS 22 /* adtc R1 */ +#define SD_APP_OP_COND 41 /* bcr [31:0] OCR R3 */ +#define SD_APP_SEND_SCR 51 /* adtc R1 */ + +/* + * SD_SWITCH argument format: + * + * [31] Check (0) or switch (1) + * [30:24] Reserved (0) + * [23:20] Function group 6 + * [19:16] Function group 5 + * [15:12] Function group 4 + * [11:8] Function group 3 + * [7:4] Function group 2 + * [3:0] Function group 1 + */ + +/* + * SD_SEND_IF_COND argument format: + * + * [31:12] Reserved (0) + * [11:8] Host Voltage Supply Flags + * [7:0] Check Pattern (0xAA) + */ + +/* + * SCR field definitions + */ + +#define SCR_SPEC_VER_0 0 /* Implements system specification 1.0 - 1.01 */ +#define SCR_SPEC_VER_1 1 /* Implements system specification 1.10 */ +#define SCR_SPEC_VER_2 2 /* Implements system specification 2.00 */ + +/* + * SD bus widths + */ +#define SD_BUS_WIDTH_1 0 +#define SD_BUS_WIDTH_4 2 + +/* + * SD_SWITCH mode + */ +#define SD_SWITCH_CHECK 0 +#define SD_SWITCH_SET 1 + +/* + * SD_SWITCH function groups + */ +#define SD_SWITCH_GRP_ACCESS 0 + +/* + * SD_SWITCH access modes + */ +#define SD_SWITCH_ACCESS_DEF 0 +#define SD_SWITCH_ACCESS_HS 1 + +#endif + -- cgit v1.2.3 From b2670b1c6ddd54be4a0f72f853122510ea5ef285 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 1 May 2007 13:35:19 +0200 Subject: wbsd: check for data opcode earlier Move the check for supported data opcodes to the beginning of the request function to avoid wedging the card. Signed-off-by: Pierre Ossman --- drivers/mmc/host/wbsd.c | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c index 9f7518b37c3..867ca6a6929 100644 --- a/drivers/mmc/host/wbsd.c +++ b/drivers/mmc/host/wbsd.c @@ -788,24 +788,7 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq) goto done; } - /* - * Does the request include data? - */ if (cmd->data) { - wbsd_prepare_data(host, cmd->data); - - if (cmd->data->error != MMC_ERR_NONE) - goto done; - } - - wbsd_send_command(host, cmd); - - /* - * If this is a data transfer the request - * will be finished after the data has - * transfered. - */ - if (cmd->data && (cmd->error == MMC_ERR_NONE)) { /* * The hardware is so delightfully stupid that it has a list * of "data" commands. If a command isn't on this list, it'll @@ -837,14 +820,30 @@ static void wbsd_request(struct mmc_host *mmc, struct mmc_request *mrq) "supported by this controller.\n", mmc_hostname(host->mmc), cmd->opcode); #endif - cmd->data->error = MMC_ERR_INVALID; - - if (cmd->data->stop) - wbsd_send_command(host, cmd->data->stop); + cmd->error = MMC_ERR_INVALID; goto done; }; + } + /* + * Does the request include data? + */ + if (cmd->data) { + wbsd_prepare_data(host, cmd->data); + + if (cmd->data->error != MMC_ERR_NONE) + goto done; + } + + wbsd_send_command(host, cmd); + + /* + * If this is a data transfer the request + * will be finished after the data has + * transfered. + */ + if (cmd->data && (cmd->error == MMC_ERR_NONE)) { /* * Dirty fix for hardware bug. */ -- cgit v1.2.3 From 7ea239d9e6d6993469a6a8ca83ff23834dfc3fce Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 31 Dec 2006 00:11:32 +0100 Subject: mmc: add bus handler Delegate protocol handling to "bus handlers". This allows the core to just handle the task of arbitrating the bus. Initialisation and pampering of cards is now done by the different bus handlers. This design also allows MMC and SD (and later SDIO) to be more cleanly separated, allowing easier maintenance. Signed-off-by: Pierre Ossman --- drivers/mmc/core/Makefile | 2 +- drivers/mmc/core/core.c | 817 ++++++++-------------------------------------- drivers/mmc/core/core.h | 37 +++ drivers/mmc/core/mmc.c | 430 ++++++++++++++++++++++++ drivers/mmc/core/sd.c | 431 ++++++++++++++++++++++++ include/linux/mmc/host.h | 7 +- 6 files changed, 1041 insertions(+), 683 deletions(-) create mode 100644 drivers/mmc/core/mmc.c create mode 100644 drivers/mmc/core/sd.c diff --git a/drivers/mmc/core/Makefile b/drivers/mmc/core/Makefile index 5977abf3e41..1075b02ae75 100644 --- a/drivers/mmc/core/Makefile +++ b/drivers/mmc/core/Makefile @@ -7,5 +7,5 @@ ifeq ($(CONFIG_MMC_DEBUG),y) endif obj-$(CONFIG_MMC) += mmc_core.o -mmc_core-y := core.o sysfs.o mmc_ops.o sd_ops.o +mmc_core-y := core.o sysfs.o mmc.o mmc_ops.o sd.o sd_ops.o diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 310be2fe194..75333a2461d 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -32,36 +32,8 @@ #include "mmc_ops.h" #include "sd_ops.h" -#define CMD_RETRIES 3 - -/* - * OCR Bit positions to 10s of Vdd mV. - */ -static const unsigned short mmc_ocr_bit_to_vdd[] = { - 150, 155, 160, 165, 170, 180, 190, 200, - 210, 220, 230, 240, 250, 260, 270, 280, - 290, 300, 310, 320, 330, 340, 350, 360 -}; - -static const unsigned int tran_exp[] = { - 10000, 100000, 1000000, 10000000, - 0, 0, 0, 0 -}; - -static const unsigned char tran_mant[] = { - 0, 10, 12, 13, 15, 20, 25, 30, - 35, 40, 45, 50, 55, 60, 70, 80, -}; - -static const unsigned int tacc_exp[] = { - 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, -}; - -static const unsigned int tacc_mant[] = { - 0, 10, 12, 13, 15, 20, 25, 30, - 35, 40, 45, 50, 55, 60, 70, 80, -}; - +extern int mmc_attach_mmc(struct mmc_host *host, u32 ocr); +extern int mmc_attach_sd(struct mmc_host *host, u32 ocr); /** * mmc_request_done - finish processing an MMC request @@ -303,6 +275,10 @@ void mmc_release_host(struct mmc_host *host) EXPORT_SYMBOL(mmc_release_host); +/* + * Internal function that does the actual ios call to the host driver, + * optionally printing some debug output. + */ static inline void mmc_set_ios(struct mmc_host *host) { struct mmc_ios *ios = &host->ios; @@ -316,17 +292,53 @@ static inline void mmc_set_ios(struct mmc_host *host) host->ops->set_ios(host, ios); } +/* + * Control chip select pin on a host. + */ void mmc_set_chip_select(struct mmc_host *host, int mode) { host->ios.chip_select = mode; mmc_set_ios(host); } +/* + * Sets the host clock to the highest possible frequency that + * is below "hz". + */ +void mmc_set_clock(struct mmc_host *host, unsigned int hz) +{ + WARN_ON(hz < host->f_min); + + if (hz > host->f_max) + hz = host->f_max; + + host->ios.clock = hz; + mmc_set_ios(host); +} + +/* + * Change the bus mode (open drain/push-pull) of a host. + */ +void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode) +{ + host->ios.bus_mode = mode; + mmc_set_ios(host); +} + +/* + * Change data bus width of a host. + */ +void mmc_set_bus_width(struct mmc_host *host, unsigned int width) +{ + host->ios.bus_width = width; + mmc_set_ios(host); +} + /* * Mask off any voltages we don't support and select * the lowest voltage */ -static u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) +u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) { int bit; @@ -347,235 +359,19 @@ static u32 mmc_select_voltage(struct mmc_host *host, u32 ocr) return ocr; } -#define UNSTUFF_BITS(resp,start,size) \ - ({ \ - const int __size = size; \ - const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; \ - const int __off = 3 - ((start) / 32); \ - const int __shft = (start) & 31; \ - u32 __res; \ - \ - __res = resp[__off] >> __shft; \ - if (__size + __shft > 32) \ - __res |= resp[__off-1] << ((32 - __shft) % 32); \ - __res & __mask; \ - }) - -/* - * Given the decoded CSD structure, decode the raw CID to our CID structure. - */ -static void mmc_decode_cid(struct mmc_card *card) -{ - u32 *resp = card->raw_cid; - - memset(&card->cid, 0, sizeof(struct mmc_cid)); - - if (mmc_card_sd(card)) { - /* - * SD doesn't currently have a version field so we will - * have to assume we can parse this. - */ - card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); - card->cid.oemid = UNSTUFF_BITS(resp, 104, 16); - card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); - card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); - card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); - card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); - card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); - card->cid.hwrev = UNSTUFF_BITS(resp, 60, 4); - card->cid.fwrev = UNSTUFF_BITS(resp, 56, 4); - card->cid.serial = UNSTUFF_BITS(resp, 24, 32); - card->cid.year = UNSTUFF_BITS(resp, 12, 8); - card->cid.month = UNSTUFF_BITS(resp, 8, 4); - - card->cid.year += 2000; /* SD cards year offset */ - } else { - /* - * The selection of the format here is based upon published - * specs from sandisk and from what people have reported. - */ - switch (card->csd.mmca_vsn) { - case 0: /* MMC v1.0 - v1.2 */ - case 1: /* MMC v1.4 */ - card->cid.manfid = UNSTUFF_BITS(resp, 104, 24); - card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); - card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); - card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); - card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); - card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); - card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); - card->cid.prod_name[6] = UNSTUFF_BITS(resp, 48, 8); - card->cid.hwrev = UNSTUFF_BITS(resp, 44, 4); - card->cid.fwrev = UNSTUFF_BITS(resp, 40, 4); - card->cid.serial = UNSTUFF_BITS(resp, 16, 24); - card->cid.month = UNSTUFF_BITS(resp, 12, 4); - card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; - break; - - case 2: /* MMC v2.0 - v2.2 */ - case 3: /* MMC v3.1 - v3.3 */ - case 4: /* MMC v4 */ - card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); - card->cid.oemid = UNSTUFF_BITS(resp, 104, 16); - card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); - card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); - card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); - card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); - card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); - card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); - card->cid.serial = UNSTUFF_BITS(resp, 16, 32); - card->cid.month = UNSTUFF_BITS(resp, 12, 4); - card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; - break; - - default: - printk("%s: card has unknown MMCA version %d\n", - mmc_hostname(card->host), card->csd.mmca_vsn); - mmc_card_set_bad(card); - break; - } - } -} - -/* - * Given a 128-bit response, decode to our card CSD structure. - */ -static void mmc_decode_csd(struct mmc_card *card) -{ - struct mmc_csd *csd = &card->csd; - unsigned int e, m, csd_struct; - u32 *resp = card->raw_csd; - - if (mmc_card_sd(card)) { - csd_struct = UNSTUFF_BITS(resp, 126, 2); - - switch (csd_struct) { - case 0: - m = UNSTUFF_BITS(resp, 115, 4); - e = UNSTUFF_BITS(resp, 112, 3); - csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10; - csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100; - - m = UNSTUFF_BITS(resp, 99, 4); - e = UNSTUFF_BITS(resp, 96, 3); - csd->max_dtr = tran_exp[e] * tran_mant[m]; - csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); - - e = UNSTUFF_BITS(resp, 47, 3); - m = UNSTUFF_BITS(resp, 62, 12); - csd->capacity = (1 + m) << (e + 2); - - csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); - csd->read_partial = UNSTUFF_BITS(resp, 79, 1); - csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); - csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); - csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); - csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); - csd->write_partial = UNSTUFF_BITS(resp, 21, 1); - break; - case 1: - /* - * This is a block-addressed SDHC card. Most - * interesting fields are unused and have fixed - * values. To avoid getting tripped by buggy cards, - * we assume those fixed values ourselves. - */ - mmc_card_set_blockaddr(card); - - csd->tacc_ns = 0; /* Unused */ - csd->tacc_clks = 0; /* Unused */ - - m = UNSTUFF_BITS(resp, 99, 4); - e = UNSTUFF_BITS(resp, 96, 3); - csd->max_dtr = tran_exp[e] * tran_mant[m]; - csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); - - m = UNSTUFF_BITS(resp, 48, 22); - csd->capacity = (1 + m) << 10; - - csd->read_blkbits = 9; - csd->read_partial = 0; - csd->write_misalign = 0; - csd->read_misalign = 0; - csd->r2w_factor = 4; /* Unused */ - csd->write_blkbits = 9; - csd->write_partial = 0; - break; - default: - printk("%s: unrecognised CSD structure version %d\n", - mmc_hostname(card->host), csd_struct); - mmc_card_set_bad(card); - return; - } - } else { - /* - * We only understand CSD structure v1.1 and v1.2. - * v1.2 has extra information in bits 15, 11 and 10. - */ - csd_struct = UNSTUFF_BITS(resp, 126, 2); - if (csd_struct != 1 && csd_struct != 2) { - printk("%s: unrecognised CSD structure version %d\n", - mmc_hostname(card->host), csd_struct); - mmc_card_set_bad(card); - return; - } - - csd->mmca_vsn = UNSTUFF_BITS(resp, 122, 4); - m = UNSTUFF_BITS(resp, 115, 4); - e = UNSTUFF_BITS(resp, 112, 3); - csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10; - csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100; - - m = UNSTUFF_BITS(resp, 99, 4); - e = UNSTUFF_BITS(resp, 96, 3); - csd->max_dtr = tran_exp[e] * tran_mant[m]; - csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); - - e = UNSTUFF_BITS(resp, 47, 3); - m = UNSTUFF_BITS(resp, 62, 12); - csd->capacity = (1 + m) << (e + 2); - - csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); - csd->read_partial = UNSTUFF_BITS(resp, 79, 1); - csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); - csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); - csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); - csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); - csd->write_partial = UNSTUFF_BITS(resp, 21, 1); - } -} - /* - * Given a 64-bit response, decode to our card SCR structure. + * Select timing parameters for host. */ -static void mmc_decode_scr(struct mmc_card *card) +void mmc_set_timing(struct mmc_host *host, unsigned int timing) { - struct sd_scr *scr = &card->scr; - unsigned int scr_struct; - u32 resp[4]; - - BUG_ON(!mmc_card_sd(card)); - - resp[3] = card->raw_scr[1]; - resp[2] = card->raw_scr[0]; - - scr_struct = UNSTUFF_BITS(resp, 60, 4); - if (scr_struct != 0) { - printk("%s: unrecognised SCR structure version %d\n", - mmc_hostname(card->host), scr_struct); - mmc_card_set_bad(card); - return; - } - - scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4); - scr->bus_widths = UNSTUFF_BITS(resp, 48, 4); + host->ios.timing = timing; + mmc_set_ios(host); } /* * Allocate a new MMC card */ -static struct mmc_card * -mmc_alloc_card(struct mmc_host *host, u32 *raw_cid) +struct mmc_card *mmc_alloc_card(struct mmc_host *host) { struct mmc_card *card; @@ -584,7 +380,6 @@ mmc_alloc_card(struct mmc_host *host, u32 *raw_cid) return ERR_PTR(-ENOMEM); mmc_init_card(card, host); - memcpy(card->raw_cid, raw_cid, sizeof(card->raw_cid)); return card; } @@ -634,406 +429,66 @@ static void mmc_power_off(struct mmc_host *host) } /* - * Discover the card by requesting its CID. - * - * Create a mmc_card entry for the discovered card, assigning - * it an RCA, and save the raw CID for decoding later. + * Assign a mmc bus handler to a host. Only one bus handler may control a + * host at any given time. */ -static void mmc_discover_card(struct mmc_host *host) +void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops) { - unsigned int err; - u32 cid[4]; - - BUG_ON(host->card); - - err = mmc_all_send_cid(host, cid); - if (err != MMC_ERR_NONE) { - printk(KERN_ERR "%s: error requesting CID: %d\n", - mmc_hostname(host), err); - return; - } - - host->card = mmc_alloc_card(host, cid); - if (IS_ERR(host->card)) { - err = PTR_ERR(host->card); - host->card = NULL; - return; - } - - if (host->mode == MMC_MODE_SD) { - host->card->type = MMC_TYPE_SD; - - err = mmc_send_relative_addr(host, &host->card->rca); - if (err != MMC_ERR_NONE) - mmc_card_set_dead(host->card); - else { - if (!host->ops->get_ro) { - printk(KERN_WARNING "%s: host does not " - "support reading read-only " - "switch. assuming write-enable.\n", - mmc_hostname(host)); - } else { - if (host->ops->get_ro(host)) - mmc_card_set_readonly(host->card); - } - } - } else { - host->card->type = MMC_TYPE_MMC; - host->card->rca = 1; - - err = mmc_set_relative_addr(host->card); - if (err != MMC_ERR_NONE) - mmc_card_set_dead(host->card); - } -} - -static void mmc_read_csd(struct mmc_host *host) -{ - int err; - - if (!host->card) - return; - if (mmc_card_dead(host->card)) - return; - - err = mmc_send_csd(host->card, host->card->raw_csd); - if (err != MMC_ERR_NONE) { - mmc_card_set_dead(host->card); - return; - } - - mmc_decode_csd(host->card); - mmc_decode_cid(host->card); -} - -static void mmc_process_ext_csd(struct mmc_host *host) -{ - int err; - u8 *ext_csd; - - if (!host->card) - return; - if (mmc_card_dead(host->card)) - return; - if (mmc_card_sd(host->card)) - return; - if (host->card->csd.mmca_vsn < CSD_SPEC_VER_4) - return; - - /* - * As the ext_csd is so large and mostly unused, we don't store the - * raw block in mmc_card. - */ - ext_csd = kmalloc(512, GFP_KERNEL); - if (!ext_csd) { - printk("%s: could not allocate a buffer to receive the ext_csd." - "mmc v4 cards will be treated as v3.\n", - mmc_hostname(host)); - return; - } - - err = mmc_send_ext_csd(host->card, ext_csd); - if (err != MMC_ERR_NONE) { - if (host->card->csd.capacity == (4096 * 512)) { - printk(KERN_ERR "%s: unable to read EXT_CSD " - "on a possible high capacity card. " - "Card will be ignored.\n", - mmc_hostname(host)); - mmc_card_set_dead(host->card); - } else { - printk(KERN_WARNING "%s: unable to read " - "EXT_CSD, performance might " - "suffer.\n", - mmc_hostname(host)); - } - goto out; - } - - host->card->ext_csd.sectors = - ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | - ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | - ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | - ext_csd[EXT_CSD_SEC_CNT + 3] << 24; - if (host->card->ext_csd.sectors) - mmc_card_set_blockaddr(host->card); - - switch (ext_csd[EXT_CSD_CARD_TYPE]) { - case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26: - host->card->ext_csd.hs_max_dtr = 52000000; - break; - case EXT_CSD_CARD_TYPE_26: - host->card->ext_csd.hs_max_dtr = 26000000; - break; - default: - /* MMC v4 spec says this cannot happen */ - printk("%s: card is mmc v4 but doesn't support " - "any high-speed modes.\n", - mmc_hostname(host)); - goto out; - } - - if (host->caps & MMC_CAP_MMC_HIGHSPEED) { - /* Activate highspeed support. */ - err = mmc_switch(host->card, MMC_SWITCH_MODE_WRITE_BYTE, - EXT_CSD_HS_TIMING, 1); - if (err != MMC_ERR_NONE) { - printk("%s: failed to switch card to mmc v4 " - "high-speed mode.\n", - mmc_hostname(host)); - goto out; - } - - mmc_card_set_highspeed(host->card); - - host->ios.timing = MMC_TIMING_MMC_HS; - mmc_set_ios(host); - } + unsigned long flags; - /* Check for host support for wide-bus modes. */ - if (host->caps & MMC_CAP_4_BIT_DATA) { - /* Activate 4-bit support. */ - err = mmc_switch(host->card, MMC_SWITCH_MODE_WRITE_BYTE, - EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4 | - EXT_CSD_CMD_SET_NORMAL); - if (err != MMC_ERR_NONE) { - printk("%s: failed to switch card to " - "mmc v4 4-bit bus mode.\n", - mmc_hostname(host)); - goto out; - } + BUG_ON(!host); + BUG_ON(!ops); - host->ios.bus_width = MMC_BUS_WIDTH_4; - mmc_set_ios(host); - } + BUG_ON(!host->claimed); -out: - kfree(ext_csd); -} + spin_lock_irqsave(&host->lock, flags); -static void mmc_read_scr(struct mmc_host *host) -{ - int err; + BUG_ON(host->bus_ops); + BUG_ON(host->bus_refs); - if (!host->card) - return; - if (mmc_card_dead(host->card)) - return; - if (!mmc_card_sd(host->card)) - return; - - err = mmc_app_send_scr(host->card, host->card->raw_scr); - if (err != MMC_ERR_NONE) { - mmc_card_set_dead(host->card); - return; - } + host->bus_ops = ops; + host->bus_refs = 1; + host->bus_dead = 0; - mmc_decode_scr(host->card); + spin_unlock_irqrestore(&host->lock, flags); } -static void mmc_read_switch_caps(struct mmc_host *host) +/* + * Remove the current bus handler from a host. Assumes that there are + * no interesting cards left, so the bus is powered down. + */ +void mmc_detach_bus(struct mmc_host *host) { - int err; - unsigned char *status; - - if (!(host->caps & MMC_CAP_SD_HIGHSPEED)) - return; - - if (!host->card) - return; - if (mmc_card_dead(host->card)) - return; - if (!mmc_card_sd(host->card)) - return; - if (host->card->scr.sda_vsn < SCR_SPEC_VER_1) - return; - - status = kmalloc(64, GFP_KERNEL); - if (!status) { - printk(KERN_WARNING "%s: Unable to allocate buffer for " - "reading switch capabilities.\n", - mmc_hostname(host)); - return; - } - - err = mmc_sd_switch(host->card, SD_SWITCH_CHECK, - SD_SWITCH_GRP_ACCESS, SD_SWITCH_ACCESS_HS, status); - if (err != MMC_ERR_NONE) { - printk("%s: unable to read switch capabilities, " - "performance might suffer.\n", - mmc_hostname(host)); - goto out; - } - - if (status[13] & 0x02) - host->card->sw_caps.hs_max_dtr = 50000000; + unsigned long flags; - err = mmc_sd_switch(host->card, SD_SWITCH_SET, - SD_SWITCH_GRP_ACCESS, SD_SWITCH_ACCESS_HS, status); - if (err != MMC_ERR_NONE || (status[16] & 0xF) != 1) { - printk(KERN_WARNING "%s: Problem switching card " - "into high-speed mode!\n", - mmc_hostname(host)); - goto out; - } + BUG_ON(!host); - mmc_card_set_highspeed(host->card); + BUG_ON(!host->claimed); + BUG_ON(!host->bus_ops); - host->ios.timing = MMC_TIMING_SD_HS; - mmc_set_ios(host); + spin_lock_irqsave(&host->lock, flags); -out: - kfree(status); -} + host->bus_dead = 1; -static unsigned int mmc_calculate_clock(struct mmc_host *host) -{ - unsigned int max_dtr = host->f_max; - - if (host->card && !mmc_card_dead(host->card)) { - if (mmc_card_highspeed(host->card) && mmc_card_sd(host->card)) { - if (max_dtr > host->card->sw_caps.hs_max_dtr) - max_dtr = host->card->sw_caps.hs_max_dtr; - } else if (mmc_card_highspeed(host->card) && !mmc_card_sd(host->card)) { - if (max_dtr > host->card->ext_csd.hs_max_dtr) - max_dtr = host->card->ext_csd.hs_max_dtr; - } else if (max_dtr > host->card->csd.max_dtr) { - max_dtr = host->card->csd.max_dtr; - } - } + spin_unlock_irqrestore(&host->lock, flags); - pr_debug("%s: selected %d.%03dMHz transfer rate\n", - mmc_hostname(host), - max_dtr / 1000000, (max_dtr / 1000) % 1000); + mmc_power_off(host); - return max_dtr; + mmc_bus_put(host); } /* - * Check whether cards we already know about are still present. - * We do this by requesting status, and checking whether a card - * responds. - * - * A request for status does not cause a state change in data - * transfer mode. + * Cleanup when the last reference to the bus operator is dropped. */ -static void mmc_check_card(struct mmc_card *card) +void __mmc_release_bus(struct mmc_host *host) { - int err; - - BUG_ON(!card); + BUG_ON(!host); + BUG_ON(host->bus_refs); + BUG_ON(!host->bus_dead); - err = mmc_send_status(card, NULL); - if (err == MMC_ERR_NONE) - return; - - mmc_card_set_dead(card); + host->bus_ops = NULL; } -static void mmc_setup(struct mmc_host *host) -{ - int err; - u32 ocr; - - host->mode = MMC_MODE_SD; - - mmc_power_up(host); - mmc_go_idle(host); - - err = mmc_send_if_cond(host, host->ocr_avail); - if (err != MMC_ERR_NONE) { - return; - } - err = mmc_send_app_op_cond(host, 0, &ocr); - - /* - * If we fail to detect any SD cards then try - * searching for MMC cards. - */ - if (err != MMC_ERR_NONE) { - host->mode = MMC_MODE_MMC; - - err = mmc_send_op_cond(host, 0, &ocr); - if (err != MMC_ERR_NONE) - return; - } - - host->ocr = mmc_select_voltage(host, ocr); - - if (host->ocr == 0) - return; - - /* - * Since we're changing the OCR value, we seem to - * need to tell some cards to go back to the idle - * state. We wait 1ms to give cards time to - * respond. - */ - mmc_go_idle(host); - - /* - * Send the selected OCR multiple times... until the cards - * all get the idea that they should be ready for CMD2. - * (My SanDisk card seems to need this.) - */ - if (host->mode == MMC_MODE_SD) { - /* - * If SD_SEND_IF_COND indicates an SD 2.0 - * compliant card and we should set bit 30 - * of the ocr to indicate that we can handle - * block-addressed SDHC cards. - */ - err = mmc_send_if_cond(host, host->ocr); - if (err == MMC_ERR_NONE) - ocr = host->ocr | (1 << 30); - - mmc_send_app_op_cond(host, ocr, NULL); - } else { - /* The extra bit indicates that we support high capacity */ - mmc_send_op_cond(host, host->ocr | (1 << 30), NULL); - } - - mmc_discover_card(host); - - /* - * Ok, now switch to push-pull mode. - */ - host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; - mmc_set_ios(host); - - mmc_read_csd(host); - - if (host->card && !mmc_card_dead(host->card)) { - err = mmc_select_card(host->card); - if (err != MMC_ERR_NONE) - mmc_card_set_dead(host->card); - } - - /* - * The card is in 1 bit mode by default so - * we only need to change if it supports the - * wider version. - */ - if (host->card && !mmc_card_dead(host->card) && - mmc_card_sd(host->card) && - (host->card->scr.bus_widths & SD_SCR_BUS_WIDTH_4) && - (host->card->host->caps & MMC_CAP_4_BIT_DATA)) { - err = mmc_app_set_bus_width(host->card, SD_BUS_WIDTH_4); - if (err != MMC_ERR_NONE) - mmc_card_set_dead(host->card); - else { - host->ios.bus_width = MMC_BUS_WIDTH_4; - mmc_set_ios(host); - } - } - - if (host->mode == MMC_MODE_SD) { - mmc_read_scr(host); - mmc_read_switch_caps(host); - } else - mmc_process_ext_csd(host); -} - - /** * mmc_detect_change - process change of state on a MMC socket * @host: host which changed state. @@ -1060,62 +515,49 @@ static void mmc_rescan(struct work_struct *work) { struct mmc_host *host = container_of(work, struct mmc_host, detect.work); + u32 ocr; + int err; - mmc_claim_host(host); - - /* - * Check for removed card and newly inserted ones. We check for - * removed cards first so we can intelligently re-select the VDD. - */ - if (host->card) { - mmc_check_card(host->card); - - mmc_release_host(host); - - if (mmc_card_dead(host->card)) { - mmc_remove_card(host->card); - host->card = NULL; - } - - goto out; - } - - mmc_setup(host); + mmc_bus_get(host); - if (host->card && !mmc_card_dead(host->card)) { + if (host->bus_ops == NULL) { /* - * (Re-)calculate the fastest clock rate which the - * attached cards and the host support. + * Only we can add a new handler, so it's safe to + * release the lock here. */ - host->ios.clock = mmc_calculate_clock(host); - mmc_set_ios(host); - } + mmc_bus_put(host); - mmc_release_host(host); + mmc_claim_host(host); - /* - * If this is a new and good card, register it. - */ - if (host->card && !mmc_card_dead(host->card)) { - if (mmc_register_card(host->card)) - mmc_card_set_dead(host->card); - } + mmc_power_up(host); + mmc_go_idle(host); - /* - * If this card is dead, destroy it. - */ - if (host->card && mmc_card_dead(host->card)) { - mmc_remove_card(host->card); - host->card = NULL; - } + mmc_send_if_cond(host, host->ocr_avail); -out: - /* - * If we discover that there are no cards on the - * bus, turn off the clock and power down. - */ - if (!host->card) - mmc_power_off(host); + err = mmc_send_app_op_cond(host, 0, &ocr); + if (err == MMC_ERR_NONE) { + if (mmc_attach_sd(host, ocr)) + mmc_power_off(host); + } else { + /* + * If we fail to detect any SD cards then try + * searching for MMC cards. + */ + err = mmc_send_op_cond(host, 0, &ocr); + if (err == MMC_ERR_NONE) { + if (mmc_attach_mmc(host, ocr)) + mmc_power_off(host); + } else { + mmc_power_off(host); + mmc_release_host(host); + } + } + } else { + if (host->bus_ops->detect && !host->bus_dead) + host->bus_ops->detect(host); + + mmc_bus_put(host); + } } @@ -1190,10 +632,18 @@ void mmc_remove_host(struct mmc_host *host) mmc_flush_scheduled_work(); - if (host->card) { - mmc_remove_card(host->card); - host->card = NULL; + mmc_bus_get(host); + if (host->bus_ops && !host->bus_dead) { + if (host->bus_ops->remove) + host->bus_ops->remove(host); + + mmc_claim_host(host); + mmc_detach_bus(host); + mmc_release_host(host); } + mmc_bus_put(host); + + BUG_ON(host->card); mmc_power_off(host); mmc_remove_host_sysfs(host); @@ -1225,10 +675,15 @@ int mmc_suspend_host(struct mmc_host *host, pm_message_t state) { mmc_flush_scheduled_work(); - if (host->card) { - mmc_remove_card(host->card); - host->card = NULL; + mmc_bus_get(host); + if (host->bus_ops && !host->bus_dead) { + if (host->bus_ops->remove) + host->bus_ops->remove(host); + mmc_detach_bus(host); } + mmc_bus_put(host); + + BUG_ON(host->card); mmc_power_off(host); diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index 1c1066342fb..fad8edc3809 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -15,7 +15,44 @@ #define MMC_CMD_RETRIES 3 +struct mmc_bus_ops { + void (*remove)(struct mmc_host *); + void (*detect)(struct mmc_host *); +}; + +void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); +void mmc_detach_bus(struct mmc_host *host); + +void __mmc_release_bus(struct mmc_host *host); + +static inline void mmc_bus_get(struct mmc_host *host) +{ + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + host->bus_refs++; + spin_unlock_irqrestore(&host->lock, flags); +} + +static inline void mmc_bus_put(struct mmc_host *host) +{ + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + host->bus_refs--; + if ((host->bus_refs == 0) && host->bus_ops) + __mmc_release_bus(host); + spin_unlock_irqrestore(&host->lock, flags); +} + void mmc_set_chip_select(struct mmc_host *host, int mode); +void mmc_set_clock(struct mmc_host *host, unsigned int hz); +void mmc_set_bus_mode(struct mmc_host *host, unsigned int mode); +void mmc_set_bus_width(struct mmc_host *host, unsigned int width); +u32 mmc_select_voltage(struct mmc_host *host, u32 ocr); +void mmc_set_timing(struct mmc_host *host, unsigned int timing); + +struct mmc_card *mmc_alloc_card(struct mmc_host *host); static inline void mmc_delay(unsigned int ms) { diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c new file mode 100644 index 00000000000..c528017d612 --- /dev/null +++ b/drivers/mmc/core/mmc.c @@ -0,0 +1,430 @@ +/* + * linux/drivers/mmc/mmc.c + * + * Copyright (C) 2003-2004 Russell King, All Rights Reserved. + * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. + * MMCv4 support Copyright (C) 2006 Philip Langdale, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#include +#include +#include + +#include "core.h" +#include "sysfs.h" +#include "mmc_ops.h" + +static const unsigned int tran_exp[] = { + 10000, 100000, 1000000, 10000000, + 0, 0, 0, 0 +}; + +static const unsigned char tran_mant[] = { + 0, 10, 12, 13, 15, 20, 25, 30, + 35, 40, 45, 50, 55, 60, 70, 80, +}; + +static const unsigned int tacc_exp[] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, +}; + +static const unsigned int tacc_mant[] = { + 0, 10, 12, 13, 15, 20, 25, 30, + 35, 40, 45, 50, 55, 60, 70, 80, +}; + +#define UNSTUFF_BITS(resp,start,size) \ + ({ \ + const int __size = size; \ + const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; \ + const int __off = 3 - ((start) / 32); \ + const int __shft = (start) & 31; \ + u32 __res; \ + \ + __res = resp[__off] >> __shft; \ + if (__size + __shft > 32) \ + __res |= resp[__off-1] << ((32 - __shft) % 32); \ + __res & __mask; \ + }) + +/* + * Given the decoded CSD structure, decode the raw CID to our CID structure. + */ +static void mmc_decode_cid(struct mmc_card *card) +{ + u32 *resp = card->raw_cid; + + /* + * The selection of the format here is based upon published + * specs from sandisk and from what people have reported. + */ + switch (card->csd.mmca_vsn) { + case 0: /* MMC v1.0 - v1.2 */ + case 1: /* MMC v1.4 */ + card->cid.manfid = UNSTUFF_BITS(resp, 104, 24); + card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); + card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); + card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); + card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); + card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); + card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); + card->cid.prod_name[6] = UNSTUFF_BITS(resp, 48, 8); + card->cid.hwrev = UNSTUFF_BITS(resp, 44, 4); + card->cid.fwrev = UNSTUFF_BITS(resp, 40, 4); + card->cid.serial = UNSTUFF_BITS(resp, 16, 24); + card->cid.month = UNSTUFF_BITS(resp, 12, 4); + card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; + break; + + case 2: /* MMC v2.0 - v2.2 */ + case 3: /* MMC v3.1 - v3.3 */ + case 4: /* MMC v4 */ + card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); + card->cid.oemid = UNSTUFF_BITS(resp, 104, 16); + card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); + card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); + card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); + card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); + card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); + card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); + card->cid.serial = UNSTUFF_BITS(resp, 16, 32); + card->cid.month = UNSTUFF_BITS(resp, 12, 4); + card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; + break; + + default: + printk("%s: card has unknown MMCA version %d\n", + mmc_hostname(card->host), card->csd.mmca_vsn); + mmc_card_set_bad(card); + break; + } +} + +/* + * Given a 128-bit response, decode to our card CSD structure. + */ +static void mmc_decode_csd(struct mmc_card *card) +{ + struct mmc_csd *csd = &card->csd; + unsigned int e, m, csd_struct; + u32 *resp = card->raw_csd; + + /* + * We only understand CSD structure v1.1 and v1.2. + * v1.2 has extra information in bits 15, 11 and 10. + */ + csd_struct = UNSTUFF_BITS(resp, 126, 2); + if (csd_struct != 1 && csd_struct != 2) { + printk("%s: unrecognised CSD structure version %d\n", + mmc_hostname(card->host), csd_struct); + mmc_card_set_bad(card); + return; + } + + csd->mmca_vsn = UNSTUFF_BITS(resp, 122, 4); + m = UNSTUFF_BITS(resp, 115, 4); + e = UNSTUFF_BITS(resp, 112, 3); + csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10; + csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100; + + m = UNSTUFF_BITS(resp, 99, 4); + e = UNSTUFF_BITS(resp, 96, 3); + csd->max_dtr = tran_exp[e] * tran_mant[m]; + csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); + + e = UNSTUFF_BITS(resp, 47, 3); + m = UNSTUFF_BITS(resp, 62, 12); + csd->capacity = (1 + m) << (e + 2); + + csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); + csd->read_partial = UNSTUFF_BITS(resp, 79, 1); + csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); + csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); + csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); + csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); + csd->write_partial = UNSTUFF_BITS(resp, 21, 1); +} + +/* + * Read and decode extended CSD. Switch to high-speed and wide bus + * if supported. + */ +static int mmc_process_ext_csd(struct mmc_card *card) +{ + int err; + u8 *ext_csd; + + BUG_ON(!card); + + err = MMC_ERR_FAILED; + + if (card->csd.mmca_vsn < CSD_SPEC_VER_4) + return MMC_ERR_NONE; + + /* + * As the ext_csd is so large and mostly unused, we don't store the + * raw block in mmc_card. + */ + ext_csd = kmalloc(512, GFP_KERNEL); + if (!ext_csd) { + printk(KERN_ERR "%s: could not allocate a buffer to " + "receive the ext_csd. mmc v4 cards will be " + "treated as v3.\n", mmc_hostname(card->host)); + return MMC_ERR_FAILED; + } + + err = mmc_send_ext_csd(card, ext_csd); + if (err != MMC_ERR_NONE) { + /* + * High capacity cards should have this "magic" size + * stored in their CSD. + */ + if (card->csd.capacity == (4096 * 512)) { + printk(KERN_ERR "%s: unable to read EXT_CSD " + "on a possible high capacity card. " + "Card will be ignored.\n", + mmc_hostname(card->host)); + } else { + printk(KERN_WARNING "%s: unable to read " + "EXT_CSD, performance might " + "suffer.\n", + mmc_hostname(card->host)); + err = MMC_ERR_NONE; + } + goto out; + } + + card->ext_csd.sectors = + ext_csd[EXT_CSD_SEC_CNT + 0] << 0 | + ext_csd[EXT_CSD_SEC_CNT + 1] << 8 | + ext_csd[EXT_CSD_SEC_CNT + 2] << 16 | + ext_csd[EXT_CSD_SEC_CNT + 3] << 24; + if (card->ext_csd.sectors) + mmc_card_set_blockaddr(card); + + switch (ext_csd[EXT_CSD_CARD_TYPE]) { + case EXT_CSD_CARD_TYPE_52 | EXT_CSD_CARD_TYPE_26: + card->ext_csd.hs_max_dtr = 52000000; + break; + case EXT_CSD_CARD_TYPE_26: + card->ext_csd.hs_max_dtr = 26000000; + break; + default: + /* MMC v4 spec says this cannot happen */ + printk(KERN_WARNING "%s: card is mmc v4 but doesn't " + "support any high-speed modes.\n", + mmc_hostname(card->host)); + goto out; + } + + if (card->host->caps & MMC_CAP_MMC_HIGHSPEED) { + /* Activate highspeed support. */ + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, 1); + if (err != MMC_ERR_NONE) { + printk(KERN_WARNING "%s: failed to switch " + "card to mmc v4 high-speed mode.\n", + mmc_hostname(card->host)); + err = MMC_ERR_NONE; + goto out; + } + + mmc_card_set_highspeed(card); + + mmc_set_timing(card->host, MMC_TIMING_MMC_HS); + } + + /* Check for host support for wide-bus modes. */ + if (card->host->caps & MMC_CAP_4_BIT_DATA) { + /* Activate 4-bit support. */ + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4); + if (err != MMC_ERR_NONE) { + printk(KERN_WARNING "%s: failed to switch " + "card to mmc v4 4-bit bus mode.\n", + mmc_hostname(card->host)); + err = MMC_ERR_NONE; + goto out; + } + + mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); + } + +out: + kfree(ext_csd); + + return err; +} + +/* + * Host is being removed. Free up the current card. + */ +static void mmc_remove(struct mmc_host *host) +{ + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_remove_card(host->card); + host->card = NULL; +} + +/* + * Card detection callback from host. + */ +static void mmc_detect(struct mmc_host *host) +{ + int err; + + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_claim_host(host); + + /* + * Just check if our card has been removed. + */ + err = mmc_send_status(host->card, NULL); + + mmc_release_host(host); + + if (err != MMC_ERR_NONE) { + mmc_remove_card(host->card); + host->card = NULL; + + mmc_claim_host(host); + mmc_detach_bus(host); + mmc_release_host(host); + } +} + +static const struct mmc_bus_ops mmc_ops = { + .remove = mmc_remove, + .detect = mmc_detect, +}; + +/* + * Starting point for MMC card init. + */ +int mmc_attach_mmc(struct mmc_host *host, u32 ocr) +{ + struct mmc_card *card; + int err; + u32 cid[4]; + unsigned int max_dtr; + + BUG_ON(!host); + BUG_ON(!host->claimed); + + mmc_attach_bus(host, &mmc_ops); + + host->ocr = mmc_select_voltage(host, ocr); + + /* + * Can we support the voltage of the card? + */ + if (!host->ocr) + goto err; + + /* + * Since we're changing the OCR value, we seem to + * need to tell some cards to go back to the idle + * state. We wait 1ms to give cards time to + * respond. + */ + mmc_go_idle(host); + + /* The extra bit indicates that we support high capacity */ + mmc_send_op_cond(host, host->ocr | (1 << 30), NULL); + + /* + * Fetch CID from card. + */ + err = mmc_all_send_cid(host, cid); + if (err != MMC_ERR_NONE) + goto err; + + /* + * Allocate card structure. + */ + card = mmc_alloc_card(host); + if (IS_ERR(card)) + goto err; + + card->type = MMC_TYPE_MMC; + card->rca = 1; + memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); + + /* + * Set card RCA. + */ + err = mmc_set_relative_addr(card); + if (err != MMC_ERR_NONE) + goto free_card; + + mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); + + /* + * Fetch CSD from card. + */ + err = mmc_send_csd(card, card->raw_csd); + if (err != MMC_ERR_NONE) + goto free_card; + + mmc_decode_csd(card); + mmc_decode_cid(card); + + /* + * Fetch and process extened CSD. + * This will switch into high-speed and wide bus modes, + * as available. + */ + err = mmc_select_card(card); + if (err != MMC_ERR_NONE) + goto free_card; + + err = mmc_process_ext_csd(card); + if (err != MMC_ERR_NONE) + goto free_card; + + /* + * Compute bus speed. + */ + max_dtr = (unsigned int)-1; + + if (mmc_card_highspeed(card)) { + if (max_dtr > card->ext_csd.hs_max_dtr) + max_dtr = card->ext_csd.hs_max_dtr; + } else if (max_dtr > card->csd.max_dtr) { + max_dtr = card->csd.max_dtr; + } + + mmc_set_clock(host, max_dtr); + + host->card = card; + + mmc_release_host(host); + + err = mmc_register_card(card); + if (err) + goto reclaim_host; + + return 0; + +reclaim_host: + mmc_claim_host(host); +free_card: + mmc_remove_card(card); + host->card = NULL; +err: + mmc_detach_bus(host); + mmc_release_host(host); + + return 0; +} + diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c new file mode 100644 index 00000000000..6c6beb48f3a --- /dev/null +++ b/drivers/mmc/core/sd.c @@ -0,0 +1,431 @@ +/* + * linux/drivers/mmc/sd.c + * + * Copyright (C) 2003-2004 Russell King, All Rights Reserved. + * SD support Copyright (C) 2004 Ian Molton, All Rights Reserved. + * Copyright (C) 2005-2007 Pierre Ossman, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +#include +#include +#include + +#include "core.h" +#include "sysfs.h" +#include "mmc_ops.h" +#include "sd_ops.h" + +#include "core.h" + +static const unsigned int tran_exp[] = { + 10000, 100000, 1000000, 10000000, + 0, 0, 0, 0 +}; + +static const unsigned char tran_mant[] = { + 0, 10, 12, 13, 15, 20, 25, 30, + 35, 40, 45, 50, 55, 60, 70, 80, +}; + +static const unsigned int tacc_exp[] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, +}; + +static const unsigned int tacc_mant[] = { + 0, 10, 12, 13, 15, 20, 25, 30, + 35, 40, 45, 50, 55, 60, 70, 80, +}; + +#define UNSTUFF_BITS(resp,start,size) \ + ({ \ + const int __size = size; \ + const u32 __mask = (__size < 32 ? 1 << __size : 0) - 1; \ + const int __off = 3 - ((start) / 32); \ + const int __shft = (start) & 31; \ + u32 __res; \ + \ + __res = resp[__off] >> __shft; \ + if (__size + __shft > 32) \ + __res |= resp[__off-1] << ((32 - __shft) % 32); \ + __res & __mask; \ + }) + +/* + * Given the decoded CSD structure, decode the raw CID to our CID structure. + */ +static void mmc_decode_cid(struct mmc_card *card) +{ + u32 *resp = card->raw_cid; + + memset(&card->cid, 0, sizeof(struct mmc_cid)); + + /* + * SD doesn't currently have a version field so we will + * have to assume we can parse this. + */ + card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); + card->cid.oemid = UNSTUFF_BITS(resp, 104, 16); + card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); + card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); + card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); + card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); + card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); + card->cid.hwrev = UNSTUFF_BITS(resp, 60, 4); + card->cid.fwrev = UNSTUFF_BITS(resp, 56, 4); + card->cid.serial = UNSTUFF_BITS(resp, 24, 32); + card->cid.year = UNSTUFF_BITS(resp, 12, 8); + card->cid.month = UNSTUFF_BITS(resp, 8, 4); + + card->cid.year += 2000; /* SD cards year offset */ +} + +/* + * Given a 128-bit response, decode to our card CSD structure. + */ +static void mmc_decode_csd(struct mmc_card *card) +{ + struct mmc_csd *csd = &card->csd; + unsigned int e, m, csd_struct; + u32 *resp = card->raw_csd; + + csd_struct = UNSTUFF_BITS(resp, 126, 2); + + switch (csd_struct) { + case 0: + m = UNSTUFF_BITS(resp, 115, 4); + e = UNSTUFF_BITS(resp, 112, 3); + csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10; + csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100; + + m = UNSTUFF_BITS(resp, 99, 4); + e = UNSTUFF_BITS(resp, 96, 3); + csd->max_dtr = tran_exp[e] * tran_mant[m]; + csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); + + e = UNSTUFF_BITS(resp, 47, 3); + m = UNSTUFF_BITS(resp, 62, 12); + csd->capacity = (1 + m) << (e + 2); + + csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); + csd->read_partial = UNSTUFF_BITS(resp, 79, 1); + csd->write_misalign = UNSTUFF_BITS(resp, 78, 1); + csd->read_misalign = UNSTUFF_BITS(resp, 77, 1); + csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); + csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); + csd->write_partial = UNSTUFF_BITS(resp, 21, 1); + break; + case 1: + /* + * This is a block-addressed SDHC card. Most + * interesting fields are unused and have fixed + * values. To avoid getting tripped by buggy cards, + * we assume those fixed values ourselves. + */ + mmc_card_set_blockaddr(card); + + csd->tacc_ns = 0; /* Unused */ + csd->tacc_clks = 0; /* Unused */ + + m = UNSTUFF_BITS(resp, 99, 4); + e = UNSTUFF_BITS(resp, 96, 3); + csd->max_dtr = tran_exp[e] * tran_mant[m]; + csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); + + m = UNSTUFF_BITS(resp, 48, 22); + csd->capacity = (1 + m) << 10; + + csd->read_blkbits = 9; + csd->read_partial = 0; + csd->write_misalign = 0; + csd->read_misalign = 0; + csd->r2w_factor = 4; /* Unused */ + csd->write_blkbits = 9; + csd->write_partial = 0; + break; + default: + printk("%s: unrecognised CSD structure version %d\n", + mmc_hostname(card->host), csd_struct); + mmc_card_set_bad(card); + return; + } +} + +/* + * Given a 64-bit response, decode to our card SCR structure. + */ +static void mmc_decode_scr(struct mmc_card *card) +{ + struct sd_scr *scr = &card->scr; + unsigned int scr_struct; + u32 resp[4]; + + BUG_ON(!mmc_card_sd(card)); + + resp[3] = card->raw_scr[1]; + resp[2] = card->raw_scr[0]; + + scr_struct = UNSTUFF_BITS(resp, 60, 4); + if (scr_struct != 0) { + printk("%s: unrecognised SCR structure version %d\n", + mmc_hostname(card->host), scr_struct); + mmc_card_set_bad(card); + return; + } + + scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4); + scr->bus_widths = UNSTUFF_BITS(resp, 48, 4); +} + +/* + * Test if the card supports high-speed mode and, if so, switch to it. + */ +static int mmc_switch_hs(struct mmc_card *card) +{ + int err; + u8 *status; + + if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED)) + return MMC_ERR_NONE; + + err = MMC_ERR_FAILED; + + status = kmalloc(64, GFP_KERNEL); + if (!status) { + printk("%s: could not allocate a buffer for switch " + "capabilities.\n", + mmc_hostname(card->host)); + return err; + } + + err = mmc_sd_switch(card, 0, 0, 1, status); + if (err != MMC_ERR_NONE) { + /* + * Card not supporting high-speed will ignore the + * command. + */ + if (err == MMC_ERR_TIMEOUT) + err = MMC_ERR_NONE; + goto out; + } + + if (status[13] & 0x02) + card->sw_caps.hs_max_dtr = 50000000; + + err = mmc_sd_switch(card, 1, 0, 1, status); + if (err != MMC_ERR_NONE) + goto out; + + if ((status[16] & 0xF) != 1) { + printk(KERN_WARNING "%s: Problem switching card " + "into high-speed mode!\n", + mmc_hostname(card->host)); + } else { + mmc_card_set_highspeed(card); + mmc_set_timing(card->host, MMC_TIMING_SD_HS); + } + +out: + kfree(status); + + return err; +} + +/* + * Host is being removed. Free up the current card. + */ +static void mmc_sd_remove(struct mmc_host *host) +{ + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_remove_card(host->card); + host->card = NULL; +} + +/* + * Card detection callback from host. + */ +static void mmc_sd_detect(struct mmc_host *host) +{ + int err; + + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_claim_host(host); + + /* + * Just check if our card has been removed. + */ + err = mmc_send_status(host->card, NULL); + + mmc_release_host(host); + + if (err != MMC_ERR_NONE) { + mmc_remove_card(host->card); + host->card = NULL; + + mmc_claim_host(host); + mmc_detach_bus(host); + mmc_release_host(host); + } +} + +static const struct mmc_bus_ops mmc_sd_ops = { + .remove = mmc_sd_remove, + .detect = mmc_sd_detect, +}; + +/* + * Starting point for SD card init. + */ +int mmc_attach_sd(struct mmc_host *host, u32 ocr) +{ + struct mmc_card *card; + int err; + u32 cid[4]; + unsigned int max_dtr; + + BUG_ON(!host); + BUG_ON(!host->claimed); + + mmc_attach_bus(host, &mmc_sd_ops); + + host->ocr = mmc_select_voltage(host, ocr); + + /* + * Can we support the voltage(s) of the card(s)? + */ + if (!host->ocr) + goto err; + + /* + * Since we're changing the OCR value, we seem to + * need to tell some cards to go back to the idle + * state. We wait 1ms to give cards time to + * respond. + */ + mmc_go_idle(host); + + /* + * If SD_SEND_IF_COND indicates an SD 2.0 + * compliant card and we should set bit 30 + * of the ocr to indicate that we can handle + * block-addressed SDHC cards. + */ + err = mmc_send_if_cond(host, host->ocr); + if (err == MMC_ERR_NONE) + ocr = host->ocr | (1 << 30); + + mmc_send_app_op_cond(host, ocr, NULL); + + /* + * Fetch CID from card. + */ + err = mmc_all_send_cid(host, cid); + if (err != MMC_ERR_NONE) + goto err; + + /* + * Allocate card structure. + */ + card = mmc_alloc_card(host); + if (IS_ERR(card)) + goto err; + + card->type = MMC_TYPE_SD; + memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); + + /* + * Set card RCA. + */ + err = mmc_send_relative_addr(host, &card->rca); + if (err != MMC_ERR_NONE) + goto free_card; + + mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); + + /* + * Fetch CSD from card. + */ + err = mmc_send_csd(card, card->raw_csd); + if (err != MMC_ERR_NONE) + goto free_card; + + mmc_decode_csd(card); + mmc_decode_cid(card); + + /* + * Fetch SCR from card. + */ + err = mmc_select_card(card); + if (err != MMC_ERR_NONE) + goto free_card; + + err = mmc_app_send_scr(card, card->raw_scr); + if (err != MMC_ERR_NONE) + goto free_card; + + mmc_decode_scr(card); + + /* + * Check if card can be switched into high-speed mode. + */ + err = mmc_switch_hs(card); + if (err != MMC_ERR_NONE) + goto free_card; + + /* + * Compute bus speed. + */ + max_dtr = (unsigned int)-1; + + if (mmc_card_highspeed(card)) { + if (max_dtr > card->sw_caps.hs_max_dtr) + max_dtr = card->sw_caps.hs_max_dtr; + } else if (max_dtr > card->csd.max_dtr) { + max_dtr = card->csd.max_dtr; + } + + mmc_set_clock(host, max_dtr); + + /* + * Switch to wider bus (if supported). + */ + if ((host->caps && MMC_CAP_4_BIT_DATA) && + (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { + err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4); + if (err != MMC_ERR_NONE) + goto free_card; + + mmc_set_bus_width(host, MMC_BUS_WIDTH_4); + } + + host->card = card; + + mmc_release_host(host); + + err = mmc_register_card(card); + if (err) + goto reclaim_host; + + return 0; + +reclaim_host: + mmc_claim_host(host); +free_card: + mmc_remove_card(card); + host->card = NULL; +err: + mmc_detach_bus(host); + mmc_release_host(host); + + return 0; +} + diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 43bf6a5c398..efae87b5c4e 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -131,6 +131,8 @@ struct mmc_host { unsigned int max_blk_count; /* maximum number of blocks in one req */ /* private data */ + spinlock_t lock; /* lock for claim and bus ops */ + struct mmc_ios ios; /* current io bus settings */ u32 ocr; /* the current OCR setting */ @@ -141,7 +143,6 @@ struct mmc_host { struct mmc_card *card; /* device attached to this host */ wait_queue_head_t wq; - spinlock_t lock; /* claimed lock */ unsigned int claimed:1; /* host exclusively claimed */ struct delayed_work detect; @@ -149,6 +150,10 @@ struct mmc_host { unsigned int removed:1; /* host is being removed */ #endif + const struct mmc_bus_ops *bus_ops; /* current bus driver */ + unsigned int bus_refs; /* reference counter */ + unsigned int bus_dead:1; /* bus has been released */ + unsigned long private[0] ____cacheline_aligned; }; -- cgit v1.2.3 From 4be34c99a2f3aa90fa42e62c0918f07afb8a645b Mon Sep 17 00:00:00 2001 From: Philip Langdale Date: Sun, 11 Mar 2007 17:15:15 -0700 Subject: MMC: Consolidate voltage definitions Consolidate the list of available voltages. Up until now, a separate set of defines has been used for host->vdd than that used for the OCR voltage mask values. Having two sets of defines allows them to get out of sync and the current sets are already inconsistent with one claiming to describe ranges and the other specific voltages. Only the SDHCI driver uses the host->vdd defines and it is easily fixed to use the OCR defines. Signed-off-by: Philip Langdale Signed-off-by: Pierre Ossman --- drivers/mmc/host/sdhci.c | 17 +++++++---------- include/linux/mmc/host.h | 25 +------------------------ 2 files changed, 8 insertions(+), 34 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 579142a7904..a57f6a3d48d 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -658,20 +658,17 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) pwr = SDHCI_POWER_ON; - switch (power) { - case MMC_VDD_170: - case MMC_VDD_180: - case MMC_VDD_190: + switch (1 << power) { + case MMC_VDD_17_18: + case MMC_VDD_18_19: pwr |= SDHCI_POWER_180; break; - case MMC_VDD_290: - case MMC_VDD_300: - case MMC_VDD_310: + case MMC_VDD_29_30: + case MMC_VDD_30_31: pwr |= SDHCI_POWER_300; break; - case MMC_VDD_320: - case MMC_VDD_330: - case MMC_VDD_340: + case MMC_VDD_32_33: + case MMC_VDD_33_34: pwr |= SDHCI_POWER_330; break; default: diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index efae87b5c4e..5a66d8a2bf1 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -16,30 +16,7 @@ struct mmc_ios { unsigned int clock; /* clock rate */ unsigned short vdd; -#define MMC_VDD_150 0 -#define MMC_VDD_155 1 -#define MMC_VDD_160 2 -#define MMC_VDD_165 3 -#define MMC_VDD_170 4 -#define MMC_VDD_180 5 -#define MMC_VDD_190 6 -#define MMC_VDD_200 7 -#define MMC_VDD_210 8 -#define MMC_VDD_220 9 -#define MMC_VDD_230 10 -#define MMC_VDD_240 11 -#define MMC_VDD_250 12 -#define MMC_VDD_260 13 -#define MMC_VDD_270 14 -#define MMC_VDD_280 15 -#define MMC_VDD_290 16 -#define MMC_VDD_300 17 -#define MMC_VDD_310 18 -#define MMC_VDD_320 19 -#define MMC_VDD_330 20 -#define MMC_VDD_340 21 -#define MMC_VDD_350 22 -#define MMC_VDD_360 23 +/* vdd stores the bit number of the selected voltage range from below. */ unsigned char bus_mode; /* command output mode */ -- cgit v1.2.3 From 55556da01284af8c2174b786b3eca8e11301b656 Mon Sep 17 00:00:00 2001 From: Philip Langdale Date: Fri, 16 Mar 2007 19:39:00 -0700 Subject: MMC: Fix handling of low-voltage cards Fix handling of low voltage MMC cards. The latest MMC and SD specs both agree that support for low-voltage operations is indicated by bit 7 in the OCR. The MMC spec states that the low voltage range is 1.65-1.95V while the SD spec leaves the actual voltage range undefined - meaning that there is still no such thing as a low voltage SD card. However, an old Sandisk spec implied that bits 7.0 represented voltages below 2.0V in 1V or 0.5V increments, and the code was accordingly written with that expectation. This confusion meant that host drivers attempting to support the typical low voltage (1.8V) would set the wrong bits in the host OCR mask (usually bits 5 and/or 6) resulting in the the low voltage mode never being used. This change corrects the low voltage range and adds sanity checks on the reserved bits (0-6) and for SD cards that claim to support low-voltage operations. Signed-off-by: Philip Langdale Signed-off-by: Pierre Ossman --- drivers/mmc/core/mmc.c | 11 +++++++++++ drivers/mmc/core/sd.c | 18 ++++++++++++++++++ drivers/mmc/host/sdhci.c | 5 ++--- include/linux/mmc/host.h | 9 +-------- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index c528017d612..c2e120b11be 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -323,6 +323,17 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) mmc_attach_bus(host, &mmc_ops); + /* + * Sanity check the voltages that the card claims to + * support. + */ + if (ocr & 0x7F) { + printk(KERN_WARNING "%s: card claims to support voltages " + "below the defined range. These will be ignored.\n", + mmc_hostname(host)); + ocr &= ~0x7F; + } + host->ocr = mmc_select_voltage(host, ocr); /* diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 6c6beb48f3a..fb18b301502 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -297,6 +297,24 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) mmc_attach_bus(host, &mmc_sd_ops); + /* + * Sanity check the voltages that the card claims to + * support. + */ + if (ocr & 0x7F) { + printk(KERN_WARNING "%s: card claims to support voltages " + "below the defined range. These will be ignored.\n", + mmc_hostname(host)); + ocr &= ~0x7F; + } + + if (ocr & MMC_VDD_165_195) { + printk(KERN_WARNING "%s: SD card claims to support the " + "incompletely defined 'low voltage range'. This " + "will be ignored.\n", mmc_hostname(host)); + ocr &= ~MMC_VDD_165_195; + } + host->ocr = mmc_select_voltage(host, ocr); /* diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index a57f6a3d48d..ff5bf73cdd2 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -659,8 +659,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) pwr = SDHCI_POWER_ON; switch (1 << power) { - case MMC_VDD_17_18: - case MMC_VDD_18_19: + case MMC_VDD_165_195: pwr |= SDHCI_POWER_180; break; case MMC_VDD_29_30: @@ -1280,7 +1279,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) if (caps & SDHCI_CAN_VDD_300) mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31; if (caps & SDHCI_CAN_VDD_180) - mmc->ocr_avail |= MMC_VDD_17_18|MMC_VDD_18_19; + mmc->ocr_avail |= MMC_VDD_165_195; if (mmc->ocr_avail == 0) { printk(KERN_ERR "%s: Hardware doesn't report any " diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 5a66d8a2bf1..b1350dfd3e9 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -65,14 +65,7 @@ struct mmc_host { unsigned int f_max; u32 ocr_avail; -#define MMC_VDD_145_150 0x00000001 /* VDD voltage 1.45 - 1.50 */ -#define MMC_VDD_150_155 0x00000002 /* VDD voltage 1.50 - 1.55 */ -#define MMC_VDD_155_160 0x00000004 /* VDD voltage 1.55 - 1.60 */ -#define MMC_VDD_160_165 0x00000008 /* VDD voltage 1.60 - 1.65 */ -#define MMC_VDD_165_170 0x00000010 /* VDD voltage 1.65 - 1.70 */ -#define MMC_VDD_17_18 0x00000020 /* VDD voltage 1.7 - 1.8 */ -#define MMC_VDD_18_19 0x00000040 /* VDD voltage 1.8 - 1.9 */ -#define MMC_VDD_19_20 0x00000080 /* VDD voltage 1.9 - 2.0 */ +#define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */ #define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */ #define MMC_VDD_21_22 0x00000200 /* VDD voltage 2.1 ~ 2.2 */ #define MMC_VDD_22_23 0x00000400 /* VDD voltage 2.2 ~ 2.3 */ -- cgit v1.2.3 From 1addfcdbe4b23a20f28a097c2469d9f0c21bef23 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 1 May 2007 14:46:08 +0200 Subject: mmc: break apart switch function Break apart the SD switch function into one that reads the capabilities and one that acts on them. Signed-off-by: Pierre Ossman --- drivers/mmc/core/sd.c | 49 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index fb18b301502..ae54e8eb7fe 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -183,16 +183,13 @@ static void mmc_decode_scr(struct mmc_card *card) } /* - * Test if the card supports high-speed mode and, if so, switch to it. + * Fetches and decodes switch information */ -static int mmc_switch_hs(struct mmc_card *card) +static int mmc_read_switch(struct mmc_card *card) { int err; u8 *status; - if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED)) - return MMC_ERR_NONE; - err = MMC_ERR_FAILED; status = kmalloc(64, GFP_KERNEL); @@ -209,14 +206,43 @@ static int mmc_switch_hs(struct mmc_card *card) * Card not supporting high-speed will ignore the * command. */ - if (err == MMC_ERR_TIMEOUT) - err = MMC_ERR_NONE; + err = MMC_ERR_NONE; goto out; } if (status[13] & 0x02) card->sw_caps.hs_max_dtr = 50000000; +out: + kfree(status); + + return err; +} + +/* + * Test if the card supports high-speed mode and, if so, switch to it. + */ +static int mmc_switch_hs(struct mmc_card *card) +{ + int err; + u8 *status; + + if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED)) + return MMC_ERR_NONE; + + if (card->sw_caps.hs_max_dtr == 0) + return MMC_ERR_NONE; + + err = MMC_ERR_FAILED; + + status = kmalloc(64, GFP_KERNEL); + if (!status) { + printk("%s: could not allocate a buffer for switch " + "capabilities.\n", + mmc_hostname(card->host)); + return err; + } + err = mmc_sd_switch(card, 1, 0, 1, status); if (err != MMC_ERR_NONE) goto out; @@ -393,7 +419,14 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) mmc_decode_scr(card); /* - * Check if card can be switched into high-speed mode. + * Fetch switch information from card. + */ + err = mmc_read_switch(card); + if (err != MMC_ERR_NONE) + goto free_card; + + /* + * Attempt to change to high-speed (if supported) */ err = mmc_switch_hs(card); if (err != MMC_ERR_NONE) -- cgit v1.2.3 From 89a73cf52ba2ae4402c53487b71ec4475544f139 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 1 May 2007 15:08:30 +0200 Subject: mmc: separate out reading EXT_CSD Separate the reading and decoding of the EXT_CSD register with the actions taken on it. Signed-off-by: Pierre Ossman --- drivers/mmc/core/mmc.c | 75 +++++++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index c2e120b11be..47449e87013 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -152,10 +152,9 @@ static void mmc_decode_csd(struct mmc_card *card) } /* - * Read and decode extended CSD. Switch to high-speed and wide bus - * if supported. + * Read and decode extended CSD. */ -static int mmc_process_ext_csd(struct mmc_card *card) +static int mmc_read_ext_csd(struct mmc_card *card) { int err; u8 *ext_csd; @@ -223,39 +222,6 @@ static int mmc_process_ext_csd(struct mmc_card *card) goto out; } - if (card->host->caps & MMC_CAP_MMC_HIGHSPEED) { - /* Activate highspeed support. */ - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, 1); - if (err != MMC_ERR_NONE) { - printk(KERN_WARNING "%s: failed to switch " - "card to mmc v4 high-speed mode.\n", - mmc_hostname(card->host)); - err = MMC_ERR_NONE; - goto out; - } - - mmc_card_set_highspeed(card); - - mmc_set_timing(card->host, MMC_TIMING_MMC_HS); - } - - /* Check for host support for wide-bus modes. */ - if (card->host->caps & MMC_CAP_4_BIT_DATA) { - /* Activate 4-bit support. */ - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4); - if (err != MMC_ERR_NONE) { - printk(KERN_WARNING "%s: failed to switch " - "card to mmc v4 4-bit bus mode.\n", - mmc_hostname(card->host)); - err = MMC_ERR_NONE; - goto out; - } - - mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); - } - out: kfree(ext_csd); @@ -391,18 +357,34 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) mmc_decode_cid(card); /* - * Fetch and process extened CSD. - * This will switch into high-speed and wide bus modes, - * as available. + * Select card, as all following commands rely on that. */ err = mmc_select_card(card); if (err != MMC_ERR_NONE) goto free_card; - err = mmc_process_ext_csd(card); + /* + * Fetch and process extened CSD. + */ + err = mmc_read_ext_csd(card); if (err != MMC_ERR_NONE) goto free_card; + /* + * Activate high speed (if supported) + */ + if ((card->ext_csd.hs_max_dtr != 0) && + (host->caps & MMC_CAP_MMC_HIGHSPEED)) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, 1); + if (err != MMC_ERR_NONE) + goto free_card; + + mmc_card_set_highspeed(card); + + mmc_set_timing(card->host, MMC_TIMING_MMC_HS); + } + /* * Compute bus speed. */ @@ -417,6 +399,19 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) mmc_set_clock(host, max_dtr); + /* + * Activate wide bus (if supported). + */ + if ((card->csd.mmca_vsn >= CSD_SPEC_VER_4) && + (host->caps & MMC_CAP_4_BIT_DATA)) { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, EXT_CSD_BUS_WIDTH_4); + if (err != MMC_ERR_NONE) + goto free_card; + + mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); + } + host->card = card; mmc_release_host(host); -- cgit v1.2.3 From 6abaa0c9fec563538f2a28a682af8c89bb9b125c Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 1 May 2007 16:00:02 +0200 Subject: mmc: support unsafe resume of cards Since many have the system root on MMC/SD we must allow some foot shooting when it comes to resume. We cannot detect if a card is removed and reinserted during suspend, so the safe approach would be to assume it was, avoiding potential filesystem corruption. This will of course not work if you cannot release the card before suspend. This commit adds a compile time option that makes the MMC layer assume the card wasn't touched if it is redetected upon resume. Signed-off-by: Pierre Ossman --- drivers/mmc/Kconfig | 2 + drivers/mmc/core/Kconfig | 17 +++ drivers/mmc/core/core.c | 29 ++++- drivers/mmc/core/core.h | 2 + drivers/mmc/core/mmc.c | 289 +++++++++++++++++++++++++++--------------- drivers/mmc/core/sd.c | 318 +++++++++++++++++++++++++++++++---------------- 6 files changed, 444 insertions(+), 213 deletions(-) create mode 100644 drivers/mmc/core/Kconfig diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index c7d64c0187f..6c97491543d 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -19,6 +19,8 @@ config MMC_DEBUG This is an option for use by developers; most people should say N here. This enables MMC core and driver debugging. +source "drivers/mmc/core/Kconfig" + source "drivers/mmc/card/Kconfig" source "drivers/mmc/host/Kconfig" diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig new file mode 100644 index 00000000000..94222b9a15e --- /dev/null +++ b/drivers/mmc/core/Kconfig @@ -0,0 +1,17 @@ +# +# MMC core configuration +# + +config MMC_UNSAFE_RESUME + bool "Allow unsafe resume (DANGEROUS)" + depends on MMC != n + help + If you say Y here, the MMC layer will assume that all cards + stayed in their respective slots during the suspend. The + normal behaviour is to remove them at suspend and + redetecting them at resume. Breaking this assumption will + in most cases result in data corruption. + + This option is usually just for embedded systems which use + a MMC/SD card for rootfs. Most people should say N here. + diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 75333a2461d..72c7cf4a9f9 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -677,14 +677,19 @@ int mmc_suspend_host(struct mmc_host *host, pm_message_t state) mmc_bus_get(host); if (host->bus_ops && !host->bus_dead) { - if (host->bus_ops->remove) - host->bus_ops->remove(host); - mmc_detach_bus(host); + if (host->bus_ops->suspend) + host->bus_ops->suspend(host); + if (!host->bus_ops->resume) { + if (host->bus_ops->remove) + host->bus_ops->remove(host); + + mmc_claim_host(host); + mmc_detach_bus(host); + mmc_release_host(host); + } } mmc_bus_put(host); - BUG_ON(host->card); - mmc_power_off(host); return 0; @@ -698,7 +703,19 @@ EXPORT_SYMBOL(mmc_suspend_host); */ int mmc_resume_host(struct mmc_host *host) { - mmc_rescan(&host->detect.work); + mmc_bus_get(host); + if (host->bus_ops && !host->bus_dead) { + mmc_power_up(host); + BUG_ON(!host->bus_ops->resume); + host->bus_ops->resume(host); + } + mmc_bus_put(host); + + /* + * We add a slight delay here so that resume can progress + * in parallel. + */ + mmc_detect_change(host, 1); return 0; } diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index fad8edc3809..177264d090a 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -18,6 +18,8 @@ struct mmc_bus_ops { void (*remove)(struct mmc_host *); void (*detect)(struct mmc_host *); + void (*suspend)(struct mmc_host *); + void (*resume)(struct mmc_host *); }; void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 47449e87013..619581e4916 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -229,55 +229,13 @@ out: } /* - * Host is being removed. Free up the current card. - */ -static void mmc_remove(struct mmc_host *host) -{ - BUG_ON(!host); - BUG_ON(!host->card); - - mmc_remove_card(host->card); - host->card = NULL; -} - -/* - * Card detection callback from host. - */ -static void mmc_detect(struct mmc_host *host) -{ - int err; - - BUG_ON(!host); - BUG_ON(!host->card); - - mmc_claim_host(host); - - /* - * Just check if our card has been removed. - */ - err = mmc_send_status(host->card, NULL); - - mmc_release_host(host); - - if (err != MMC_ERR_NONE) { - mmc_remove_card(host->card); - host->card = NULL; - - mmc_claim_host(host); - mmc_detach_bus(host); - mmc_release_host(host); - } -} - -static const struct mmc_bus_ops mmc_ops = { - .remove = mmc_remove, - .detect = mmc_detect, -}; - -/* - * Starting point for MMC card init. + * Handle the detection and initialisation of a card. + * + * In the case of a resume, "curcard" will contain the card + * we're trying to reinitialise. */ -int mmc_attach_mmc(struct mmc_host *host, u32 ocr) +static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, + struct mmc_card *oldcard) { struct mmc_card *card; int err; @@ -287,27 +245,6 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) BUG_ON(!host); BUG_ON(!host->claimed); - mmc_attach_bus(host, &mmc_ops); - - /* - * Sanity check the voltages that the card claims to - * support. - */ - if (ocr & 0x7F) { - printk(KERN_WARNING "%s: card claims to support voltages " - "below the defined range. These will be ignored.\n", - mmc_hostname(host)); - ocr &= ~0x7F; - } - - host->ocr = mmc_select_voltage(host, ocr); - - /* - * Can we support the voltage of the card? - */ - if (!host->ocr) - goto err; - /* * Since we're changing the OCR value, we seem to * need to tell some cards to go back to the idle @@ -317,7 +254,9 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) mmc_go_idle(host); /* The extra bit indicates that we support high capacity */ - mmc_send_op_cond(host, host->ocr | (1 << 30), NULL); + err = mmc_send_op_cond(host, ocr | (1 << 30), NULL); + if (err != MMC_ERR_NONE) + goto err; /* * Fetch CID from card. @@ -326,16 +265,23 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) if (err != MMC_ERR_NONE) goto err; - /* - * Allocate card structure. - */ - card = mmc_alloc_card(host); - if (IS_ERR(card)) - goto err; + if (oldcard) { + if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) + goto err; + + card = oldcard; + } else { + /* + * Allocate card structure. + */ + card = mmc_alloc_card(host); + if (IS_ERR(card)) + goto err; - card->type = MMC_TYPE_MMC; - card->rca = 1; - memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); + card->type = MMC_TYPE_MMC; + card->rca = 1; + memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); + } /* * Set card RCA. @@ -346,15 +292,17 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); - /* - * Fetch CSD from card. - */ - err = mmc_send_csd(card, card->raw_csd); - if (err != MMC_ERR_NONE) - goto free_card; + if (!oldcard) { + /* + * Fetch CSD from card. + */ + err = mmc_send_csd(card, card->raw_csd); + if (err != MMC_ERR_NONE) + goto free_card; - mmc_decode_csd(card); - mmc_decode_cid(card); + mmc_decode_csd(card); + mmc_decode_cid(card); + } /* * Select card, as all following commands rely on that. @@ -363,12 +311,14 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) if (err != MMC_ERR_NONE) goto free_card; - /* - * Fetch and process extened CSD. - */ - err = mmc_read_ext_csd(card); - if (err != MMC_ERR_NONE) - goto free_card; + if (!oldcard) { + /* + * Fetch and process extened CSD. + */ + err = mmc_read_ext_csd(card); + if (err != MMC_ERR_NONE) + goto free_card; + } /* * Activate high speed (if supported) @@ -412,11 +362,157 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) mmc_set_bus_width(card->host, MMC_BUS_WIDTH_4); } - host->card = card; + if (!oldcard) + host->card = card; + + return MMC_ERR_NONE; + +free_card: + if (!oldcard) + mmc_remove_card(card); +err: + + return MMC_ERR_FAILED; +} + +/* + * Host is being removed. Free up the current card. + */ +static void mmc_remove(struct mmc_host *host) +{ + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_remove_card(host->card); + host->card = NULL; +} + +/* + * Card detection callback from host. + */ +static void mmc_detect(struct mmc_host *host) +{ + int err; + + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_claim_host(host); + + /* + * Just check if our card has been removed. + */ + err = mmc_send_status(host->card, NULL); + + mmc_release_host(host); + + if (err != MMC_ERR_NONE) { + mmc_remove_card(host->card); + host->card = NULL; + + mmc_claim_host(host); + mmc_detach_bus(host); + mmc_release_host(host); + } +} + +#ifdef CONFIG_MMC_UNSAFE_RESUME + +/* + * Suspend callback from host. + */ +static void mmc_suspend(struct mmc_host *host) +{ + BUG_ON(!host); + BUG_ON(!host->card); + mmc_claim_host(host); + mmc_deselect_cards(host); + host->card->state &= ~MMC_STATE_HIGHSPEED; mmc_release_host(host); +} - err = mmc_register_card(card); +/* + * Resume callback from host. + * + * This function tries to determine if the same card is still present + * and, if so, restore all state to it. + */ +static void mmc_resume(struct mmc_host *host) +{ + int err; + + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_claim_host(host); + + err = mmc_sd_init_card(host, host->ocr, host->card); + if (err != MMC_ERR_NONE) { + mmc_remove_card(host->card); + host->card = NULL; + + mmc_detach_bus(host); + } + + mmc_release_host(host); +} + +#else + +#define mmc_suspend NULL +#define mmc_resume NULL + +#endif + +static const struct mmc_bus_ops mmc_ops = { + .remove = mmc_remove, + .detect = mmc_detect, + .suspend = mmc_suspend, + .resume = mmc_resume, +}; + +/* + * Starting point for MMC card init. + */ +int mmc_attach_mmc(struct mmc_host *host, u32 ocr) +{ + int err; + + BUG_ON(!host); + BUG_ON(!host->claimed); + + mmc_attach_bus(host, &mmc_ops); + + /* + * Sanity check the voltages that the card claims to + * support. + */ + if (ocr & 0x7F) { + printk(KERN_WARNING "%s: card claims to support voltages " + "below the defined range. These will be ignored.\n", + mmc_hostname(host)); + ocr &= ~0x7F; + } + + host->ocr = mmc_select_voltage(host, ocr); + + /* + * Can we support the voltage of the card? + */ + if (!host->ocr) + goto err; + + /* + * Detect and init the card. + */ + err = mmc_sd_init_card(host, host->ocr, NULL); + if (err != MMC_ERR_NONE) + goto err; + + mmc_release_host(host); + + err = mmc_register_card(host->card); if (err) goto reclaim_host; @@ -424,8 +520,7 @@ int mmc_attach_mmc(struct mmc_host *host, u32 ocr) reclaim_host: mmc_claim_host(host); -free_card: - mmc_remove_card(card); + mmc_remove_card(host->card); host->card = NULL; err: mmc_detach_bus(host); diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index ae54e8eb7fe..6597e778f70 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -263,55 +263,13 @@ out: } /* - * Host is being removed. Free up the current card. - */ -static void mmc_sd_remove(struct mmc_host *host) -{ - BUG_ON(!host); - BUG_ON(!host->card); - - mmc_remove_card(host->card); - host->card = NULL; -} - -/* - * Card detection callback from host. - */ -static void mmc_sd_detect(struct mmc_host *host) -{ - int err; - - BUG_ON(!host); - BUG_ON(!host->card); - - mmc_claim_host(host); - - /* - * Just check if our card has been removed. - */ - err = mmc_send_status(host->card, NULL); - - mmc_release_host(host); - - if (err != MMC_ERR_NONE) { - mmc_remove_card(host->card); - host->card = NULL; - - mmc_claim_host(host); - mmc_detach_bus(host); - mmc_release_host(host); - } -} - -static const struct mmc_bus_ops mmc_sd_ops = { - .remove = mmc_sd_remove, - .detect = mmc_sd_detect, -}; - -/* - * Starting point for SD card init. + * Handle the detection and initialisation of a card. + * + * In the case of a resume, "curcard" will contain the card + * we're trying to reinitialise. */ -int mmc_attach_sd(struct mmc_host *host, u32 ocr) +static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, + struct mmc_card *oldcard) { struct mmc_card *card; int err; @@ -321,34 +279,6 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) BUG_ON(!host); BUG_ON(!host->claimed); - mmc_attach_bus(host, &mmc_sd_ops); - - /* - * Sanity check the voltages that the card claims to - * support. - */ - if (ocr & 0x7F) { - printk(KERN_WARNING "%s: card claims to support voltages " - "below the defined range. These will be ignored.\n", - mmc_hostname(host)); - ocr &= ~0x7F; - } - - if (ocr & MMC_VDD_165_195) { - printk(KERN_WARNING "%s: SD card claims to support the " - "incompletely defined 'low voltage range'. This " - "will be ignored.\n", mmc_hostname(host)); - ocr &= ~MMC_VDD_165_195; - } - - host->ocr = mmc_select_voltage(host, ocr); - - /* - * Can we support the voltage(s) of the card(s)? - */ - if (!host->ocr) - goto err; - /* * Since we're changing the OCR value, we seem to * need to tell some cards to go back to the idle @@ -363,11 +293,13 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) * of the ocr to indicate that we can handle * block-addressed SDHC cards. */ - err = mmc_send_if_cond(host, host->ocr); + err = mmc_send_if_cond(host, ocr); if (err == MMC_ERR_NONE) - ocr = host->ocr | (1 << 30); + ocr |= 1 << 30; - mmc_send_app_op_cond(host, ocr, NULL); + err = mmc_send_app_op_cond(host, ocr, NULL); + if (err != MMC_ERR_NONE) + goto err; /* * Fetch CID from card. @@ -376,15 +308,22 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) if (err != MMC_ERR_NONE) goto err; - /* - * Allocate card structure. - */ - card = mmc_alloc_card(host); - if (IS_ERR(card)) - goto err; + if (oldcard) { + if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) + goto err; - card->type = MMC_TYPE_SD; - memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); + card = oldcard; + } else { + /* + * Allocate card structure. + */ + card = mmc_alloc_card(host); + if (IS_ERR(card)) + goto err; + + card->type = MMC_TYPE_SD; + memcpy(card->raw_cid, cid, sizeof(card->raw_cid)); + } /* * Set card RCA. @@ -395,35 +334,42 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) mmc_set_bus_mode(host, MMC_BUSMODE_PUSHPULL); - /* - * Fetch CSD from card. - */ - err = mmc_send_csd(card, card->raw_csd); - if (err != MMC_ERR_NONE) - goto free_card; + if (!oldcard) { + /* + * Fetch CSD from card. + */ + err = mmc_send_csd(card, card->raw_csd); + if (err != MMC_ERR_NONE) + goto free_card; - mmc_decode_csd(card); - mmc_decode_cid(card); + mmc_decode_csd(card); + mmc_decode_cid(card); + } /* - * Fetch SCR from card. + * Select card, as all following commands rely on that. */ err = mmc_select_card(card); if (err != MMC_ERR_NONE) goto free_card; - err = mmc_app_send_scr(card, card->raw_scr); - if (err != MMC_ERR_NONE) - goto free_card; + if (!oldcard) { + /* + * Fetch SCR from card. + */ + err = mmc_app_send_scr(card, card->raw_scr); + if (err != MMC_ERR_NONE) + goto free_card; - mmc_decode_scr(card); + mmc_decode_scr(card); - /* - * Fetch switch information from card. - */ - err = mmc_read_switch(card); - if (err != MMC_ERR_NONE) - goto free_card; + /* + * Fetch switch information from card. + */ + err = mmc_read_switch(card); + if (err != MMC_ERR_NONE) + goto free_card; + } /* * Attempt to change to high-speed (if supported) @@ -458,11 +404,164 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) mmc_set_bus_width(host, MMC_BUS_WIDTH_4); } - host->card = card; + if (!oldcard) + host->card = card; + + return MMC_ERR_NONE; + +free_card: + if (!oldcard) + mmc_remove_card(card); +err: + + return MMC_ERR_FAILED; +} + +/* + * Host is being removed. Free up the current card. + */ +static void mmc_sd_remove(struct mmc_host *host) +{ + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_remove_card(host->card); + host->card = NULL; +} + +/* + * Card detection callback from host. + */ +static void mmc_sd_detect(struct mmc_host *host) +{ + int err; + + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_claim_host(host); + + /* + * Just check if our card has been removed. + */ + err = mmc_send_status(host->card, NULL); mmc_release_host(host); - err = mmc_register_card(card); + if (err != MMC_ERR_NONE) { + mmc_remove_card(host->card); + host->card = NULL; + + mmc_claim_host(host); + mmc_detach_bus(host); + mmc_release_host(host); + } +} + +#ifdef CONFIG_MMC_UNSAFE_RESUME + +/* + * Suspend callback from host. + */ +static void mmc_sd_suspend(struct mmc_host *host) +{ + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_claim_host(host); + mmc_deselect_cards(host); + host->card->state &= ~MMC_STATE_HIGHSPEED; + mmc_release_host(host); +} + +/* + * Resume callback from host. + * + * This function tries to determine if the same card is still present + * and, if so, restore all state to it. + */ +static void mmc_sd_resume(struct mmc_host *host) +{ + int err; + + BUG_ON(!host); + BUG_ON(!host->card); + + mmc_claim_host(host); + + err = mmc_sd_init_card(host, host->ocr, host->card); + if (err != MMC_ERR_NONE) { + mmc_remove_card(host->card); + host->card = NULL; + + mmc_detach_bus(host); + } + + mmc_release_host(host); +} + +#else + +#define mmc_sd_suspend NULL +#define mmc_sd_resume NULL + +#endif + +static const struct mmc_bus_ops mmc_sd_ops = { + .remove = mmc_sd_remove, + .detect = mmc_sd_detect, + .suspend = mmc_sd_suspend, + .resume = mmc_sd_resume, +}; + +/* + * Starting point for SD card init. + */ +int mmc_attach_sd(struct mmc_host *host, u32 ocr) +{ + int err; + + BUG_ON(!host); + BUG_ON(!host->claimed); + + mmc_attach_bus(host, &mmc_sd_ops); + + /* + * Sanity check the voltages that the card claims to + * support. + */ + if (ocr & 0x7F) { + printk(KERN_WARNING "%s: card claims to support voltages " + "below the defined range. These will be ignored.\n", + mmc_hostname(host)); + ocr &= ~0x7F; + } + + if (ocr & MMC_VDD_165_195) { + printk(KERN_WARNING "%s: SD card claims to support the " + "incompletely defined 'low voltage range'. This " + "will be ignored.\n", mmc_hostname(host)); + ocr &= ~MMC_VDD_165_195; + } + + host->ocr = mmc_select_voltage(host, ocr); + + /* + * Can we support the voltage(s) of the card(s)? + */ + if (!host->ocr) + goto err; + + /* + * Detect and init the card. + */ + err = mmc_sd_init_card(host, host->ocr, NULL); + if (err != MMC_ERR_NONE) + goto err; + + mmc_release_host(host); + + err = mmc_register_card(host->card); if (err) goto reclaim_host; @@ -470,8 +569,7 @@ int mmc_attach_sd(struct mmc_host *host, u32 ocr) reclaim_host: mmc_claim_host(host); -free_card: - mmc_remove_card(card); + mmc_remove_card(host->card); host->card = NULL; err: mmc_detach_bus(host); -- cgit v1.2.3 From bd766312618d2ecc85bce663f95faec601447ecb Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 1 May 2007 16:11:57 +0200 Subject: mmc: remove old card states Remove card states that no longer make any sense. Signed-off-by: Pierre Ossman --- drivers/mmc/core/mmc.c | 22 ++++++++++++++-------- drivers/mmc/core/sd.c | 23 +++++++++++++++-------- drivers/mmc/core/sysfs.c | 5 ++--- include/linux/mmc/card.h | 12 +++--------- 4 files changed, 34 insertions(+), 28 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 619581e4916..42cc2867ed7 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -56,7 +56,7 @@ static const unsigned int tacc_mant[] = { /* * Given the decoded CSD structure, decode the raw CID to our CID structure. */ -static void mmc_decode_cid(struct mmc_card *card) +static int mmc_decode_cid(struct mmc_card *card) { u32 *resp = card->raw_cid; @@ -101,15 +101,16 @@ static void mmc_decode_cid(struct mmc_card *card) default: printk("%s: card has unknown MMCA version %d\n", mmc_hostname(card->host), card->csd.mmca_vsn); - mmc_card_set_bad(card); - break; + return -EINVAL; } + + return 0; } /* * Given a 128-bit response, decode to our card CSD structure. */ -static void mmc_decode_csd(struct mmc_card *card) +static int mmc_decode_csd(struct mmc_card *card) { struct mmc_csd *csd = &card->csd; unsigned int e, m, csd_struct; @@ -123,8 +124,7 @@ static void mmc_decode_csd(struct mmc_card *card) if (csd_struct != 1 && csd_struct != 2) { printk("%s: unrecognised CSD structure version %d\n", mmc_hostname(card->host), csd_struct); - mmc_card_set_bad(card); - return; + return -EINVAL; } csd->mmca_vsn = UNSTUFF_BITS(resp, 122, 4); @@ -149,6 +149,8 @@ static void mmc_decode_csd(struct mmc_card *card) csd->r2w_factor = UNSTUFF_BITS(resp, 26, 3); csd->write_blkbits = UNSTUFF_BITS(resp, 22, 4); csd->write_partial = UNSTUFF_BITS(resp, 21, 1); + + return 0; } /* @@ -300,8 +302,12 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, if (err != MMC_ERR_NONE) goto free_card; - mmc_decode_csd(card); - mmc_decode_cid(card); + err = mmc_decode_csd(card); + if (err < 0) + goto free_card; + err = mmc_decode_cid(card); + if (err < 0) + goto free_card; } /* diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 6597e778f70..c1dfd03d559 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -88,7 +88,7 @@ static void mmc_decode_cid(struct mmc_card *card) /* * Given a 128-bit response, decode to our card CSD structure. */ -static void mmc_decode_csd(struct mmc_card *card) +static int mmc_decode_csd(struct mmc_card *card) { struct mmc_csd *csd = &card->csd; unsigned int e, m, csd_struct; @@ -151,15 +151,16 @@ static void mmc_decode_csd(struct mmc_card *card) default: printk("%s: unrecognised CSD structure version %d\n", mmc_hostname(card->host), csd_struct); - mmc_card_set_bad(card); - return; + return -EINVAL; } + + return 0; } /* * Given a 64-bit response, decode to our card SCR structure. */ -static void mmc_decode_scr(struct mmc_card *card) +static int mmc_decode_scr(struct mmc_card *card) { struct sd_scr *scr = &card->scr; unsigned int scr_struct; @@ -174,12 +175,13 @@ static void mmc_decode_scr(struct mmc_card *card) if (scr_struct != 0) { printk("%s: unrecognised SCR structure version %d\n", mmc_hostname(card->host), scr_struct); - mmc_card_set_bad(card); - return; + return -EINVAL; } scr->sda_vsn = UNSTUFF_BITS(resp, 56, 4); scr->bus_widths = UNSTUFF_BITS(resp, 48, 4); + + return 0; } /* @@ -342,7 +344,10 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, if (err != MMC_ERR_NONE) goto free_card; - mmc_decode_csd(card); + err = mmc_decode_csd(card); + if (err < 0) + goto free_card; + mmc_decode_cid(card); } @@ -361,7 +366,9 @@ static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, if (err != MMC_ERR_NONE) goto free_card; - mmc_decode_scr(card); + err = mmc_decode_scr(card); + if (err < 0) + goto free_card; /* * Fetch switch information from card. diff --git a/drivers/mmc/core/sysfs.c b/drivers/mmc/core/sysfs.c index 5c9ce02e7e5..843b1fbba55 100644 --- a/drivers/mmc/core/sysfs.c +++ b/drivers/mmc/core/sysfs.c @@ -72,12 +72,11 @@ static void mmc_release_card(struct device *dev) /* * This currently matches any MMC driver to any MMC card - drivers * themselves make the decision whether to drive this card in their - * probe method. However, we force "bad" cards to fail. + * probe method. */ static int mmc_bus_match(struct device *dev, struct device_driver *drv) { - struct mmc_card *card = dev_to_mmc_card(dev); - return !mmc_card_bad(card); + return 1; } static int diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 800425e0516..badf702fcff 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -69,11 +69,9 @@ struct mmc_card { #define MMC_TYPE_SD 1 /* SD card */ unsigned int state; /* (our) card state */ #define MMC_STATE_PRESENT (1<<0) /* present in sysfs */ -#define MMC_STATE_DEAD (1<<1) /* device no longer in stack */ -#define MMC_STATE_BAD (1<<2) /* unrecognised device */ -#define MMC_STATE_READONLY (1<<3) /* card is read-only */ -#define MMC_STATE_HIGHSPEED (1<<4) /* card is in high speed mode */ -#define MMC_STATE_BLOCKADDR (1<<5) /* card uses block-addressing */ +#define MMC_STATE_READONLY (1<<1) /* card is read-only */ +#define MMC_STATE_HIGHSPEED (1<<2) /* card is in high speed mode */ +#define MMC_STATE_BLOCKADDR (1<<3) /* card uses block-addressing */ u32 raw_cid[4]; /* raw card CID */ u32 raw_csd[4]; /* raw card CSD */ u32 raw_scr[2]; /* raw card SCR */ @@ -88,15 +86,11 @@ struct mmc_card { #define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD) #define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT) -#define mmc_card_dead(c) ((c)->state & MMC_STATE_DEAD) -#define mmc_card_bad(c) ((c)->state & MMC_STATE_BAD) #define mmc_card_readonly(c) ((c)->state & MMC_STATE_READONLY) #define mmc_card_highspeed(c) ((c)->state & MMC_STATE_HIGHSPEED) #define mmc_card_blockaddr(c) ((c)->state & MMC_STATE_BLOCKADDR) #define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) -#define mmc_card_set_dead(c) ((c)->state |= MMC_STATE_DEAD) -#define mmc_card_set_bad(c) ((c)->state |= MMC_STATE_BAD) #define mmc_card_set_readonly(c) ((c)->state |= MMC_STATE_READONLY) #define mmc_card_set_highspeed(c) ((c)->state |= MMC_STATE_HIGHSPEED) #define mmc_card_set_blockaddr(c) ((c)->state |= MMC_STATE_BLOCKADDR) -- cgit v1.2.3 From d97956f86bec90ab131b9f1af60c0e686198d45e Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 1 May 2007 16:14:29 +0200 Subject: mmc: make tifm_sd_set_dma_data() static This patch makes the needlessly global tifm_sd_set_dma_data() static. Signed-off-by: Adrian Bunk Signed-off-by: Pierre Ossman --- drivers/mmc/host/tifm_sd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/tifm_sd.c b/drivers/mmc/host/tifm_sd.c index b0d77d29841..7511f961c67 100644 --- a/drivers/mmc/host/tifm_sd.c +++ b/drivers/mmc/host/tifm_sd.c @@ -259,7 +259,7 @@ static void tifm_sd_bounce_block(struct tifm_sd *host, struct mmc_data *r_data) } } -int tifm_sd_set_dma_data(struct tifm_sd *host, struct mmc_data *r_data) +static int tifm_sd_set_dma_data(struct tifm_sd *host, struct mmc_data *r_data) { struct tifm_dev *sock = host->dev; unsigned int t_size = TIFM_DMA_TSIZE * r_data->blksz; -- cgit v1.2.3 From 3647afcec11f2fcfc6269e513ff97fec1374d1b8 Mon Sep 17 00:00:00 2001 From: Arnaud Patard Date: Tue, 1 May 2007 16:18:36 +0200 Subject: mmc-omap: add missing '\n' This patch add a missing '\n' at the end of the 'cover is open' string in mmc_omap_switch_handler(). Signed-off-by: Arnaud Patard Signed-off-by: Tony Lindgren Signed-off-by: Pierre Ossman --- drivers/mmc/host/omap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index e851384e51f..1b232c6dd01 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -604,7 +604,7 @@ static void mmc_omap_switch_handler(struct work_struct *work) } if (mmc_omap_cover_is_open(host)) { if (!complained) { - dev_info(mmc_dev(host->mmc), "cover is open"); + dev_info(mmc_dev(host->mmc), "cover is open\n"); complained = 1; } if (mmc_omap_enable_poll) -- cgit v1.2.3 From 46a6730e3ff9add5089ddd007f998b97fb4e8571 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 1 May 2007 16:34:16 +0200 Subject: mmc-omap: Fix omap to use MMC_POWER_ON As discussed earlier on LKML: http://lkml.org/lkml/2006/5/4/44 Signed-off-by: Tony Lindgren Signed-off-by: Pierre Ossman --- drivers/mmc/host/omap.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index 1b232c6dd01..fc044a5ed64 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -969,8 +969,10 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) mmc_omap_power(host, 0); break; case MMC_POWER_UP: - case MMC_POWER_ON: + /* Cannot touch dsor yet, just power up MMC */ mmc_omap_power(host, 1); + return; + case MMC_POWER_ON: dsor |= 1 << 11; break; } @@ -986,7 +988,7 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) * Writing to the CON register twice seems to do the trick. */ for (i = 0; i < 2; i++) OMAP_MMC_WRITE(host, CON, dsor); - if (ios->power_mode == MMC_POWER_UP) { + if (ios->power_mode == MMC_POWER_ON) { /* Send clock cycles, poll completion */ OMAP_MMC_WRITE(host, IE, 0); OMAP_MMC_WRITE(host, STAT, 0xffff); -- cgit v1.2.3 From d3af5abe9a809becbe4b413144b607844560d445 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 1 May 2007 16:36:00 +0200 Subject: mmc-omap: Clean up omap set_ios and make MMC_POWER_ON work Move divisor calculation into a separate function and re-arrange the init order to make MMC_POWER_ON work. Signed-off-by: Tony Lindgren Signed-off-by: Pierre Ossman --- drivers/mmc/host/omap.c | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index fc044a5ed64..1914e65d4db 100644 --- a/drivers/mmc/host/omap.c +++ b/drivers/mmc/host/omap.c @@ -936,33 +936,41 @@ static void mmc_omap_power(struct mmc_omap_host *host, int on) } } -static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +static int mmc_omap_calc_divisor(struct mmc_host *mmc, struct mmc_ios *ios) { struct mmc_omap_host *host = mmc_priv(mmc); + int func_clk_rate = clk_get_rate(host->fclk); int dsor; - int realclock, i; - - realclock = ios->clock; if (ios->clock == 0) - dsor = 0; - else { - int func_clk_rate = clk_get_rate(host->fclk); - - dsor = func_clk_rate / realclock; - if (dsor < 1) - dsor = 1; + return 0; - if (func_clk_rate / dsor > realclock) - dsor++; + dsor = func_clk_rate / ios->clock; + if (dsor < 1) + dsor = 1; - if (dsor > 250) - dsor = 250; + if (func_clk_rate / dsor > ios->clock) dsor++; - if (ios->bus_width == MMC_BUS_WIDTH_4) - dsor |= 1 << 15; - } + if (dsor > 250) + dsor = 250; + dsor++; + + if (ios->bus_width == MMC_BUS_WIDTH_4) + dsor |= 1 << 15; + + return dsor; +} + +static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct mmc_omap_host *host = mmc_priv(mmc); + int dsor; + int i; + + dsor = mmc_omap_calc_divisor(mmc, ios); + host->bus_mode = ios->bus_mode; + host->hw_bus_mode = host->bus_mode; switch (ios->power_mode) { case MMC_POWER_OFF: @@ -977,9 +985,6 @@ static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) break; } - host->bus_mode = ios->bus_mode; - host->hw_bus_mode = host->bus_mode; - clk_enable(host->fclk); /* On insanely high arm_per frequencies something sometimes -- cgit v1.2.3