1. samsung平台I2C控制器
三星平台的I2C控制器支持四种工作模式:master tansmit、master receive、slave transmit、slave receive。由于嵌入式系统中,I2C接口常用来接camera、CTP等,所以一般只用到master transmit和master receive两种模式。
master transmit方式:先发送start信号,然后发送1个字节的地址数据(最低位为0),等待从设备返回ACK信号,然后连续写N个字节的数据,每个字节发送完毕后都要等待从设备的ACK信号,最终发送stop信号结束。
master receive方式:先发送start信号,然后发送1字节的地址数据(最低位为1),等待从设备返回ACK信号,然后连续读N个字节的数据,每个字节读取完毕后,要发送ACK信号给从设备,最终发送stop信号结束。
注意:不能在写模式的中间实现读操作,例如要读取从设备的0xA5寄存器的值,流程应该时:start + slave addr + 0xA5 + stop + start + slave addr + readbyte + stop,其中readbyte是读取出来的寄存器数据。
中断发生的时刻:发送完每个字节、接收完每个字节。(start信号和stop信号发送完毕后不会收到中断,另外,ACK信号是通过IICSTAT寄存器来检测的)
三星平台的I2C控制器主要有IICCON、IICSTAT、IICDS这三个寄存器。
IICCON[7] 用来控制是否发送ACK信号,如果enable,那么每次接收1个字节后就会发送ACK信号
IICCON[5] 用来控制是否允许中断,IICCON[4] 为中断标志,此位清零后才可以继续I2C的读写。
IICSTAT[7:6] 用于控制当前的工作模式:11-master transmit、10-master receive
IICSTAT[5] 用于发送start/stop信号,以及判断当前I2C总线是否处于忙状态。写1 - 发送start信号,写0 - 发送stop信号,读出1 - 当前I2C总线忙
IICSTAT[4] 使能I2C接口的数据收发
IICSTAT[0] 用于判断是否收到ACK信号,0 - 收到ACK信号,1 - 没收到ACK信号
IICDS[7:0] 写 - 要发送的数据,读 - 接收到的数据。
因此,I2C接口的使用流程如下:
1)初始化I2C接口,即配置SDA、SCL对应的GPIO,配置clk频率
2)在读写操作时,打开i2c的时钟,判断总线是否忙状态(可以重试多次判断,比如每毫秒检测一次,最多400次)
3)使能I2C中断,允许ACK,配置工作模式,发送start信号,发送从设备地址
4)等待中断,在中断处理里判断是否收到ACK,然后可以连续发送或接收数据(每发完或接收到一个字节时就会产生一次中断)注意这里要么全部是发送,要么全部是接收
5)最后发送stop信号,并关闭中断及时钟
2. linux I2C 总线驱动
I2C总线驱动的主要目的是编写i2c_algorithm,并注册i2c_adapter。代码路径:drivers/i2c/busses/i2c-s3c2410.c,这个总线驱动代码会被很多三星平台使用,例如s5pv210
此总线驱动使用了平台设备驱动模型,先来看一下它的平台设备:
arch/arm/plat-samsung/devs.c 平台设备platform_device
static struct resource s3c_i2c0_resource[] = {
[0] = DEFINE_RES_MEM(S3C_PA_IIC, SZ_4K), //控制寄存器地址
[1] = DEFINE_RES_IRQ(IRQ_IIC), //I2C0中断
};
struct platform_device s3c_device_i2c0 = {
.name = "s3c2410-i2c",
.id = 0,
.num_resources = ARRAY_SIZE(s3c_i2c0_resource),
.resource = s3c_i2c0_resource,
};
struct s3c2410_platform_i2c default_i2c_data __initdata = {
.flags = 0,
.slave_addr = 0x10,
.frequency = 100*1000,
.sda_delay = 100, //初始化s3c2410_platfrom_i2c结构的数据
};
void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd)
{
struct s3c2410_platform_i2c *npd;
if (!pd) {
pd = &default_i2c_data;
pd->bus_num = 0; //后面写I2C设备驱动时要指定它挂在哪个bus_num上
}
npd = s3c_set_platdata(pd, sizeof(struct s3c2410_platform_i2c),
&s3c_device_i2c0); /*将s3c2410_platfrom_i2c数据存储到
pdev->dev.platform_data中*/
if (!npd->cfg_gpio)
npd->cfg_gpio = s3c_i2c0_cfg_gpio; //配置I2C对应的GPIO
}
//============================================================================
struct s3c2410_platform_i2c {
int bus_num;
unsigned int flags;
unsigned int slave_addr; //注意这个并不是我们外接的I2C设备地址
unsigned long frequency; 而是将本控制器做为从设备时的地址
unsigned int sda_delay;
void (*cfg_gpio)(struct platform_device *dev);
};
/*每个I2C平台设备都会在其platfrom_data中存储一个s3c2410_platfrom_i2c数据,
其中bus_num就是指定给这个I2C总线的编号,在编写I2C设备驱动时会用到它,
使用i2c_register_board_info注册I2C设备时,就要指定它挂在哪个I2C总线上*/
struct s3c24xx_i2c {
struct i2c_msg *msg;
unsigned int msg_num;
unsigned int msg_idx;
unsigned int msg_ptr;
unsigned int irq;
enum s3c24xx_i2c_state state;
void __iomem *regs;
struct clk *clk;
struct device *dev;
struct i2c_adapter adap;
struct s3c2410_platform_i2c *pdata;
};
static u32 s3c24xx_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING;
}
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c;
struct s3c2410_platform_i2c *pdata = NULL
pdata = pdev->dev.platform_data;
i2c = devm_kzalloc(&pdev->dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL);
i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
memcpy(i2c->pdata, pdata, sizeof(*pdata));
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->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2c->regs = ioremap(res->start, resource_size(res));
i2c->adap.algo_data = i2c;
s3c24xx_i2c_init(i2c); //初始化I2C控制器
i2c->irq = ret = platform_get_irq(pdev, 0);
request_irq(i2c->irq, s3c24xx_i2c_irq, 0, dev_name(&pdev->dev), i2c);
i2c->adap.nr = i2c->pdata->bus_num; //获得总线号
i2c_add_numbered_adapter(&i2c->adap); //注册i2c_adapter
}
//我们以后写I2C设备驱动时会用到i2c_transfer来实现读写操作
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
/*例如,向I2C设备的寄存器0x5A写入数据0x46,I2C设备的地址是0x35*/
u8 writebuf[2] = {0x5A,0x46};
struct i2c_msg msgs[] = {
{
.addr = 0x35,
.flags = 0,
.len = 2,
.buf = writebuf,
},
};
i2c_transfer(i2c_adapter,msgs,1);
/*例如,从I2C设备的寄存器0x5A读取数据*/
u8 reg = 0x5A,value;
struct i2c_msg msgs[] = {
{
.addr = 0x35,
.flags = 0,
.len = 1,
.buf = ®,
},
{
.addr = 0x35,
.flags = I2C_M_RD,
.len = 1,
.buf = &value,
},
};
i2c_transfer(i2c_adapter,msgs,2);
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
int ret,try;
for (ret = 0, try = 0; try <= adap->retries; try++) {
ret = adap->algo->master_xfer(adap, msgs, num);
if (ret != -EAGAIN)
break;
}
}
//可以看到只需通过i2c_adapter->i2c_algorithm->master_xfer就可以实现数据读写。
/*在总线驱动里,我们将master_xfer指向了s3c24xx_i2c_xfer,另外还注册了I2C的中断
服务函数s3c24xx_i2c_irq*/
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
for (retry = 0; retry < adap->retries; retry++) {
ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
}
}
static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
struct i2c_msg *msgs, int num)
{
i2c->msg = msgs;
i2c->msg_num = num;
i2c->msg_ptr = 0; //用来索引i2c_msg->buf里的数据
i2c->msg_idx = 0;
i2c->state = STATE_START; //状态机处于START状态
s3c24xx_i2c_enable_irq(i2c); //允许I2C中断
s3c24xx_i2c_message_start(i2c, msgs); //发送start信号以及设备地址
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
//此处等待msgs全部发送完毕,在中断里会wake_up(&i2c->wait);
ret = i2c->msg_idx; //成功送出的msg个数
}
static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
{
struct s3c24xx_i2c *i2c = dev_id;
status = readl(i2c->regs + S3C2410_IICSTAT);
i2c_s3c_irq_nextbyte(i2c, status);
return IRQ_HANDLED;
}
static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
{
switch (i2c->state) {
case STATE_START:
if (iicstat & S3C2410_IICSTAT_LASTBIT &&
!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
/* ack was not received... */
dev_dbg(i2c->dev, "ack was not received\n");
s3c24xx_i2c_stop(i2c, -ENXIO); //在这个函数里发送stop信号,关中断
goto out_ack; //并wake_up(&i2c->wait)
}
if (i2c->msg->flags & I2C_M_RD)
i2c->state = STATE_READ;
else
i2c->state = STATE_WRITE;
if (i2c->state == STATE_READ)
goto prepare_read;
case STATE_WRITE:
if(i2c->msg_ptr < i2c->msg->len){
u8 byte = i2c->msg->buf[i2c->msg_ptr++];
writeb(byte, i2c->regs + S3C2410_IICDS);
}else if(i2c->msg_idx < i2c->msg_num -1){
dev_dbg(i2c->dev, "WRITE: Next Message\n");
i2c->msg_ptr = 0;
i2c->msg_idx++;
i2c->msg++;
s3c24xx_i2c_message_start(i2c, i2c->msg);
i2c->state = STATE_START;
}else{
s3c24xx_i2c_stop(i2c, 0); //发送stop信号,并wake_up(i2c->wait);
}
break;
case STATE_READ:
byte = readb(i2c->regs + S3C2410_IICDS);
i2c->msg->buf[i2c->msg_ptr++] = byte;
prepare_read:
if(i2c->msg_ptr >= i2c->msg->len){
if(i2c->msg_idx >= i2c->msg_num -1){
dev_dbg(i2c->dev, "READ: Send Stop\n");
s3c24xx_i2c_stop(i2c, 0);
}else{
dev_dbg(i2c->dev, "READ: Next Transfer\n");
i2c->msg_ptr = 0;
i2c->msg_idx++;
i2c->msg++;
}
}
break;
}
out_ack:
tmp = readl(i2c->regs + S3C2410_IICCON);
tmp &= ~S3C2410_IICCON_IRQPEND;
writel(tmp, i2c->regs + S3C2410_IICCON);
}
static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
struct i2c_msg *msg)
{
unsigned int addr = (msg->addr & 0x7f) << 1;
unsigned long stat;
unsigned long iiccon;
stat = S3C2410_IICSTAT_TXRXEN;
if (msg->flags & I2C_M_RD) {
stat |= S3C2410_IICSTAT_MASTER_RX;
addr |= 1;
} else
stat |= S3C2410_IICSTAT_MASTER_TX;
s3c24xx_i2c_enable_ack(i2c);
iiccon = readl(i2c->regs + S3C2410_IICCON);
writel(stat, i2c->regs + S3C2410_IICSTAT);
writeb(addr, i2c->regs + S3C2410_IICDS);
writel(iiccon, i2c->regs + S3C2410_IICCON);
stat |= S3C2410_IICSTAT_START;
writel(stat, i2c->regs + S3C2410_IICSTAT);
}
static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret)
{
iicstat &= ~S3C2410_IICSTAT_START;
writel(iicstat, i2c->regs + S3C2410_IICSTAT);
i2c->state = STATE_STOP;
wake_up(&i2c->wait);
s3c24xx_i2c_disable_irq(i2c);
}
从代码中可以看到i2c_adapter->i2c_algorithm->master_xfer的具体实现,并清楚地看到I2C数据是如何通过i2c_msg来实现读写的。
可以知道,我们定义了多少个I2C平台设备platform_device->name能够和此驱动的platform_driver->id_table->name匹配上,
就会注册多少个i2c_adapter,每个i2c_adapter->nr是不一样的,它就是我们在平台设备里所指定的bus_num
3. i2c_adapter、i2c_algorithm、i2c_client、i2c_devinfo、i2c_board_info
三星平台上一般带有3、4个I2C接口,那么就会注册3、4个i2c_adapter,每个i2c_adapter的nr即bus_num是不一样的,不过它们可能会共用一套i2c_algorithm算法。i2c_client其实就是绑定了i2c_adapter的从设备实体,例如电容触摸屏、摄像头芯片、EEPROM等,每个器件都会有一个i2c_client与之对应。如果这些I2C接口的芯片都挂在了同一条I2C总线上,那么这些i2c_client就使用的是同一个i2c_adapter。
struct i2c_client {
unsigned short flags;
unsigned short addr; //从设备地址,例如wm8994芯片的地址为0x1A
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; //设备使用的中断,例如电容触摸屏的中断
struct list_head detected;
};
struct i2c_devinfo {
struct list_head list; //所有的I2C设备都会放在一条链表上
int busnum; //I2C设备挂在哪条总线上
struct i2c_board_info board_info; //I2C设备的信息
};
struct i2c_board_info {
char type[I2C_NAME_SIZE]; //I2C设备的名字
unsigned short flags;
unsigned short addr; //I2C设备地址
void *platform_data; //自定义的一些数据
struct dev_archdata *archdata;
struct device_node *of_node;
int irq; //设备使用的中断
};
#define I2C_BOARD_INFO(dev_type, dev_addr) \
.type = dev_type, .addr = (dev_addr)
/*我们为某个I2C设备定义一个i2c_board_info结构,然后调用i2c_register_board_info
来注册它,最终我们会得到一个i2c_devinfo对象,并且挂到链表上
在注册i2c_adapter的时候就会用到它*/
LIST_HEAD(__i2c_board_list);
int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info,
unsigned len)
{
int status;
for (status = 0; len; len--, info++) {
struct i2c_devinfo *devinfo;
devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
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);
}
return status;
}
/*编写I2C设备驱动时,我们会先在平台的init_machine时,注册I2C设备*/
static struct ft5x06_ts_platform_data ft5x06_platformdata = {
.x_max = 320,
.y_max = 480,
.reset_gpio = FT5X06_RESET_GPIO,
.irq_gpio = FT5X06_IRQ_GPIO,
.irqflags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
};
static struct i2c_board_info ft5x06_device_info[] __initdata = {
{
I2C_BOARD_INFO("ft5x06_ts", 0x38),
.platform_data = &ft5x06_platformdata,
.irq = IRQ_EINT7,
},
};
static void __init smdkv210_machine_init(void)
{
.....
i2c_register_board_info(2,ft5x06_device_info,ARRAY_SIZE(ft5x06_device_info));
.....
}
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
....
i2c_register_adapter(adap);
}
static int i2c_register_adapter(struct i2c_adapter *adap)
{
....
if (adap->timeout == 0)
adap->timeout = HZ;
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);
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
i2c_scan_static_board_info(adap); //扫描i2c_devinfo链表,创建i2c_client
}
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo;
list_for_each_entry(devinfo, &__i2c_board_list, list) {
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,
&devinfo->board_info))
dev_err(&adapter->dev,
"Can't create device at 0x%02x\n",
devinfo->board_info.addr);
}
}
/*可以看到只要i2c_devinfo->busnum和i2c_adapter->nr匹配,就会为这个I2C设备
创建i2c_client,详见i2c_new_device函数*/
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
client = kzalloc(sizeof *client, GFP_KERNEL);
client->adapter = adap; //将这个i2c_adapter绑定到这个i2c_client上
client->dev.platform_data = info->platform_data;
client->flags = info->flags;
client->addr = info->addr;
client->irq = info->irq;
strlcpy(client->name, info->type, sizeof(client->name));
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node;
device_register(&client->dev);
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
client->name, dev_name(&client->dev));
}
/*可以看到在i2c_scan_static_board_info里,会为每个匹配成功的i2c_devinfo创建
i2c_client,其数据大都是从i2c_board_info里拿过来的*/
编写I2C设备驱动时,主要就是定义一个i2c_driver,然后i2c_add_driver注册就可以了,当这个i2c_driver和某个I2C设备匹配时,就会调用到i2c_driver->probe,在这里可以拿到这个I2C设备的i2c_client,然后就可以从i2c_client拿到设备相关的信息以及绑定的i2c_adapter,要实现I2C读写的话,只需要调用i2c_transfer就可以了。
struct i2c_driver {
unsigned int class;
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
struct device_driver driver;
const struct i2c_device_id *id_table;
};
struct i2c_device_id {
char name[I2C_NAME_SIZE];
kernel_ulong_t driver_data /* Data private to the driver */
__attribute__((aligned(sizeof(kernel_ulong_t))));
};
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
int res;
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type; //注意到它和i2c_client->dev.bus是一样的
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
*/
res = driver_register(&driver->driver);
if (res)
return res;
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
return 0;
}
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match, //i2c_client->name与i2c_driver->id_table->name
.probe = i2c_device_probe, //在这个probe里会调用到i2c_driver->probe
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
const struct i2c_client *client)
{
while (id->name[0]) {
if (strcmp(client->name, id->name) == 0)
return id;
id++;
}
return NULL;
}
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
if (!client)
return 0;
driver = to_i2c_driver(drv);
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
return 0;
}
struct ft5x06_ts_data {
struct i2c_client *client;
struct input_dev *input_dev;
const struct ft5x06_ts_platform_data *pdata; //自定义的数据
};
static int ft5x06_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ft5x06_ts_data *ts;
u8 reg_addr,reg_value;
int ret,err;
ts = devm_kzalloc(&client->dev,
sizeof(struct ft5x06_ts_data), GFP_KERNEL);
ts->client = client;
ts->pdata = client->dev.platform_data;
/*
reg_addr = 0xA3; //#define FT_REG_ID 0xA3
struct i2c_msg msgs[] = {
{
.addr = client->addr,
.flags = 0,
.len = 1,
.buf = reg_addr,
},
{
.addr = client->addr,
.flags = I2C_M_RD,
.len = 1,
.buf = reg_value,
},
};
ret = i2c_transfer(client->adapter, msgs, 2);
dev_info(&client->dev, "Device ID = 0x%x\n", reg_value);
*/
err = request_threaded_irq(client->irq, NULL,
ft5x06_ts_interrupt,
ts->pdata->irqflags | IRQF_ONESHOT,
client->dev.driver->name, ts);
....
}
static const struct i2c_device_id ft5x06_ts_id[] = {
{"ft5x06_ts", 0}, //和i2c_register_board_info时使用的名字匹配就会
{}, //调用到i2c_driver->probe,并把i2c_client传进来
};
static struct i2c_driver ft5x06_ts_driver = {
.probe = ft5x06_ts_probe,
.remove = __devexit_p(ft5x06_ts_remove),
.driver = {
.name = "ft5x06_ts",
.owner = THIS_MODULE,
.id_table = ft5x06_ts_id,
};
static int __init ft5x06_ts_init(void)
{
return i2c_add_driver(&ft5x06_ts_driver);
}
module_init(ft5x06_ts_init);
static void __exit ft5x06_ts_exit(void)
{
i2c_del_driver(&ft5x06_ts_driver);
}
module_exit(ft5x06_ts_exit);
1. 通过i2c_register_board_info会得到一个i2c_devinfo,在注册的时候就已经提供了I2C设备的一些信息以及要挂在哪个busnum上。
2. 注册i2c总线驱动时,即i2c_register_adapter时,会扫描i2c_devinfo链表,根据i2c_adapter->nr来和i2c_devinfo->busnum来匹配,匹配成功后就会为这个I2C设备创建一个i2c_client,并把i2c_board_info都存到i2c_client上(i2c_client->name就是来自i2c_board_info->type),并将这个i2c_adapter绑定到设备的i2c_client上。
3. 注册i2c设备驱动时,通过i2c_driver->id_table->name与i2c_client->name匹配,匹配成功后就可以调用到i2c_driver->probe,并传来配对的i2c_client,然后就可以使用i2c_client->adapter来实现I2C总线读写了。
关于i2c_adapter->nr与哪个I2C接口对应,以及I2C总线读写操作,请回顾第2节 linux I2C 总线驱动
一般平台厂商已经做好了I2C总线驱动,i2c_adapter不需要去修改。当我们需要为某个I2C设备编写驱动时,只需要在machine_init里注册一个i2c_board_info并指定busnum(根据实际硬件连接,从注册i2c_adapter时所使用的平台设备去找),然后定义一个i2c_driver并通过i2c_add_driver注册之后就可以用了。