IIC驱动---平台设备驱动层分析(5)

本文详细解读了基于Samsung平台的S3C2410 I2C驱动架构,涉及资源分配、平台设备初始化、i2c平台数据设置、平台设备注册和驱动程序注册注销过程。重点介绍了I2C设备注册及硬件初始化,包括时钟配置、中断处理和适配器注册等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于平台设备驱动架构分析。

  • 以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); //注册设备

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

L7256

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值