SDIO驱动(8)Host驱动实现

来源:互联网 发布:tftp软件怎么使用 编辑:程序博客网 时间:2024/06/11 13:37
Linux 2.6.38S3C2440

看C/C++的源码,我们从main函数开始,因为这是代码的入口;在driver的世界里,这个功能的担当是module_init:

module_init(s3cmci_init);module_exit(s3cmci_exit);
当然,module_init/module_exit不是函数,而是宏定义。module_init修饰的函数会被放到.init段中,该段中的内容在系统启动过程时依次得到运行,于是函数s3cmci_init就开始执行了,相应的module_exit做的是相反的工作。s3cmci_init相当简单,就一行:
static int __init s3cmci_init(void){return platform_driver_register(&s3cmci_driver);}
仅仅调用platform子系统的platform_driver_register函数,注册一个platform驱动-这里指的就是SOS上的host controller驱动,用来操作具体硬件。host平台驱动结构体

static struct platform_driver s3cmci_driver = {.driver= {.name= "s3c-sdi",.owner= THIS_MODULE,.pm= s3cmci_pm_ops,},.id_table= s3cmci_driver_ids,.probe= s3cmci_probe,.remove= __devexit_p(s3cmci_remove),.shutdown= s3cmci_shutdown,};
3行,name是驱动的名称,注册成功的话在/sys/bus/platform/drivers目录下可以看到"s3c-sdi"
4行,顾名思义,这个驱动的所有者是谁?当前模块。在Linux的世界中,一个独立的功能实体就是一个模块,一个模块的抽象由struct module描述,THIS_MODULE是一个宏:

#define THIS_MODULE (&__this_module)

代表当前模块,这样通过THIS_MODULE宏就可以引用模块的struct module结构,比如使用THIS_MODULE->state可以获得当前模块的状态。类似的,还记得在SDIO驱动(5)sdio总线上的probe提到的current吗?当时初始化一个等待队列这么用的:

DECLARE_WAITQUEUE(wait, current);

5行的pm,自然就是power manager相关的东东了,简单看下:

#ifdef CONFIG_PMstatic const struct dev_pm_ops s3cmci_pm = {.suspend= s3cmci_suspend,.resume= s3cmci_resume,};#define s3cmci_pm_ops &s3cmci_pm#else /* CONFIG_PM */#define s3cmci_pm_ops NULL#endif /* CONFIG_PM */
PM是编译内核是的可选项,选中的话在系统进入休眠的过程中suspend得到调用使mmc子系统进入休眠状态,唤醒自然是resume的工作了。


7行id_table,直说,是platform总线用来match的!熟悉sdio总线上driver和设备的match的我们是不是应该找到plateform总线的match函数看一下呢?

9、10行,platform_driver的remove和shutdown有什么区别呢?调用的时机不同,详细描述参见“Linux驱动中remove和shutdown的区别”。

第8行的s3cmci_probe是本篇的重点,也是目前遇到的最庞大的函数,下面贴出来慢慢分析:

static int __devinit s3cmci_probe(struct platform_device *pdev){struct s3cmci_host *host;struct mmc_host*mmc;int ret;int i;mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);if (!mmc) {ret = -ENOMEM;goto probe_out;}for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++) {ret = gpio_request(i, dev_name(&pdev->dev));if (ret) {dev_err(&pdev->dev, "failed to get gpio %d\n", i);for (i--; i >= S3C2410_GPE(5); i--)gpio_free(i);goto probe_free_host;}}host = mmc_priv(mmc);host->mmc = mmc;host->pdev= pdev;host->pdata = pdev->dev.platform_data;if (!host->pdata) {pdev->dev.platform_data = &s3cmci_def_pdata;host->pdata = &s3cmci_def_pdata;}spin_lock_init(&host->complete_lock);tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);{host->sdiimsk= S3C2440_SDIIMSK;host->sdidata= S3C2440_SDIDATA;host->clk_div= 1;}host->complete_what = COMPLETION_NONE;host->pio_active = XFER_NONE;#ifdef CONFIG_MMC_S3C_PIODMAhost->dodma= host->pdata->use_dma;#endifhost->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (!host->mem) {dev_err(&pdev->dev,"failed to get io memory region resouce.\n");ret = -ENOENT;goto probe_free_gpio;}host->mem = request_mem_region(host->mem->start,       resource_size(host->mem), pdev->name);if (!host->mem) {dev_err(&pdev->dev, "failed to request io memory region.\n");ret = -ENOENT;goto probe_free_gpio;}host->base = ioremap(host->mem->start, resource_size(host->mem));if (!host->base) {dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");ret = -EINVAL;goto probe_free_mem_region;}host->irq = platform_get_irq(pdev, 0);if (host->irq == 0) {dev_err(&pdev->dev, "failed to get interrupt resouce.\n");ret = -EINVAL;goto probe_iounmap;}if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {dev_err(&pdev->dev, "failed to request mci interrupt.\n");ret = -ENOENT;goto probe_iounmap;}/* We get spurious interrupts even when we have set the IMSK * register to ignore everything, so use disable_irq() to make * ensure we don't lock the system with un-serviceable requests. */disable_irq(host->irq);host->irq_state = false;if (!host->pdata->no_detect) {ret = gpio_request(host->pdata->gpio_detect, "s3cmci detect");if (ret) {dev_err(&pdev->dev, "failed to get detect gpio\n");goto probe_free_irq;}host->irq_cd = gpio_to_irq(host->pdata->gpio_detect);if (host->irq_cd >= 0) {if (request_irq(host->irq_cd, s3cmci_irq_cd,IRQF_TRIGGER_RISING |IRQF_TRIGGER_FALLING,DRIVER_NAME, host)) {dev_err(&pdev->dev,"can't get card detect irq.\n");ret = -ENOENT;goto probe_free_gpio_cd;}} else {dev_warn(&pdev->dev, "host detect has no irq available\n");gpio_direction_input(host->pdata->gpio_detect);}} elsehost->irq_cd = -1;if (!host->pdata->no_wprotect) {ret = gpio_request(host->pdata->gpio_wprotect, "s3cmci wp");if (ret) {dev_err(&pdev->dev, "failed to get writeprotect\n");goto probe_free_irq_cd;}gpio_direction_input(host->pdata->gpio_wprotect);}/* depending on the dma state, get a dma channel to use. */if (s3cmci_host_usedma(host)) {host->dma = s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client,host);if (host->dma < 0) {dev_err(&pdev->dev, "cannot get DMA channel.\n");if (!s3cmci_host_canpio()) {ret = -EBUSY;goto probe_free_gpio_wp;} else {dev_warn(&pdev->dev, "falling back to PIO.\n");host->dodma = 0;}}}host->clk = clk_get(&pdev->dev, "sdi");if (IS_ERR(host->clk)) {dev_err(&pdev->dev, "failed to find clock source.\n");ret = PTR_ERR(host->clk);host->clk = NULL;goto probe_free_dma;}ret = clk_enable(host->clk);if (ret) {dev_err(&pdev->dev, "failed to enable clock source.\n");goto clk_free;}host->clk_rate = clk_get_rate(host->clk);mmc->ops = &s3cmci_ops;mmc->ocr_avail= MMC_VDD_32_33 | MMC_VDD_33_34;#ifdef CONFIG_MMC_S3C_HW_SDIO_IRQmmc->caps= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;#elsemmc->caps= MMC_CAP_4_BIT_DATA;#endifmmc->f_min = host->clk_rate / (host->clk_div * 256);mmc->f_max = host->clk_rate / host->clk_div;if (host->pdata->ocr_avail)mmc->ocr_avail = host->pdata->ocr_avail;mmc->max_blk_count= 4095;mmc->max_blk_size= 4095;mmc->max_req_size= 4095 * 512;mmc->max_seg_size= mmc->max_req_size;mmc->max_segs= 128;dbg(host, dbg_debug,    "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",    "2440",    host->base, host->irq, host->irq_cd, host->dma);ret = s3cmci_cpufreq_register(host);if (ret) {dev_err(&pdev->dev, "failed to register cpufreq\n");goto free_dmabuf;}ret = mmc_add_host(mmc);if (ret) {dev_err(&pdev->dev, "failed to add mmc host.\n");goto free_cpufreq;}s3cmci_debugfs_attach(host);platform_set_drvdata(pdev, mmc);dev_info(&pdev->dev, "%s - using %s, %s SDIO IRQ\n", mmc_hostname(mmc), s3cmci_host_usedma(host) ? "dma" : "pio", mmc->caps & MMC_CAP_SDIO_IRQ ? "hw" : "sw");return 0; free_cpufreq:s3cmci_cpufreq_deregister(host); free_dmabuf:clk_disable(host->clk); clk_free:clk_put(host->clk); probe_free_dma:if (s3cmci_host_usedma(host))s3c2410_dma_free(host->dma, &s3cmci_dma_client); probe_free_gpio_wp:if (!host->pdata->no_wprotect)gpio_free(host->pdata->gpio_wprotect); probe_free_gpio_cd:if (!host->pdata->no_detect)gpio_free(host->pdata->gpio_detect); probe_free_irq_cd:if (host->irq_cd >= 0)free_irq(host->irq_cd, host); probe_free_irq:free_irq(host->irq, host); probe_iounmap:iounmap(host->base); probe_free_mem_region:release_mem_region(host->mem->start, resource_size(host->mem)); probe_free_gpio:for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++)gpio_free(i); probe_free_host:mmc_free_host(mmc); probe_out:return ret;}
struct mmc_host是EMMC子系统框架使用的对象,可以把它看作OOP里的abstract类,抽象了所有host controller共同的属性;struct s3cmci_host是在共同属性之外,独属于s3c2440 host controller的一些特性,比如clock、pin分配、dma启用情况等等。

原创粉丝点击