/* * Copy from sg to a dma block - used for transfers */ static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data) { unsigned int len, i, size; unsigned *dmabuf = host->buffer; size = data->blksz * data->blocks; len = data->sg_len; /* MCI1 rev2xx Data Write Operation and number of bytes erratum */ if (at91mci_is_mci1rev2xx()) if (host->total_length == 12) memset(dmabuf, 0, 12); /* * Just loop through all entries. Size might not * be the entire list though so make sure that * we do not transfer too much. */ for (i = 0; i < len; i++) { struct scatterlist *sg; int amount; unsigned int *sgbuffer; sg = &data->sg[i]; sgbuffer = kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; amount = min(size, sg->length); size -= amount; if (cpu_is_at91rm9200()) { /* AT91RM9200 errata */ int index; for (index = 0; index < (amount / 4); index++) *dmabuf++ = swab32(sgbuffer[index]); } else { char *tmpv = (char *)dmabuf; memcpy(tmpv, sgbuffer, amount); tmpv += amount; dmabuf = (unsigned *)tmpv; } kunmap_atomic(sgbuffer, KM_BIO_SRC_IRQ); if (size == 0) break; } /* * Check that we didn't get a request to transfer * more data than can fit into the SG list. */ BUG_ON(size != 0); }
static void at91_mci_enable(struct at91mci_host *host) { unsigned int mr; at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); at91_mci_write(host, AT91_MCI_DTOR, AT91_MCI_DTOMUL_1M | AT91_MCI_DTOCYC); mr = AT91_MCI_PDCMODE | 0x34a; if (at91mci_is_mci1rev2xx()) mr |= AT91_MCI_RDPROOF | AT91_MCI_WRPROOF; at91_mci_write(host, AT91_MCI_MR, mr); at91_mci_write(host, AT91_MCI_SDCR, host->board->slot_b); }
static inline void at91_mci_sg_to_dma(struct at91mci_host *host, struct mmc_data *data) { unsigned int len, i, size; unsigned *dmabuf = host->buffer; size = data->blksz * data->blocks; len = data->sg_len; if (at91mci_is_mci1rev2xx()) if (host->total_length == 12) memset(dmabuf, 0, 12); for (i = 0; i < len; i++) { struct scatterlist *sg; int amount; unsigned int *sgbuffer; sg = &data->sg[i]; sgbuffer = kmap_atomic(sg_page(sg)) + sg->offset; amount = min(size, sg->length); size -= amount; if (cpu_is_at91rm9200()) { int index; for (index = 0; index < (amount / 4); index++) *dmabuf++ = swab32(sgbuffer[index]); } else { char *tmpv = (char *)dmabuf; memcpy(tmpv, sgbuffer, amount); tmpv += amount; dmabuf = (unsigned *)tmpv; } kunmap_atomic(sgbuffer); if (size == 0) break; } BUG_ON(size != 0); }
static int __init at91_mci_probe(struct platform_device *pdev) { struct mmc_host *mmc; struct at91mci_host *host; struct resource *res; int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENXIO; if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) return -EBUSY; mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev); if (!mmc) { ret = -ENOMEM; dev_dbg(&pdev->dev, "couldn't allocate mmc host\n"); goto fail6; } mmc->ops = &at91_mci_ops; mmc->f_min = 375000; mmc->f_max = 25000000; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; mmc->caps = 0; mmc->max_blk_size = MCI_MAXBLKSIZE; mmc->max_blk_count = MCI_BLKATONCE; mmc->max_req_size = MCI_BUFSIZE; mmc->max_segs = MCI_BLKATONCE; mmc->max_seg_size = MCI_BUFSIZE; host = mmc_priv(mmc); host->mmc = mmc; host->bus_mode = 0; host->board = pdev->dev.platform_data; if (host->board->wire4) { 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; } if (at91mci_is_mci1rev2xx()) { if (host->board->wire4 || !host->board->slot_b) mmc->caps |= MMC_CAP_SDIO_IRQ; } if (gpio_is_valid(host->board->det_pin)) { ret = gpio_request(host->board->det_pin, "mmc_detect"); if (ret < 0) { dev_dbg(&pdev->dev, "couldn't claim card detect pin\n"); goto fail4b; } } if (gpio_is_valid(host->board->wp_pin)) { ret = gpio_request(host->board->wp_pin, "mmc_wp"); if (ret < 0) { dev_dbg(&pdev->dev, "couldn't claim wp sense pin\n"); goto fail4; } } if (gpio_is_valid(host->board->vcc_pin)) { ret = gpio_request(host->board->vcc_pin, "mmc_vcc"); if (ret < 0) { dev_dbg(&pdev->dev, "couldn't claim vcc switch pin\n"); goto fail3; } } host->mci_clk = clk_get(&pdev->dev, "mci_clk"); if (IS_ERR(host->mci_clk)) { ret = -ENODEV; dev_dbg(&pdev->dev, "no mci_clk?\n"); goto fail2; } host->baseaddr = ioremap(res->start, resource_size(res)); if (!host->baseaddr) { ret = -ENOMEM; goto fail1; } clk_enable(host->mci_clk); at91_mci_disable(host); at91_mci_enable(host); host->irq = platform_get_irq(pdev, 0); ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED, mmc_hostname(mmc), host); if (ret) { dev_dbg(&pdev->dev, "request MCI interrupt failed\n"); goto fail0; } setup_timer(&host->timer, at91_timeout_timer, (unsigned long)host); platform_set_drvdata(pdev, mmc); if (gpio_is_valid(host->board->det_pin)) { host->present = !gpio_get_value(host->board->det_pin); } else host->present = -1; mmc_add_host(mmc); if (gpio_is_valid(host->board->det_pin)) { ret = request_irq(gpio_to_irq(host->board->det_pin), at91_mmc_det_irq, 0, mmc_hostname(mmc), host); if (ret) dev_warn(&pdev->dev, "request MMC detect irq failed\n"); else device_init_wakeup(&pdev->dev, 1); } pr_debug("Added MCI driver\n"); return 0; fail0: clk_disable(host->mci_clk); iounmap(host->baseaddr); fail1: clk_put(host->mci_clk); fail2: if (gpio_is_valid(host->board->vcc_pin)) gpio_free(host->board->vcc_pin); fail3: if (gpio_is_valid(host->board->wp_pin)) gpio_free(host->board->wp_pin); fail4: if (gpio_is_valid(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: release_mem_region(res->start, resource_size(res)); dev_err(&pdev->dev, "probe failed, err %d\n", ret); return ret; }
static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd) { unsigned int cmdr, mr; unsigned int block_length; struct mmc_data *data = cmd->data; unsigned int blocks; unsigned int ier = 0; host->cmd = cmd; if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) { pr_debug("Clearing timeout\n"); at91_mci_write(host, AT91_MCI_ARGR, 0); at91_mci_write(host, AT91_MCI_CMDR, AT91_MCI_OPDCMD); while (!(at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_CMDRDY)) { pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR)); } } cmdr = cmd->opcode; if (mmc_resp_type(cmd) == MMC_RSP_NONE) cmdr |= AT91_MCI_RSPTYP_NONE; else { cmdr |= AT91_MCI_MAXLAT; if (mmc_resp_type(cmd) == MMC_RSP_R2) cmdr |= AT91_MCI_RSPTYP_136; else cmdr |= AT91_MCI_RSPTYP_48; } if (data) { if (cpu_is_at91rm9200() || cpu_is_at91sam9261()) { if (data->blksz & 0x3) { pr_debug("Unsupported block size\n"); cmd->error = -EINVAL; mmc_request_done(host->mmc, host->request); return; } if (data->flags & MMC_DATA_STREAM) { pr_debug("Stream commands not supported\n"); cmd->error = -EINVAL; mmc_request_done(host->mmc, host->request); return; } } block_length = data->blksz; blocks = data->blocks; if (data->flags & MMC_DATA_READ) cmdr |= (AT91_MCI_TRDIR | AT91_MCI_TRCMD_START); else if (data->flags & MMC_DATA_WRITE) cmdr |= AT91_MCI_TRCMD_START; if (cmd->opcode == SD_IO_RW_EXTENDED) { cmdr |= AT91_MCI_TRTYP_SDIO_BLOCK; } else { if (data->flags & MMC_DATA_STREAM) cmdr |= AT91_MCI_TRTYP_STREAM; if (data->blocks > 1) cmdr |= AT91_MCI_TRTYP_MULTIPLE; } } else { block_length = 0; blocks = 0; } if (host->flags & FL_SENT_STOP) cmdr |= AT91_MCI_TRCMD_STOP; if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) cmdr |= AT91_MCI_OPDCMD; pr_debug("Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08X)\n", cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(host, AT91_MCI_MR)); if (!data) { at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS | ATMEL_PDC_RXTDIS); at91_mci_write(host, ATMEL_PDC_RPR, 0); at91_mci_write(host, ATMEL_PDC_RCR, 0); at91_mci_write(host, ATMEL_PDC_RNPR, 0); at91_mci_write(host, ATMEL_PDC_RNCR, 0); at91_mci_write(host, ATMEL_PDC_TPR, 0); at91_mci_write(host, ATMEL_PDC_TCR, 0); at91_mci_write(host, ATMEL_PDC_TNPR, 0); at91_mci_write(host, ATMEL_PDC_TNCR, 0); ier = AT91_MCI_CMDRDY; } else { mr = at91_mci_read(host, AT91_MCI_MR) & 0x5fff; mr |= (data->blksz & 0x3) ? AT91_MCI_PDCFBYTE : 0; mr |= (block_length << 16); mr |= AT91_MCI_PDCMODE; at91_mci_write(host, AT91_MCI_MR, mr); if (!(cpu_is_at91rm9200() || cpu_is_at91sam9261())) at91_mci_write(host, AT91_MCI_BLKR, AT91_MCI_BLKR_BCNT(blocks) | AT91_MCI_BLKR_BLKLEN(block_length)); at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); if (cmdr & AT91_MCI_TRCMD_START) { data->bytes_xfered = 0; host->transfer_index = 0; host->in_use_index = 0; if (cmdr & AT91_MCI_TRDIR) { host->total_length = 0; 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 ; } else { host->total_length = block_length * blocks; if (at91mci_is_mci1rev2xx()) if (host->total_length < 12) host->total_length = 12; at91_mci_sg_to_dma(host, data); pr_debug("Transmitting %d bytes\n", host->total_length); at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); at91_mci_write(host, ATMEL_PDC_TCR, (data->blksz & 0x3) ? host->total_length : host->total_length / 4); ier = AT91_MCI_CMDRDY; } } } at91_mci_write(host, AT91_MCI_ARGR, cmd->arg); at91_mci_write(host, AT91_MCI_CMDR, cmdr); if (cmdr & AT91_MCI_TRCMD_START) { if (cmdr & AT91_MCI_TRDIR) at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN); } at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier); }
/* * Send a command */ static void at91_mci_send_command(struct at91mci_host *host, struct mmc_command *cmd) { unsigned int cmdr, mr; unsigned int block_length; struct mmc_data *data = cmd->data; unsigned int blocks; unsigned int ier = 0; host->cmd = cmd; /* Needed for leaving busy state before CMD1 */ if ((at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_RTOE) && (cmd->opcode == 1)) { pr_debug("Clearing timeout\n"); at91_mci_write(host, AT91_MCI_ARGR, 0); at91_mci_write(host, AT91_MCI_CMDR, AT91_MCI_OPDCMD); while (!(at91_mci_read(host, AT91_MCI_SR) & AT91_MCI_CMDRDY)) { /* spin */ pr_debug("Clearing: SR = %08X\n", at91_mci_read(host, AT91_MCI_SR)); } } cmdr = cmd->opcode; if (mmc_resp_type(cmd) == MMC_RSP_NONE) cmdr |= AT91_MCI_RSPTYP_NONE; else { /* if a response is expected then allow maximum response latancy */ cmdr |= AT91_MCI_MAXLAT; /* set 136 bit response for R2, 48 bit response otherwise */ if (mmc_resp_type(cmd) == MMC_RSP_R2) cmdr |= AT91_MCI_RSPTYP_136; else cmdr |= AT91_MCI_RSPTYP_48; } if (data) { if (cpu_is_at91rm9200() || cpu_is_at91sam9261()) { if (data->blksz & 0x3) { pr_debug("Unsupported block size\n"); cmd->error = -EINVAL; mmc_request_done(host->mmc, host->request); return; } if (data->flags & MMC_DATA_STREAM) { pr_debug("Stream commands not supported\n"); cmd->error = -EINVAL; mmc_request_done(host->mmc, host->request); return; } } block_length = data->blksz; blocks = data->blocks; /* always set data start - also set direction flag for read */ if (data->flags & MMC_DATA_READ) cmdr |= (AT91_MCI_TRDIR | AT91_MCI_TRCMD_START); else if (data->flags & MMC_DATA_WRITE) cmdr |= AT91_MCI_TRCMD_START; if (cmd->opcode == SD_IO_RW_EXTENDED) { cmdr |= AT91_MCI_TRTYP_SDIO_BLOCK; } else { if (data->flags & MMC_DATA_STREAM) cmdr |= AT91_MCI_TRTYP_STREAM; if (data->blocks > 1) cmdr |= AT91_MCI_TRTYP_MULTIPLE; } } else { block_length = 0; blocks = 0; } if (host->flags & FL_SENT_STOP) cmdr |= AT91_MCI_TRCMD_STOP; if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) cmdr |= AT91_MCI_OPDCMD; /* * Set the arguments and send the command */ pr_debug("Sending command %d as %08X, arg = %08X, blocks = %d, length = %d (MR = %08X)\n", cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(host, AT91_MCI_MR)); if (!data) { at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS | ATMEL_PDC_RXTDIS); at91_mci_write(host, ATMEL_PDC_RPR, 0); at91_mci_write(host, ATMEL_PDC_RCR, 0); at91_mci_write(host, ATMEL_PDC_RNPR, 0); at91_mci_write(host, ATMEL_PDC_RNCR, 0); at91_mci_write(host, ATMEL_PDC_TPR, 0); at91_mci_write(host, ATMEL_PDC_TCR, 0); at91_mci_write(host, ATMEL_PDC_TNPR, 0); at91_mci_write(host, ATMEL_PDC_TNCR, 0); ier = AT91_MCI_CMDRDY; } else { /* zero block length and PDC mode */ mr = at91_mci_read(host, AT91_MCI_MR) & 0x5fff; mr |= (data->blksz & 0x3) ? AT91_MCI_PDCFBYTE : 0; mr |= (block_length << 16); mr |= AT91_MCI_PDCMODE; at91_mci_write(host, AT91_MCI_MR, mr); if (!(cpu_is_at91rm9200() || cpu_is_at91sam9261())) at91_mci_write(host, AT91_MCI_BLKR, AT91_MCI_BLKR_BCNT(blocks) | AT91_MCI_BLKR_BLKLEN(block_length)); /* * Disable the PDC controller */ at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); if (cmdr & AT91_MCI_TRCMD_START) { data->bytes_xfered = 0; host->transfer_index = 0; host->in_use_index = 0; if (cmdr & AT91_MCI_TRDIR) { /* * Handle a read */ host->total_length = 0; 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 { /* * Handle a write */ host->total_length = block_length * blocks; /* * MCI1 rev2xx Data Write Operation and * number of bytes erratum */ if (at91mci_is_mci1rev2xx()) if (host->total_length < 12) host->total_length = 12; at91_mci_sg_to_dma(host, data); pr_debug("Transmitting %d bytes\n", host->total_length); at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); at91_mci_write(host, ATMEL_PDC_TCR, (data->blksz & 0x3) ? host->total_length : host->total_length / 4); ier = AT91_MCI_CMDRDY; } } } /* * Send the command and then enable the PDC - not the other way round as * the data sheet says */ at91_mci_write(host, AT91_MCI_ARGR, cmd->arg); at91_mci_write(host, AT91_MCI_CMDR, cmdr); if (cmdr & AT91_MCI_TRCMD_START) { if (cmdr & AT91_MCI_TRDIR) at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN); } /* Enable selected interrupts */ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier); }
/* * Probe for the device */ static int __init at91_mci_probe(struct platform_device *pdev) { struct mmc_host *mmc; struct at91mci_host *host; struct resource *res; int ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENXIO; if (!request_mem_region(res->start, res->end - res->start + 1, DRIVER_NAME)) return -EBUSY; mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev); if (!mmc) { ret = -ENOMEM; dev_dbg(&pdev->dev, "couldn't allocate mmc host\n"); goto fail6; } mmc->ops = &at91_mci_ops; mmc->f_min = 375000; mmc->f_max = 25000000; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; mmc->caps = 0; mmc->max_blk_size = 4095; mmc->max_blk_count = mmc->max_req_size; host = mmc_priv(mmc); host->mmc = mmc; host->buffer = NULL; host->bus_mode = 0; host->board = pdev->dev.platform_data; if (host->board->wire4) { 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"); } /* 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) */ if (host->board->det_pin) { 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; } } if (host->board->wp_pin) { ret = gpio_request(host->board->wp_pin, "mmc_wp"); if (ret < 0) { dev_dbg(&pdev->dev, "couldn't claim wp sense pin\n"); goto fail4; } } if (host->board->vcc_pin) { ret = gpio_request(host->board->vcc_pin, "mmc_vcc"); if (ret < 0) { dev_dbg(&pdev->dev, "couldn't claim vcc switch pin\n"); goto fail3; } } /* * Get Clock */ host->mci_clk = clk_get(&pdev->dev, "mci_clk"); if (IS_ERR(host->mci_clk)) { ret = -ENODEV; dev_dbg(&pdev->dev, "no mci_clk?\n"); goto fail2; } /* * Map I/O region */ host->baseaddr = ioremap(res->start, res->end - res->start + 1); if (!host->baseaddr) { ret = -ENOMEM; goto fail1; } /* * Reset hardware */ clk_enable(host->mci_clk); /* Enable the peripheral clock */ at91_mci_disable(host); at91_mci_enable(host); /* * Allocate the MCI interrupt */ host->irq = platform_get_irq(pdev, 0); ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED, mmc_hostname(mmc), host); if (ret) { dev_dbg(&pdev->dev, "request MCI interrupt failed\n"); goto fail0; } setup_timer(&host->timer, at91_timeout_timer, (unsigned long)host); platform_set_drvdata(pdev, mmc); /* * Add host to MMC layer */ if (host->board->det_pin) { host->present = !gpio_get_value(host->board->det_pin); } else host->present = -1; mmc_add_host(mmc); /* * monitor card insertion/removal if we can */ if (host->board->det_pin) { ret = request_irq(gpio_to_irq(host->board->det_pin), at91_mmc_det_irq, 0, mmc_hostname(mmc), host); if (ret) dev_warn(&pdev->dev, "request MMC detect irq failed\n"); else device_init_wakeup(&pdev->dev, 1); } pr_debug("Added MCI driver\n"); return 0; fail0: clk_disable(host->mci_clk); iounmap(host->baseaddr); fail1: clk_put(host->mci_clk); fail2: if (host->board->vcc_pin) gpio_free(host->board->vcc_pin); fail3: if (host->board->wp_pin) gpio_free(host->board->wp_pin); fail4: if (host->board->det_pin) gpio_free(host->board->det_pin); fail5: mmc_free_host(mmc); fail6: release_mem_region(res->start, res->end - res->start + 1); dev_err(&pdev->dev, "probe failed, err %d\n", ret); return ret; }