Linux 下 i2c switch pca9548驱动
作者: 韩大卫 @吉林师范大学
现有的关于 i2c switch 资料非常少。即使阅读完官方的datasheet.也不能写出完全正确的操作。
因为内核中的驱动本身不是那么完善的。还有一些资料是单片机编程的,可惜在linux上并不能成功执行。
pca954x 系列是一种 i2c switch 芯片,比如 pca9548 可实现8个开关, 进而实现多了8条i2c通道。
这样可以在有限的i2c 资源上扩展出足够多的接口。解决了在使用 i2c总线容量的问题。
Pca954x 内部只有一个控制寄存器。属于无子地址设备。做I/O 访问时,只需要向0x00 地址处做写操作即可,
这就可实现pca954x 下挂设备 i2c bus 的选路。
但是在现有的pca954x 驱动函数中,没有实现自动对内部控制寄存器进行相应配置,这样的话就需要额外的写一个附加的配置函数,实现这种功能。
如果没有这种配置函数,只是使用现有的内核代码,那么会出现有一些难以发现的问题,但是还是被我遇到了。
在我看来,这种问题暂且不能算bug,但至少应该去优化,毕竟,如果每次在访问不同的i2c bus 时,
需要我们手动的去操作i2c switch 的开关,这多少会影响执行效率,代码量变大。还有一点,
我们自己编写的配置函数,是严重依赖于硬件的,即我们的开关位置打开的位置需要自己判断,
在代码上固定写出, 可移植性差。稍后可以在我的代码中看到这种缺陷。
基于以上原因, 我认为pca954x 的驱动应该修改。有时间的话我会整理出自己的代码,融入到内核代码中去,再提供出API 供大家使用。
I2C 1 地址 0x71,0x72,0x73 上都是pca9548 , 每个pca9548上 挂了 8 个 千兆以太网光模块sfp。 这样 我们系统上就可以同时挂载 24 个 千兆以太网光模块sfp。
I2C 0 地址 0x70 也是pca9548, 挂了2个万兆以太网光模块XFP,还有3个温度传感器TMP411.
*********** ***************
下面的内容是i2c bus 选路函数。之后是从内核代码入手的分析过程,以证明我的判断,阅读起来肯定是
有些难度,因为驱动的工作本身亦如此。如果不是从事嵌入式linux驱动的,就不必深究。
阅读本文前提是在linux的用户层和内核层要有所了解,请参考其他资料。
*********** ************************
如果需要完整的代码,请联系我:
[email protected]
转载请务必表明出处。
******* ****************************
// 这是需要我们自己添加的函数。使用它来控制 i2c bus 的选路。
// 0x70 在 i2c 0上 , 0x71 0x72 0x73 在 i2c 1 上。
// 如果是操作的 i2c bus 是/dev/i2c-10 ,程序根据 10 来判断i2c bus 的选路。
// /dev/i2c-2 到 /dev/i2c-9 属于 0x70 的pca9548
// /dev/i2c-10 到 /dev/i2c-17 属于 0x71 的pca9548
// /dev/i2c-18 到 /dev/i2c-25 属于0x72 的pca9548
// /dev/i2c-26 到 /dev/i2c-33 属于 0x73 的pca9548
inline int i2c_bus_chan_enable(char* argv,int flag){
int ret,tmp;
unsigned char val = 0;
unsigned short addr;
char *s = argv;
while(*s++ != '-' && *s);
if(*s)
ret = atoi(s);
if(ret < 10 && ret != 1)
addr = 0x70;
else
addr = ret < 18 ? 0x71 : (ret > 25 ? 0x73 : 0x72);
if(addr != 0x70){
tmp = ( addr == 0x71 ? 10 : (addr == 0x72 ? 18 : 25));
val = 1 << ((ret - tmp) % 8 ) ;
}
else{
// 给相应的 i2c bus 置1
if( ret == 2 )
val = 1 << 1;
else if( ret == 3 )
val = 1 << 2;
else if( ret == 4 )
val = 1 << 3;
else if( ret == 9 )
val = 1 << 7;
else if( ret == 8 )
val = 1 << 6;
}
// 先向 pca9548 的 i2c 地址 写相应的数值,打开相应的i2c bus
ret = i2c_write_data(addr,0x00,val);
if(ret < 0){
printf("i2c switch init error!\n");
return -1;
}
return 0;
}
********* *******************
下面是在此函数的使用:
main.c{
…..
int ret,tmp;
unsigned char val = 0;
char cmd_buf[1024] = {0};
unsigned short addr ;
i2c_path(argv[2],0);
i2c_bus_chan_enable(argv[2],1);
printf("offset = 0x%x\n",offset);
for(addr = 0x00; addr < 0xff ;addr++){
ret = i2c_read_data(addr,0x00,&val);
if(!ret)
printf("addr = %x,val = %x\n",addr,val);
}
}else
error_info();
}
…..
}
inline int
i2c_read_data(u16 addr, u8 offset, u8 *val)
{
int ret = 0;
struct i2c_rdwr_ioctl_data *data;
if ((data = (struct i2c_rdwr_ioctl_data *)malloc(sizeof(struct i2c_rdwr_ioctl_data))) == NULL)
return -1;
data->nmsgs = 2;
if ((data->msgs = (struct i2c_msg *)malloc(data->nmsgs * sizeof(struct i2c_msg))) == NULL) {
ret = -1;
goto errexit3;
}
if ((data->msgs[0].buf = (unsigned char *)malloc(sizeof(unsigned char))) == NULL) {
ret = -1;
goto errexit2;
}
if ((data->msgs[1].buf = (unsigned char *)malloc(sizeof(unsigned char))) == NULL) {
ret = -1;
goto errexit1;
}
data->msgs[0].addr = addr;
data->msgs[0].flags = 0;
data->msgs[0].len = 1;
data->msgs[0].buf[0] = offset;
data->msgs[1].addr = addr;
data->msgs[1].flags = I2C_M_RD;
data->msgs[1].len = 1;
data->msgs[1].buf[0] = 0;
if ((ret = __i2c_send(fd, data)) < 0)
goto errexit0;
*val = data->msgs[1].buf[0];
errexit0:
free(data->msgs[1].buf);
errexit1:
free(data->msgs[0].buf);
errexit2:
free(data->msgs);
errexit3:
free(data);
return ret;
}
static int
__i2c_send(int fd, struct i2c_rdwr_ioctl_data *data)
{
int ret;
if (fd < 0)
return -1;
if (data == NULL)
return -1;
if (data->msgs == NULL || data->nmsgs == 0)
return -1;
ret = ioctl(fd, I2C_RDWR, (unsigned long)data) ;
if(ret < 0)
return -1;
return 0;
}
********* **********************
下面是驱动的分析过程, /driver/i2c/i2c-mux.c 到 /driver/i2c/muxes/pca954x.c
******** **********************
在内核源代码 drivers/i2c/i2c-mux.c 中:
struct i2c_mux_priv {
struct i2c_adapter adap;
struct i2c_algorithm algo;
struct i2c_adapter *parent;
void *mux_priv; /* the mux chip/device */
u32 chan_id; /* the channel id */
int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id);
int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id);
};
static int i2c_mux_master_xfer(struct i2c_adapter *adap,
struct i2c_msg msgs[], int num)
{
struct i2c_mux_priv *priv = adap->algo_data;
struct i2c_adapter *parent = priv->parent;
int ret;
/* Switch to the right mux port and perform the transfer. */
ret = priv->select(parent, priv->mux_priv, priv->chan_id);
if (ret >= 0)
ret = parent->algo->master_xfer(parent, msgs, num);
if (priv->deselect)
priv->deselect(parent, priv->mux_priv, priv->chan_id);
return ret;
}
/* i2c_mux_master_xfer() 这个函数 的实现调用: parent->algo->master_xfer()
master_xfer() 在上一层是在i2c-core.c中的 i2c_transfer().
master_xfer() 的下一层是在 driver/i2c/busses/i2c-XXX.c 中的
xxx_i2c_xfer() ,xxx为具体cpu,这个xxx_i2c_xfer() 真正读写了cpu的i2c
寄存器,实现了数据通信。
*/
static int i2c_mux_smbus_xfer(struct i2c_adapter *adap,
u16 addr, unsigned short flags,
char read_write, u8 command,
int size, union i2c_smbus_data *data)
{
struct i2c_mux_priv *priv = adap->algo_data;
struct i2c_adapter *parent = priv->parent;
int ret;
/* Select the right mux port and perform the transfer. */
ret = priv->select(parent, priv->mux_priv, priv->chan_id);
if (ret >= 0)
ret = parent->algo->smbus_xfer(parent, addr, flags,
read_write, command, size, data);
if (priv->deselect)
priv->deselect(parent, priv->mux_priv, priv->chan_id);
return ret;
}
/*
smbus_xfer() 同 master_xfer() 。
在 driver/i2c/i2c.h 中可以看到:
struct i2c_algorithm {
/* If an adapter algorithm can't do I2C-level access, set master_xfer
to NULL. If an adapter algorithm can do SMBus access, set
smbus_xfer. If set to NULL, the SMBus protocol is simulated
using common I2C messages */
/* master_xfer should return the number of messages successfully
processed, or a negative value on error */
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
};
通过 parent->algo->smbus_xfer
但是重要的master_xfer(), smbus_xfer() 在一般cpu中没有相应的实现。
比如我的 driver/i2c/busses/i2c-octeon.c :
static const struct i2c_algorithm octeon_i2c_algo = {
.master_xfer = octeon_i2c_xfer,
.functionality = octeon_i2c_functionality,
};
可以看到,只有 master_xfer 的实现函数。
*/
/* Return the parent's functionality */
static u32 i2c_mux_functionality(struct i2c_adapter *adap)
{
struct i2c_mux_priv *priv = adap->algo_data;
struct i2c_adapter *parent = priv->parent;
return parent->algo->functionality(parent);
}
/*
这个函数填充了struct i2c_algorithm中的
u32 (*functionality) (struct i2c_adapter *);
*/
struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
struct device *mux_dev,
void *mux_priv, u32 force_nr, u32 chan_id,
int (*select) (struct i2c_adapter *,
void *, u32),
int (*deselect) (struct i2c_adapter *,
void *, u32))
{
struct i2c_mux_priv *priv;
int ret;
priv = kzalloc(sizeof(struct i2c_mux_priv), GFP_KERNEL);
if (!priv)
return NULL;
/* Set up private adapter data */
priv->parent = parent;
priv->mux_priv = mux_priv;
priv->chan_id = chan_id;
priv->select = select;
priv->deselect = deselect;
/* Need to do algo dynamically because we don't know ahead
* of time what sort of physical adapter we'll be dealing with.
*/
if (parent->algo->master_xfer)
priv->algo.master_xfer = i2c_mux_master_xfer;
if (parent->algo->smbus_xfer)
priv->algo.smbus_xfer = i2c_mux_smbus_xfer;
priv->algo.functionality = i2c_mux_functionality;
/* Now fill out new adapter structure */
snprintf(priv->adap.name, sizeof(priv->adap.name),
"i2c-%d-mux (chan_id %d)", i2c_adapter_id(parent), chan_id);
priv->adap.owner = THIS_MODULE;
priv->adap.id = parent->id;
priv->adap.algo = &priv->algo;
priv->adap.algo_data = priv;
priv->adap.dev.parent = &parent->dev;
if (force_nr) {
priv->adap.nr = force_nr;
ret = i2c_add_numbered_adapter(&priv->adap);
} else {
ret = i2c_add_adapter(&priv->adap);
}
if (ret < 0) {
dev_err(&parent->dev,
"failed to add mux-adapter (error=%d)\n",
ret);
kfree(priv);
return NULL;
}
dev_info(&parent->dev, "Added multiplexed i2c bus %d\n",
i2c_adapter_id(&priv->adap));
#ifdef CONFIG_OF
/* Try to get populate the mux adapter's of_node */
if (mux_dev->of_node) {
struct device_node *child = NULL;
const __be32 *reg;
int len;
for (;;) {
child = of_get_next_child(mux_dev->of_node, child);
if (!child)
break;
reg = of_get_property(child, "reg", &len);
if (!reg || (len < sizeof(__be32)))
continue;
if (chan_id == be32_to_cpup(reg)) {
priv->adap.dev.of_node = child;
break;
}
}
}
of_i2c_register_devices(&priv->adap);
#endif
return &priv->adap;
}
/*
这个函数的作用是:向内核注册一个 i2c_mux_adap
过程是:填充一个 struct i2c_mux_priv *priv;
最后返回这个进行设备注册过的priv->adap
结构体定义是:
struct i2c_mux_priv {
struct i2c_adapter adap;
struct i2c_algorithm algo;
struct i2c_adapter *parent;
void *mux_priv; //the mux chip/device
u32 chan_id; // the channel id
// 使能函数,关闭函数
int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id);
int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id);
};
1)priv->parent = parent;
priv->mux_priv = mux_priv;
priv->chan_id = chan_id;
priv->select = select;
priv->deselect = deselect;
2)根据parent 中algo中存在的master_xfer(),smbus_xfer()函数 ,做相应填充。
if (parent->algo->master_xfer)
priv->algo.master_xfer = i2c_mux_master_xfer;
if (parent->algo->smbus_xfer)
priv->algo.smbus_xfer = i2c_mux_smbus_xfer;
priv->algo.functionality = i2c_mux_functionality;
3) 填充priv 的 adap.
Adap的结构是:
struct i2c_adapter {
struct module *owner;
unsigned int id;
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 */
u8 level; /* nesting level for lockdep */
struct mutex bus_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
int nr;
char name[48];
struct completion dev_released;
};
priv->adap.owner = THIS_MODULE;
priv->adap.id = parent->id;
priv->adap.algo = &priv->algo;
priv->adap.algo_data = priv;
priv->adap.dev.parent = &parent->dev;
4)根据 force_nr 决定怎样将priv->adap 进行注册。
if (force_nr) {
priv->adap.nr = force_nr;
ret = i2c_add_numbered_adapter(&priv->adap);
} else {
ret = i2c_add_adapter(&priv->adap);
}
5)
of_i2c_register_devices(&priv->adap);
最后将priv->adap 进行 设备注册。
*/
******************** *****************************
drivers/i2c/muxes/pca954x.c
static struct i2c_driver pca954x_driver = {
.driver = {
.name = "pca954x",
.owner = THIS_MODULE,
},
.probe = pca954x_probe,
.remove = pca954x_remove,
.id_table = pca954x_id,
};
在pca054x_prove() 中:
static int pca954x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adap = to_i2c_adapter(client->dev.parent);
struct pca954x_platform_data *pdata = client->dev.platform_data;
int num, force;
struct pca954x *data;
int ret = -ENODEV;
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
goto err;
data = kzalloc(sizeof(struct pca954x), GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
goto err;
}
i2c_set_clientdata(client, data);
/* Write the mux register at addr to verify
* that the mux is in fact present. This also
* initializes the mux to disconnected state.
*/
if (i2c_smbus_write_byte(client, 0) < 0) {
dev_warn(&client->dev, "probe failed\n");
goto exit_free;
}
data->type = id->driver_data;
data->last_chan = 0; /* force the first selection */
/* Now create an adapter for each channel */
for (num = 0; num < chips[data->type].nchans; num++) {
force = 0; /* dynamic adap number */
if (pdata) {
if (num < pdata->num_modes)
/* force static number */
force = pdata->modes[num].adap_id;
else
/* discard unconfigured channels */
break;
}
data->virt_adaps[num] =
i2c_add_mux_adapter(adap, &client->dev, client,
force, num, pca954x_select_chan,
(pdata && pdata->modes[num].deselect_on_exit)
? pca954x_deselect_mux : NULL);
if (data->virt_adaps[num] == NULL) {
ret = -ENODEV;
dev_err(&client->dev,
"failed to register multiplexed adapter"
" %d as bus %d\n", num, force);
goto virt_reg_failed;
}
}
dev_info(&client->dev,
"registered %d multiplexed busses for I2C %s %s\n",
num, chips[data->type].muxtype == pca954x_ismux
? "mux" : "switch", client->name);
return 0;
virt_reg_failed:
for (num--; num >= 0; num--)
i2c_del_mux_adapter(data->virt_adaps[num]);
exit_free:
kfree(data);
err:
return ret;
}
/*
pca954x_platfrom_data 的结构定义是:
struct pca954x_platform_data
/* Per channel initialisation data:
* @adap_id: bus number for the adapter. 0 = don't care
* @deselect_on_exit: set this entry to 1, if your H/W needs deselection
* of this channel after transaction.
*
*/
struct pca954x_platform_mode {
int adap_id;
unsigned int deselect_on_exit:1;
};
/* Per mux/switch data, used with i2c_register_board_info */
struct pca954x_platform_data {
struct pca954x_platform_mode *modes;
int num_modes;
};
2) chips[] 的定义是:
static const struct chip_desc chips[] = {
[pca_9540] = {
.nchans = 2,
.enable = 0x4,
.muxtype = pca954x_ismux,
},
[pca_9543] = {
.nchans = 2,
.muxtype = pca954x_isswi,
},
[pca_9544] = {
.nchans = 4,
.enable = 0x4,
.muxtype = pca954x_ismux,
},
[pca_9545] = {
.nchans = 4,
.muxtype = pca954x_isswi,
},
[pca_9547] = {
.nchans = 8,
.enable = 0x8,
.muxtype = pca954x_ismux,
},
[pca_9548] = {
.nchans = 8,
.muxtype = pca954x_isswi,
},
};
其中, pca_9540, pca_9542 等被定义为枚举类型:
一共8个 芯片类型。
enum pca_type {
pca_9540,
pca_9542,
pca_9543,
pca_9544,
pca_9545,
pca_9546,
pca_9547,
pca_9548,
};
*/
static int pca954x_select_chan(struct i2c_adapter *adap,
void *client, u32 chan)
{
struct pca954x *data = i2c_get_clientdata(client);
const struct chip_desc *chip = &chips[data->type];
u8 regval;
int ret = 0;
/* we make switches look like muxes, not sure how to be smarter */
if (chip->muxtype == pca954x_ismux)
regval = chan | chip->enable;
else
regval = 1 << chan;
/* Only select the channel if its different from the last channel */
if (data->last_chan != regval) {
ret = pca954x_reg_write(adap, client, regval);
data->last_chan = regval;
}
return ret;
}
一,
填充一个struct pca954x 的结构体, 定义如下:
struct pca954x {
enum pca_type type;
struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS];
u8 last_chan; /* last register value */
};
enum pca_tyep type 的定义是:
enum pca_type {
pca_9540,
pca_9542,
pca_9543,
pca_9544,
pca_9545,
pca_9546,
pca_9547,
pca_9548,
};
二,struct chip_desc 的定义是:
struct chip_desc {
u8 nchans;
u8 enable; /* used for muxes only */
enum muxtype {
pca954x_ismux = 0,
pca954x_isswi
} muxtype;
};
const struct chip_desc *chip = &chips[data->type];
用一个指针“引用”到了具体类型的pca954x 芯片数据。
if (chip->muxtype == pca954x_ismux)
regval = chan | chip->enable;
else
regval = 1 << chan;
根据具体芯片的 muxtype 来决定 选路通道。
if (data->last_chan != regval) {
ret = pca954x_reg_write(adap, client, regval);
data->last_chan = regval;
}
如果该选路 不是芯片上一次使用的通道,那么执行新选路,执行后再将此选路作为新的last_chan.
pca954x_reg_write() 函数作用是: 通知cpu,client 执行新选路通道。
定义如下:
static int pca954x_reg_write(struct i2c_adapter *adap,
struct i2c_client *client, u8 val)
{
int ret = -ENODEV;
if (adap->algo->master_xfer) {
struct i2c_msg msg;
char buf[1];
msg.addr = client->addr;
msg.flags = 0;
msg.len = 1;
buf[0] = val;
msg.buf = buf;
ret = adap->algo->master_xfer(adap, &msg, 1);
} else {
union i2c_smbus_data data;
ret = adap->algo->smbus_xfer(adap, client->addr,
client->flags,
I2C_SMBUS_WRITE,
val, I2C_SMBUS_BYTE, &data);
}
return ret;
}
/* Write to mux register. Don't use i2c_transfer()/i2c_smbus_xfer()
for this as they will try to lock adapter a second time */
根据提示,不能使用i2c_transfer()/i2c_smbus_xfer(),
只能使用 adap->algo->master_xfer() , 或者adap->algo->smbus_xfer()
struct i2c_msg msg;
char buf[1];
msg.addr = client->addr;
msg.flags = 0;
msg.len = 1;
buf[0] = val;
msg.buf = buf;
包装一个 struct i2c_msg . 将其直接使用 master_xfer()发送出去。master_xfer() 的实现就是在具体的
cpu层的 octeon_i2c_xfer() 函数。
static int octeon_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs,
int num)
{
struct i2c_msg *pmsg;
int i;
int ret = 0;
struct octeon_i2c *i2c = i2c_get_adapdata(adap);
if (num == 1) {
if (msgs[0].len > 0 && msgs[0].len <= 8) {
if (msgs[0].flags & I2C_M_RD){
ret = octeon_i2c_simple_read(i2c, msgs);
}else{
ret = octeon_i2c_simple_write(i2c, msgs);
}goto out;
}
.....
}
// 下面这个内容不用关心
这个octeon_i2c_simple_write() 函数如下:
static int octeon_i2c_simple_write(struct octeon_i2c *i2c, struct i2c_msg *msgs)
{
u64 cmd;
int i, j;
int ret = 0;
octeon_i2c_enable_hlc(i2c);
retry:
cmd = SW_TWSI_V | SW_TWSI_SOVR;
/* SIZE */
cmd |= (u64)(msgs[0].len - 1) << SW_TWSI_SIZE_SHIFT;
/* A */
cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_A_SHIFT;
if (msgs[0].flags & I2C_M_TEN)
cmd |= SW_TWSI_OP_10;
else
cmd |= SW_TWSI_OP_7;
for (i = 0, j = msgs[0].len - 1; i < msgs[0].len && i < 4; i++, j--){
cmd |= (u64)msgs[0].buf[j] << (8 * i);
}
if (msgs[0].len >= 4) {
u64 ext = 0;
for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--)
ext |= (u64)msgs[0].buf[j] << (8 * i);
__raw_writeq(ext, i2c->twsi_base + SW_TWSI_EXT);
}
octeon_i2c_hlc_int_clear(i2c);
__raw_writeq(cmd, i2c->twsi_base + SW_TWSI);
ret = octeon_i2c_hlc_wait(i2c);
if (ret)
goto err;
cmd = __raw_readq(i2c->twsi_base + SW_TWSI);
if ((cmd & SW_TWSI_R) == 0) {
if (octeon_i2c_lost_arb(cmd))
goto retry;
ret = -EIO;
goto err;
}
err:
return ret;
}
上面的宏是Cpu中相应的寄存器地址。
总的来说就是根据函数,参数填充一个 64bit 的cmd, 将cmd 交给cpu 执行。
可以看这个地方:
cmd |= (u64)(msgs[0].addr & 0x7full) << SW_TWSI_A_SHIFT;
作用是将addr 填充到 cmd 的“ i2c 从设备地址” 寄存器处。
for (i = 0, j = msgs[0].len - 1; i < msgs[0].len && i < 4; i++, j--){
cmd |= (u64)msgs[0].buf[j] << (8 * i);
的作用是将buf[] 填充到cmd 的 “偏移地址” 寄存器处。
__raw_writeq(cmd, i2c->twsi_base + SW_TWSI);
执行cmd。 这样就把buf (内容为regval) 执行了。Regval 的意义体现在此。
这样的一次操作可以通知cpu , 接下来的动作要进入的i2c 通道。
综上, ret = pca954x_reg_write(adap, client, regval);
的作用就是通知cpu : 打开通道为 regval 的通道。下一次cpu执行命令,直接进入这条通道。
********* *****************
下面是实验记录:
可以看到,两次 data->last_chan 不一样时候,先写寄存器.
两次data->last_chan 相同时候,不做操作。
root@(none):~# eep -d /dev/i2c-10 -ro 0x50 0x02
07
root@(none):~# dmesg
[ 424.482728] i2c-dev.c i2cdev_ioctl: cmd = 1794 , arg = 3
[ 424.482741] i2c-dev.c i2cdev_ioctl: cmd = 1793 , arg = 3
[ 424.482974] i2c-dev.c i2cdev_ioctl: cmd = 1799 , arg = 4916637712
[ 424.482984] i2c-dev.c: i2cdev_ioctl_rdrw:arg = 250df010
[ 424.482997] i2c_mux_master_xfer: msgs = 50 , num = 2
[ 424.483008] pca954x_select_chan:before.data->last_chan = 1, regval = 1
[ 424.483019] pca954x_select_chan:after.data->last_chan = 1
[ 424.483028] octeon_i2c_xfer: num = 2
[ 424.483038] octeon_i2c_xfer:msgs[0].addr = 50, msgs[0].flags = 0, msgs[0].len = 1
[ 424.483050] octeon_i2c_xfer:msgs[0].buf[0] = 2
[ 424.483060] octeon_i2c_xfer:msgs[1].addr = 50, msgs[1].flags = 1, msgs[1].len = 1
[ 424.483072] octeon_i2c_xfer:msgs[1].buf[1] = 0
[ 424.483082] octeon_i2c_ia_read:befer readq:cmd = 8380500200000000
[ 424.483494] octeon_i2c_ia_read:afer readq:cmd = 0380500200000007
[ 424.483505] octeon_i2c_ia_read: msgs.buf[0] = 07
[ 424.483512]
//两次data->last_chan 相同.不做操作.
root@(none):~# eep -d /dev/i2c-11 -ro 0x50 0x02
07
root@(none):~# dmesg
[ 452.262746] i2c-dev.c i2cdev_ioctl: cmd = 1794 , arg = 3
[ 452.262758] i2c-dev.c i2cdev_ioctl: cmd = 1793 , arg = 3
[ 452.262992] i2c-dev.c i2cdev_ioctl: cmd = 1799 , arg = 5013766160
[ 452.263002] i2c-dev.c: i2cdev_ioctl_rdrw:arg = 2ad80010
[ 452.263015] i2c_mux_master_xfer: msgs = 50 , num = 2
[ 452.263026] pca954x_select_chan:before.data->last_chan = 1, regval = 2
[ 452.263037] octeon_i2c_xfer: num = 1
[ 452.263047] octeon_i2c_xfer:msgs[0].addr = 71, msgs[0].flags = 0, msgs[0].len = 1
[ 452.263075] octeon_i2c_xfer:msgs[0].buf[0] = 2
[ 452.263084] octeon_i2c_simple_write:
[ 452.263091] octeon_i2c_simple_write:cmd = 8080710000000000
[ 452.263102] octeon_i2c_simple_write:msgs[0].buf[0] = 02,cmd = 8080710000000002
[ 452.263319] octeon_i2c_simple_write:after readq cmd = 01807100ffffffff
[ 452.263328]
[ 452.263334] pca954x_select_chan:after.data->last_chan = 2
[ 452.263344] octeon_i2c_xfer: num = 2
[ 452.263353] octeon_i2c_xfer:msgs[0].addr = 50, msgs[0].flags = 0, msgs[0].len = 1
[ 452.263365] octeon_i2c_xfer:msgs[0].buf[0] = 2
[ 452.263375] octeon_i2c_xfer:msgs[1].addr = 50, msgs[1].flags = 1, msgs[1].len = 1
[ 452.263387] octeon_i2c_xfer:msgs[1].buf[1] = 0
[ 452.263396] octeon_i2c_ia_read:befer readq:cmd = 8380500200000000
[ 452.263799] octeon_i2c_ia_read:afer readq:cmd = 0380500200000007
[ 452.263810] octeon_i2c_ia_read: msgs.buf[0] = 07
[ 452.263817]
//不同的时候,有相应的操作。
********** ********************