static int __devinit s3cmci_probe(struct platform_device *pdev)
{
struct s3cmci_host *host;//本质上是对通过mmc_host的一个自我实现的封装,
//就说里面不但有mmc_host还有一些三星自己的东东
struct mmc_host*mmc;//这个才是对一个mmc控制器的描述体
int ret;
int is2440;//判断是否是2440,注意学习这种方式,在一个驱动支持多个设备时的
int i;
is2440 = platform_get_device_id(pdev)->driver_data;
//在下面的platform_device_id定义中可以看到支持的多个设备
/*static struct platform_device_id s3cmci_driver_ids[] = {
{
.name = "s3c2410-sdi",
.driver_data = 0,
}, {
.name = "s3c2412-sdi",
.driver_data = 1,
}, {
.name = "s3c2440-sdi",
.driver_data = 1,
},
{ }
};
*/
//其实在驱动和dirver的匹配中可以看到第二项的匹配
//是根据id_table 进行匹配的
/*
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
// Attempt an OF style match first
if (of_driver_match_device(dev, drv))
return 1;
// Then try to match against the id table
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
// fall-back to driver name match
return (strcmp(pdev->name, drv->name) == 0);
}
*/
//#####①mmc_alloc_host详细的情况稍后分析,应该在core中
mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
//这里分配和初始化一个mmc_host,可以看出大小其实包含
//s3cmci_host的大小,下文可以看到它放到了mmc的private中
if (!mmc) {
ret = -ENOMEM;
goto probe_out;
}
//从电路原理图上可以看出,SD分别使用S3C2440的
// 复用IO端口GPE7-10作为4根数据信号线、
//使用GPE6作命令信号线、使用GPE5作时钟信号线
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);//s3cmci_host指定host的private域
host->mmc = mmc;//s3cmci_host的host域又指定mmc_host,
//这是一个非常好的上下层结构互找的例子
host->pdev = pdev;//host的平台设备域,这样就可以拿到平台设备注册
//时的资源
host->is2440 = is2440;//处理特殊标志
host->pdata = pdev->dev.platform_data;
//将保存在平台设备中的平台数据拿出来
//如果为空的话,设置默认平台数据,看得出来其稳定性的考虑
//其实就是两个标志,一个是写保护,一个是插入检测
/*
static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
//This is currently here to avoid a number of if (host->pdata)
//checks. Any zero fields to ensure reasonable defaults are picked.
.no_wprotect = 1,
.no_detect = 1,
};
*/
if (!host->pdata) {
pdev->dev.platform_data = &s3cmci_def_pdata;
host->pdata = &s3cmci_def_pdata;
}
spin_lock_init(&host->complete_lock);//初始化自旋锁
//初始化一个tasklet,
//########②pio_tasklet稍后分析,属于中断下半部机制
tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
//SDI主机控制器的中断屏蔽寄存器和数据寄存器和时钟分频系数
if (is2440) {
host->sdiimsk = S3C2440_SDIIMSK;
host->sdidata = S3C2440_SDIDATA;
host->clk_div = 1;//设置预分频值,即:Prescaler的值
} else {
host->sdiimsk = S3C2410_SDIIMSK;
host->sdidata = S3C2410_SDIDATA;
host->clk_div = 2;//设置预分频值,即:Prescaler的值
}
//complete_what定义在s3cmci_host结构体中,
//用来记录请求处理所处的当前状态,这里初始化为
//COMPLETION_NONE即无状态,
//定义在头文件的s3cmci_waitfor中,里面枚举了6种状态。
host->complete_what = COMPLETION_NONE;
//pio_active定义在s3cmci_host结构体中,
//用来标记请求处理数据在FIFO方式下的数据方向是读还是写
host->pio_active = XFER_NONE;
//是否使用piodma
#ifdef CONFIG_MMC_S3C_PIODMA
host->dodma = host->pdata->use_dma;
#endif
//获取mem资源,并申请然后映射到虚拟地址
host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!host->mem) {
dev_err(&pdev->dev,
"failed to get io memory region resource.\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 resource.\n");
ret = -EINVAL;
goto probe_iounmap;
}
//卡控制器的中断#####③ISR一会儿再说
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. */
/*即使我们使用IMSK来屏蔽所有的中断,我们
还是会得到假的中断,为了不至于锁住
系统,我们在这里禁止掉中断,并将
中断状态设置为false*/
disable_irq(host->irq);
host->irq_state = false;
/*加入有插拔检测口,那么我们为它申请插拔检测
用的gpio口和中断*/
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);
}
} else
host->irq_cd = -1;
//如果有写保护则为它申请写保护用的
//gpio口,并设置为输入状态以供读取
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. */
//如果使用dma则为其申请dma通道
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;
}
//通过SDI的时钟源获取CPU的PCLK频率,
//这里为什么要获得CPU的PCLK频率呢,
//通过数据手册SDI控制器的方框图得知,
//SDI的时钟频率(SDCLK)=PCLK/(Prescaler+1)
host->clk_rate = clk_get_rate(host->clk);
//为mmc_host_ops赋值
//######⑤各个回调函数用到时再分析
mmc->ops = &s3cmci_ops;
mmc->ocr_avail= MMC_VDD_32_33 | MMC_VDD_33_34;//设置工作电压范围
#ifdef CONFIG_MMC_S3C_HW_SDIO_IRQ
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
#else
mmc->caps = MMC_CAP_4_BIT_DATA;//设置总线宽度为4位
#endif
mmc->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;//一个request中的最大block数量
mmc->max_blk_size= 4095;//mmc block的最大值
mmc->max_req_size= 4095 * 512;//一次请求的最大数byte数
mmc->max_seg_size= mmc->max_req_size;//最大seg大小
mmc->max_segs = 128;//最多的segs
dbg(host, dbg_debug,
"probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",
(host->is2440?"2440":""),
host->base, host->irq, host->irq_cd, host->dma);
//Linux的通知链机制
ret = s3cmci_cpufreq_register(host);
if (ret) {
dev_err(&pdev->dev, "failed to register cpufreq\n");
goto free_dmabuf;
}
//将SDI host设备注册到系统中,其实到这里我们可以看出
//最终的两步就是mmc_alloc_host和mmc_add_host
//中间的一系列的初始化无非是为mmc_add_host做准备
//####⑥有待分析啦
ret = mmc_add_host(mmc);
if (ret) {
dev_err(&pdev->dev, "failed to add mmc host.\n");
goto free_cpufreq;
}
s3cmci_debugfs_attach(host);
//将SDI host设备的数据赋值给系统平台设备,这样通过
//平台设备即可以拿到整个mmc_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 s3cmci_host *host;//本质上是对通过mmc_host的一个自我实现的封装,
//就说里面不但有mmc_host还有一些三星自己的东东
struct mmc_host*mmc;//这个才是对一个mmc控制器的描述体
int ret;
int is2440;//判断是否是2440,注意学习这种方式,在一个驱动支持多个设备时的
int i;
is2440 = platform_get_device_id(pdev)->driver_data;
//在下面的platform_device_id定义中可以看到支持的多个设备
/*static struct platform_device_id s3cmci_driver_ids[] = {
{
.name = "s3c2410-sdi",
.driver_data = 0,
}, {
.name = "s3c2412-sdi",
.driver_data = 1,
}, {
.name = "s3c2440-sdi",
.driver_data = 1,
},
{ }
};
*/
//其实在驱动和dirver的匹配中可以看到第二项的匹配
//是根据id_table 进行匹配的
/*
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
// Attempt an OF style match first
if (of_driver_match_device(dev, drv))
return 1;
// Then try to match against the id table
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
// fall-back to driver name match
return (strcmp(pdev->name, drv->name) == 0);
}
*/
//#####①mmc_alloc_host详细的情况稍后分析,应该在core中
mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);
//这里分配和初始化一个mmc_host,可以看出大小其实包含
//s3cmci_host的大小,下文可以看到它放到了mmc的private中
if (!mmc) {
ret = -ENOMEM;
goto probe_out;
}
//从电路原理图上可以看出,SD分别使用S3C2440的
// 复用IO端口GPE7-10作为4根数据信号线、
//使用GPE6作命令信号线、使用GPE5作时钟信号线
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);//s3cmci_host指定host的private域
host->mmc = mmc;//s3cmci_host的host域又指定mmc_host,
//这是一个非常好的上下层结构互找的例子
host->pdev = pdev;//host的平台设备域,这样就可以拿到平台设备注册
//时的资源
host->is2440 = is2440;//处理特殊标志
host->pdata = pdev->dev.platform_data;
//将保存在平台设备中的平台数据拿出来
//如果为空的话,设置默认平台数据,看得出来其稳定性的考虑
//其实就是两个标志,一个是写保护,一个是插入检测
/*
static struct s3c24xx_mci_pdata s3cmci_def_pdata = {
//This is currently here to avoid a number of if (host->pdata)
//checks. Any zero fields to ensure reasonable defaults are picked.
.no_wprotect = 1,
.no_detect = 1,
};
*/
if (!host->pdata) {
pdev->dev.platform_data = &s3cmci_def_pdata;
host->pdata = &s3cmci_def_pdata;
}
spin_lock_init(&host->complete_lock);//初始化自旋锁
//初始化一个tasklet,
//########②pio_tasklet稍后分析,属于中断下半部机制
tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);
//SDI主机控制器的中断屏蔽寄存器和数据寄存器和时钟分频系数
if (is2440) {
host->sdiimsk = S3C2440_SDIIMSK;
host->sdidata = S3C2440_SDIDATA;
host->clk_div = 1;//设置预分频值,即:Prescaler的值
} else {
host->sdiimsk = S3C2410_SDIIMSK;
host->sdidata = S3C2410_SDIDATA;
host->clk_div = 2;//设置预分频值,即:Prescaler的值
}
//complete_what定义在s3cmci_host结构体中,
//用来记录请求处理所处的当前状态,这里初始化为
//COMPLETION_NONE即无状态,
//定义在头文件的s3cmci_waitfor中,里面枚举了6种状态。
host->complete_what = COMPLETION_NONE;
//pio_active定义在s3cmci_host结构体中,
//用来标记请求处理数据在FIFO方式下的数据方向是读还是写
host->pio_active = XFER_NONE;
//是否使用piodma
#ifdef CONFIG_MMC_S3C_PIODMA
host->dodma = host->pdata->use_dma;
#endif
//获取mem资源,并申请然后映射到虚拟地址
host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!host->mem) {
dev_err(&pdev->dev,
"failed to get io memory region resource.\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 resource.\n");
ret = -EINVAL;
goto probe_iounmap;
}
//卡控制器的中断#####③ISR一会儿再说
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. */
/*即使我们使用IMSK来屏蔽所有的中断,我们
还是会得到假的中断,为了不至于锁住
系统,我们在这里禁止掉中断,并将
中断状态设置为false*/
disable_irq(host->irq);
host->irq_state = false;
/*加入有插拔检测口,那么我们为它申请插拔检测
用的gpio口和中断*/
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);
}
} else
host->irq_cd = -1;
//如果有写保护则为它申请写保护用的
//gpio口,并设置为输入状态以供读取
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. */
//如果使用dma则为其申请dma通道
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;
}
//通过SDI的时钟源获取CPU的PCLK频率,
//这里为什么要获得CPU的PCLK频率呢,
//通过数据手册SDI控制器的方框图得知,
//SDI的时钟频率(SDCLK)=PCLK/(Prescaler+1)
host->clk_rate = clk_get_rate(host->clk);
//为mmc_host_ops赋值
//######⑤各个回调函数用到时再分析
mmc->ops = &s3cmci_ops;
mmc->ocr_avail= MMC_VDD_32_33 | MMC_VDD_33_34;//设置工作电压范围
#ifdef CONFIG_MMC_S3C_HW_SDIO_IRQ
mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
#else
mmc->caps = MMC_CAP_4_BIT_DATA;//设置总线宽度为4位
#endif
mmc->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;//一个request中的最大block数量
mmc->max_blk_size= 4095;//mmc block的最大值
mmc->max_req_size= 4095 * 512;//一次请求的最大数byte数
mmc->max_seg_size= mmc->max_req_size;//最大seg大小
mmc->max_segs = 128;//最多的segs
dbg(host, dbg_debug,
"probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n",
(host->is2440?"2440":""),
host->base, host->irq, host->irq_cd, host->dma);
//Linux的通知链机制
ret = s3cmci_cpufreq_register(host);
if (ret) {
dev_err(&pdev->dev, "failed to register cpufreq\n");
goto free_dmabuf;
}
//将SDI host设备注册到系统中,其实到这里我们可以看出
//最终的两步就是mmc_alloc_host和mmc_add_host
//中间的一系列的初始化无非是为mmc_add_host做准备
//####⑥有待分析啦
ret = mmc_add_host(mmc);
if (ret) {
dev_err(&pdev->dev, "failed to add mmc host.\n");
goto free_cpufreq;
}
s3cmci_debugfs_attach(host);
//将SDI host设备的数据赋值给系统平台设备,这样通过
//平台设备即可以拿到整个mmc_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;
}