Linux的i2c驱动详解
Posted 风雨田
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux的i2c驱动详解相关的知识,希望对你有一定的参考价值。
目录(?)[-]1 简介
I2C 总线仅仅使用 SCL 、 SDA 两根信号线就实现了设备之间的数据交互,极大地简化对硬件资源和 PCB 板布线空间的占用。因此, I2C 总线被非常广泛地应用在 EEPROM 、实时钟、小型 LCD 等设备与 CPU 的接口中。 Linux I2C GPIO驱动是在没有专用I2C芯片的情况下,用GPIO口来模拟I2C总线时序,完成Linux与I2C设备的通信过程。用两根GPIO,分别模拟SDA和SCL。它与使用i2c芯片的驱动有所不同的是传输算法的实现,GPIO模拟i2c驱动中有自己的一套传输算法。GPIO模拟I2C是要占用CPU资源的,而用I2C芯片是不占CPU资源的。使用i2c子系统,而不使用普通字符设备,有以下好处:
1) 使用Linux I2C子系统,不需要去过于详细了解I2C操作。
2) 编写驱动可移植性强。
3) 可以使用内核资源,当面对复杂I2C器件,工作量相对少得多。
I2C工作原理:I2C总线标准的两根传输线,SDA是数据线,Scl是时钟线,当SCL为高,SDA由高-à低时,发送启动信息,发送9个脉冲,1-7是地址,8是读写控制位,9是ACK应答位,所以挂在I2C上的被控设备都接受所发送的信息,并把接收到的7位地址与自己的地址进行比较,如果相同ACK就会反馈应答。当SCL为低,SDA由低-à高,则发送停止信号。
2 架构
Linux的I2C构架分为三个部分:
1)I2C core框架
提供了核心数据结构的定义和相关接口函数,用来实现I2C适配器
驱动和设备驱动的注册、注销管理,以及I2C通信方法上层的、与具体适配器无关的代码,为系统中每个I2C总线增加相应的读写方法。
I2C core框架具体实现在/drivers/i2c目录下的i2c-core.c和i2c-dev.c
2) I2C总线驱动
定义描述具体I2C总线适配器的i2c_adapter数据结构、实现在具体I2C适配器上的I2C总线通信方法,并由i2c_algorithm数据结构进行描述。 经过I2C总线驱动的的代码,可以为我们控制I2C产生开始位、停止位、读写周期以及从设备的读写、产生ACK等。
I2C总线驱动具体实现在/drivers/i2c目录下busses文件夹。例如:Linux I2C GPIO总线驱动为i2c_gpio.c. I2C总线算法在/drivers/i2c目录下algos文件夹。例如:Linux I2C GPIO总线驱动算法实现在i2c_algo_bit.c.
3) I2C 设备驱动
是对具体I2C硬件驱动的实现。I2C 设备驱动通过I2C适配器与CPU通信。其中主要包含i2c_driver和i2c_client数据结构,i2c_driver结构对应一套具体的驱动方法,例如:probe、remove、suspend等,需要自己申明。i2c_client数据结构由内核根据具体的设备注册信息自动生成,设备驱动根据硬件具体情况填充。具体使用下面介绍。
I2C 设备驱动具体实现放在在/drivers/i2c目录下chips文件夹。
3 设备注册
下面以GPIO模拟i2c总线的驱动为例,来介绍设备注册,对于使用i2c芯片的驱动都是大同小异,主要在传输算法上的区别。首先make menuconfig把i2c-gpio选上,让它能编进内核。设备注册包括两种设备的注册,i2c-gpio总线和i2c设备驱动。
1) i2c-gpio总线注册
/drivers/i2c/busses/i2c_gpio.c是i2c-gpio总线驱动源码。在这里可以看到i2c-gpio的注册:
static struct platform_driver i2c_gpio_driver = {
.driver = {
.name = "i2c-gpio", //驱动名字
.owner = THIS_MODULE,
},
.probe = i2c_gpio_probe,
.remove = __devexit_p(i2c_gpio_remove),
};
static int __init i2c_gpio_init(void)
{
int ret;
ret = platform_driver_register(&i2c_gpio_driver);//注册成平台设备
if (ret)
printk(KERN_ERR "i2c-gpio: probe failed: %d\n", ret);
return ret;
}
module_init(i2c_gpio_init);
platform是linux虚拟的总线,称为platform总线,相应的设备称为platform_device,相应的驱动称为platform_driver。我们知道i2c总线也对应一个设备,在这里就是对应的i2c_adapte结构,这在后面会有详细介绍。在这里可以看到它将i2c总线驱动注册成平台设备驱动platform_driver_register(&i2c_gpio_driver)。
把i2c_gpio设备注册为平台设备,需要在mach_xxx的板级文件(devices.c)中添加i2c-gpio需要用到的资源定义,即将i2c总线设备封装成平台设备,下面首先定义总线占用的系统资源:
static struct i2c_gpio_platform_data i2c3_data = {
.sda_pin = CONFIG_SDA_PIN;
.scl_pin = CONFIG_SCL_PIN; //设置需要用到的gpio引脚
.udelay = 0, //设置I2C工作频率,如果没有默认值为50
.timeout = 0, //设置I2C工作超时,如果没有默认值为10
};
由于i2c_gpio驱动需要注册到platform总线上面,还需要在mach_xxx的板级文件中添加i2c-gpio的platform_device结构。
static struct platform_device i2c3_device = {
.name = "i2c-gpio", //必须和i2c-gpio驱动的名字相同
.id = 2, //总线ID号
.dev = {
.platform_data = &i2c3_data,
},
};
注册i2c-gpio驱动前要有一个GPIO的设置过程,设置过程如下:
{
//SDA
Pnx_gpio_set_mode(GPIO_F8,GPIO_MODE_MUX1)
Pnx_gpio_set_direction(GPIO_F8,GPIO_DIR_OUTPUT)
//SCL
Pnx_gpio_set_mode(GPIO_F7,GPIO_MODE_MUX1)
Pnx_gpio_set_direction(GPIO_F7,GPIO_DIR_OUTPUT)
};
最后把i2c-gpio设备注册进platform总线。
platform_device_register(&i2c3_device);
2) 把i2c设备驱动注册到i2c-gpio总线
例如:设备驱动源码在/drivers/i2c/chips/lis35de.c,其注册到i2c总线需要的
做法如下。首先定义设备ID:
static const struct i2c_device_id lis35de_id[] = {
{ "lis35de", 0 },//设备名和设备是有数据长度
{ }
};
然后声明i2c_driver结构:
static struct i2c_driver st_lis35de_driver = {
.probe = st_lis35de_probe,
.remove = st_lis35de_remove,
.suspend = st_lis35de_suspend,
.resume = st_lis35de_resume,//上面4个函数根据具体情况取舍
.id_table = lis35de_id,
.driver = {
.name = "lis35de", //驱动名字
},
};
最后调用static inline int i2c_add_driver(struct i2c_driver *driver)注册lis35de驱动到I2C总线,如下:
static int __init st_lis35de_init(void)
{
return i2c_add_driver(&st_lis35de_driver);//注册st_lis35de_driver
};
module_init(st_lis35de_init);
但是到目前还不知道注册到那根I2C总线,现在把lis35de设备驱动添加到我们想要的i2c-gpio总线上。使用内核提供的函数i2c_register_board_info,在mach_xxx的板级文件中把设备信息注册到需要注册的I2C总线上面。
int __init i2c_register_board_info(int busnum,//设备需要注册到的总线ID
struct i2c_board_info const *info,//设备信息包括设备名,地址等
unsigned len)
例如:把lis35de驱动注册到i2c-gpio总线,总线ID为2。
static struct i2c_board_info i2c_devices_lis35de[] = {
{
I2C_BOARD_INFO("lis35de", 0x1C), //设备名和地址
},
};
i2c_register_board_info(2,i2c_devices_lis35de,ARRAY_SIZE(i2c_devices_lis35de));
arch/arm/mach-pnx67xx/board_pnx67xx_wavex.c中unsigned int pnx_modem_gpio_reserved[]下注释掉GPIO_F7,GPIO_F8,防止内核认为F8,F7已经使用过了,至此已经把i2c-gpio总线注册到系统,把设备驱动注册到i2c-gpio总线。
前面说了那么多,是不是有点乱了,这里我们在来理一下:
(一)i2c总线驱动
1)在那个devices.c文件中,声明平台设备占用的系统资源,然后定义一个平台设备,并注册这个平台设备到平台总线上
2)在i2c-gpio.c文件中,声明该驱动支持的设备列表,然后定义一个平台驱动结构,并注册这个平台驱动到平台总线上
(二)i2c设备驱动
1)同样在devices.c文件下,在对应总线的设备列表中声明一个i2c设备结构,然后通过i2c_register_board_info()函数,将这个设备列表注册到i2c总线上
2)在lis35de.c文件中,声明支持的i2c设备列表和一个i2c设备驱动结构体i2c_driver,然后将其注册到i2c总线上
注意:这里不管是设备还是驱动先注册到总线上,他们都会自动请求匹配总线上的所有驱动或设备。
4 I2C关键数据结构和详细注册流程
上面的描述都是i2c系统的框架,具体的数据结构注册流程下面会详细介绍。
4.1 关键数据结构
在i2c.h头文件中定义了i2c_adapter、i2c_algorithm、i2c_driver和i2c_client 4个比较关键的数据结构。
1)i2c_algorithm对应一套通信方法。
用来实现具体的收发算法,此数据结构非常重要,通过其中的收发函数会调用具体的硬件收发操作,对于i2c-gpio总线的通信方法实现在/drivers/i2c目录下algos文件夹i2c_algo_bit.c。
struct i2c_algorithm { int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,int num); //i2c传输函数指针 int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data * data); //smbus传输函数指针 u32 (*functionality) (struct i2c_adapter *); //返回适配器支持功能 }; |
2)i2c_adapter
用来定义总线上的每一个adapter(适配器),每一个adapter都需要i2c_algorithm中提供的通信函数来控制适配器的访问周期,因此在i2c_adapter中包含i2c_algorithm指针。i2c_algorithm的关键函数master_xfer用于产生I2C访问信号,以i2c_msg为单位。
struct i2c_adapter { struct module *owner; //所属模块 unsigned int id; //algorithm类型,定义在i2c-id.h以I2C_ALGO_开始 unsigned int class; /* classes to allow probing for */ const struct i2c_algorithm *algo; void *algo_data; //algorithm数据
int (*client_register)(struct i2c_client *); //client注册时调用 int (*client_unregister)(struct i2c_client *);
/* data fields that are valid for all devices */ u8 level; /* nesting level for lockdep */ struct mutex bus_lock; struct mutex clist_lock;
int timeout; /* in jiffies */ int retries; struct device dev; /* 适配器设备 */
int nr; struct list_head clients; /* DEPRECATED */ char name[48]; struct completion dev_released; };
struct i2c_msg { __u16 addr; /* 从机地址 */ __u16 flags; #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ #define I2C_M_RD 0x0001 /* read data, from slave to master */ #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */ __u16 len; /* msg length */ __u8 *buf; /* pointer to msg data */ }; |
3) i2c_driver结构体
struct i2c_driver {
int id;
unsigned int class;
int (*attach_adapter)(struct i2c_adapter *);//依附i2c_adapter
int (*detach_adapter)(struct i2c_adapter *);//脱离i2c_adapter
int (*detach_client)(struct i2c_client *) __deprecated; //脱离i2c_ client
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
void (*shutdown)(struct i2c_client *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
//类似ioctl
struct device_driver driver;
const struct i2c_device_id *id_table;
int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *);
/* Device detection callback for automatic device creation */
const struct i2c_client_address_data *address_data;
struct list_head clients;
};
4)i2c_client结构体。
struct i2c_client {
unsigned short flags; /* 标志 */
unsigned short addr; /* 低7位为芯片地址 */
char name[I2C_NAME_SIZE]; //设备名
struct i2c_adapter *adapter; /*依附i2c_adapter */
struct i2c_driver *driver; /*依附i2c_ driver */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head list; /* DEPRECATED */
struct list_head detected;
struct completion released;
};
4.2详细注册流程
在平台设备驱动注册时,i2c-gpio总线被注册后,第一个执行的函数是i2c_gpio_probe:在这里主要做了3件事,首先构筑了一个i2c_adapter,然后申请gpio,最后关键调用i2c_bit_add_bus。虽然i2c总线被注册成平台设备,但这里主要讲解i2c总线部分内容,对平台设备和驱动的注册过程不做详述,感兴趣的可以查阅平台驱动部分内容。
static int __devinit i2c_gpio_probe(struct platform_device *pdev)
{ pdata = pdev->dev.platform_data; //获得具体硬件结构 if (!pdata) return -ENXIO; ret = -ENOMEM; adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);//在这里获得了适配器 if (!adap) goto err_alloc_adap; bit_data = kzalloc(sizeof(struct i2c_algo_bit_data), GFP_KERNEL);//在这里定义了具体硬件的实现 if (!bit_data) goto err_alloc_bit_data; ret = gpio_request(pdata->sda_pin, "sda"); if (ret) goto err_request_sda; ret = gpio_request(pdata->scl_pin, "scl"); if (ret) goto err_request_scl; if (pdata->sda_is_open_drain) { //如果集电极开路 gpio_direction_output(pdata->sda_pin, 1);//设定方向为输出 bit_data->setsda = i2c_gpio_setsda_val;//设定setsda实现函数 } else { gpio_direction_input(pdata->sda_pin);//如果集电极不是开路,那么设定方向为输入 bit_data->setsda = i2c_gpio_setsda_dir;//设定setsda的实现函数 } if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {//集电极开路并仅做输出 gpio_direction_output(pdata->scl_pin, 1);//设定方向 bit_data->setscl = i2c_gpio_setscl_val; //设定setscl的实现函数 } else { //集电极不是开路时候 gpio_direction_input(pdata->scl_pin); bit_data->setscl = i2c_gpio_setscl_dir; } if (!pdata->scl_is_output_only) //仅仅作为输出 bit_data->getscl = i2c_gpio_getscl; bit_data->getsda = i2c_gpio_getsda; if (pdata->udelay) //关于延时的设定 bit_data->udelay = pdata->udelay; else if (pdata->scl_is_output_only) bit_data->udelay = 50; /* 10 kHz */ else bit_data->udelay = 5; /* 100 kHz */ if (pdata->timeout) bit_data->timeout = pdata->timeout; else bit_data->timeout = HZ / 10; /* 100 ms */ bit_data->data = pdata; //让bit_data与platform_data相关联 adap->owner = THIS_MODULE; snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id); adap->algo_data = bit_data; //algo_data中加入具体的实现方法,被algo中函数调用 adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adap->dev.parent = &pdev->dev; /* * If "dev->id" is negative we consider it as zero. * The reason to do so is to avoid sysfs names that only make * sense when there are multiple adapters. */ adap->nr = (pdev->id != -1) ? pdev->id : 0; ret = i2c_bit_add_numbered_bus(adap); //添加进入总线,在i2c-algo-bit.c中定义 if (ret) goto err_add_bus; platform_set_drvdata(pdev, adap);//加定适配器到platform设备中去 return 0; }
|
此函数是整个i2c-gpio.c的关键,几乎整个文件函数的注册以及algo中相关函数的注册都与他相关。i2c_bit_add_bus:在这里调用i2c_bit_prepare_bus。
int i2c_bit_add_numbered_bus(struct i2c_adapter *adap)
{
int err;
err = i2c_bit_prepare_bus(adap); //这里就是算法和适配器的关联函数,算法在别处已有实现
if (err)
return err;
return i2c_add_numbered_adapter(adap);
}
i2c_bit_prepare_bus:可以看到原来分配到的适配器在这里和i2c_bit_algo算法结合了。
static int i2c_bit_prepare_bus(struct i2c_adapter *adap)
{
struct i2c_algo_bit_data *bit_adap = adap->algo_data;
if (bit_test) {
int ret = test_bus(bit_adap, adap->name);
if (ret < 0)
return -ENODEV;
}
/* register new adapter to i2c module... */
adap->algo = &i2c_bit_algo; //没错,就是这里的关联
adap->timeout = 100; /* default values, should */
adap->retries = 3; /* be replaced by defines */
return 0;
}
再回头看i2c_bit_add_bus中调用的i2c_add_numbered_adapter(adap):
int i2c_add_adapter(struct i2c_adapter *adapter)
{
int id, res = 0;
retry:
if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
return -ENOMEM;
mutex_lock(&core_lock);
/* "above" here means "above or equal to", sigh */
res = idr_get_new_above(&i2c_adapter_idr, adapter,
__i2c_first_dynamic_bus_num, &id);
mutex_unlock(&core_lock);
if (res < 0) {
if (res == -EAGAIN)
goto retry;
return res;
}
adapter->nr = id;
return i2c_register_adapter(adapter);
}
在i2c_add_numbered_adapter最后调用了i2c_register_adapter(adapter);
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = 0, dummy;
/* Can‘t register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p)))
return -EAGAIN;
mutex_init(&adap->bus_lock);
mutex_init(&adap->clist_lock);
INIT_LIST_HEAD(&adap->clients);
mutex_lock(&core_lock);
/* Add the adapter to the driver core.
* If the parent pointer is not set up,
* we add this adapter to the host bus.
*/
if (adap->dev.parent == NULL) {
adap->dev.parent = &platform_bus;
pr_debug("I2C adapter driver [%s] forgot to specify "
"physical device\n", adap->name);
}
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
adap->dev.release = &i2c_adapter_dev_release;
adap->dev.class = &i2c_adapter_class;
res = device_register(&adap->dev);
if (res)
goto out_list;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
/* create pre-declared device nodes for new-style drivers */
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap); //静态扫描挂在总线上的所有i2c设备,然后逐一创建一个i2c_client结构
/* Notify drivers */
dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,
i2c_do_add_adapter);
out_unlock:
mutex_unlock(&core_lock);
return res;
out_list:
idr_remove(&i2c_adapter_idr, adap->nr);
goto out_unlock;
}
其中的i2c_scan_static_board_info(adap)调用i2c_new_device:
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo;
mutex_lock(&__i2c_board_lock);
list_for_each_entry(devinfo, &__i2c_board_list, list) {
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,
&devinfo->board_info)) //就是这里,它根据devinfo来创建一个新的i2c_client设备结构
printk(KERN_ERR "i2c-core: can‘t create i2c%d-%04x\n",
i2c_adapter_id(adapter),
devinfo->board_info.addr);
}
mutex_unlock(&__i2c_board_lock);
}
i2c_new_device:
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
int status;
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
client->adapter = adap;
client->dev.platform_data = info->platform_data;
if (info->archdata)
client->dev.archdata = *info->archdata;
client->flags = info->flags;
client->addr = info->addr;
client->irq = info->irq;
strlcpy(client->name, info->type, sizeof(client->name));
/* a new style driver may be bound to this device when we
* return from this function, or any later moment (e.g. maybe
* hotplugging will load the driver module). and the device
* refcount model is the standard driver model one.
*/
status = i2c_attach_client(client);
if (status < 0) {
kfree(client);
client = NULL;
}
return client;
}
在i2c_new_device中,内核为我们分配i2c_client,把我们在板级文件中申明的设备名和地址:
static struct i2c_board_info i2c_devices[] = {
{
I2C_BOARD_INFO("lis35de", 0x1C), //设备名和地址
},
填充到i2c_client。一个i2c_client就代表着一个位于adapter适配器上,地址为client->addr,使用设备驱动的一个i2c设备。到这里就可以通过 Linux I2C核心提供的不依赖硬件接口的函数了,接受/发送函数等。
也许你会疑惑在创建i2c_client设备时,是根据devinfo来创建的,那么这个devinfo是从那来的?我们在devices.c中不是注册的是i2c_board_info这个结构吗?没错,在devices.c中的确是注册的i2c_board_info结构,下面我们来看看注册这个结构数组的这个i2c_regester_board_info()函数:
int __init
i2c_register_board_info(int busnum,
struct i2c_board_info const *info, unsigned len)
{
int status;
down_write(&__i2c_board_lock);
/* dynamic bus numbers will be assigned after the last static one */
if (busnum >= __i2c_first_dynamic_bus_num)
__i2c_first_dynamic_bus_num = busnum + 1;
for (status = 0; len; len--, info++) {
struct i2c_devinfo *devinfo;
devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
if (!devinfo) {
pr_debug("i2c-core: can‘t register boardinfo!\n");
status = -ENOMEM;
break;
}
devinfo->busnum = busnum;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list);
}
up_write(&__i2c_board_lock);
return status;
}
看到了吧,在这个注册函数里,它创建一个devinfo结构变量,并用总线号和i2c_board_info结构来初始化这个devinfo变量,然后加入一个全局的devinfo链表,来看看
以上是关于Linux的i2c驱动详解的主要内容,如果未能解决你的问题,请参考以下文章