📄 at91_mci.c
字号:
else at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_TXTEN); } return ier;}/* * Wait for a command to complete */static void at91mci_process_command(struct at91mci_host *host, struct mmc_command *cmd){ unsigned int ier; ier = at91_mci_send_command(host, cmd); pr_debug("setting ier to %08X\n", ier); /* Stop on errors or the required value */ at91_mci_write(host, AT91_MCI_IER, AT91_MCI_ERRORS | ier);}/* * Process the next step in the request */static void at91mci_process_next(struct at91mci_host *host){ if (!(host->flags & FL_SENT_COMMAND)) { host->flags |= FL_SENT_COMMAND; at91mci_process_command(host, host->request->cmd); } else if ((!(host->flags & FL_SENT_STOP)) && host->request->stop) { host->flags |= FL_SENT_STOP; at91mci_process_command(host, host->request->stop); } else mmc_request_done(host->mmc, host->request);}/* * Handle a command that has been completed */static void at91mci_completed_command(struct at91mci_host *host){ struct mmc_command *cmd = host->cmd; unsigned int status; at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); cmd->resp[0] = at91_mci_read(host, AT91_MCI_RSPR(0)); cmd->resp[1] = at91_mci_read(host, AT91_MCI_RSPR(1)); cmd->resp[2] = at91_mci_read(host, AT91_MCI_RSPR(2)); cmd->resp[3] = at91_mci_read(host, AT91_MCI_RSPR(3)); if (host->buffer) { dma_free_coherent(NULL, host->total_length, host->buffer, host->physical_address); host->buffer = NULL; } status = at91_mci_read(host, AT91_MCI_SR); pr_debug("Status = %08X [%08X %08X %08X %08X]\n", status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); if (status & (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE | AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE | AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)) { if ((status & AT91_MCI_RCRCE) && ((cmd->opcode == MMC_SEND_OP_COND) || (cmd->opcode == SD_APP_OP_COND))) { cmd->error = MMC_ERR_NONE; } else { if (status & (AT91_MCI_RTOE | AT91_MCI_DTOE)) cmd->error = MMC_ERR_TIMEOUT; else if (status & (AT91_MCI_RCRCE | AT91_MCI_DCRCE)) cmd->error = MMC_ERR_BADCRC; else if (status & (AT91_MCI_OVRE | AT91_MCI_UNRE)) cmd->error = MMC_ERR_FIFO; else cmd->error = MMC_ERR_FAILED; pr_debug("Error detected and set to %d (cmd = %d, retries = %d)\n", cmd->error, cmd->opcode, cmd->retries); } } else cmd->error = MMC_ERR_NONE; at91mci_process_next(host);}/* * Handle an MMC request */static void at91_mci_request(struct mmc_host *mmc, struct mmc_request *mrq){ struct at91mci_host *host = mmc_priv(mmc); host->request = mrq; host->flags = 0; at91mci_process_next(host);}/* * Set the IOS */static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios){ int clkdiv; struct at91mci_host *host = mmc_priv(mmc); unsigned long at91_master_clock = clk_get_rate(host->mci_clk); host->bus_mode = ios->bus_mode; if (ios->clock == 0) { /* Disable the MCI controller */ at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIDIS); clkdiv = 0; } else { /* Enable the MCI controller */ at91_mci_write(host, AT91_MCI_CR, AT91_MCI_MCIEN); if ((at91_master_clock % (ios->clock * 2)) == 0) clkdiv = ((at91_master_clock / ios->clock) / 2) - 1; else clkdiv = (at91_master_clock / ios->clock) / 2; pr_debug("clkdiv = %d. mcck = %ld\n", clkdiv, at91_master_clock / (2 * (clkdiv + 1))); } if (ios->bus_width == MMC_BUS_WIDTH_4 && host->board->wire4) { pr_debug("MMC: Setting controller bus width to 4\n"); at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) | AT91_MCI_SDCBUS); } else { pr_debug("MMC: Setting controller bus width to 1\n"); at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); } /* Set the clock divider */ at91_mci_write(host, AT91_MCI_MR, (at91_mci_read(host, AT91_MCI_MR) & ~AT91_MCI_CLKDIV) | clkdiv); /* maybe switch power to the card */ if (host->board->vcc_pin) { switch (ios->power_mode) { case MMC_POWER_OFF: at91_set_gpio_value(host->board->vcc_pin, 0); break; case MMC_POWER_UP: case MMC_POWER_ON: at91_set_gpio_value(host->board->vcc_pin, 1); break; } }}/* * Handle an interrupt */static irqreturn_t at91_mci_irq(int irq, void *devid){ struct at91mci_host *host = devid; int completed = 0; unsigned int int_status, int_mask; int_status = at91_mci_read(host, AT91_MCI_SR); int_mask = at91_mci_read(host, AT91_MCI_IMR); pr_debug("MCI irq: status = %08X, %08X, %08X\n", int_status, int_mask, int_status & int_mask); int_status = int_status & int_mask; if (int_status & AT91_MCI_ERRORS) { completed = 1; if (int_status & AT91_MCI_UNRE) pr_debug("MMC: Underrun error\n"); if (int_status & AT91_MCI_OVRE) pr_debug("MMC: Overrun error\n"); if (int_status & AT91_MCI_DTOE) pr_debug("MMC: Data timeout\n"); if (int_status & AT91_MCI_DCRCE) pr_debug("MMC: CRC error in data\n"); if (int_status & AT91_MCI_RTOE) pr_debug("MMC: Response timeout\n"); if (int_status & AT91_MCI_RENDE) pr_debug("MMC: Response end bit error\n"); if (int_status & AT91_MCI_RCRCE) pr_debug("MMC: Response CRC error\n"); if (int_status & AT91_MCI_RDIRE) pr_debug("MMC: Response direction error\n"); if (int_status & AT91_MCI_RINDE) pr_debug("MMC: Response index error\n"); } else { /* Only continue processing if no errors */ if (int_status & AT91_MCI_TXBUFE) { pr_debug("TX buffer empty\n"); at91_mci_handle_transmitted(host); } if (int_status & AT91_MCI_RXBUFF) { pr_debug("RX buffer full\n"); at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY); } if (int_status & AT91_MCI_ENDTX) pr_debug("Transmit has ended\n"); if (int_status & AT91_MCI_ENDRX) { pr_debug("Receive has ended\n"); at91mci_post_dma_read(host); } if (int_status & AT91_MCI_NOTBUSY) { pr_debug("Card is ready\n"); at91_mci_write(host, AT91_MCI_IER, AT91_MCI_CMDRDY); } if (int_status & AT91_MCI_DTIP) pr_debug("Data transfer in progress\n"); if (int_status & AT91_MCI_BLKE) pr_debug("Block transfer has ended\n"); if (int_status & AT91_MCI_TXRDY) pr_debug("Ready to transmit\n"); if (int_status & AT91_MCI_RXRDY) pr_debug("Ready to receive\n"); if (int_status & AT91_MCI_CMDRDY) { pr_debug("Command ready\n"); completed = 1; } } if (completed) { pr_debug("Completed command\n"); at91_mci_write(host, AT91_MCI_IDR, 0xffffffff); at91mci_completed_command(host); } else at91_mci_write(host, AT91_MCI_IDR, int_status); return IRQ_HANDLED;}static irqreturn_t at91_mmc_det_irq(int irq, void *_host){ struct at91mci_host *host = _host; int present = !at91_get_gpio_value(irq); /* * we expect this irq on both insert and remove, * and use a short delay to debounce. */ if (present != host->present) { host->present = present; pr_debug("%s: card %s\n", mmc_hostname(host->mmc), present ? "insert" : "remove"); if (!present) { pr_debug("****** Resetting SD-card bus width ******\n"); at91_mci_write(host, AT91_MCI_SDCR, at91_mci_read(host, AT91_MCI_SDCR) & ~AT91_MCI_SDCBUS); } mmc_detect_change(host->mmc, msecs_to_jiffies(100)); } return IRQ_HANDLED;}static int at91_mci_get_ro(struct mmc_host *mmc){ int read_only = 0; struct at91mci_host *host = mmc_priv(mmc); if (host->board->wp_pin) { read_only = at91_get_gpio_value(host->board->wp_pin); printk(KERN_WARNING "%s: card is %s\n", mmc_hostname(mmc), (read_only ? "read-only" : "read-write") ); } else { printk(KERN_WARNING "%s: host does not support reading read-only " "switch. Assuming write-enable.\n", mmc_hostname(mmc)); } return read_only;}static const struct mmc_host_ops at91_mci_ops = { .request = at91_mci_request, .set_ios = at91_mci_set_ios, .get_ro = at91_mci_get_ro,};/* * Probe for the device */static int __init at91_mci_probe(struct platform_device *pdev){ struct mmc_host *mmc; struct at91mci_host *host; struct resource *res; int ret; pr_debug("Probe MCI devices\n"); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENXIO; if (!request_mem_region(res->start, res->end - res->start + 1, DRIVER_NAME)) return -EBUSY; mmc = mmc_alloc_host(sizeof(struct at91mci_host), &pdev->dev); if (!mmc) { pr_debug("Failed to allocate mmc host\n"); release_mem_region(res->start, res->end - res->start + 1); return -ENOMEM; } mmc->ops = &at91_mci_ops; mmc->f_min = 375000; mmc->f_max = 25000000; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; mmc->caps = MMC_CAP_BYTEBLOCK; host = mmc_priv(mmc); host->mmc = mmc; host->buffer = NULL; host->bus_mode = 0; host->board = pdev->dev.platform_data; if (host->board->wire4) {#ifdef SUPPORT_4WIRE mmc->caps |= MMC_CAP_4_BIT_DATA;#else printk("AT91 MMC: 4 wire bus mode not supported by this driver - using 1 wire\n");#endif } /* * Get Clock */ host->mci_clk = clk_get(&pdev->dev, "mci_clk"); if (IS_ERR(host->mci_clk)) { printk(KERN_ERR "AT91 MMC: no clock defined.\n"); mmc_free_host(mmc); release_mem_region(res->start, res->end - res->start + 1); return -ENODEV; } /* * Map I/O region */ host->baseaddr = ioremap(res->start, res->end - res->start + 1); if (!host->baseaddr) { clk_put(host->mci_clk); mmc_free_host(mmc); release_mem_region(res->start, res->end - res->start + 1); return -ENOMEM; } /* * Reset hardware */ clk_enable(host->mci_clk); /* Enable the peripheral clock */ at91_mci_disable(host); at91_mci_enable(host); /* * Allocate the MCI interrupt */ host->irq = platform_get_irq(pdev, 0); ret = request_irq(host->irq, at91_mci_irq, IRQF_SHARED, DRIVER_NAME, host); if (ret) { printk(KERN_ERR "AT91 MMC: Failed to request MCI interrupt\n"); clk_disable(host->mci_clk); clk_put(host->mci_clk); mmc_free_host(mmc); iounmap(host->baseaddr); release_mem_region(res->start, res->end - res->start + 1); return ret; } platform_set_drvdata(pdev, mmc); /* * Add host to MMC layer */ if (host->board->det_pin) host->present = !at91_get_gpio_value(host->board->det_pin); else host->present = -1; mmc_add_host(mmc); /* * monitor card insertion/removal if we can */ if (host->board->det_pin) { ret = request_irq(host->board->det_pin, at91_mmc_det_irq, 0, DRIVER_NAME, host); if (ret) printk(KERN_ERR "AT91 MMC: Couldn't allocate MMC detect irq\n"); } pr_debug("Added MCI driver\n"); return 0;}/* * Remove a device */static int __exit at91_mci_remove(struct platform_device *pdev){ struct mmc_host *mmc = platform_get_drvdata(pdev); struct at91mci_host *host; struct resource *res; if (!mmc) return -1; host = mmc_priv(mmc); if (host->present != -1) { free_irq(host->board->det_pin, host); cancel_delayed_work(&host->mmc->detect); } at91_mci_disable(host); mmc_remove_host(mmc); free_irq(host->irq, host); clk_disable(host->mci_clk); /* Disable the peripheral clock */ clk_put(host->mci_clk); iounmap(host->baseaddr); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, res->end - res->start + 1); mmc_free_host(mmc); platform_set_drvdata(pdev, NULL); pr_debug("MCI Removed\n"); return 0;}#ifdef CONFIG_PMstatic int at91_mci_suspend(struct platform_device *pdev, pm_message_t state){ struct mmc_host *mmc = platform_get_drvdata(pdev); int ret = 0; if (mmc) ret = mmc_suspend_host(mmc, state); return ret;}static int at91_mci_resume(struct platform_device *pdev){ struct mmc_host *mmc = platform_get_drvdata(pdev); int ret = 0; if (mmc) ret = mmc_resume_host(mmc); return ret;}#else#define at91_mci_suspend NULL#define at91_mci_resume NULL#endifstatic struct platform_driver at91_mci_driver = { .remove = __exit_p(at91_mci_remove), .suspend = at91_mci_suspend, .resume = at91_mci_resume, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, },};static int __init at91_mci_init(void){ return platform_driver_probe(&at91_mci_driver, at91_mci_probe);}static void __exit at91_mci_exit(void){ platform_driver_unregister(&at91_mci_driver);}module_init(at91_mci_init);module_exit(at91_mci_exit);MODULE_DESCRIPTION("AT91 Multimedia Card Interface driver");MODULE_AUTHOR("Nick Randell");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -