分类:
2010-03-12 19:41:36
mmci控制器驱动(2.6.22.5) -- (1) 驱动注册
R.wen mmci为ARM的sd/mmc主控制器的驱动. 并且这个控制器是挂接在ARM的amba总线之下的, 所以驱动的注册会用到amba总线的一些函数. 1).驱动的注册. 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); } 这个注册函数可以在前面的介绍中可以看到, 它定义了一个amba_driver结构,然后通过amba_driver_register(&mmci_driver)往amba总线上注 册这个驱动. 并且,如我们之前所说的, 这个注册函数最终会调用驱动中的probe函数完成驱动的初始化操作. 这就是mmc_probe(): 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_host结构, 还有一个特殊的mmci_host结构. mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev); if (!mmc) { ret = -ENOMEM; goto rel_regions; } host = mmc_priv(mmc); 两个结构关联起来 //以下这段代码获取时钟的频率 //得到一个clk结构 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; //得到设备寄存器的MMU后的基址 host->base = ioremap(dev->res.start, SZ_4K); if (!host->base) { ret = -ENOMEM; goto clk_disable; } //这是请求操作函数结构,最终的对MMC卡的操作都是通过它来完成的. 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; } 先来看这个函数的最后一部分: 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); 这是建立一个定时器, 时间为1秒, 超时后,它就会执行mmci_check_status函数: 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); } 我们可以看到, 这个函数通过mod_timer(), 修改这个定时器, 使之1秒之后再次执行, 这个就是说, MMC控制器是通过轮询的方式,每隔1秒调 用mmci_check_status去查询这个设备的状态有没有改变. 状态的查询是通过 host->plat->status(mmc_dev(host->mmc)去完成的: static unsigned int realview_mmc_status(struct device *dev) { struct amba_device *adev = container_of(dev, struct amba_device, dev); u32 mask; if (adev->res.start == REALVIEW_MMCI0_BASE) mask = 1; else mask = 2; return readl(REALVIEW_SYSMCI) & mask; } struct mmc_platform_data realview_mmc0_plat_data = { .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, .status = realview_mmc_status, }; 它是通过读取相应的状态寄存器位,然后跟原来的状态比较(status ^ host->oldstat), 如果发生了改变, 则调用mmc_detect_change去处理这 个变化. /** * mmc_detect_change - process change of state on a MMC socket * @host: host which changed state. * @delay: optional delay to wait before detection (jiffies) * * All we know is that card(s) have been inserted or removed * from the socket(s). We don't know which socket or cards. */ void mmc_detect_change(struct mmc_host *host, unsigned long delay) { #ifdef CONFIG_MMC_DEBUG unsigned long flags; spin_lock_irqsave(&host->lock, flags); BUG_ON(host->removed); spin_unlock_irqrestore(&host->lock, flags); #endif mmc_schedule_delayed_work(&host->detect, delay); } 显然,他是通过工作队列来做的. 这个host->detect是一个delayed_work结构. 代表一个任务. 这个任务是在probe中通过mmc_alloc_host已经 初始化了.mmc_alloc_host我们后面在看, 这里我们只要知道它有这个这个操作INIT_DELAYED_WORK(&host->detect, mmc_rescan), 就是说每当 发现状态有所改变, MMCI最终会执行这个mmc_rescan(). static void mmc_rescan(struct work_struct *work) { struct mmc_host *host = container_of(work, struct mmc_host, detect.work); u32 ocr; int err; mmc_bus_get(host); if (host->bus_ops == NULL) { //检测SD卡 /* * Only we can add a new handler, so it's safe to * release the lock here. */ mmc_bus_put(host); mmc_claim_host(host); mmc_power_up(host); mmc_go_idle(host); mmc_send_if_cond(host, host->ocr_avail); err = mmc_send_app_op_cond(host, 0, &ocr); if (err == MMC_ERR_NONE) { if (mmc_attach_sd(host, ocr)) mmc_power_off(host); } else { //检测MMC卡 /* * If we fail to detect any SD cards then try * searching for MMC cards. */ err = mmc_send_op_cond(host, 0, &ocr); if (err == MMC_ERR_NONE) { //如果没有出错 if (mmc_attach_mmc(host, ocr)) mmc_power_off(host); } else { mmc_power_off(host); mmc_release_host(host); } } } else { if (host->bus_ops->detect && !host->bus_dead) host->bus_ops->detect(host); mmc_bus_put(host); } } 看MMC卡的情况, 如果没出错, 则会调用mmc_attach_mmc,这个函数将会为一张MMC卡分配一个mmc_card结构, 初始化她并将她加入队列 host- >cards中去. **回头看mmci_probe: 她先通过以下方式申请一个mmc_host和mmci_host结构, struct mmci_host *host; struct mmc_host *mmc; mmc = mmc_alloc_host(sizeof(struct mmci_host), &dev->dev); 这两个结构是一般和特殊的关系, mmc_host是通用的mmc host控制器的结构, 而特殊的控制器有好多种,这里是arm的MMCI, 还有at91的, intel的等等. 可以想到, 都是MMC控制器, 每个特殊的控制器肯定有很多相同的属性, 所以这里就抽象出一个父结构,相当于面向对象里的父类 , 这里就是mmc_host了. /** * mmc_alloc_host - initialise the per-host structure. * @extra: sizeof private data structure * @dev: pointer to host device model structure * * Initialise the per-host structure. */ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) { struct mmc_host *host; host = mmc_alloc_host_sysfs(extra, dev); if (host) { spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); INIT_DELAYED_WORK(&host->detect, mmc_rescan); /* * By default, hosts do not support SGIO or large requests. * They have to set these according to their abilities. */ host->max_hw_segs = 1; host->max_phys_segs = 1; host->max_seg_size = PAGE_CACHE_SIZE; host->max_req_size = PAGE_CACHE_SIZE; host->max_blk_size = 512; host->max_blk_count = PAGE_CACHE_SIZE / 512; } return host; } 我们看到,首先是分配一个空间的函数, 有个extra参数,就是用于为特殊设备分配额外空间的大小. 如果分配成功, 则初始化一个等待队列和一个工作队列, 而这个工作队列就是我们先前说提到的. 在接下来就是一个控制器的默认参数的设置了. 分配完空间后, 接着就是host = mmc_priv(mmc), static inline void *mmc_priv(struct mmc_host *host) { return (void *)host->private; } 就是将他们关联起来, 这样通过通用的mmc_host,就可以找到特殊的mmci_host了. **下一步就是时钟频率的设置, MMC卡的传输速率跟这个频率有直接的关系. 比如, 这个频率是20MHz, 而且MMC卡工作在4位模式, 则它的传输 速率就是20M * 4 = 80M/sec, 就是每秒10M多字节的速度. 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->mclk = clk_get_rate(host->clk); 先取出一个时钟结构, 这个是根据MCLK这个名字,然后取出这个相应的结构的. 这个是在系统初始化的时候设置好的. 如何通过clk_get_rate取出一个整型的速率值. **接着就是 struct mmc_platform_data { unsigned int ocr_mask; /* available voltages */ u32 (*translate_vdd)(struct device *, unsigned int); unsigned int (*status)(struct device *); }; struct mmc_platform_data *plat = dev->dev.platform_data; host->plat = plat; 这个也是通过dev传过来的参数, 我们在上面已经看到了它的使用, 就是用来检测控制器状态的变化. mmc控制器操作函数的设置, mmc->ops = &mmci_ops; 假设一个一个进程要读一个MMC卡上的一个文件, 它就要将这个请求(request)发给mmc块设备(mmc_block), mmc块设备最终调用这个结构中的 函数处理这个请求. static const struct mmc_host_ops mmci_ops = { .request = mmci_request, .set_ios = mmci_set_ios, }; mmci_request()函数就是直接操作控制器的的寄存器了. ** 还有一些SGIO的设置等等,和如下的一些值的初始化: 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; **下一步就是中断的处理了, 做法很简单,就是--关中断, 设置中断处理例程, 开中断. writel(0, host->base + MMCIMASK0); writel(0, host->base + MMCIMASK1); writel(0xfff, host->base + MMCICLEAR); ## 两个IRQ 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); ** 将MMC写会到dev的drvdata中, 关联起来 amba_set_drvdata(dev, mmc); ** 最后一步就是我们一开始所说的定时器设置了. |