pxamci.c
来自「linux 内核源代码」· C语言 代码 · 共 685 行 · 第 1/2 页
C
685 行
cmdat &= ~CMDAT_BUSY; cmdat |= CMDAT_DATAEN | CMDAT_DMAEN; if (mrq->data->flags & MMC_DATA_WRITE) cmdat |= CMDAT_WRITE; if (mrq->data->flags & MMC_DATA_STREAM) cmdat |= CMDAT_STREAM; } pxamci_start_cmd(host, mrq->cmd, cmdat);}static int pxamci_get_ro(struct mmc_host *mmc){ struct pxamci_host *host = mmc_priv(mmc); if (host->pdata && host->pdata->get_ro) return host->pdata->get_ro(mmc_dev(mmc)); /* Host doesn't support read only detection so assume writeable */ return 0;}static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios){ struct pxamci_host *host = mmc_priv(mmc); if (ios->clock) { unsigned long rate = host->clkrate; unsigned int clk = rate / ios->clock; if (host->clkrt == CLKRT_OFF) clk_enable(host->clk); /* * clk might result in a lower divisor than we * desire. check for that condition and adjust * as appropriate. */ if (rate / clk > ios->clock) clk <<= 1; host->clkrt = fls(clk) - 1; /* * we write clkrt on the next command */ } else { pxamci_stop_clock(host); if (host->clkrt != CLKRT_OFF) { host->clkrt = CLKRT_OFF; clk_disable(host->clk); } } if (host->power_mode != ios->power_mode) { host->power_mode = ios->power_mode; if (host->pdata && host->pdata->setpower) host->pdata->setpower(mmc_dev(mmc), ios->vdd); if (ios->power_mode == MMC_POWER_ON) host->cmdat |= CMDAT_INIT; } if (ios->bus_width == MMC_BUS_WIDTH_4) host->cmdat |= CMDAT_SD_4DAT; else host->cmdat &= ~CMDAT_SD_4DAT; pr_debug("PXAMCI: clkrt = %x cmdat = %x\n", host->clkrt, host->cmdat);}static void pxamci_enable_sdio_irq(struct mmc_host *host, int enable){ struct pxamci_host *pxa_host = mmc_priv(host); if (enable) pxamci_enable_irq(pxa_host, SDIO_INT); else pxamci_disable_irq(pxa_host, SDIO_INT);}static const struct mmc_host_ops pxamci_ops = { .request = pxamci_request, .get_ro = pxamci_get_ro, .set_ios = pxamci_set_ios, .enable_sdio_irq = pxamci_enable_sdio_irq,};static void pxamci_dma_irq(int dma, void *devid){ struct pxamci_host *host = devid; int dcsr = DCSR(dma); DCSR(dma) = dcsr & ~DCSR_STOPIRQEN; if (dcsr & DCSR_ENDINTR) { writel(BUF_PART_FULL, host->base + MMC_PRTBUF); } else { printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n", mmc_hostname(host->mmc), dma, dcsr); host->data->error = -EIO; pxamci_data_done(host, 0); }}static irqreturn_t pxamci_detect_irq(int irq, void *devid){ struct pxamci_host *host = mmc_priv(devid); mmc_detect_change(devid, host->pdata->detect_delay); return IRQ_HANDLED;}static int pxamci_probe(struct platform_device *pdev){ struct mmc_host *mmc; struct pxamci_host *host = NULL; struct resource *r; int ret, irq; r = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); if (!r || irq < 0) return -ENXIO; r = request_mem_region(r->start, SZ_4K, DRIVER_NAME); if (!r) return -EBUSY; mmc = mmc_alloc_host(sizeof(struct pxamci_host), &pdev->dev); if (!mmc) { ret = -ENOMEM; goto out; } mmc->ops = &pxamci_ops; /* * We can do SG-DMA, but we don't because we never know how much * data we successfully wrote to the card. */ mmc->max_phys_segs = NR_SG; /* * Our hardware DMA can handle a maximum of one page per SG entry. */ mmc->max_seg_size = PAGE_SIZE; /* * Block length register is only 10 bits before PXA27x. */ mmc->max_blk_size = (cpu_is_pxa21x() || cpu_is_pxa25x()) ? 1023 : 2048; /* * Block count register is 16 bits. */ mmc->max_blk_count = 65535; host = mmc_priv(mmc); host->mmc = mmc; host->dma = -1; host->pdata = pdev->dev.platform_data; host->clkrt = CLKRT_OFF; host->clk = clk_get(&pdev->dev, "MMCCLK"); if (IS_ERR(host->clk)) { ret = PTR_ERR(host->clk); host->clk = NULL; goto out; } host->clkrate = clk_get_rate(host->clk); /* * Calculate minimum clock rate, rounding up. */ mmc->f_min = (host->clkrate + 63) / 64; mmc->f_max = host->clkrate; mmc->ocr_avail = host->pdata ? host->pdata->ocr_mask : MMC_VDD_32_33|MMC_VDD_33_34; mmc->caps = 0; host->cmdat = 0; if (!cpu_is_pxa21x() && !cpu_is_pxa25x()) { mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ; host->cmdat |= CMDAT_SDIO_INT_EN; } host->sg_cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); if (!host->sg_cpu) { ret = -ENOMEM; goto out; } spin_lock_init(&host->lock); host->res = r; host->irq = irq; host->imask = MMC_I_MASK_ALL; host->base = ioremap(r->start, SZ_4K); if (!host->base) { ret = -ENOMEM; goto out; } /* * Ensure that the host controller is shut down, and setup * with our defaults. */ pxamci_stop_clock(host); writel(0, host->base + MMC_SPI); writel(64, host->base + MMC_RESTO); writel(host->imask, host->base + MMC_I_MASK); host->dma = pxa_request_dma(DRIVER_NAME, DMA_PRIO_LOW, pxamci_dma_irq, host); if (host->dma < 0) { ret = -EBUSY; goto out; } ret = request_irq(host->irq, pxamci_irq, 0, DRIVER_NAME, host); if (ret) goto out; platform_set_drvdata(pdev, mmc); if (host->pdata && host->pdata->init) host->pdata->init(&pdev->dev, pxamci_detect_irq, mmc); mmc_add_host(mmc); return 0; out: if (host) { if (host->dma >= 0) pxa_free_dma(host->dma); if (host->base) iounmap(host->base); if (host->sg_cpu) dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); if (host->clk) clk_put(host->clk); } if (mmc) mmc_free_host(mmc); release_resource(r); return ret;}static int pxamci_remove(struct platform_device *pdev){ struct mmc_host *mmc = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); if (mmc) { struct pxamci_host *host = mmc_priv(mmc); if (host->pdata && host->pdata->exit) host->pdata->exit(&pdev->dev, mmc); mmc_remove_host(mmc); pxamci_stop_clock(host); writel(TXFIFO_WR_REQ|RXFIFO_RD_REQ|CLK_IS_OFF|STOP_CMD| END_CMD_RES|PRG_DONE|DATA_TRAN_DONE, host->base + MMC_I_MASK); DRCMRRXMMC = 0; DRCMRTXMMC = 0; free_irq(host->irq, host); pxa_free_dma(host->dma); iounmap(host->base); dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); clk_put(host->clk); release_resource(host->res); mmc_free_host(mmc); } return 0;}#ifdef CONFIG_PMstatic int pxamci_suspend(struct platform_device *dev, pm_message_t state){ struct mmc_host *mmc = platform_get_drvdata(dev); int ret = 0; if (mmc) ret = mmc_suspend_host(mmc, state); return ret;}static int pxamci_resume(struct platform_device *dev){ struct mmc_host *mmc = platform_get_drvdata(dev); int ret = 0; if (mmc) ret = mmc_resume_host(mmc); return ret;}#else#define pxamci_suspend NULL#define pxamci_resume NULL#endifstatic struct platform_driver pxamci_driver = { .probe = pxamci_probe, .remove = pxamci_remove, .suspend = pxamci_suspend, .resume = pxamci_resume, .driver = { .name = DRIVER_NAME, },};static int __init pxamci_init(void){ return platform_driver_register(&pxamci_driver);}static void __exit pxamci_exit(void){ platform_driver_unregister(&pxamci_driver);}module_init(pxamci_init);module_exit(pxamci_exit);MODULE_DESCRIPTION("PXA Multimedia Card Interface Driver");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?