From 14d836e7499c53a1f6a65086c3d11600e871a971 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Fri, 13 Apr 2007 19:04:38 +0200 Subject: [PATCH 01/46] 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 86439a0bb271..95b0da6abe87 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 d749f08601b8..587dccf95f8e 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 e324f0a623dc..7400f4bc114f 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 05ccfc43168f..7a3e32ec46b8 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 d06718b0e2ab..6fb4fa423219 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 */ From 976d9276c826d6b35e4a2478fd4978dbd63bdd6f Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 13 Apr 2007 22:47:01 +0200 Subject: [PATCH 02/46] 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 4a73e8b2428d..e8f896c61b34 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; From 5721dbf217b073b40e31936781379ab2d17ea2ae Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 13 Apr 2007 23:25:59 +0200 Subject: [PATCH 03/46] 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 7a3e32ec46b8..673c64661d36 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 6fb4fa423219..873bda1e59b4 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 */ }; From 4552f0cbd45225f2c1cbadc224505f14f8749569 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:12 +1000 Subject: [PATCH 04/46] 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 bc60e2fc3c2c..d6652b3301dc 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 6b10ebe9d936..6799b9cca055 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 0581d09c58fc..8905b129e4e1 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; + unsigned int fifo_status = 0; + + 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); + + 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)); - if (sock_irq_status & FIFO_EVENT) { - 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 (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 3deb0a6c1370..4470961655c5 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; From 8dc4a61eca31dd45a9d45f9bc9c67d959f0f6cbd Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:13 +1000 Subject: [PATCH 05/46] 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 6799b9cca055..dcff45a19bcb 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 4470961655c5..ee1056396b95 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; }; From e23f2b8a1a52c00f0150659eb0bfde3a73976ffe Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:14 +1000 Subject: [PATCH 06/46] 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 d6652b3301dc..fd7b8dadc821 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 dcff45a19bcb..6b2c447dc8ee 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 8905b129e4e1..4388ee9062a0 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 ee1056396b95..57b2653494cf 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 From 3540af8ffddcdbc7573451ac0b5cd57a2eaf8af5 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:15 +1000 Subject: [PATCH 07/46] 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 fd7b8dadc821..e5655fef42d7 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 6b2c447dc8ee..ef8a97b819dd 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 57b2653494cf..d9de79275c21 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) { From 342c0ec4859446140c0dc5d7d903bb3b3f0577cd Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:16 +1000 Subject: [PATCH 08/46] 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 e5655fef42d7..34746798ba8e 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++) { From 6113ed73e61a13db9da48831e1b35788b7f837cc Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:17 +1000 Subject: [PATCH 09/46] 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 | 50 ++++++++++++++++++++++++---------------- include/linux/tifm.h | 16 ++++++++----- 3 files changed, 50 insertions(+), 44 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 34746798ba8e..e051f9da9c5c 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 ef8a97b819dd..f0cce2a642df 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) + return rc; - if (rc) { - spin_lock(&tifm_adapter_lock); - idr_remove(&tifm_adapter_idr, fm->id); - spin_unlock(&tifm_adapter_lock); - } + 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 d9de79275c21..a7bd654e2ee5 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); From 2428a8fe2261e901e058d9ea8b6ed7e1b4268b79 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:18 +1000 Subject: [PATCH 10/46] 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 | 98 +++++++++++++++------------------------- drivers/misc/tifm_core.c | 39 +++++++++++----- include/linux/tifm.h | 4 +- 3 files changed, 67 insertions(+), 74 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index e051f9da9c5c..356386904a5d 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,69 +152,46 @@ 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); - } - spin_lock_irqsave(&fm->lock, flags); - } + 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), fm->addr + FM_CLEAR_INTERRUPT_ENABLE); diff --git a/drivers/misc/tifm_core.c b/drivers/misc/tifm_core.c index f0cce2a642df..1e591989835d 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 (dev) { - spin_lock_init(&dev->lock); + if (!tifm_media_type_name(type, 0)) + return sock; - 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 = 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; + + 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 a7bd654e2ee5..82da028d8c07 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); From 88de1b2fed2bbe9eb1b7310195be84cf143efb4f Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:19 +1000 Subject: [PATCH 11/46] 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 | 62 +++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c index 356386904a5d..eafa5575f312 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); - - 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); - writel(TIFM_IRQ_ENABLE, - fm->addr + FM_SET_INTERRUPT_ENABLE); - fm->socket_change_set = 0; + fm->socket_change_set |= bad_sockets; + if (fm->socket_change_set) + tifm_queue_work(&fm->media_switcher); spin_unlock_irqrestore(&fm->lock, flags); + writel(TIFM_IRQ_ENABLE, + fm->addr + FM_SET_INTERRUPT_ENABLE); + return 0; } From 4e64f223857b138e3474bedc967d51db25c414b3 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:20 +1000 Subject: [PATCH 12/46] 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 1e591989835d..70220beb3e04 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) From b039d4a187a4064c926159db063004377281b041 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:21 +1000 Subject: [PATCH 13/46] 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 4388ee9062a0..fe236cb95e5c 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); From 592d372ae89dd5b43117cf5113a910f67f6e6a7e Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:22 +1000 Subject: [PATCH 14/46] 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 | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index fe236cb95e5c..bf00e8cf670c 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); - writel(0, sock->addr + SOCK_MMCSD_INT_ENABLE); - mmiowb(); 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); 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); } From 0007d4837ac94d672f313cfc462f879b5d06f221 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:23 +1000 Subject: [PATCH 15/46] 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 bf00e8cf670c..fe8cb1aa681f 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); From dfef26d9aad4f983da232b259ee7f7faec479b2d Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 16:59:24 +1000 Subject: [PATCH 16/46] 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 fe8cb1aa681f..52499548abe8 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; - host->written_blocks = 0; - host->cmd_flags &= ~CARD_BUSY; - tifm_sd_prepare_data(host, mrq->cmd); + 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->req = mrq; @@ -542,19 +563,41 @@ 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; + 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); } - 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), @@ -564,109 +607,6 @@ static void tifm_sd_end_cmd(unsigned long data) 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; - } 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_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; @@ -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; From 72dc9d9619dd4682f4197e7a7f19af22fd6516a7 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 17:05:23 +1000 Subject: [PATCH 17/46] 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 | 285 ++++++++++++++++++++++-------------------- include/linux/tifm.h | 1 + 2 files changed, 151 insertions(+), 135 deletions(-) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index 52499548abe8..103060f490a6 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: - 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; - } - 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) { - tifm_sd_exec(host, host->req->stop); - host->state = SCMD; - } else { - host->state = READY; - } - } else { - host->state = FIFO; - } - goto change_state; - } - break; - case FIFO: - 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; - } else { - host->state = READY; - } - goto change_state; - } - break; - case READY: - tasklet_schedule(&host->finish_tasklet); - return; - } + 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 */ @@ -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); + 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)) { + host->cmd_flags |= FIFO_READY; + tifm_sd_check_status(host); + } + } + writel(fifo_status, sock->addr + SOCK_DMA_FIFO_STATUS); - - host->cmd_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 */ @@ -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; - tifm_sd_exec(host, host->req->stop); - host->state = SCMD; - goto done; + if (host->cmd_flags & SCMD_ACTIVE) { + host->req->stop->error = cmd_error; + host->cmd_flags |= SCMD_READY; } else { - host->req->cmd->error = error_code; + 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); } - } else { - host->req->cmd->error = error_code; } - 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++; + 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_status & TIFM_MMCSD_EOFB) host->cmd_flags &= ~CARD_BUSY; - } + else if (host_status & TIFM_MMCSD_CB) + host->cmd_flags |= CARD_BUSY; - if (host->req) - tifm_sd_process_cmd(sock, host, host_status); + 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 82da028d8c07..c8449fcea0c7 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 */ From 5897d657b58efb244b1f82a912ee93e5141ed14c Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 17:05:24 +1000 Subject: [PATCH 18/46] 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 103060f490a6..d20ccfcd911e 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 From 13cdf48ef15befbd36f8295091b9e0f9bd322963 Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 17:05:25 +1000 Subject: [PATCH 19/46] 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 | 447 ++++++++++++++++++++++++++++-------------- include/linux/tifm.h | 1 + 2 files changed, 299 insertions(+), 149 deletions(-) diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/tifm_sd.c index d20ccfcd911e..8e69514e415d 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; + struct tifm_dev *sock = host->dev; + unsigned char *buf; + unsigned int pos = 0, val; - 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; - } - } - 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); - } - } - } + 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; } @@ -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 c8449fcea0c7..7ccad0795466 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 From 91f8d0118a0e1f25f809f3fde5a7616a1eaabc2b Mon Sep 17 00:00:00 2001 From: Alex Dubov Date: Thu, 12 Apr 2007 17:05:26 +1000 Subject: [PATCH 20/46] 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 eafa5575f312..9dcff14e752c 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 70220beb3e04..d195fb088f4a 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 8e69514e415d..f692a2ec09c5 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 7ccad0795466..2a196982601f 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) From c4030698029bb30d220fb0342a34280c629cee01 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sat, 28 Apr 2007 14:21:10 +0200 Subject: [PATCH 21/46] 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 9dcff14e752c..1ba6c085419a 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" From de85989511f3a0e15b04d18582b23d428d6ddbbd Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 28 Apr 2007 14:59:35 +0200 Subject: [PATCH 22/46] 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 e8f896c61b34..2ba46273496b 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); } From 85a18ad93ec66888d85758630019b10a84257f3c Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 17 Feb 2007 22:15:27 +0100 Subject: [PATCH 23/46] 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 2ba46273496b..f772df93a398 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 95b0da6abe87..63fbde8756ac 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 e45712acfac5..5d9896c260a2 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 c90b6768329d..d740ab94fa25 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 From 9c2c0af950345e63ef86f28eca44333a1e1e709b Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 26 Dec 2006 15:25:58 +0100 Subject: [PATCH 24/46] 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 f772df93a398..ccd3037da249 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 5d9896c260a2..1ca50542ce19 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) From f74d132cec60b686bce1f284822c1a496700bd3c Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Fri, 9 Feb 2007 22:49:31 +0100 Subject: [PATCH 25/46] 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 459f4b4feded..e37943c314cb 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 b834be261ab7..b7156a4555b5 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 0de5c9e94e74..7ee2045acbef 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 5941dd951e82..d11c2d23ceea 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 1e96a2f65022..e851384e51f4 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 9774fc68b61a..a98ff98fa567 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 587dccf95f8e..579142a7904b 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 f692a2ec09c5..b0d77d298412 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 673c64661d36..9f7518b37c33 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 1937151665c7..84c726934ace 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 a38a28c4bbd8..ef4f570381d1 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 1b3555d4b41e..eb91145c00c4 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 bfcef8a1ad8b..c89f41091304 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 d740ab94fa25..3ca91a6fc23f 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 */ /* From 29041dbe199b0dff392bf1b9d634357da0b3208f Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 10 Feb 2007 15:52:23 +0100 Subject: [PATCH 26/46] 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 ccd3037da249..9ffeeb2cba45 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 e0e82d849d5f..06f264b2f79c 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; } From 98ac2162699f7e9880683cb954891817f20b607c Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 23 Dec 2006 20:03:02 +0100 Subject: [PATCH 27/46] 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/{mmc_block.c => block.c} | 3 ++- drivers/mmc/{mmc_queue.c => queue.c} | 9 ++++----- drivers/mmc/{mmc_queue.h => queue.h} | 0 4 files changed, 7 insertions(+), 7 deletions(-) rename drivers/mmc/{mmc_block.c => block.c} (99%) rename drivers/mmc/{mmc_queue.c => queue.c} (97%) rename drivers/mmc/{mmc_queue.h => queue.h} (100%) diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 83ffb9326a54..9ef010a51608 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/mmc_block.c b/drivers/mmc/block.c similarity index 99% rename from drivers/mmc/mmc_block.c rename to drivers/mmc/block.c index 63fbde8756ac..8eba037a18e0 100644 --- a/drivers/mmc/mmc_block.c +++ b/drivers/mmc/block.c @@ -2,6 +2,7 @@ * 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 @@ -37,7 +38,7 @@ #include #include -#include "mmc_queue.h" +#include "queue.h" /* * max 8 partitions per card diff --git a/drivers/mmc/mmc_queue.c b/drivers/mmc/queue.c similarity index 97% rename from drivers/mmc/mmc_queue.c rename to drivers/mmc/queue.c index c27e42645cdb..aa75ac11a19e 100644 --- a/drivers/mmc/mmc_queue.c +++ b/drivers/mmc/queue.c @@ -1,7 +1,8 @@ /* - * linux/drivers/mmc/mmc_queue.c + * 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 @@ -14,7 +15,7 @@ #include #include -#include "mmc_queue.h" +#include "queue.h" #define MMC_QUEUE_SUSPENDED (1 << 0) @@ -179,7 +180,6 @@ int mmc_init_queue(struct mmc_queue *mq, struct mmc_card *card, spinlock_t *lock blk_cleanup_queue(mq->queue); return ret; } -EXPORT_SYMBOL(mmc_init_queue); void mmc_cleanup_queue(struct mmc_queue *mq) { @@ -226,7 +226,6 @@ void mmc_queue_suspend(struct mmc_queue *mq) down(&mq->thread_sem); } } -EXPORT_SYMBOL(mmc_queue_suspend); /** * mmc_queue_resume - resume a previously suspended MMC request queue @@ -247,4 +246,4 @@ void mmc_queue_resume(struct mmc_queue *mq) spin_unlock_irqrestore(q->queue_lock, flags); } } -EXPORT_SYMBOL(mmc_queue_resume); + diff --git a/drivers/mmc/mmc_queue.h b/drivers/mmc/queue.h similarity index 100% rename from drivers/mmc/mmc_queue.h rename to drivers/mmc/queue.h From 1c6a0718f0bfdab0d9b7da5f7b74f38a0058c03a Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 11 Feb 2007 19:57:36 +0100 Subject: [PATCH 28/46] 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/card/Kconfig | 17 +++++ drivers/mmc/card/Makefile | 11 ++++ drivers/mmc/{ => card}/block.c | 0 drivers/mmc/{ => card}/queue.c | 0 drivers/mmc/{ => card}/queue.h | 0 drivers/mmc/host/Kconfig | 103 +++++++++++++++++++++++++++++ drivers/mmc/host/Makefile | 18 +++++ drivers/mmc/{ => host}/at91_mci.c | 0 drivers/mmc/{ => host}/au1xmmc.c | 0 drivers/mmc/{ => host}/au1xmmc.h | 0 drivers/mmc/{ => host}/imxmmc.c | 0 drivers/mmc/{ => host}/imxmmc.h | 0 drivers/mmc/{ => host}/mmci.c | 0 drivers/mmc/{ => host}/mmci.h | 0 drivers/mmc/{ => host}/omap.c | 0 drivers/mmc/{ => host}/pxamci.c | 0 drivers/mmc/{ => host}/pxamci.h | 0 drivers/mmc/{ => host}/sdhci.c | 0 drivers/mmc/{ => host}/sdhci.h | 0 drivers/mmc/{ => host}/tifm_sd.c | 0 drivers/mmc/{ => host}/wbsd.c | 0 drivers/mmc/{ => host}/wbsd.h | 0 24 files changed, 158 insertions(+), 127 deletions(-) create mode 100644 drivers/mmc/card/Kconfig create mode 100644 drivers/mmc/card/Makefile rename drivers/mmc/{ => card}/block.c (100%) rename drivers/mmc/{ => card}/queue.c (100%) rename drivers/mmc/{ => card}/queue.h (100%) create mode 100644 drivers/mmc/host/Kconfig create mode 100644 drivers/mmc/host/Makefile rename drivers/mmc/{ => host}/at91_mci.c (100%) rename drivers/mmc/{ => host}/au1xmmc.c (100%) rename drivers/mmc/{ => host}/au1xmmc.h (100%) rename drivers/mmc/{ => host}/imxmmc.c (100%) rename drivers/mmc/{ => host}/imxmmc.h (100%) rename drivers/mmc/{ => host}/mmci.c (100%) rename drivers/mmc/{ => host}/mmci.h (100%) rename drivers/mmc/{ => host}/omap.c (100%) rename drivers/mmc/{ => host}/pxamci.c (100%) rename drivers/mmc/{ => host}/pxamci.h (100%) rename drivers/mmc/{ => host}/sdhci.c (100%) rename drivers/mmc/{ => host}/sdhci.h (100%) rename drivers/mmc/{ => host}/tifm_sd.c (100%) rename drivers/mmc/{ => host}/wbsd.c (100%) rename drivers/mmc/{ => host}/wbsd.h (100%) diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 12af9c718764..c7d64c0187fb 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. +source "drivers/mmc/card/Kconfig" -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. +source "drivers/mmc/host/Kconfig" endmenu diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 9ef010a51608..4d2bdfeb8d76 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/card/Kconfig b/drivers/mmc/card/Kconfig new file mode 100644 index 000000000000..01a9fd376a1f --- /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 000000000000..cf8c939867f5 --- /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/block.c b/drivers/mmc/card/block.c similarity index 100% rename from drivers/mmc/block.c rename to drivers/mmc/card/block.c diff --git a/drivers/mmc/queue.c b/drivers/mmc/card/queue.c similarity index 100% rename from drivers/mmc/queue.c rename to drivers/mmc/card/queue.c diff --git a/drivers/mmc/queue.h b/drivers/mmc/card/queue.h similarity index 100% rename from drivers/mmc/queue.h rename to drivers/mmc/card/queue.h diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig new file mode 100644 index 000000000000..ed4deab2203d --- /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 000000000000..6685f64345b4 --- /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/at91_mci.c b/drivers/mmc/host/at91_mci.c similarity index 100% rename from drivers/mmc/at91_mci.c rename to drivers/mmc/host/at91_mci.c diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/host/au1xmmc.c similarity index 100% rename from drivers/mmc/au1xmmc.c rename to drivers/mmc/host/au1xmmc.c diff --git a/drivers/mmc/au1xmmc.h b/drivers/mmc/host/au1xmmc.h similarity index 100% rename from drivers/mmc/au1xmmc.h rename to drivers/mmc/host/au1xmmc.h diff --git a/drivers/mmc/imxmmc.c b/drivers/mmc/host/imxmmc.c similarity index 100% rename from drivers/mmc/imxmmc.c rename to drivers/mmc/host/imxmmc.c diff --git a/drivers/mmc/imxmmc.h b/drivers/mmc/host/imxmmc.h similarity index 100% rename from drivers/mmc/imxmmc.h rename to drivers/mmc/host/imxmmc.h diff --git a/drivers/mmc/mmci.c b/drivers/mmc/host/mmci.c similarity index 100% rename from drivers/mmc/mmci.c rename to drivers/mmc/host/mmci.c diff --git a/drivers/mmc/mmci.h b/drivers/mmc/host/mmci.h similarity index 100% rename from drivers/mmc/mmci.h rename to drivers/mmc/host/mmci.h diff --git a/drivers/mmc/omap.c b/drivers/mmc/host/omap.c similarity index 100% rename from drivers/mmc/omap.c rename to drivers/mmc/host/omap.c diff --git a/drivers/mmc/pxamci.c b/drivers/mmc/host/pxamci.c similarity index 100% rename from drivers/mmc/pxamci.c rename to drivers/mmc/host/pxamci.c diff --git a/drivers/mmc/pxamci.h b/drivers/mmc/host/pxamci.h similarity index 100% rename from drivers/mmc/pxamci.h rename to drivers/mmc/host/pxamci.h diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/host/sdhci.c similarity index 100% rename from drivers/mmc/sdhci.c rename to drivers/mmc/host/sdhci.c diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/host/sdhci.h similarity index 100% rename from drivers/mmc/sdhci.h rename to drivers/mmc/host/sdhci.h diff --git a/drivers/mmc/tifm_sd.c b/drivers/mmc/host/tifm_sd.c similarity index 100% rename from drivers/mmc/tifm_sd.c rename to drivers/mmc/host/tifm_sd.c diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/host/wbsd.c similarity index 100% rename from drivers/mmc/wbsd.c rename to drivers/mmc/host/wbsd.c diff --git a/drivers/mmc/wbsd.h b/drivers/mmc/host/wbsd.h similarity index 100% rename from drivers/mmc/wbsd.h rename to drivers/mmc/host/wbsd.h From 3b91e5507cddaca53bccf1524ff11a0ac5c85531 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 11 Feb 2007 20:43:19 +0100 Subject: [PATCH 29/46] 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 9ffeeb2cba45..0242c6a21daf 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 c89f41091304..92efe8e5be7e 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; }; From d2b46f66b4b342be07a4194bd5e82384d07e470d Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 28 Apr 2007 16:52:12 +0200 Subject: [PATCH 30/46] 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 aa75ac11a19e..2e77963db334 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); From b5af25bee2de2f6cd1ac74ba737cbc4f3d303e5d Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sat, 28 Apr 2007 17:30:50 +0200 Subject: [PATCH 31/46] 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 0242c6a21daf..94c04725726d 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; } From b855885e3b60cf6f9452848712a62517b94583eb Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Wed, 3 Jan 2007 19:47:29 +0100 Subject: [PATCH 32/46] 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 | 997 +++++++++++++++++---------------------- include/linux/mmc/card.h | 8 - include/linux/mmc/host.h | 4 +- include/linux/mmc/mmc.h | 9 +- 5 files changed, 438 insertions(+), 593 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 8eba037a18e0..8a84e4dc1b2a 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 94c04725726d..3f50b8882c89 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,51 +410,26 @@ 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; + 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; - 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(card->host, card->rca, + &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) + return err; - 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; - } + card->host->ios.bus_width = MMC_BUS_WIDTH_4; + mmc_set_ios(card->host); } - 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; - - cmd.opcode = MMC_SELECT_CARD; - cmd.arg = 0; - cmd.flags = MMC_RSP_NONE | MMC_CMD_AC; - - mmc_wait_for_cmd(host, &cmd, 0); - } -} - static inline void mmc_delay(unsigned int ms) { @@ -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; - } + if (err != MMC_ERR_NONE) + mmc_card_set_dead(host->card); + else { + host->card->rca = cmd.resp[0] >> 16; - 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 (!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; + cmd.opcode = MMC_SET_RELATIVE_ADDR; + cmd.arg = host->card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - 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); - } + 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; + struct mmc_command cmd; + int err; - list_for_each_entry(card, &host->cards, node) { - struct mmc_command cmd; - int err; + if (!host->card) + return; + if (mmc_card_dead(host->card)) + return; - if (card->state & (MMC_STATE_DEAD|MMC_STATE_PRESENT)) - continue; + cmd.opcode = MMC_SEND_CSD; + cmd.arg = host->card->rca << 16; + cmd.flags = MMC_RSP_R2 | MMC_CMD_AC; - cmd.opcode = MMC_SEND_CSD; - cmd.arg = 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; - } - - memcpy(card->raw_csd, cmd.resp, sizeof(card->raw_csd)); - - mmc_decode_csd(card); - mmc_decode_cid(card); + 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_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; + memset(&cmd, 0, sizeof(struct mmc_command)); - 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_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, 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 (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; - 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; - - 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; - } - - mmc_card_set_highspeed(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(card->host)); - continue; - } - - host->ios.bus_width = MMC_BUS_WIDTH_4; - mmc_set_ios(host); + 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; } - kfree(ext_csd); + 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); - mmc_deselect_cards(host); + 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_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; + if (!host->card) + return; + if (mmc_card_dead(host->card)) + return; + if (!mmc_card_sd(host->card)) + return; - 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_APP_CMD; + cmd.arg = host->card->rca << 16; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; - cmd.opcode = MMC_APP_CMD; - cmd.arg = 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(card); - continue; - } - - 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, 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*)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(card); - continue; - } - - card->raw_scr[0] = ntohl(card->raw_scr[0]); - card->raw_scr[1] = ntohl(card->raw_scr[1]); - - mmc_decode_scr(card); + 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; } - mmc_deselect_cards(host); + 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) { - 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; + memset(&cmd, 0, sizeof(struct mmc_command)); - err = mmc_select_card(host, card); - if (err != MMC_ERR_NONE) { - mmc_card_set_dead(card); - continue; - } + cmd.opcode = SD_SWITCH; + cmd.arg = 0x00FFFFF1; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - memset(&cmd, 0, sizeof(struct mmc_command)); + memset(&data, 0, sizeof(struct mmc_data)); - cmd.opcode = SD_SWITCH; - cmd.arg = 0x00FFFFF1; - cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + mmc_set_data_timeout(&data, host->card, 0); - memset(&data, 0, sizeof(struct mmc_data)); + data.blksz = 64; + data.blocks = 1; + data.flags = MMC_DATA_READ; + data.sg = &sg; + data.sg_len = 1; - mmc_set_data_timeout(&data, card, 0); + memset(&mrq, 0, sizeof(struct mmc_request)); - data.blksz = 64; - data.blocks = 1; - data.flags = MMC_DATA_READ; - data.sg = &sg; - data.sg_len = 1; + mrq.cmd = &cmd; + mrq.data = &data; - memset(&mrq, 0, sizeof(struct mmc_request)); + sg_init_one(&sg, status, 64); - mrq.cmd = &cmd; - mrq.data = &data; + mmc_wait_for_req(host, &mrq); - 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(card->host)); - continue; - } - - if (status[13] & 0x02) - 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, 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)); - continue; - } - - mmc_card_set_highspeed(card); - - host->ios.timing = MMC_TIMING_SD_HS; - mmc_set_ios(host); + 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; } - kfree(status); + if (status[13] & 0x02) + host->card->sw_caps.hs_max_dtr = 50000000; - mmc_deselect_cards(host); + 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) { - 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; + struct mmc_command cmd; + int err; - mmc_deselect_cards(host); + BUG_ON(!card); - list_for_each_safe(l, n, &host->cards) { - struct mmc_card *card = mmc_list_to_card(l); - struct mmc_command cmd; - int err; + 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(card->host, &cmd, CMD_RETRIES); + if (err == MMC_ERR_NONE) + return; - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err == MMC_ERR_NONE) - continue; - - 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; + int err; + u32 ocr; - host->mode = MMC_MODE_SD; + host->mode = MMC_MODE_SD; - mmc_power_up(host); - mmc_idle_cards(host); + 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); - - /* - * 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); - - /* - * 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_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. @@ -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_release_host(host); + + if (mmc_card_dead(host->card)) { + mmc_remove_card(host->card); + host->card = NULL; + } + + goto out; + } mmc_setup(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 (!list_empty(&host->cards)) { + 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 card is dead, destroy it. - */ - if (mmc_card_dead(card)) { - list_del(&card->node); - mmc_remove_card(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 (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 1ca50542ce19..7d98990ac94e 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 92efe8e5be7e..6ea3c0ea3e15 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 cdc54be804f1..b3d80efc6434 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 From aaac1b470bd0dccb30912356617069dc6199cc80 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Wed, 28 Feb 2007 15:33:10 +0100 Subject: [PATCH 33/46] 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/{mmc.c => core/core.c} | 4 ++-- drivers/mmc/{mmc.h => core/core.h} | 6 +++--- drivers/mmc/{mmc_sysfs.c => core/sysfs.c} | 4 ++-- include/linux/mmc/card.h | 2 +- include/linux/mmc/{mmc.h => core.h} | 6 +++--- include/linux/mmc/host.h | 2 +- 8 files changed, 24 insertions(+), 18 deletions(-) create mode 100644 drivers/mmc/core/Makefile rename drivers/mmc/{mmc.c => core/core.c} (99%) rename drivers/mmc/{mmc.h => core/core.h} (91%) rename drivers/mmc/{mmc_sysfs.c => core/sysfs.c} (99%) rename include/linux/mmc/{mmc.h => core.h} (97%) diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 4d2bdfeb8d76..9979f5e9765b 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 000000000000..f911fbd2845b --- /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/mmc.c b/drivers/mmc/core/core.c similarity index 99% rename from drivers/mmc/mmc.c rename to drivers/mmc/core/core.c index 3f50b8882c89..334e663e465b 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/core/core.c @@ -1,5 +1,5 @@ /* - * linux/drivers/mmc/mmc.c + * 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. @@ -25,7 +25,7 @@ #include #include -#include "mmc.h" +#include "core.h" #define CMD_RETRIES 3 diff --git a/drivers/mmc/mmc.h b/drivers/mmc/core/core.h similarity index 91% rename from drivers/mmc/mmc.h rename to drivers/mmc/core/core.h index 149affe0b686..076cb2f49a0f 100644 --- a/drivers/mmc/mmc.h +++ b/drivers/mmc/core/core.h @@ -1,5 +1,5 @@ /* - * linux/drivers/mmc/mmc.h + * linux/drivers/mmc/core/core.h * * Copyright (C) 2003 Russell King, All Rights Reserved. * @@ -7,8 +7,8 @@ * 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 +#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); diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/core/sysfs.c similarity index 99% rename from drivers/mmc/mmc_sysfs.c rename to drivers/mmc/core/sysfs.c index 06f264b2f79c..bf9a5f8beb86 100644 --- a/drivers/mmc/mmc_sysfs.c +++ b/drivers/mmc/core/sysfs.c @@ -1,5 +1,5 @@ /* - * linux/drivers/mmc/mmc_sysfs.c + * linux/drivers/mmc/core/sysfs.c * * Copyright (C) 2003 Russell King, All Rights Reserved. * @@ -18,7 +18,7 @@ #include #include -#include "mmc.h" +#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) diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 7d98990ac94e..800425e05165 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/mmc.h b/include/linux/mmc/core.h similarity index 97% rename from include/linux/mmc/mmc.h rename to include/linux/mmc/core.h index b3d80efc6434..d8fd66cf28be 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/core.h @@ -1,12 +1,12 @@ /* - * linux/include/linux/mmc/mmc.h + * 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 MMC_H -#define MMC_H +#ifndef LINUX_MMC_CORE_H +#define LINUX_MMC_CORE_H #include #include diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 6ea3c0ea3e15..43bf6a5c398d 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 */ From da7fbe58d2d347e95af699ddf04d885be6362bbe Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 24 Dec 2006 22:46:55 +0100 Subject: [PATCH 34/46] 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/{protocol.h => mmc.h} | 54 +-- include/linux/mmc/sd.h | 83 ++++ 13 files changed, 844 insertions(+), 517 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 rename include/linux/mmc/{protocol.h => mmc.h} (85%) create mode 100644 include/linux/mmc/sd.h diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 8a84e4dc1b2a..d24ab234394c 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 f911fbd2845b..5977abf3e41b 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 334e663e465b..310be2fe1944 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) +void mmc_set_chip_select(struct mmc_host *host, int mode) { - 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); - } + 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)) { + err = mmc_app_send_scr(host->card, host->card->raw_scr); + if (err != MMC_ERR_NONE) { 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; + 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 076cb2f49a0f..1c1066342fba 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 000000000000..7dd720fa5895 --- /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 000000000000..7a481e8ca5ea --- /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 000000000000..9697ce581101 --- /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 000000000000..1240fddba5e3 --- /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 bf9a5f8beb86..5c9ce02e7e55 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 000000000000..80e29b358282 --- /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 d8fd66cf28be..04bbe12fae8d 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/protocol.h b/include/linux/mmc/mmc.h similarity index 85% rename from include/linux/mmc/protocol.h rename to include/linux/mmc/mmc.h index 3ca91a6fc23f..e3ed9b95040e 100644 --- a/include/linux/mmc/protocol.h +++ b/include/linux/mmc/mmc.h @@ -22,8 +22,8 @@ * 15 May 2002 */ -#ifndef MMC_MMC_PROTOCOL_H -#define MMC_MMC_PROTOCOL_H +#ifndef MMC_MMC_H +#define MMC_MMC_H /* Standard MMC commands (4.1) type argument response */ /* class 1 */ @@ -78,21 +78,6 @@ #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: * @@ -104,27 +89,6 @@ * [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 @@ -289,19 +253,5 @@ struct _mmc_csd { #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 000000000000..f310062cffb4 --- /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 + From b2670b1c6ddd54be4a0f72f853122510ea5ef285 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 1 May 2007 13:35:19 +0200 Subject: [PATCH 35/46] 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 9f7518b37c33..867ca6a69298 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. */ From 7ea239d9e6d6993469a6a8ca83ff23834dfc3fce Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Sun, 31 Dec 2006 00:11:32 +0100 Subject: [PATCH 36/46] 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 | 831 +++++++------------------------------- 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, 1048 insertions(+), 690 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 5977abf3e41b..1075b02ae754 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 310be2fe1944..75333a2461df 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. + * Select timing parameters for host. */ -static void mmc_decode_cid(struct mmc_card *card) +void mmc_set_timing(struct mmc_host *host, unsigned int timing) { - 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); + 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]; + unsigned long flags; - BUG_ON(host->card); + BUG_ON(!host); + BUG_ON(!ops); - 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; - } + BUG_ON(!host->claimed); - host->card = mmc_alloc_card(host, cid); - if (IS_ERR(host->card)) { - err = PTR_ERR(host->card); - host->card = NULL; - return; - } + spin_lock_irqsave(&host->lock, flags); - if (host->mode == MMC_MODE_SD) { - host->card->type = MMC_TYPE_SD; + BUG_ON(host->bus_ops); + BUG_ON(host->bus_refs); - 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; + host->bus_ops = ops; + host->bus_refs = 1; + host->bus_dead = 0; - 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); - } - - /* 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; - } - - 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; - - 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; - } - - mmc_decode_scr(host->card); -} - -static void mmc_read_switch_caps(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; - - 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; - } - - 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; + spin_unlock_irqrestore(&host->lock, flags); } /* - * 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. + * Remove the current bus handler from a host. Assumes that there are + * no interesting cards left, so the bus is powered down. */ -static void mmc_check_card(struct mmc_card *card) +void mmc_detach_bus(struct mmc_host *host) { - int err; + unsigned long flags; - BUG_ON(!card); + BUG_ON(!host); - err = mmc_send_status(card, NULL); - if (err == MMC_ERR_NONE) - return; + BUG_ON(!host->claimed); + BUG_ON(!host->bus_ops); - mmc_card_set_dead(card); + spin_lock_irqsave(&host->lock, flags); + + host->bus_dead = 1; + + spin_unlock_irqrestore(&host->lock, flags); + + mmc_power_off(host); + + mmc_bus_put(host); } -static void mmc_setup(struct mmc_host *host) +/* + * Cleanup when the last reference to the bus operator is dropped. + */ +void __mmc_release_bus(struct mmc_host *host) { - int err; - u32 ocr; + BUG_ON(!host); + BUG_ON(host->bus_refs); + BUG_ON(!host->bus_dead); - 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); + host->bus_ops = NULL; } - /** * 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); + mmc_bus_get(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)) { + 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_claim_host(host); + + mmc_power_up(host); + mmc_go_idle(host); + + mmc_send_if_cond(host, host->ocr_avail); + + 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); } - - 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); } @@ -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 1c1066342fba..fad8edc38099 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 000000000000..c528017d6128 --- /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 000000000000..6c6beb48f3a8 --- /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 43bf6a5c398d..efae87b5c4e7 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; }; From 4be34c99a2f3aa90fa42e62c0918f07afb8a645b Mon Sep 17 00:00:00 2001 From: Philip Langdale Date: Sun, 11 Mar 2007 17:15:15 -0700 Subject: [PATCH 37/46] 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 579142a7904b..a57f6a3d48d3 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 efae87b5c4e7..5a66d8a2bf17 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 */ From 55556da01284af8c2174b786b3eca8e11301b656 Mon Sep 17 00:00:00 2001 From: Philip Langdale Date: Fri, 16 Mar 2007 19:39:00 -0700 Subject: [PATCH 38/46] 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 c528017d6128..c2e120b11bef 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 6c6beb48f3a8..fb18b301502e 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 a57f6a3d48d3..ff5bf73cdd25 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 5a66d8a2bf17..b1350dfd3e91 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 */ From 1addfcdbe4b23a20f28a097c2469d9f0c21bef23 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 1 May 2007 14:46:08 +0200 Subject: [PATCH 39/46] 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 fb18b301502e..ae54e8eb7fea 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) From 89a73cf52ba2ae4402c53487b71ec4475544f139 Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 1 May 2007 15:08:30 +0200 Subject: [PATCH 40/46] 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 c2e120b11bef..47449e870131 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); From 6abaa0c9fec563538f2a28a682af8c89bb9b125c Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 1 May 2007 16:00:02 +0200 Subject: [PATCH 41/46] 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 | 322 +++++++++++++++++++++++++-------------- 6 files changed, 446 insertions(+), 215 deletions(-) create mode 100644 drivers/mmc/core/Kconfig diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index c7d64c0187fb..6c97491543db 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 000000000000..94222b9a15ea --- /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 75333a2461df..72c7cf4a9f9d 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 fad8edc38099..177264d090ac 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 47449e870131..619581e49163 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. + * Handle the detection and initialisation of a card. + * + * In the case of a resume, "curcard" will contain the card + * we're trying to reinitialise. */ -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) +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->type = MMC_TYPE_MMC; - card->rca = 1; - 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_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); - 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_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_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 ae54e8eb7fea..6597e778f70e 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -262,6 +262,161 @@ out: return err; } +/* + * Handle the detection and initialisation of a card. + * + * In the case of a resume, "curcard" will contain the card + * we're trying to reinitialise. + */ +static int mmc_sd_init_card(struct mmc_host *host, u32 ocr, + struct mmc_card *oldcard) +{ + struct mmc_card *card; + int err; + u32 cid[4]; + unsigned int max_dtr; + + BUG_ON(!host); + BUG_ON(!host->claimed); + + /* + * 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, ocr); + if (err == MMC_ERR_NONE) + ocr |= 1 << 30; + + err = mmc_send_app_op_cond(host, ocr, NULL); + if (err != MMC_ERR_NONE) + goto err; + + /* + * Fetch CID from card. + */ + err = mmc_all_send_cid(host, cid); + if (err != MMC_ERR_NONE) + 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_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); + + 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); + } + + /* + * Select card, as all following commands rely on that. + */ + err = mmc_select_card(card); + 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); + + /* + * 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) + 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); + } + + 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. */ @@ -303,9 +458,60 @@ static void mmc_sd_detect(struct mmc_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, }; /* @@ -313,10 +519,7 @@ static const struct mmc_bus_ops mmc_sd_ops = { */ 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); @@ -350,119 +553,15 @@ int mmc_attach_sd(struct mmc_host *host, u32 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. + * Detect and init the card. */ - 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); + err = mmc_sd_init_card(host, host->ocr, NULL); 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); - - /* - * 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) - 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); + 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); From bd766312618d2ecc85bce663f95faec601447ecb Mon Sep 17 00:00:00 2001 From: Pierre Ossman Date: Tue, 1 May 2007 16:11:57 +0200 Subject: [PATCH 42/46] 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 619581e49163..42cc2867ed7d 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 6597e778f70e..c1dfd03d559a 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 5c9ce02e7e55..843b1fbba557 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 800425e05165..badf702fcff4 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) From d97956f86bec90ab131b9f1af60c0e686198d45e Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 1 May 2007 16:14:29 +0200 Subject: [PATCH 43/46] 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 b0d77d298412..7511f961c67b 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; From 3647afcec11f2fcfc6269e513ff97fec1374d1b8 Mon Sep 17 00:00:00 2001 From: Arnaud Patard Date: Tue, 1 May 2007 16:18:36 +0200 Subject: [PATCH 44/46] 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 e851384e51f4..1b232c6dd016 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) From 46a6730e3ff9add5089ddd007f998b97fb4e8571 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 1 May 2007 16:34:16 +0200 Subject: [PATCH 45/46] 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 1b232c6dd016..fc044a5ed64e 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); From d3af5abe9a809becbe4b413144b607844560d445 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Tue, 1 May 2007 16:36:00 +0200 Subject: [PATCH 46/46] 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 | 55 ++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c index fc044a5ed64e..1914e65d4db1 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 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; + + if (ios->clock == 0) + return 0; + + dsor = func_clk_rate / ios->clock; + if (dsor < 1) + dsor = 1; + + if (func_clk_rate / dsor > ios->clock) + dsor++; + + 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 realclock, i; + int 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; - } + 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