static int __devinit s3cmci_probe(struct platform_device *pdev) { //该结构体定义在头文件中,现在实例 一个名为host的结构体指针为结构体中的成员赋值做准备 struct s3cmci_host *host; //实例一个名为mmc的结构体指针, 用于与Core核心层中的mmc_host结构体指针相关联 struct mmc_host *mmc; int ret; //初始化一个名为 complete_lock的自旋锁以备后用,该自旋锁的定义在s3cmci_host结构体中 spin_lock_init(&host->complete_lock); //初始化一个名为pio_tasklet的tasklet,用于实现中断的底半部 机制,底半部服务函数为pio_tasklet, //将host结构体变量作为服务函数的参数。注意:这里tasklet的变量名与 服务函数名称同名了(这是可以的)。 tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
//分配 mmc_host结构体指针的内存空间大小,该函数在host.c中实现,这里要注意一点,为什么参数 //是s3cmci_host结构体 的大小,到host.c中看,实际这里分配的是mmc_host加s3cmci_host的大小。 mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev); if (!mmc) { ret = -ENOMEM; goto probe_out; }
//调用mmc_priv函数将 mmc_host和s3cmci_host结构体的对象关联起来,mmc_priv定义在host.h中 host = mmc_priv(mmc); //下面就开始初始化 s3cmci_host结构体的各成员 host->mmc = mmc; host->pdev = pdev;
host->pdata = pdev->dev.platform_data; //SDI主机控制器的中断屏蔽寄存 器和数据寄存器,他们定义在mach-s3c2410/include/mach/regs-sdi.h中 host->sdiimsk = S3C2440_SDIIMSK; host->sdidata = S3C2440_SDIDATA; //complete_what定义 在s3cmci_host结构体中,用来记录请求处理所处的当前状态,这里初始化为 //COMPLETION_NONE即 无状态,定义在头文件的s3cmci_waitfor中,里面枚举了6种状态。 host->complete_what = COMPLETION_NONE; //pio_active定义在 s3cmci_host结构体中,用来标记请求处理数据在FIFO方式下的数据方向是读还是写 host->pio_active = XFER_NONE; //dodma和dma方便用于标记 是否要使用DMA数据传输方式和DMA通道资源,0表示不使用DMA功能 host->dodma = 0; host->dma = S3CMCI_DMA; //从SDI平台设备资源中获取 SDI的IO端口资源,该资源在plat-s3c24xx/devs.c的s3c_sdi_resource中指定的 host->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_host; } //申请SDI的IO端口资源所占用的IO空间(要注意理解IO空间和内存空间的区 别) host->mem = request_mem_region(host->mem->start, RESSIZE(host->mem), pdev->name); if (!host->mem) { dev_err(&pdev->dev, "failed to request io memory region.\n"); ret = -ENOENT; goto probe_free_host; }
//将SDI的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定 义在io.h中。 //注意:IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作。 host->base = ioremap(host->mem->start, RESSIZE(host->mem)); if (!host->base) { dev_err(&pdev->dev, "failed to ioremap() io memory region.\n"); ret = -EINVAL; goto probe_free_mem_region; }
//同样从SDI平台设备资源中获取SDI的中断号 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; }
//申请SDI的中断服务,服务函数为 s3cmci_irq,主要参数为host 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; }
//在SDI未准备好之前先屏蔽SDI的中断功能 disable_irq(host->irq);
//根据开发板原理图分别设置GPG8、GPH8端口为SD卡插入拔出的检测和有无 写保护的检查,
//注意:其实有没有写保护就是检查SD卡侧面有个移动按钮的开关,MMC卡无此功能
host->pdata->gpio_detect = S3C2410_GPG8;
host->pdata->gpio_wprotect = S3C2410_GPH8;
// 获取GPG8复用端口中断功能的中断号 host->irq_cd = s3c2410_gpio_getirq(host->pdata->gpio_detect); //GPG8是复用端口,要使用中断功 能则要配置成中断功能,GPG8对应的中断功能是外部中断EINT16,这个数据手册上有讲到 s3c2410_gpio_cfgpin(S3C2410_GPG8, S3C2410_GPG8_EINT16);
//申请SDI的卡检测中断服务,服 务函数为s3cmci_irq_cd,主要参数也为host 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_irq; }
//获取DMA通道并申请DMA中 断,S3C2440中DMA的使用在后续的文章中再了解 if (s3c2410_dma_request(S3CMCI_DMA, &s3cmci_dma_client, NULL) < 0) { dev_err(&pdev->dev, "unable to get DMA channel.\n"); ret = -EBUSY; goto probe_free_irq_cd; }
//从平台时钟队列中获取SDI的时钟源,在arch/arm/plat- s3c24xx/s3c2410-clock.c中有定义 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_host; }
//启动获取的时钟源 ret = clk_enable(host->clk); if (ret) { dev_err(&pdev->dev, "failed to enable clock source.\n"); goto clk_free; }
//通过SDI的时钟源获取CPU的 PCLK频率,这里为什么要获得CPU的PCLK频率呢, //通过数据手册SDI控制器的方框图得知,SDI的时钟频率 (SDCLK)=PCLK/(Prescaler+1) host->clk_rate = clk_get_rate(host->clk); host->clk_div = 1;//设置预分频值,即:Prescaler的值 //下面对mmc_host进行初始化 mmc->ops = &s3cmci_ops; //SDI主机控制器操作结构体 mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; //设置工作电压范围 mmc->caps = MMC_CAP_4_BIT_DATA; //设置总线宽度为4位 mmc->f_min = host->clk_rate / (host->clk_div * 256); //设置最小工作频率 mmc->f_max = host->clk_rate / host->clk_div; //设置最大工作频率 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_phys_segs = 128; mmc->max_hw_segs = 128;
//Linux的通知链机制,实现到后面再讲 ret = s3cmci_cpufreq_register(host); if (ret) { dev_err(&pdev->dev, "failed to register cpufreq\n"); goto free_dmabuf; }
//将SDI host设备注册到系统中 ret = mmc_add_host(mmc); if (ret) { dev_err(&pdev->dev, "failed to add mmc host.\n"); goto free_cpufreq; }
//将SDI host设备的数据赋值给系统平台设备 platform_set_drvdata(pdev, mmc);
return 0;
//以下是错误处理 free_cpufreq: s3cmci_cpufreq_deregister(host);
free_dmabuf: clk_disable(host->clk);
clk_free: clk_put(host->clk);
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, RESSIZE(host->mem));
probe_free_host: mmc_free_host(mmc); probe_out: return ret; }
|