基于平台设备驱动架构分析。
- 以arch/arm/plat-samsung/dev-i2c0.c为参考例子
static struct resource s3c_i2c_resource[] = {
[0] = {
.start = S3C_PA_IIC,
.end = S3C_PA_IIC + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_IIC,
.end = IRQ_IIC,
.flags = IORESOURCE_IRQ,
},
};
struct platform_device s3c_device_i2c0 = {
.name = "s3c2410-i2c",
#ifdef CONFIG_S3C_DEV_I2C1
.id = 0,
#else
.id = -1,
#endif
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource = s3c_i2c_resource,
};
//构建i2c平台数据
static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {
.flags = 0,
.slave_addr = 0x10,
.frequency = 100*1000, //传输速率 100kps
.sda_delay = 100, //间隔时间
};
- 注册平台设备:arch/arm/mach-s5pv210/mach-smdkv210.c的 smdkv210_machine_init()函数
static void __init smdkv210_machine_init(void)
{
s3c_pm_init();
smdkv210_dm9000_init();
samsung_keypad_set_platdata(&smdkv210_keypad_data);
s3c24xx_ts_set_platdata(&s3c_ts_platform);
// 调用iic平台数据
s3c_i2c0_set_platdata(NULL);
s3c_i2c1_set_platdata(NULL);
s3c_i2c2_set_platdata(NULL);
// 调用板卡信息:管理每个IIC模块可以管理的iic子设备,最终i2c_register_board_info()会// 形成__i2c_board_list设备链表
i2c_register_board_info(0, smdkv210_i2c_devs0,
ARRAY_SIZE(smdkv210_i2c_devs0));
i2c_register_board_info(1, smdkv210_i2c_devs1,
ARRAY_SIZE(smdkv210_i2c_devs1));
i2c_register_board_info(2, smdkv210_i2c_devs2,
ARRAY_SIZE(smdkv210_i2c_devs2));
s3c_ide_set_platdata(&smdkv210_ide_pdata);
s3c_fb_set_platdata(&smdkv210_lcd0_pdata);
platform_add_devices(smdkv210_devices, ARRAY_SIZE(smdkv210_devices));
}
static struct platform_device *smdkv210_devices[] __initdata = {
.......
&s3c_device_i2c0,
.......
};
//注册平台设备
int platform_add_devices(struct platform_device **devs, int num)
{
int i, ret = 0;
for (i = 0; i < num; i++) {
ret = platform_device_register(devs[i]);
if (ret) {
while (--i >= 0)
platform_device_unregister(devs[i]);
break;
}
}
return ret;
}
- /drivers/i2c/busses/i2c-s3c2410.c
step1: subsys_initcall(i2c_adap_s3c_init); // 入口函数
step2: module_exit(i2c_adap_s3c_exit); //出口函数
static int __init i2c_adap_s3c_init(void)
{
return platform_driver_register(&s3c24xx_i2c_driver);//注册平台设备驱动
}
static void __exit i2c_adap_s3c_exit(void)
{
platform_driver_unregister(&s3c24xx_i2c_driver);//注销平台设备驱动
}
static struct platform_device_id s3c24xx_driver_ids[] = {
{
.name = "s3c2410-i2c",
.driver_data = TYPE_S3C2410,
}, {
.name = "s3c2440-i2c",
.driver_data = TYPE_S3C2440,
}, { },
};
static struct platform_driver s3c24xx_i2c_driver = {
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
.id_table = s3c24xx_driver_ids,
.driver = {
.owner = THIS_MODULE,
.name = "s3c-i2c",
.pm = S3C24XX_DEV_PM_OPS,
},
};
step3:通过id_table来匹配(即执行.match函数)并执行.probe = s3c24xx_i2c_probe,函数。
当 s3c24xx_i2c_driver的.id_table = s3c24xx_driver_ids,中的name与arch/arm/plat-samsung/dev-i2c0.c中的平台设备s3c_device_i2c0的.name = "s3c2410-i2c",相同。则会去执行platform_driver的.probe = s3c24xx_i2c_probe,函数。
step4: s3c24xx_i2c_probe()函数
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
.....
pdata = pdev->dev.platform_data; // 获取平台设备数据
i2c->adap.algo = &s3c24xx_i2c_algorithm; //设置adapter
==============================
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
//i2c_transfer()函数调用 adap->algo->master_xfer(adap,msgs,num);.master_xfer = s3c24xx_i2c_xfer,指定了调用的函数
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
==============================
clk_enable(i2c->clk); //使能IIC时钟
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获取平台资源:IO内存地址资源
i2c->ioarea = request_mem_region(res->start, resource_size(res),
pdev->name);
i2c->regs = ioremap(res->start, resource_size(res));// 将物理地址映射为虚拟地址
ret = s3c24xx_i2c_init(i2c); // 硬件初始化I2C控制器
i2c->irq = ret = platform_get_irq(pdev, 0); //获取IRQ资源
ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,dev_name(&pdev->dev), i2c);
......
ret = i2c_add_numbered_adapter(&i2c->adap); //注册IIC适配器
platform_set_drvdata(pdev, i2c); //设置平台驱动数据
}
step5: s3c24xx_i2c_init():硬件初始化具体分析
static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
{
//获取iic平台数据: i2c->dev->platform_data对应Dev-i2c0.c中s3c_i2c0_set_platdata()函数中的//s3c_device_i2c0.dev.platform_data赋值为__initdata。
pdata = i2c->dev->platform_data;
//配置管脚功能
if (pdata->cfg_gpio)
pdata->cfg_gpio(to_platform_device(i2c->dev));
//配置S3C2410_IICCON寄存器
unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;
writel(iicon, i2c->regs + S3C2410_IICCON);
//设置时钟频率
if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {
writel(0, i2c->regs + S3C2410_IICCON);
dev_err(i2c->dev, "cannot meet bus frequency required\n");
return -EINVAL;
}
return 0;
}
step6: request_irq();注册IIC发送接收中断,当发送/接收中断来了之后,执行s3c24xx_i2c_irq
static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
{
struct s3c24xx_i2c *i2c = dev_id;
unsigned long status;
unsigned long tmp;
// 获取S3C2410_IICSTAT寄存器的信息
status = readl(i2c->regs + S3C2410_IICSTAT);
if (status & S3C2410_IICSTAT_ARBITR) { // 总线调用是否成功
/* deal with arbitration loss */
dev_err(i2c->dev, "deal with arbitration loss\n");
}
if (i2c->state == STATE_IDLE) {
dev_dbg(i2c->dev, "IRQ: error i2c->state == IDLE\n");
tmp = readl(i2c->regs + S3C2410_IICCON);
tmp &= ~S3C2410_IICCON_IRQPEND;
writel(tmp, i2c->regs + S3C2410_IICCON);
goto out;
}
//通过IIC中断来发送/接收数据
i2c_s3c_irq_nextbyte(i2c, status);
out:
return IRQ_HANDLED;
}
step7: 注册适配器:i2c_add_numbered_adapter()函数
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
status = i2c_register_adapter(adap);
return status;
}
=================================================================================
static int i2c_register_adapter(struct i2c_adapter *adap)
{
dev_set_name(&adap->dev, "i2c-%d", adap->nr); // 指定适配器的名字
adap->dev.bus = &i2c_bus_type; //指定适配器所属的总线
adap->dev.type = &i2c_adapter_type; //指定适配器类型
res = device_register(&adap->dev); //注册设备
}