I2c 分析---基于linux 3.08
m6tv-bsp:
I2C 的makefile 会告诉我们只需要关注里面的那些文件就OK了
obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
obj-$(CONFIG_I2C) += i2c-core.o
obj-$(CONFIG_I2C_SMBUS) += i2c-smbus.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
obj-$(CONFIG_I2C_MUX) += i2c-mux.o
obj-y += algos/ busses/ muxes/
-----------------------------------------------------------------------------
BSP:i2c_register_board_info(0, aml_i2c_bus_info_a,ARRAY_SIZE(aml_i2c_bus_info_a));
1. i2c-boardinfo.c
LIST_HEAD(__i2c_board_list);//定义初始化一个新的双向链表
int __i2c_first_dynamic_bus_num;
i2c_register_board_info(int busnum,struct i2c_board_info const *info, unsigned len)
作用是:把所有I2C外设的info注册到链表里去,包括:bus(adapte),i2c address,irq...
2. i2c-core.c i2c.h
struct i2c_client - represent an I2C slave device
struct i2c_client {
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit */
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct i2c_driver *driver; /* and our access routines */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
};
struct i2c_driver - represent an I2C device driver
int (*attach_adapter)(struct i2c_adapter *) __deprecated; * @attach_adapter: Callback for bus addition (deprecated)
int (*detach_adapter)(struct i2c_adapter *) __deprecated; * @detach_adapter: Callback for bus removal (deprecated)
#define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)
//通过成员获取i2c_driver的结构体的地址
例如: struct device_driver *drv ; struct i2c_driver *driver; = to_i2c_driver(drv);
#define to_i2c_client(d) container_of(d, struct i2c_client, dev)
/*
* i2c_adapter is the structure used to identify a physical i2c bus along
* with the access algorithms necessary to access it.
*/
struct i2c_adapter {
struct module *owner;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
/* data fields that are valid for all devices */
struct rt_mutex bus_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
};
struct i2c_msg - an I2C transaction segment beginning with START
struct i2c_msg {
__u16 addr; /* slave address */
__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 */
};
container_of:它的作用显而易见,那就是根据一个结构体变量中的一个域成员变量的指针来获取指向整个结构体变量的指针。
struct demo_struct {
type1 member1;
type2 member2;
type3 member3;
type4 member4;
};
struct demo_struct demo;
同时,在另一个地方,获得了变量demo中的某一个域成员变量的指针,比如:
type3 *memp = get_member_pointer_from_somewhere();
此时,如果需要获取指向整个结构体变量的指针,而不仅仅只是其某一个域成员变量的指针,我们就可以这么做:
struct demo_struct *demop = container_of(memp, struct demo_struct, member3);
i2c_verify_client - return parameter as i2c_client, or NULL
instantiate:实例化就是给,数据成员分配内存,构造对象。
* i2c_new_device - instantiate an i2c device
* @adap: the adapter managing the device //管理该设备的I2C 控制器--adapter
* @info: describes one I2C device; bus_num is ignored
* Context: can sleep
This returns the new i2c client。
i2c_new_device()
--->
device_register()
------------
device_register()--->
device_initialize(dev); //初始化设备
device_add(dev); //添加设备
---> 调用bus_add_device在sysfs中添加两个链接:一个在总线目录下指向设备,
另一个在设备的目录下指向总线子系统。
error = bus_add_device(dev); //将设备添加进总线中
bus_probe_device(dev); //现在该为设备在总线上寻找合适的驱动了
----> device_attach(dev); //为设备寻找驱动
----> device_bind_driver(dev); //那么将设备和驱动绑定
--->bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
//否则,在总线上寻找驱动与该设备进行匹配
------------->
driver_bound(dev); //驱动绑定设备
bus_for_each_drv(){
while ((drv = next_driver(&i)) && !error) //遍历总线上的驱动
error = fn(drv, data); //将驱动和设备进行匹配,这里的fn=__device_attach
}
static int __device_attach(struct device_driver *drv, void *data){
struct device *dev = data;
if (!driver_match_device(drv, dev)) //现用总线上的match匹配函数进行低级匹配
return 0;
return driver_probe_device(drv, dev); //在来高级匹配
}
最终会调用总线下面的probe
ret = really_probe(dev, drv); //调用真正的匹配
如果是i2c bus 下面的,最终是调用什么probe呢?
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);
最终会调用本设备驱动里的probe函数.
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
根据adapter的nr创建I2C device ,
list_for_each_entry(devinfo, &__i2c_board_list, list)
__i2c_board_list 里的所有项都会创建一个i2c_cilent
static int i2c_register_adapter(struct i2c_adapter *adap)
/* create pre-declared device nodes */
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
/* Notify drivers */
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
* i2c_add_adapter - declare i2c adapter, use dynamic bus number//声明一个i2c 适配器,利用动态总线数
* @adapter: the adapter to add
* Context: can sleep
int i2c_add_adapter(struct i2c_adapter *adapter)
* bus_for_each_dev - device iterator.
* @bus: bus type.
* @start: device to start iterating from.
* @data: data for the callback.
* @fn: function to be called for each device.
res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
//对每个dev都会调用fn回调
* An i2c_driver is used with one or more i2c_client (device) nodes to access
* i2c slave chips, on a bus instance associated with some i2c_adapter.
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
----->ret = adap->algo->master_xfer(adap, msgs, num);//这里是发送函数,master_xfer是SOC adapter下的
* i2c_master_send - issue a single I2C message in master transmit mode
* @client: Handle to slave device
* @buf: Data that will be written to the slave
* @count: How many bytes to write, must be less than 64k since msg.len is u16
*
* Returns negative errno, or else the number of bytes written.
/* Keep track of adapters which will be added or removed later */
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
int bus_register_notifier(struct bus_type *bus, struct notifier_block *nb)
{
return blocking_notifier_chain_register(&bus->p->bus_notifier, nb);-------> notifier_chain_register(&nh->head, n);
}
===========================================================================================
如何写一个I2C驱动?
1. adapter bus 与芯片相关的代码?
2. 芯片外设的驱动?
**************************************************************
1.drivers\i2c\busses\i2c-at91.c
platform_driver_register(&at91_i2c_driver);
static int __devinit at91_i2c_probe(struct platform_device *pdev)
a.分配一个adatper适配器(主芯片的控制器)
adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); //
b.初始化adapter
adapter->algo = &at91_algorithm;----------->提供了I2C 通讯的方法
adapter->class = I2C_CLASS_HWMON;
adapter->dev.parent = &pdev->dev;
c. 初始化硬件:
at91_twi_hwinit(); /* initialize TWI controller */
d. i2c_add_numbered_adapter(adapter);或者:i2c_add_adapter(adapter)
e. static struct i2c_algorithm at91_algorithm = {
.master_xfer = at91_xfer,
.functionality = at91_func,
};
f. at91_xfer :Generic i2c master transfer entrypoint.
int at91_xfer(struct i2c_adapter *adap, struct i2c_msg *pmsg, int num)
if (pmsg->flags & I2C_M_RD)
ret = xfer_read(adap, pmsg->buf, pmsg->len);
else
ret = xfer_write(adap, pmsg->buf, pmsg->len);
g. 实现:xfer_read(),xfer_write() 这两个函数.读写操作
********************************************************************************
1. \drivers\amlogic\i2c\aml_i2c.c
static int aml_i2c_probe(struct platform_device *pdev)
a.分配结构体
struct aml_i2c *i2c = kzalloc(sizeof(struct aml_i2c), GFP_KERNEL);
b. 设置adapter
/*setup adapter*/
i2c->adap.nr = pdev->id==-1? 0: pdev->id;
i2c->adap.class = I2C_CLASS_HWMON;
i2c->adap.algo = &aml_i2c_algorithm;
i2c->adap.retries = 2;
i2c->adap.timeout = 5;
c.
i2c_set_adapdata(&i2c->adap, i2c);
ret = i2c_add_numbered_adapter(&i2c->adap);
d.
i2c->master_i2c_speed2 = plat->master_i2c_speed2;
/*setup adapter 2*/
i2c->adap2.nr = i2c->adap.nr+1;
i2c->adap2.class = I2C_CLASS_HWMON;
i2c->adap2.algo = &aml_i2c_algorithm_s2;
i2c->adap2.retries = 2;
i2c->adap2.timeout = 5;
//memset(i2c->adap.name, 0 , 48);
sprintf(i2c->adap2.name, ADAPTER_NAME"%d", i2c->adap2.nr);
i2c_set_adapdata(&i2c->adap2, i2c);
ret = i2c_add_numbered_adapter(&i2c->adap2);
-----------------------------------------------------------------
3.i2c 外设驱动:
at24.c 驱动的实现:
a. i2c_add_driver(&at24_driver); 然后会调用下面的probe函数
static struct i2c_driver at24_driver = {
.driver = {
.name = "at24",
.owner = THIS_MODULE,
},
.probe = at24_probe,
.remove = __devexit_p(at24_remove),
.id_table = at24_ids,
};
b. static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
一个i2c_client 代表一个i2c 设备.
struct at24_data *at24; //分配一个i2c_client
at24 = kzalloc(sizeof(struct at24_data) + num_addresses * sizeof(struct i2c_client *), GFP_KERNEL);
c. at24->client[0] = client; //把参数上的结构体赋值给设备下的client
d. i2c_set_clientdata(client, at24); // clinet->data = at24; 作为它的私有数据
....
最终对EEPROM的操作都是调用
status = i2c_transfer(client->adapter, msg, 2);
下面再看看功放的驱动:tas5711.c
a.i2c_add_driver(&tas5711_i2c_driver);
b..probe = tas5711_i2c_probe,
tas5711 = devm_kzalloc(&i2c->dev, sizeof(struct tas5711_priv),
i2c_set_clientdata(i2c, tas5711);
阅读(2279) | 评论(0) | 转发(0) |