您的位置:首页 > 运维架构 > Linux

Linux 下 i2c switch(选路芯片mux) — pca9548

2015-12-05 14:20 459 查看
作者: 韩大卫@吉林师范大学

现有的关于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的用户层和内核层要有所了解,请参考其他资料。

*********** ************************

如果需要完整的代码,请联系我:handawei@jusontech.com

转载请务必表明出处。

******* ****************************

// 这是需要我们自己添加的函数。使用它来控制 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,intflag){

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("i2cswitch 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 = (structi2c_rdwr_ioctl_data *)malloc(sizeof(struct i2c_rdwr_ioctl_data))) == NULL)

return -1;

data->nmsgs = 2;

if ((data->msgs = (structi2c_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(structi2c_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 portand 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 andperform 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'tdo I2C-level access, set master_xfer

to NULL. If an adapteralgorithm can do SMBus access, set

smbus_xfer. If set toNULL, the SMBus protocol is simulated

using common I2Cmessages */

/* master_xfer should return thenumber of messages successfully

processed, or anegative value on error */

int (*master_xfer)(structi2c_adapter *adap, struct i2c_msg *msgs,

int num);

int (*smbus_xfer) (structi2c_adapter *adap, u16 addr,

unsigned short flags, char read_write,

u8 command, int size, union i2c_smbus_data *data);

/* To determine what the adaptersupports */

u32 (*functionality) (structi2c_adapter *);

};

通过 parent->algo->smbus_xfer

但是重要的master_xfer(), smbus_xfer() 在一般cpu中没有相应的实现。

比如我的 driver/i2c/busses/i2c-octeon.c :

static const struct i2c_algorithmocteon_i2c_algo = {

.master_xfer = octeon_i2c_xfer,

.functionality =octeon_i2c_functionality,

};

可以看到,只有 master_xfer 的实现函数。

*/

/* Return the parent's functionality */

static u32 i2c_mux_functionality(structi2c_adapter *adap)

{

struct i2c_mux_priv *priv =adap->algo_data;

struct i2c_adapter *parent =priv->parent;

returnparent->algo->functionality(parent);

}

/*

这个函数填充了struct i2c_algorithm中的

u32 (*functionality) (struct i2c_adapter*);

*/

struct i2c_adapter *i2c_add_mux_adapter(structi2c_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(structi2c_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 dynamicallybecause we don't know ahead

* of time what sort ofphysical 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 adapterstructure */

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 muxadapter'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 muxchip/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 forall 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(structpca954x), GFP_KERNEL);

if (!data) {

ret = -ENOMEM;

goto err;

}

i2c_set_clientdata(client, data);

/* Write the mux register at addrto verify

* that the mux is in factpresent. This also

* initializes the mux todisconnected 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);

gotovirt_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 withi2c_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(structi2c_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 likemuxes, not sure how to be smarter */

if (chip->muxtype ==pca954x_ismux)

regval = chan |chip->enable;

else

regval = 1 <<chan;

/* Only select the channel if itsdifferent 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 muxesonly */

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_datadata;

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 usei2c_transfer()/i2c_smbus_xfer()

for this as they will try to lockadapter 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);

}gotoout;

}

.....

}

// 下面这个内容不用关心

这个octeon_i2c_simple_write() 函数如下:

static int octeon_i2c_simple_write(structocteon_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))

gotoretry;

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:beferreadq:cmd = 8380500200000000

[ 424.483494] octeon_i2c_ia_read:aferreadq: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:beferreadq:cmd = 8380500200000000

[ 452.263799] octeon_i2c_ia_read:aferreadq:cmd = 0380500200000007

[ 452.263810] octeon_i2c_ia_read:msgs.buf[0] = 07

[ 452.263817]

//不同的时候,有相应的操作。

********** ******************** http://www.xuebuyuan.com/1453220.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: