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; }
|