mmci.c
来自「linux 内核源代码」· C语言 代码 · 共 712 行 · 第 1/2 页
C
712 行
u32 status; int ret = 0; spin_lock(&host->lock); do { struct mmc_command *cmd; struct mmc_data *data; status = readl(host->base + MMCISTATUS); status &= readl(host->base + MMCIMASK0); writel(status, host->base + MMCICLEAR); DBG(host, "irq0 %08x\n", status); data = host->data; if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN| MCI_RXOVERRUN|MCI_DATAEND|MCI_DATABLOCKEND) && data) mmci_data_irq(host, data, status); cmd = host->cmd; if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND) && cmd) mmci_cmd_irq(host, cmd, status); ret = 1; } while (status); spin_unlock(&host->lock); return IRQ_RETVAL(ret);}static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq){ struct mmci_host *host = mmc_priv(mmc); WARN_ON(host->mrq != NULL); if (mrq->data && !is_power_of_2(mrq->data->blksz)) { printk(KERN_ERR "%s: Unsupported block size (%d bytes)\n", mmc_hostname(mmc), mrq->data->blksz); mrq->cmd->error = -EINVAL; mmc_request_done(mmc, mrq); return; } spin_lock_irq(&host->lock); host->mrq = mrq; if (mrq->data && mrq->data->flags & MMC_DATA_READ) mmci_start_data(host, mrq->data); mmci_start_command(host, mrq->cmd, 0); spin_unlock_irq(&host->lock);}static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios){ struct mmci_host *host = mmc_priv(mmc); u32 clk = 0, pwr = 0; if (ios->clock) { if (ios->clock >= host->mclk) { clk = MCI_CLK_BYPASS; host->cclk = host->mclk; } else { clk = host->mclk / (2 * ios->clock) - 1; if (clk > 256) clk = 255; host->cclk = host->mclk / (2 * (clk + 1)); } clk |= MCI_CLK_ENABLE; } if (host->plat->translate_vdd) pwr |= host->plat->translate_vdd(mmc_dev(mmc), ios->vdd); switch (ios->power_mode) { case MMC_POWER_OFF: break; case MMC_POWER_UP: pwr |= MCI_PWR_UP; break; case MMC_POWER_ON: pwr |= MCI_PWR_ON; break; } if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) pwr |= MCI_ROD; writel(clk, host->base + MMCICLOCK); if (host->pwr != pwr) { host->pwr = pwr; writel(pwr, host->base + MMCIPOWER); }}static const struct mmc_host_ops mmci_ops = { .request = mmci_request, .set_ios = mmci_set_ios,};static void mmci_check_status(unsigned long data){ struct mmci_host *host = (struct mmci_host *)data; unsigned int status; status = host->plat->status(mmc_dev(host->mmc)); if (status ^ host->oldstat) mmc_detect_change(host->mmc, 0); host->oldstat = status; mod_timer(&host->timer, jiffies + HZ);}static int mmci_probe(struct amba_device *dev, void *id){ struct mmc_platform_data *plat = dev->dev.platform_data; struct mmci_host *host; struct mmc_host *mmc; int ret; /* must have platform data */ if (!plat) { ret = -EINVAL; goto out; } ret = amba_request_regions(dev, DRIVER_NAME); if (ret) goto out; mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev); if (!mmc) { ret = -ENOMEM; goto rel_regions; } host = mmc_priv(mmc); host->clk = clk_get(&dev->dev, "MCLK"); if (IS_ERR(host->clk)) { ret = PTR_ERR(host->clk); host->clk = NULL; goto host_free; } ret = clk_enable(host->clk); if (ret) goto clk_free; host->plat = plat; host->mclk = clk_get_rate(host->clk); host->mmc = mmc; host->base = ioremap(dev->res.start, SZ_4K); if (!host->base) { ret = -ENOMEM; goto clk_disable; } mmc->ops = &mmci_ops; mmc->f_min = (host->mclk + 511) / 512; mmc->f_max = min(host->mclk, fmax); mmc->ocr_avail = plat->ocr_mask; mmc->caps = MMC_CAP_MULTIWRITE; /* * We can do SGIO */ mmc->max_hw_segs = 16; mmc->max_phys_segs = NR_SG; /* * Since we only have a 16-bit data length register, we must * ensure that we don't exceed 2^16-1 bytes in a single request. */ mmc->max_req_size = 65535; /* * Set the maximum segment size. Since we aren't doing DMA * (yet) we are only limited by the data length register. */ mmc->max_seg_size = mmc->max_req_size; /* * Block size can be up to 2048 bytes, but must be a power of two. */ mmc->max_blk_size = 2048; /* * No limit on the number of blocks transferred. */ mmc->max_blk_count = mmc->max_req_size; spin_lock_init(&host->lock); writel(0, host->base + MMCIMASK0); writel(0, host->base + MMCIMASK1); writel(0xfff, host->base + MMCICLEAR); ret = request_irq(dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host); if (ret) goto unmap; ret = request_irq(dev->irq[1], mmci_pio_irq, IRQF_SHARED, DRIVER_NAME " (pio)", host); if (ret) goto irq0_free; writel(MCI_IRQENABLE, host->base + MMCIMASK0); amba_set_drvdata(dev, mmc); mmc_add_host(mmc); printk(KERN_INFO "%s: MMCI rev %x cfg %02x at 0x%016llx irq %d,%d\n", mmc_hostname(mmc), amba_rev(dev), amba_config(dev), (unsigned long long)dev->res.start, dev->irq[0], dev->irq[1]); init_timer(&host->timer); host->timer.data = (unsigned long)host; host->timer.function = mmci_check_status; host->timer.expires = jiffies + HZ; add_timer(&host->timer); return 0; irq0_free: free_irq(dev->irq[0], host); unmap: iounmap(host->base); clk_disable: clk_disable(host->clk); clk_free: clk_put(host->clk); host_free: mmc_free_host(mmc); rel_regions: amba_release_regions(dev); out: return ret;}static int mmci_remove(struct amba_device *dev){ struct mmc_host *mmc = amba_get_drvdata(dev); amba_set_drvdata(dev, NULL); if (mmc) { struct mmci_host *host = mmc_priv(mmc); del_timer_sync(&host->timer); mmc_remove_host(mmc); writel(0, host->base + MMCIMASK0); writel(0, host->base + MMCIMASK1); writel(0, host->base + MMCICOMMAND); writel(0, host->base + MMCIDATACTRL); free_irq(dev->irq[0], host); free_irq(dev->irq[1], host); iounmap(host->base); clk_disable(host->clk); clk_put(host->clk); mmc_free_host(mmc); amba_release_regions(dev); } return 0;}#ifdef CONFIG_PMstatic int mmci_suspend(struct amba_device *dev, pm_message_t state){ struct mmc_host *mmc = amba_get_drvdata(dev); int ret = 0; if (mmc) { struct mmci_host *host = mmc_priv(mmc); ret = mmc_suspend_host(mmc, state); if (ret == 0) writel(0, host->base + MMCIMASK0); } return ret;}static int mmci_resume(struct amba_device *dev){ struct mmc_host *mmc = amba_get_drvdata(dev); int ret = 0; if (mmc) { struct mmci_host *host = mmc_priv(mmc); writel(MCI_IRQENABLE, host->base + MMCIMASK0); ret = mmc_resume_host(mmc); } return ret;}#else#define mmci_suspend NULL#define mmci_resume NULL#endifstatic struct amba_id mmci_ids[] = { { .id = 0x00041180, .mask = 0x000fffff, }, { .id = 0x00041181, .mask = 0x000fffff, }, { 0, 0 },};static struct amba_driver mmci_driver = { .drv = { .name = DRIVER_NAME, }, .probe = mmci_probe, .remove = mmci_remove, .suspend = mmci_suspend, .resume = mmci_resume, .id_table = mmci_ids,};static int __init mmci_init(void){ return amba_driver_register(&mmci_driver);}static void __exit mmci_exit(void){ amba_driver_unregister(&mmci_driver);}module_init(mmci_init);module_exit(mmci_exit);module_param(fmax, uint, 0444);MODULE_DESCRIPTION("ARM PrimeCell PL180/181 Multimedia Card Interface driver");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?