X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fmmc%2Fhost%2Fat91_mci.c;h=5f3a599ead07bbdfae11f7abc0198285e743db81;hb=1a13f8fa76c880be41d6b1e6a2b44404bcbfdf9e;hp=4e1b2e6288dae295a1fb2e1a8699823515d6e163;hpb=0b3520f2df8e4e5dad6a7b2743d037bf06c22024;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c index 4e1b2e6..5f3a599 100644 --- a/drivers/mmc/host/at91_mci.c +++ b/drivers/mmc/host/at91_mci.c @@ -65,6 +65,7 @@ #include #include #include +#include #include @@ -78,6 +79,17 @@ #define DRIVER_NAME "at91_mci" +static inline int at91mci_is_mci1rev2xx(void) +{ + return ( cpu_is_at91sam9260() + || cpu_is_at91sam9263() + || cpu_is_at91cap9() + || cpu_is_at91sam9rl() + || cpu_is_at91sam9g10() + || cpu_is_at91sam9g20() + ); +} + #define FL_SENT_COMMAND (1 << 0) #define FL_SENT_STOP (1 << 1) @@ -88,6 +100,10 @@ #define at91_mci_read(host, reg) __raw_readl((host)->baseaddr + (reg)) #define at91_mci_write(host, reg, val) __raw_writel((val), (host)->baseaddr + (reg)) +#define MCI_BLKSIZE 512 +#define MCI_MAXBLKSIZE 4095 +#define MCI_BLKATONCE 256 +#define MCI_BUFSIZE (MCI_BLKSIZE * MCI_BLKATONCE) /* * Low level type for this driver @@ -200,8 +216,8 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data size = data->blksz * data->blocks; len = data->sg_len; - /* AT91SAM926[0/3] Data Write Operation and number of bytes erratum */ - if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) + /* MCI1 rev2xx Data Write Operation and number of bytes erratum */ + if (at91mci_is_mci1rev2xx()) if (host->total_length == 12) memset(dmabuf, 0, 12); @@ -233,7 +249,7 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data dmabuf = (unsigned *)tmpv; } - kunmap_atomic(((void *)sgbuffer) - sg->offset, KM_BIO_SRC_IRQ); + kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ); if (size == 0) break; @@ -247,80 +263,14 @@ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data } /* - * Prepare a dma read - */ -static void at91_mci_pre_dma_read(struct at91mci_host *host) -{ - int i; - struct scatterlist *sg; - struct mmc_command *cmd; - struct mmc_data *data; - - pr_debug("pre dma read\n"); - - cmd = host->cmd; - if (!cmd) { - pr_debug("no command\n"); - return; - } - - data = cmd->data; - if (!data) { - pr_debug("no data\n"); - return; - } - - for (i = 0; i < 2; i++) { - /* nothing left to transfer */ - if (host->transfer_index >= data->sg_len) { - pr_debug("Nothing left to transfer (index = %d)\n", host->transfer_index); - break; - } - - /* Check to see if this needs filling */ - if (i == 0) { - if (at91_mci_read(host, ATMEL_PDC_RCR) != 0) { - pr_debug("Transfer active in current\n"); - continue; - } - } - else { - if (at91_mci_read(host, ATMEL_PDC_RNCR) != 0) { - pr_debug("Transfer active in next\n"); - continue; - } - } - - /* Setup the next transfer */ - pr_debug("Using transfer index %d\n", host->transfer_index); - - sg = &data->sg[host->transfer_index++]; - pr_debug("sg = %p\n", sg); - - sg->dma_address = dma_map_page(NULL, sg_page(sg), sg->offset, sg->length, DMA_FROM_DEVICE); - - pr_debug("dma address = %08X, length = %d\n", sg->dma_address, sg->length); - - if (i == 0) { - at91_mci_write(host, ATMEL_PDC_RPR, sg->dma_address); - at91_mci_write(host, ATMEL_PDC_RCR, (data->blksz & 0x3) ? sg->length : sg->length / 4); - } - else { - at91_mci_write(host, ATMEL_PDC_RNPR, sg->dma_address); - at91_mci_write(host, ATMEL_PDC_RNCR, (data->blksz & 0x3) ? sg->length : sg->length / 4); - } - } - - pr_debug("pre dma read done\n"); -} - -/* * Handle after a dma read */ static void at91_mci_post_dma_read(struct at91mci_host *host) { struct mmc_command *cmd; struct mmc_data *data; + unsigned int len, i, size; + unsigned *dmabuf = host->buffer; pr_debug("post dma read\n"); @@ -336,42 +286,39 @@ static void at91_mci_post_dma_read(struct at91mci_host *host) return; } - while (host->in_use_index < host->transfer_index) { - struct scatterlist *sg; + size = data->blksz * data->blocks; + len = data->sg_len; - pr_debug("finishing index %d\n", host->in_use_index); + at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX); + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); - sg = &data->sg[host->in_use_index++]; + for (i = 0; i < len; i++) { + struct scatterlist *sg; + int amount; + unsigned int *sgbuffer; - pr_debug("Unmapping page %08X\n", sg->dma_address); + sg = &data->sg[i]; - dma_unmap_page(NULL, sg->dma_address, sg->length, DMA_FROM_DEVICE); + sgbuffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; + amount = min(size, sg->length); + size -= amount; if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ - unsigned int *buffer; int index; - - /* Swap the contents of the buffer */ - buffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; - pr_debug("buffer = %p, length = %d\n", buffer, sg->length); - - for (index = 0; index < (sg->length / 4); index++) - buffer[index] = swab32(buffer[index]); - - kunmap_atomic(buffer, KM_BIO_SRC_IRQ); + for (index = 0; index < (amount / 4); index++) + sgbuffer[index] = swab32(*dmabuf++); + } else { + char *tmpv = (char *)dmabuf; + memcpy(sgbuffer, tmpv, amount); + tmpv += amount; + dmabuf = (unsigned *)tmpv; } - flush_dcache_page(sg_page(sg)); - - data->bytes_xfered += sg->length; - } - - /* Is there another transfer to trigger? */ - if (host->transfer_index < data->sg_len) - at91_mci_pre_dma_read(host); - else { - at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_ENDRX); - at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); + flush_kernel_dcache_page(sg_page(sg)); + kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ); + data->bytes_xfered += amount; + if (size == 0) + break; } pr_debug("post dma read done\n"); @@ -463,7 +410,7 @@ static void at91_mci_enable(struct at91mci_host *host) at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC); mr = AT91_MCI_PDCMODE | 0x34a; - if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) + if (at91mci_is_mci1rev2xx()) mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF; at91_mci_write(host, AT91_MCI_MR, mr); @@ -604,10 +551,14 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command /* * Handle a read */ - host->buffer = NULL; host->total_length = 0; - at91_mci_pre_dma_read(host); + at91_mci_write(host, ATMEL_PDC_RPR, host->physical_address); + at91_mci_write(host, ATMEL_PDC_RCR, (data->blksz & 0x3) ? + (blocks * block_length) : (blocks * block_length) / 4); + at91_mci_write(host, ATMEL_PDC_RNPR, 0); + at91_mci_write(host, ATMEL_PDC_RNCR, 0); + ier = AT91_MCI_ENDRX /* | AT91_MCI_RXBUFF */; } else { @@ -616,27 +567,15 @@ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command */ host->total_length = block_length * blocks; /* - * AT91SAM926[0/3] Data Write Operation and + * MCI1 rev2xx Data Write Operation and * number of bytes erratum */ - if (cpu_is_at91sam9260 () || cpu_is_at91sam9263()) + if (at91mci_is_mci1rev2xx()) if (host->total_length < 12) host->total_length = 12; - host->buffer = kmalloc(host->total_length, GFP_KERNEL); - if (!host->buffer) { - pr_debug("Can't alloc tx buffer\n"); - cmd->error = -ENOMEM; - mmc_request_done(host->mmc, host->request); - return; - } - at91_mci_sg_to_dma(host, data); - host->physical_address = dma_map_single(NULL, - host->buffer, host->total_length, - DMA_TO_DEVICE); - pr_debug("Transmitting %d bytes\n", host->total_length); at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); @@ -703,14 +642,6 @@ static void at91_mci_completed_command(struct at91mci_host *host, unsigned int s cmd->resp[2] = at91_mci_read(host, AT91_MCI_RSPR(2)); cmd->resp[3] = at91_mci_read(host, AT91_MCI_RSPR(3)); - if (host->buffer) { - dma_unmap_single(NULL, - host->physical_address, host->total_length, - DMA_TO_DEVICE); - kfree(host->buffer); - host->buffer = NULL; - } - pr_debug("Status = %08X/%08x [%08X %08X %08X %08X]\n", status, at91_mci_read(host, AT91_MCI_SR), cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); @@ -756,7 +687,8 @@ static void at91_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) host->request = mrq; host->flags = 0; - mod_timer(&host->timer, jiffies + HZ); + /* more than 1s timeout needed with slow SD cards */ + mod_timer(&host->timer, jiffies + msecs_to_jiffies(2000)); at91_mci_process_next(host); } @@ -944,7 +876,8 @@ static irqreturn_t at91_mmc_det_irq(int irq, void *_host) pr_debug("****** Resetting SD-card bus width ******\n"); at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); } - mmc_detect_change(host->mmc, msecs_to_jiffies(100)); + /* 0.5s needed because of early card detect switch firing */ + mmc_detect_change(host->mmc, msecs_to_jiffies(500)); } return IRQ_HANDLED; } @@ -1008,24 +941,42 @@ static int __init at91_mci_probe(struct platform_device *pdev) mmc->f_min = 375000; mmc->f_max = 25000000; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = MMC_CAP_SDIO_IRQ; + mmc->caps = 0; - mmc->max_blk_size = 4095; - mmc->max_blk_count = mmc->max_req_size; + mmc->max_blk_size = MCI_MAXBLKSIZE; + mmc->max_blk_count = MCI_BLKATONCE; + mmc->max_req_size = MCI_BUFSIZE; + mmc->max_phys_segs = MCI_BLKATONCE; + mmc->max_hw_segs = MCI_BLKATONCE; + mmc->max_seg_size = MCI_BUFSIZE; host = mmc_priv(mmc); host->mmc = mmc; - host->buffer = NULL; host->bus_mode = 0; host->board = pdev->dev.platform_data; if (host->board->wire4) { - if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) + if (at91mci_is_mci1rev2xx()) mmc->caps |= MMC_CAP_4_BIT_DATA; else dev_warn(&pdev->dev, "4 wire bus mode not supported" " - using 1 wire\n"); } + host->buffer = dma_alloc_coherent(&pdev->dev, MCI_BUFSIZE, + &host->physical_address, GFP_KERNEL); + if (!host->buffer) { + ret = -ENOMEM; + dev_err(&pdev->dev, "Can't allocate transmit buffer\n"); + goto fail5; + } + + /* Add SDIO capability when available */ + if (at91mci_is_mci1rev2xx()) { + /* at91mci MCI1 rev2xx sdio interrupt erratum */ + if (host->board->wire4 || !host->board->slot_b) + mmc->caps |= MMC_CAP_SDIO_IRQ; + } + /* * Reserve GPIOs ... board init code makes sure these pins are set * up as GPIOs with the right direction (input, except for vcc) @@ -1034,7 +985,7 @@ static int __init at91_mci_probe(struct platform_device *pdev) ret = gpio_request(host->board->det_pin, "mmc_detect"); if (ret < 0) { dev_dbg(&pdev->dev, "couldn't claim card detect pin\n"); - goto fail5; + goto fail4b; } } if (host->board->wp_pin) { @@ -1134,6 +1085,10 @@ fail3: fail4: if (host->board->det_pin) gpio_free(host->board->det_pin); +fail4b: + if (host->buffer) + dma_free_coherent(&pdev->dev, MCI_BUFSIZE, + host->buffer, host->physical_address); fail5: mmc_free_host(mmc); fail6: @@ -1156,6 +1111,10 @@ static int __exit at91_mci_remove(struct platform_device *pdev) host = mmc_priv(mmc); + if (host->buffer) + dma_free_coherent(&pdev->dev, MCI_BUFSIZE, + host->buffer, host->physical_address); + if (host->board->det_pin) { if (device_can_wakeup(&pdev->dev)) free_irq(gpio_to_irq(host->board->det_pin), host); @@ -1198,7 +1157,7 @@ static int at91_mci_suspend(struct platform_device *pdev, pm_message_t state) enable_irq_wake(host->board->det_pin); if (mmc) - ret = mmc_suspend_host(mmc, state); + ret = mmc_suspend_host(mmc); return ret; }