4.1 概述
I2C总线驱动是I2C适配器的软件实现,提供I2C适配器与从设备间完成数据通信的能力,比如起始,停止,应答信号和master_xfer的实现函数。
I2C总线驱动由i2c_adapter和i2c_algorithm来描述
4.2 S3c2440I2C控制器的硬件描述
S3c2440处理器内部集成了一个I2C控制器,通过四个寄存器来进行控制:
IICCON I2C控制寄存器
IICSTAT I2C状态寄存器
IICDS I2C收发数据移位寄存器
IICADD I2C地址寄存器
通过IICCON,IICDS,IICADD寄存器操作,可在I2C总线上产生开始位、停止位、数据和地址,而传输的状态则通过IICSTAT寄存器来获取。
4.3 i2c-s3c2410总线驱动分析(platform_driver)
I2C总线驱动代码在drivers/i2c/busses/i2c-s3c2410.c,这个代码同样支持s3c2410,s3c6410,s5pc110等Samsung 系列的芯片。
初始化模块和卸载模块
static int __init i2c_adap_s3c_init(void) { returnplatform_driver_register(&s3c24xx_i2c_driver); } static void __exit i2c_adap_s3c_exit(void) { platform_driver_unregister(&s3c24xx_i2c_driver); }
总线驱动是基于platform来实现的,很符合设备驱动模型的思想。
static struct platform_drivers3c24xx_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, .of_match_table= s3c24xx_i2c_match, }, };
s3c24xx_i2c_probe函数
当调用platform_driver_register函数注册platform_driver结构体时,如果platformdevice 和 platform driver匹配成功后,会调用probe函数,来初始化适配器硬件。
static int s3c24xx_i2c_probe(structplatform_device *pdev) { …… /*初始化适配器信息 */ strlcpy(i2c->adap.name,"s3c2410-i2c", sizeof(i2c->adap.name)); i2c->adap.owner = THIS_MODULE; i2c->adap.algo = &s3c24xx_i2c_algorithm; i2c->adap.retries= 2; i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; i2c->tx_setup = 50; /*初始化自旋锁和等待队列头 */ spin_lock_init(&i2c->lock); init_waitqueue_head(&i2c->wait); /*映射寄存器 */ res= platform_get_resource(pdev, IORESOURCE_MEM, 0); i2c->ioarea= request_mem_region(res->start, resource_size(res), pdev->name); i2c->regs= ioremap(res->start, resource_size(res)); /*设置I2C核心需要的信息 */ i2c->adap.algo_data= i2c; i2c->adap.dev.parent= &pdev->dev; /*初始化I2C控制器 */ ret= s3c24xx_i2c_init(i2c); /*申请中断 */ i2c->irq= ret = platform_get_irq(pdev, 0); ret= request_irq(i2c->irq, s3c24xx_i2c_irq, 0, dev_name(&pdev->dev), i2c); /* 注册I2C适配器 */ ret= i2c_add_numbered_adapter(&i2c->adap); …… }
Probe主要工作是时能硬件并申请I2C适配器使用的IO地址,中断号等,然后向I2C核心添加这个适配器。I2c_adapter注册过程i2c_add_numbered_adapter->i2c_register_adapter
I2C总线通信方法
static const struct i2c_algorithms3c24xx_i2c_algorithm = { .master_xfer = s3c24xx_i2c_xfer, .functionality = s3c24xx_i2c_func, };
s3c24xx_i2c_xfer函数是总线通信方式的具体实现,依赖于s3c24xx_i2c_doxfer和s3c24xx_i2c_message_start两个函数;
static int s3c24xx_i2c_doxfer(structs3c24xx_i2c *i2c, struct i2c_msg *msgs, int num) { ret =s3c24xx_i2c_set_master(i2c); i2c->msg = msgs; i2c->msg_num= num; i2c->msg_ptr= 0; i2c->msg_idx= 0; i2c->state = STATE_START; s3c24xx_i2c_message_start(i2c,msgs); }
首先设置s3c I2C设备器为主设备,然后调用s3c24xx_i2c_message_start函数启动I2C消息传输。
s3c24xx_i2c_func函数返回适配器所支持的通信功能。
4.4 适配器的设备资源(platform_device)
S3c2440的I2C总线驱动是基于platform来实现,前面我们分析了platformdriver部分,再来看下platform device部分。
在arch/arm/plat-samsung/dev-i2c0.c文件中定义了platform_device结构体以及I2C控制器的资源信息:
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, }; struct s3c2410_platform_i2cdefault_i2c_data __initdata = { .flags = 0, .slave_addr = 0x10, /* I2C适配器的地址 */ .frequency = 100*1000, /* 总线频率 */ .sda_delay = 100, /* SDA边沿延迟时间ns */ }; void __init s3c_i2c0_set_platdata(structs3c2410_platform_i2c *pd) { structs3c2410_platform_i2c *npd; if(!pd) pd= &default_i2c_data; npd= s3c_set_platdata(pd, sizeof(struct s3c2410_platform_i2c), &s3c_device_i2c0); if(!npd->cfg_gpio) npd->cfg_gpio= s3c_i2c0_cfg_gpio; }
在板文件中把platform_device注册进内核:
static struct platform_device*mini2440_devices[] __initdata = { …… &s3c_device_i2c0, …… };
调用s3c_i2c0_set_platdata 函数把适配器具体的数据赋值给dev.platform_data:
static void __init mini2440_init(void) { …… s3c_i2c0_set_platdata(NULL); }
I2C总线驱动就分析到这里。