一、 概述:
I2C工作原理:
I2C总线标准的两根传输线,SDA是数据线,Scl是时钟线,当SCL为高,SDA由高到低时,发送启动信息,发送9个脉冲,1-7是地址,8是读写控制位,9是ACK应答位,所以挂在I2C上的被控设备都接受所发送的信息,并把接收到的7位地址与自己的地址进行比较,如果相同ACK就会反馈应答。当SCL为低,SDA由低-à高,则发送停止信号。
Linux的I2C构架分为三个部分:
1)I2C core框架
提供了核心数据结构的定义和相关接口函数,用来实现I2C适配器
驱动和设备驱动的注册、注销管理,以及I2C通信方法上层的、与具体适配器无关的代码,为系统中每个I2C总线增加相应的读写方法。
2) I2C总线驱动
定义描述具体I2C总线适配器的i2c_adapter数据结构、实现在具体I2C适配器上的I2C总线通信方法,并由i2c_algorithm数据结构进行描述。 经过I2C总线驱动的的代码,可以为我们控制I2C产生开始位、停止位、读写周期以及从设备的读写、产生ACK等。
3) I2C 设备驱动
是对具体I2C硬件驱动的实现。I2C 设备驱动通过I2C适配器与CPU通信。其中主要包含i2c_driver和i2c_client数据结构,i2c_driver结构对应一套具体的驱动方法,例如:probe、remove、suspend等,需要自己申明。i2c_client数据结构由内核根据具体的设备注册信息自动生成,设备驱动根据硬件具体情况填充。
编写具体的I2C驱动时,工程师需要处理的主要工作如下:
1).提供I2C适配器的硬件驱动,探测,初始化I2C适配器(如申请I2C的I/O地址和中断号),驱动CPU控制的I2C适配器从硬件上产生。
2).提供I2C控制的algorithm, 用具体适配器的xxx_xfer()函数填充i2c_algorithm的master_xfer指针,并把i2c_algorithm指针赋给i2c_adapter的algo指针。
3).实现I2C设备驱动中的i2c_driver接口,用具体yyy的yyy_probe(),yyy_remove(),yyy_suspend(),yyy_resume()函数指针和i2c_device_id设备ID表赋给i2c_driver的probe,remove,suspend,resume和id_table指针。
4).实现I2C设备所对应类型的具体 ,i2c_driver只是实现设备与总线的挂接。
二、下面详细描述I2C设备驱动如何挂载到总线。
下面以GPIO模拟i2c总线的驱动为例,来介绍实现I2C设备驱动如何挂载到总线上
1) i2c-gpio总线和i2c设备驱动设备树
首先要添加 i2c-gpio总线和i2c设备驱动设备树节点,总线设备树节点无需添加,只需在添加设备驱动时,将对应的的I2C总线打开,添加节点内容在不同平台所相对应的设备树dts文件里,做法如下:
&i2c4 {status = "okay";<name>[@<unit_address>]@{ //其中name就是设备名,最长可以是31个字符长度。unit_address一般是设备地址,用来唯一标识一个节点compatible = "name2"; //需要与name_ids的 .compatible一致,不然注册不成功reg = <unit_address>;irq_gpio = <&gpio7 GPIO_B2 IRQ_TYPE_LEVEL_LOW>;//中断脚GPIOreset_gpio = <&gpio7 GPIO_B1 GPIO_ACTIVE_HIGH>; //复位脚GPIO};}
2) i2c-gpio总线和i2c设备驱动注册
i2c总线注册无需自己注册,把i2c设备驱动注册到i2c-gpio总线。做法如下,
I2C设备树
做法如下。首先定义设备ID:static const struct i2c_device_id name_id[] = {{ "name1", 0 },//设备名和设备是有数据长度{ }};
设备树名称:
static struct of_device_id name_ids[] = {{ .compatible = "name2" },//需要与设备树compatible一致,不然注册不成功{ }
};然后声明i2c_driver结构:static struct i2c_driver name_driver = {.probe = name_probe, .remove = name_remove,.suspend = name_suspend,.resume = name_resume,//上面4个函数根据具体情况取舍.id_table = name_id,.driver = {.name = "name1", //需要与name_id里的设备名一致.of_match_table = of_match_ptr(name_ids),},};
最后调用static inline int i2c_add_driver(struct i2c_driver *driver)注册name驱动到I2C总线,如下:static int __init name_init(void){return i2c_add_driver(&name_driver);//注册name_driver};module_init(name_init);
3) 实现设备与总线的挂接
i2c_driver实现设备与总线的挂接。用具体yyy的yyy_probe(),yyy_remove(),yyy_suspend(),yyy_resume()函数指针和i2c_device_id设备ID表赋给i2c_driver的probe,remove,suspend,resume和id_table指针。其中.probe指针所对应的yyy_probe()函数指针则判断上电时序、I2C是否读取正确,返回值正确才能够实现I2C驱动设备的使用。
yyy_probe()函数指针判断大致流程如下,以上面缩写的设备树为例:
第一步;从设备数获取指定GPIO属性信息irq_gpio = of_get_named_gpio(np, "irq_gpio",0);reset_gpio = of_get_named_gpio(np, "irq_gpio", 0);
第二步:申请GPIO管脚ret = gpio_request(reset_gpio, "touch_gpio_reset");ret += gpio_request(irq_gpio, "touch_gpio_irq");
第三步:判断上电时序
不同设备有不同的上电时序,根绝供应商所提供的资料编写
第四步:读取I2C
s32 name_i2c_read(struct i2c_client *client, u8 *buf, s32 len)
{struct i2c_msg msgs[2];s32 ret=-1;s32 retries = 0;GTP_DEBUG_FUNC();msgs[0].flags = !I2C_M_RD;msgs[0].addr = client->addr;msgs[0].len = GTP_ADDR_LENGTH;msgs[0].buf = &buf[0];
#ifdef CONFIG_I2C_ROCKCHIP_COMPATmsgs[0].scl_rate=200 * 1000;//msgs[0].scl_rate = 300 * 1000; // for Rockchip, etc.
#endifmsgs[1].flags = I2C_M_RD;msgs[1].addr = client->addr;msgs[1].len = len - GTP_ADDR_LENGTH;msgs[1].buf = &buf[GTP_ADDR_LENGTH];
#ifdef CONFIG_I2C_ROCKCHIP_COMPATmsgs[1].scl_rate=200 * 1000;//msgs[1].scl_rate = 300 * 1000; // for Rockchip, etc.
#endifwhile(retries < 5){ret = i2c_transfer(client->adapter, msgs, 2);if(ret == 2)break;retries++;}if((retries >= 5)){printk("I2C Read: 0x%04X, %d bytes failed, errcode: %d! Process reset.", (((u16)(buf[0] << 8)) | buf[1]), len-2, ret);}return ret;
}
第五步:读取到正确的I2C,将GPIO管脚释放gpio_free(reset_gpio);gpio_free(irq_gpio);
4) 实现I2C设备所对应类型的具体
实现 I2C 设备驱动的文件操作接口,即实现具体设备 yyy 的 yyy_read() 、 yyy_write() 和 yyy_ioctl() 函数等