1: mmcsd的芯片适配层,暂且这么说吧。
static int mxcmci_probe(struct platform_device *pdev) { struct mxc_mmc_platform_data *mmc_plat = pdev->dev.platform_data; struct mmc_host *mmc; struct mxcmci_host *host = NULL; int card_gpio_status; int ret = -ENODEV;
if (!mmc_plat) { return -EINVAL; }
mmc = mmc_alloc_host(sizeof(struct mxcmci_host), &pdev->dev); if (!mmc) { return -ENOMEM; } platform_set_drvdata(pdev, mmc);
mmc->ops = &mxcmci_ops; mmc->ocr_avail = mmc_plat->ocr_mask;
/* Hack to work with LP1070 */ mmc->ocr_avail |= MMC_VDD_31_32;
mmc->max_phys_segs = NR_SG; mmc->caps = MMC_CAP_4_BIT_DATA;
host = mmc_priv(mmc); host->mmc = mmc; host->dma = -1; host->dma_dir = DMA_NONE; host->id = pdev->id; host->mxc_mmc_suspend_flag = 0; host->sdio_set_wake_enable = 0; host->mode = -1; host->plat_data = mmc_plat; if (!host->plat_data) { ret = -EINVAL; goto out; }
host->clk = clk_get(&pdev->dev, "sdhc_clk"); clk_enable(host->clk);
mmc->f_min = mmc_plat->min_clk; mmc->f_max = mmc_plat->max_clk; pr_debug("SDHC:%d clock:%lu\n", pdev->id, clk_get_rate(host->clk));
spin_lock_init(&host->lock); host->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!host->res) { ret = -ENOMEM; goto out; }
if (!request_mem_region(host->res->start, host->res->end - host->res->start + 1, pdev->name)) { printk(KERN_ERR "request_mem_region failed\n"); ret = -ENOMEM; goto out; } host->base = (void *)IO_ADDRESS(host->res->start); if (!host->base) { ret = -ENOMEM; goto out1; } printk(KERN_INFO "%s--irq:%d,,id:0x%x\n",__FUNCTION__,host->irq,pdev->id); host->irq = platform_get_irq(pdev, 0);//irq 11 INT_SDHC1
if (!host->irq) { ret = -ENOMEM; goto out1; } #ifdef CONFIG_MACH_MX27_MDK27V0 #ifdef CONFIG_MACH_MX27_TUOSI #else if (pdev->id == 1) { goto skip_detect_irq; } #endif #endif
host->detect_irq = platform_get_irq(pdev, 1);//gpio_irq: SSI3_RXDAT
printk(KERN_INFO"%s--dec irq:0x%x\n",__FUNCTION__,host->detect_irq); if (!host->detect_irq) { goto out1; }
do { card_gpio_status = host->plat_data->status(host->mmc->dev); if (card_gpio_status) { set_irq_type(host->detect_irq, IRQT_FALLING); } else { set_irq_type(host->detect_irq, IRQT_RISING); } } while (card_gpio_status != host->plat_data->status(host->mmc->dev));
ret = request_irq(host->detect_irq, mxcmci_gpio_irq, 0, pdev->name, host); if (ret) { goto out1; }
skip_detect_irq: mxcmci_softreset(host);
if (__raw_readl(host->base + MMC_REV_NO) != SDHC_REV_NO) { printk(KERN_ERR "%s: wrong rev.no. 0x%08x. aborting.\n", pdev->name, MMC_REV_NO); goto out3; } __raw_writel(READ_TO_VALUE, host->base + MMC_READ_TO);
__raw_writel(INT_CNTR_END_CMD_RES, host->base + MMC_INT_CNTR);
ret = request_irq(host->irq, mxcmci_irq, 0, pdev->name, host); if (ret) { goto out3; }
host->mmc->sdio_irq = platform_get_irq(pdev, 2); if (host->mmc->sdio_irq) { mmc->caps |= MMC_CAP_SDIO; #ifdef CONFIG_MACH_MX27_MDK27V0 sdhc_setup_irq(host->mmc->sdio_irq, host->mmc); #else set_irq_chip(host->mmc->sdio_irq, &mxc_sdio_irq_chip); set_irq_chipdata(host->mmc->sdio_irq, host->mmc); set_irq_handler(host->mmc->sdio_irq, handle_level_irq); set_irq_flags(host->mmc->sdio_irq, IRQF_VALID); #endif }
gpio_sdhc_active(pdev->id);
if ((ret = mmc_add_host(mmc)) < 0) { goto out4; }
#ifdef CONFIG_MACH_MX27_MDK27V0 if (pdev->id >= 0 && pdev->id < 2) { mxc_mci_hosts[pdev->id] = host; } #endif
printk(KERN_INFO "%s-%d found\n", pdev->name, pdev->id);
return 0;
out4: gpio_sdhc_inactive(pdev->id); free_irq(host->irq, host); out3: free_irq(host->detect_irq, host); pr_debug("%s: Error in initializing....", pdev->name); out1: release_mem_region(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start + 1); out: clk_disable(host->clk); mmc_free_host(mmc); platform_set_drvdata(pdev, NULL); return ret; }
首先调用mmc_alloc_host去申请一个struct mmc_host *mmc,即是: struct mmc_host { struct device *dev; struct class_device class_dev; int index; const struct mmc_host_ops *ops; unsigned int f_min; unsigned int f_max; u32 ocr_avail;
unsigned long caps; /* Host capabilities */
#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can the host do 4 bit transfers */ #define MMC_CAP_MULTIWRITE (1 << 1) /* Can accurately report bytes sent to card on error */ #define MMC_CAP_BYTEBLOCK (1 << 2) /* Can do non-log2 block sizes */ #define MMC_CAP_SDIO (1 << 3) /* Can the host support SDIO */
unsigned int sdio_irq;
/* host specific block data */ unsigned int max_seg_size; /* see blk_queue_max_segment_size */ unsigned short max_hw_segs; /* see blk_queue_max_hw_segments */ unsigned short max_phys_segs; /* see blk_queue_max_phys_segments */ unsigned short max_sectors; /* see blk_queue_max_sectors */ unsigned short unused;
/* private data */ struct mmc_ios ios; /* current io bus settings */ u32 ocr; /* the current OCR setting */
unsigned int mode; /* current card mode of host */ #define MMC_MODE_MMC 0 #define MMC_MODE_SD 1 #define MMC_MODE_SDIO 2
struct list_head cards; /* devices attached to this host */
wait_queue_head_t wq; spinlock_t lock; /* card_busy lock */ struct mmc_card *card_busy; /* the MMC card claiming host */ struct mmc_card *card_selected; /* the selected MMC card */
struct work_struct detect;
unsigned long private[0] ____cacheline_aligned; }; 这个结构跟mmc_alloc_host函数都是由MMC核心层mmc.c文件提供的,注意的是,申请的时候还多申请了一个结构体空间struct mxcmci_host *host,即为struct mmc_host的private所指向的地址。接下来看一下这个申请函数: 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_LIST_HEAD(&host->cards); INIT_WORK(&host->detect, mmc_rescan, host);
/* * 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_sectors = 1 << (PAGE_CACHE_SHIFT - 9); host->max_seg_size = PAGE_CACHE_SIZE; }
return host; } 看到第一行,又调用mmc_alloc_host_sysfs申请空间,这个函数由mmc_sysfs.c文件提供: struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev) { struct mmc_host *host;
host = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL); if (host) { memset(host, 0, sizeof(struct mmc_host) + extra);
host->dev = dev; host->class_dev.dev = host->dev; host->class_dev.class = &mmc_host_class; class_device_initialize(&host->class_dev); }
return host; } 可以看到,除申请空间外,它还初始化一个class,是供sys目录使用的。所以退出该函数,我们回到mmc_alloc_host中,接下来初始化host->lock,等待队列host->wq,host所要管理的card链表host->cards,工作队列host->detect和工作函数mmc_rescan,具体这个mmc_rescan函数以后再解。接下来看,就是设置几个host specific block data,这几个变量做什么用的还不清楚。然后函数返回host。 回到mxcmci_probe,下面调用platform_set_drvdata将pdev->dev->driver_data指向mmc即刚才申请到的host结构体。下一行初始化host的ops为mxcmci_ops,即: static struct mmc_host_ops mxcmci_ops = { .request = mxcmci_request, .set_ios = mxcmci_set_ios, #ifdef CONFIG_MACH_MX27_MDK27V0 .get_ro = mxcmci_get_ro #endif }; 前面两个函数应该是与SD的协议有关的实现,最后一个是通过读取GPIO来获得SD卡是否有是只读的(是否有写保护)。 接下来初始化mmc->ocr_avail,这个参数好像是与SD的供电有关的,下面的一些max_phys_regs,caps,等等都还不清楚,到plat_data的时候是指向mmc_plat即是platform_data,在devices.c中有: static struct platform_device mxcsdhc1_device = { .name = "mxcmci", .id = 0, .dev = { .release = mxc_nop_release, .platform_data = &mmc_data, }, .num_resources = ARRAY_SIZE(mxcsdhc1_resources), .resource = mxcsdhc1_resources, }; 其中,我们可以看到,platform_data指向mmc_data,即: static struct mxc_mmc_platform_data mmc_data = { .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30, .min_clk = 150000, .max_clk = 25000000, .card_inserted_state = 0, .status = sdhc_get_card_det_status, }; sdhc_get_card_det_status通过查询GOIO返回SD卡的状态,1为remove,0为insert。 接下来设置CLK,初始化mxcmmc_host的host->lock,下面调用platform_get_resource获得MX27关于SD卡的寄存器段,这个是用物理地址描述的,接着调用request_mem_region申请注册这段IO内存的使用权,然后调用IO_ADDRESS转换该寄存器段的首地址,赋给host->base,这个为虚拟地址,后面的驱动就可以直接使用这个虚拟地址读写寄存器了。下面再调用platform_get_irq获得SDHC给ARM端的中断号,为宏INT_SDHC1,值为11。下面来看pdev->id,因为我们系统中只用一个SD接口,所以在platform_data初始化的时候设置该值为0,所以这里就不会goto skip_detect_irq了。下面再调用platform_get_irq获得host->detect_irq中断号,这里需要说明的是: detect_irq中断其实就是一个GPIO中断,接在SD卡座的CD_SW端,当SD卡未考入时为高电平,当插入时为低电平,拨出时恢复为高电平,我们系统里设置GPIO的中断类型为边沿触发,所以检测插入时为上升沿,下降沿时为拨出。 下面的do while即是根据SD卡的状态来设置这个中断的触发沿。接下来request_irq,不用说,是申请中断处理函数--mxcmci_gpio_irq,这个中断处理函数以后再说。下面就是调用mxcmci_softreset: static void mxcmci_softreset(struct mxcmci_host *host) { /* reset sequence */ __raw_writel(0x8, host->base + MMC_STR_STP_CLK); __raw_writel(0x9, host->base + MMC_STR_STP_CLK); __raw_writel(0x1, host->base + MMC_STR_STP_CLK); __raw_writel(0x1, host->base + MMC_STR_STP_CLK); __raw_writel(0x1, host->base + MMC_STR_STP_CLK); __raw_writel(0x1, host->base + MMC_STR_STP_CLK); __raw_writel(0x1, host->base + MMC_STR_STP_CLK); __raw_writel(0x1, host->base + MMC_STR_STP_CLK); __raw_writel(0x1, host->base + MMC_STR_STP_CLK); __raw_writel(0x1, host->base + MMC_STR_STP_CLK); __raw_writel(0x3f, host->base + MMC_CLK_RATE); __raw_writel(0xff, host->base + MMC_RES_TO); __raw_writel(512, host->base + MMC_BLK_LEN); __raw_writel(1, host->base + MMC_NOB); } 该函数就是设置MX27的SD卡相关的寄存器,复位SD控制器。 接下来的三个__raw_writel也都是设置SD控制器,下面再调用request_irq申请MMC的中断,处理函数为mxcmci_irq接下来还是platform_get_irq,获得host->mmc->sdio_irq,这个就看不懂了,这个中断是用来做什么的呢??? 下面调用gpio_sdhc_active设置GPIO用作MMC,终于差不多结束了,调用mmc_add_host,为mmc.c里的一个函数: int mmc_add_host(struct mmc_host *host) { int ret;
ret = mmc_add_host_sysfs(host); if (ret == 0) { mmc_power_off(host); mmc_detect_change(host, 0); }
return ret; } 该函数创建sys文件里mmc对应的目录,成功后调用mmc_power_off及mmc_detect_change,其中: static void mmc_power_off(struct mmc_host *host) { host->ios.clock = 0; host->ios.vdd = 0; host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.chip_select = MMC_CS_DONTCARE; host->ios.power_mode = MMC_POWER_OFF; host->ios.bus_width = MMC_BUS_WIDTH_1; mmc_set_ios(host); } 函数调用了mmc_set_ios: static inline void mmc_set_ios(struct mmc_host *host) { struct mmc_ios *ios = &host->ios; pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u width %u\n", mmc_hostname(host), ios->clock, ios->bus_mode, ios->power_mode, ios->chip_select, ios->vdd, ios->bus_width); host->ops->set_ios(host, ios); } ops->set_ios即是我们上面所初始化的,platform_data里的一个东东。 而: void mmc_detect_change(struct mmc_host *host, unsigned long delay) { if (delay) mmc_schedule_delayed_work(&host->detect, delay); else mmc_schedule_work(&host->detect); } 应该是唤醒工作队列中的mmc_rescan函数。
|