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 + -
显示快捷键?