📄 omap_hsmmc.c
字号:
mmc_clk_disable_aggressive(host);}static struct mmc_host_ops mmc_omap_ops = { .request = omap_mmc_request, .set_ios = omap_mmc_set_ios,};/* * Routine implementing the driver probe method */static int __init omap_mmc_probe(struct platform_device *pdev){ struct omap_mmc_conf *minfo = pdev->dev.platform_data; struct mmc_host *mmc; struct mmc_omap_host *host = NULL; struct resource *res; int ret = 0, irq, *addr; if (minfo == NULL) { dev_err(&pdev->dev, "platform data missing\n"); return -ENXIO; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); irq = platform_get_irq(pdev, 0); if (res == NULL || irq < 0) return -ENXIO; res = request_mem_region(res->start, res->end - res->start + 1, pdev->name); if (res == NULL) return -EBUSY; mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev); if (!mmc) { ret = -ENOMEM; goto mmc_alloc_err; } host = mmc_priv(mmc); host->mmc = mmc; sema_init(&host->sem, 1); host->use_dma = OMAP_USE_DMA; host->dma_ch = -1; host->initstream = 0; host->mem_res = res; host->irq = irq; host->id = pdev->id; host->mapbase = (void *)host->mem_res->start; host->base = (void __iomem *)IO_ADDRESS(host->mapbase); mmc->ops = &mmc_omap_ops; mmc->f_min = 400000; mmc->f_max = 52000000; mmc->mode = MMC_CARD_NONE; host->card_detected = 1; host->is_high_capacity = 0; host->flag_err = 0; host->cmd_12 = 0; host->cmd_13 = 0; host->crc_retry = 0; spin_lock_init(&host->dma_lock); host->chain_id = -1; host->sg_dma_len = 0; if (cpu_is_omap2430()) { if (host->id == OMAP_MMC1_DEVID) { host->fclk = clk_get(&pdev->dev, "mmchs1_fck"); if (IS_ERR(host->fclk)) { ret = PTR_ERR(host->fclk); host->fclk = NULL; goto clk_get_err; } host->iclk = clk_get(&pdev->dev, "mmchs1_ick"); if (IS_ERR(host->iclk)) { ret = PTR_ERR(host->iclk); host->iclk = NULL; clk_put(host->fclk); goto clk_get_err; } host->dbclk = clk_get(&pdev->dev, "mmchsdb1_fck"); /* * Only through a error message, MMC can still work * without debounce clock. */ if (IS_ERR(host->dbclk)) dev_dbg(mmc_dev(host->mmc), "Failed to get debounce" "clock for MMC1\n"); } else { host->fclk = clk_get(&pdev->dev, "mmchs2_fck"); if (IS_ERR(host->fclk)) { ret = PTR_ERR(host->fclk); host->fclk = NULL; goto clk_get_err; } host->iclk = clk_get(&pdev->dev, "mmchs2_ick"); if (IS_ERR(host->iclk)) { ret = PTR_ERR(host->iclk); host->iclk = NULL; clk_put(host->fclk); goto clk_get_err; } host->dbclk = clk_get(&pdev->dev, "mmchsdb2_fck"); /* * Only through a error message, MMC can still work * without debounce clock. */ if (IS_ERR(host->dbclk)) dev_dbg(mmc_dev(host->mmc), "Failed to get" "debounce clock for MMC2\n"); } } if (cpu_is_omap34xx()) { /* 3430-ES1.0 Sil errata fix */ if (is_sil_rev_less_than(OMAP3430_REV_ES2_0)) { host->gptfck = clk_get(&pdev->dev, "gpt10_fck"); if (IS_ERR(host->gptfck)) { ret = PTR_ERR(host->gptfck); host->gptfck = NULL; goto clk_get_err; } } host->fclk = clk_get(&pdev->dev, "mmc_fck"); if (IS_ERR(host->fclk)) { ret = PTR_ERR(host->fclk); host->fclk = NULL; goto clk_get_err; } host->iclk = clk_get(&pdev->dev, "mmc_ick"); if (IS_ERR(host->iclk)) { ret = PTR_ERR(host->iclk); clk_put(host->fclk); host->iclk = NULL; goto clk_get_err; } } mmc_clk_enable(host); if (cpu_is_omap2430()) { if (clk_enable(host->dbclk) != 0) dev_dbg(mmc_dev(host->mmc), "Failed to enable debounce clock for MMC%d\n", host->id); omap_writel(omap_readl(OMAP2_CONTROL_DEVCONF1) | MMC1_ACTIVE_OVERWRITE, OMAP2_CONTROL_DEVCONF1); if (minfo->wire4) /* OMAP2430 ES2.0 and onwards can support 4-bit */ if (omap2_cpu_rev() >= 1) mmc->caps = MMC_CAP_4_BIT_DATA; } if (host->id == OMAP_MMC1_DEVID) { if (cpu_is_omap34xx()) { addr = (int *)&OMAP2_CONTROL_PBIAS_1; *addr |= (1 << 2); addr = (int *)&OMAP2_CONTROL_DEVCONF0; *addr |= (1 << 24); /* There is no 8-bit field in the structure yet */ if (minfo->wire4) { if (cpu_is_omap3410()) { mmc->caps = MMC_CAP_4_BIT_DATA; } else mmc->caps = MMC_CAP_8_BIT_DATA; } OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | SDVS30); } else if (cpu_is_omap2430()) { /* OMAP2430 MMC1 on ES1.0 and ES2.1 can support 3V */ if (omap2_cpu_rev() == 0 || omap2_cpu_rev() > 1) { OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | SDVS30); } else { /* OMAP2430 MMC1 ES2.0 - 1.8V only */ OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | SDVS18); } } } else if (host->id == OMAP_MMC2_DEVID) { if (cpu_is_omap34xx()) { addr = (int *)&OMAP2_CONTROL_DEVCONF1; *addr |= (1 << 6); if (minfo->wire4) mmc->caps = MMC_CAP_4_BIT_DATA; } OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | SDVS18); } /* Use scatterlist DMA to reduce per-transfer costs. * NOTE max_seg_size assumption that small blocks aren't * normally used (except e.g. for reading SD registers). */ mmc->max_phys_segs = 128; /* Largest sized scatter list * the driver could handle. Since this is * managed by us in software, we can tune * this value */ mmc->max_hw_segs = 128; /* Largest number of address/length * pairs the host adapter can actually * give at once to the device. This value * should be kept same as scatter list */ mmc->max_blk_size = 512; /* Block Length at max can be 1024 */ mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_seg_size = mmc->max_req_size; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; OMAP_HSMMC_WRITE(host->base, CAPA,OMAP_HSMMC_READ(host->base, CAPA) | VS30 | VS18); mmc->caps |= MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; /* Set the controller to AUTO IDLE mode */ OMAP_HSMMC_WRITE(host->base, SYSCONFIG, OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE); /* Set SD bus power bit */ OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | SDBP); if (machine_is_omap_2430sdp() || machine_is_omap_3430sdp() || machine_is_omap_3430labrador() || machine_is_omap3evm() || machine_is_omap3_beagle() ) { /* * Create sysfs entries for enabling/disabling hotplug * support for MMC cards */ if (device_create_file(&pdev->dev, &dev_attr_mmc_cover_switch) < 0) { dev_dbg(mmc_dev(host->mmc), "Unable to create sysfs" "attribute for MMC1 cover switch\n"); } if (device_create_file(&pdev->dev, &dev_attr_mmc_card_detect) < 0) { dev_dbg(mmc_dev(host->mmc), "Unable to create sysfs" "attribute for MMC1 card detect\n"); } } /* Request IRQ for MMC operations */ ret = request_irq(host->irq, mmc_omap_irq, 0, pdev->name, host); if (ret) { dev_dbg(mmc_dev(host->mmc), "Unable to grab HSMMC IRQ"); goto irq_err; } host->card_detect_irq = minfo->switch_pin; if (minfo->switch_pin >= 0) { if (machine_is_omap_2430sdp() || machine_is_omap_3430sdp() || machine_is_omap_3430labrador() || machine_is_omap3evm() || machine_is_omap3_beagle() ) { host->card_detect_irq = TWL4030_GPIO_IRQ_NO(minfo->switch_pin); INIT_WORK(&host->mmc_carddetect_work, mmc_omap_detect); if (setup_mmc_carddetect_irq(minfo->switch_pin)) { free_irq(host->irq, host); goto irq_err; } } } if (minfo->switch_pin >= 0) { ret = request_irq(host->card_detect_irq, mmc_omap_irq_cd, IRQF_DISABLED, pdev->name,host); if (ret < 0) { dev_dbg(mmc_dev(host->mmc), "Unable to grab T2 GPIO IRQ"); free_irq(host->irq, host); goto irq_err; } } if (host->id == OMAP_MMC1_DEVID) saved_host1 = host; else saved_host2 = host; platform_set_drvdata(pdev, host); mmc_clk_disable_aggressive(host); mmc_add_host(mmc); return 0;clk_get_err: dev_dbg(mmc_dev(host->mmc), "Error getting clock for MMC\n"); if (host) { mmc_free_host(mmc); } return ret;mmc_alloc_err: if (host) mmc_free_host(mmc); return ret;irq_err: mmc_clk_disable(host); if (cpu_is_omap2430()) clk_disable(host->dbclk); clk_put(host->fclk); clk_put(host->iclk); if (cpu_is_omap2430()) clk_put(host->dbclk); if (host) mmc_free_host(mmc); return ret;}/* * Routine implementing the driver remove method */static int omap_mmc_remove(struct platform_device *pdev){ struct mmc_omap_host *host = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); if (host) { free_irq(host->irq, host); free_irq(host->card_detect_irq, host); flush_scheduled_work(); /* Free the clks */ mmc_clk_disable(host); if (cpu_is_omap2430()) clk_disable(host->dbclk); clk_put(host->fclk); clk_put(host->iclk); if (cpu_is_omap2430()) clk_put(host->dbclk); mmc_free_host(host->mmc); } return 0;}/* * Routine to suspend the MMC device */static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state){ int ret = 0; int status; struct mmc_omap_host *host = platform_get_drvdata(pdev); if (host && host->suspended) return 0; if (host) { /* Notify the core to suspend the host */ ret = mmc_suspend_host(host->mmc, state); if (ret == 0) { host->suspended = 1; /* Temporarily enabling the clocks for configuration */ mmc_clk_enable_aggressive(host); if (machine_is_omap_2430sdp() || machine_is_omap_3430sdp() || machine_is_omap_3430labrador() || machine_is_omap3evm() || machine_is_omap3_beagle() ) { disable_irq(host->card_detect_irq); ret = mask_carddetect_int(host->id); if (ret) dev_dbg(mmc_dev(host->mmc), "Unable to mask the card detect" "interrupt in suspend\n"); } if (cpu_is_omap34xx() || (cpu_is_omap2430() && omap2_cpu_rev() == 2)) { if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { OMAP_HSMMC_WRITE(host->base,HCTL, OMAP_HSMMC_READ(host->base,HCTL) & SDVSCLR); OMAP_HSMMC_WRITE(host->base,HCTL, OMAP_HSMMC_READ(host->base,HCTL) |SDVS30); OMAP_HSMMC_WRITE(host->base,HCTL, OMAP_HSMMC_READ(host->base,HCTL) | SDBP); } } /* Disable Interrupts */ OMAP_HSMMC_WRITE(host->base, ISE, INT_CLEAR); OMAP_HSMMC_WRITE(host->base, IE, INT_CLEAR); /* Clearing the STAT register*/ status = OMAP_HSMMC_READ(host->base, STAT); OMAP_HSMMC_WRITE(host->base, STAT, status); /* disable clks for MMC1 */ mmc_clk_disable(host); if (cpu_is_omap2430()) clk_disable(host->dbclk); if (cpu_is_omap2430()) { if (host->id == OMAP_MMC1_DEVID) { if (omap2_cpu_rev() == 2) { omap_writel(omap_readl(OMAP2_CONTROL_DEVCONF1) & ~MMC1_ACTIVE_OVERWRITE, OMAP2_CONTROL_DEVCONF1); } } } ret = mmc_omap_power(host,0); if (ret != 0) dev_dbg(mmc_dev(host->mmc), "Unable to disable power to MMC1\n"); host->initstream = 0; } } return ret;}/* * Routine to resume the MMC device */static int omap_mmc_resume(struct platform_device *pdev){ int ret = 0; struct mmc_omap_host *host = platform_get_drvdata(pdev); if (host && !host->suspended) return 0; if (host) { if (cpu_is_omap2430()) { if (host->id == OMAP_MMC1_DEVID) { if (omap2_cpu_rev() == 2) omap_writel(omap_readl(OMAP2_CONTROL_DEVCONF1) | MMC1_ACTIVE_OVERWRITE, OMAP2_CONTROL_DEVCONF1); } } ret = mmc_omap_power(host,1); if (ret != 0) { dev_dbg(mmc_dev(host->mmc), "Unable to enable power to MMC1\n"); return ret; } mmc_clk_enable(host); if (cpu_is_omap2430()) { if (clk_enable(host->dbclk) != 0) dev_dbg(mmc_dev(host->mmc), "Unable to enable debounce" "clock for MMC1\n"); } if (machine_is_omap_2430sdp() || machine_is_omap_3430sdp() || machine_is_omap_3430labrador() || machine_is_omap3evm() || machine_is_omap3_beagle() ) { enable_irq(host->card_detect_irq); ret = unmask_carddetect_int(host->id); if (ret) dev_dbg(mmc_dev(host->mmc), "Unable to unmask the card" "detect interrupt\n"); } /* Notify the core to resume the host */ ret = mmc_resume_host(host->mmc); if (ret == 0) host->suspended = 0; } return ret;clk_en_err: dev_dbg(mmc_dev(host->mmc), "Unable to enable MMC clocks during resume\n"); return -1;}static struct platform_driver omap_mmc_driver = { .probe = omap_mmc_probe, .remove = omap_mmc_remove, .suspend = omap_mmc_suspend, .resume = omap_mmc_resume, .driver = { .name = "hsmmc-omap", },};/* * Driver init method */static int __init omap_mmc_init(void){ /* Register the MMC driver */ if (platform_driver_register(&omap_mmc_driver)) { printk(KERN_ERR ":failed to register MMC driver\n"); return -ENODEV; } return 0;}/* * Driver exit method */static void __exit omap_mmc_cleanup(void){ /* Unregister MMC driver */ platform_driver_unregister(&omap_mmc_driver);}module_init(omap_mmc_init);module_exit(omap_mmc_cleanup);MODULE_DESCRIPTION("OMAP 2430/3430 Multimedia Card driver");MODULE_LICENSE("GPL");MODULE_AUTHOR("Texas Instruments");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -