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

2023-05-16

作者: 韩大卫@吉林师范大学




    现有的关于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

 


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux 下 i2c switch(选路芯片mux) — pca9548 的相关文章

  • 无法执行'x86_64-conda_cos6-linux-gnu-gcc':没有这样的文件或目录(pysam安装)

    我正在尝试安装 pysam 执行后 python path to pysam master setup py build 这个错误的产生是 unable to execute x86 64 conda cos6 linux gnu gcc
  • 在 Ubuntu 16.04 上找不到 printf.c

    我最近切换到Ubuntu 16 04 我在用vscode作为 Ubuntu 上的 IDE 我配置了其他语言 但我无法做到这一点C C 我创建c cpp properties json launch json tasks json 当我开始编
  • linux x86 汇编语言 sys_read 调用的第一个参数应为 0 (stdin)

    我正在编写一个简单的汇编程序来从标准输入读取 如 scanf 这是我的代码 section bss num resb 5 section txt global start start mov eax 3 sys read mov ebx 0
  • 设置 Apache POI 的路径

    我想创建 Excel 文件并使用 java 程序在该文件中写入数据 That is here http www techbrainwave com p 554我在 java 文件所在的位置提取了 Apache POI 并将该路径包含在路径变
  • 没有可用的符号表信息

    我正在测试第三方的库 它崩溃了 当我想查看崩溃的原因时 我的 gdb 告诉我没有可用的调试符号 Program received signal SIGSEGV Segmentation fault Switching to Thread 0
  • GCC 和 ld 找不到导出的符号...但它们在那里

    我有一个 C 库和一个 C 应用程序 尝试使用从该库导出的函数和类 该库构建良好 应用程序可以编译 但无法链接 我得到的错误遵循以下形式 app source file cpp text 0x2fdb 对 lib namespace Get
  • 如何更改 Ubuntu 14.04 上的 php-cli 版本?

    我是 Linux 新手 在篡改时破坏了一些 php 设置 如果我执行一个包含以下内容的 php 脚本 phpinfo 它显示 php 版本为 5 6 但通过命令行 如果我运行php v它返回 7 0 版本 我想让两个版本匹配 我怎样才能修复
  • 并行运行 make 时出错

    考虑以下制作 all a b a echo a exit 1 b echo b start sleep 1 echo b end 当运行它时make j2我收到以下输出 echo a echo b start a exit 1 b star
  • 在 Linux 中禁用历史记录 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 要在 Linux 环境中禁用历史记录 我执行了以下命令 export HISTFILESIZE 0 export HISTSIZE 0 u
  • Godaddy 托管上的 CakePHP 控制台

    我一直在努力让我的 CakePHP 网站在 Godaddy 网格托管 帐户上运行 我的蛋糕应用程序设置是从帐户的子目录托管的 并且可以通过子域访问 我必须调整我的 htaccess 文件才能使其正常工作 现在我需要让 CakePHP 控制台
  • xsel -o 对于 OS X 等效项

    是否有一个等效的解决方案可以在 OS X 中抓取选定的文本 就像适用于 Linux 的 xsel o 一样 只需要当前的选择 这样我就可以在 shell 脚本中使用文本 干杯 埃里克 你也许可以安装xsel在 MacOS 上 更新 根据 A
  • 在 Mac OS X 上构建 Linux 内核

    我正在做一个修改Linux内核的项目 我有一台桌面 Linux 机器 在上面构建内核没有问题 不过 我要去旅行 我想在途中工作 我只有一台 MacBook 当我尝试构建 Linux 内核时 它抱怨说elf h was not found 我
  • 拆分字符串以仅获取前 5 个字符

    我想去那个地点 var log src ap kernelmodule 10 001 100 但看起来我的代码必须处理 ap kernelmodule 10 002 100 ap kernelmodule 10 003 101 等 我想使用
  • 修改linux下的路径

    虽然我认为我已经接近 Linux 专业人士 但显然我仍然是一个初学者 当我登录服务器时 我需要使用最新版本的R 统计软件 R 安装在 2 个地方 当我运行以下命令时 which R I get usr bin R 进而 R version
  • 使用 find - 删除除任何一个之外的所有文件/目录(在 Linux 中)

    如果我们想删除我们使用的所有文件和目录 rm rf 但是 如果我希望一次性删除除一个特定文件之外的所有文件和目录怎么办 有什么命令可以做到这一点吗 rm rf 可以轻松地一次性删除 甚至可以删除我最喜欢的文件 目录 提前致谢 find ht
  • 使用 grep 查找包含所有搜索字符串的行

    我有一个文件 其中包含很多与此类似的行 id 2796 some model Profile message type MODEL SAVE fields account 14 address null modification times
  • 如何根据 HTTP 请求使用 Python 和 Flask 执行 shell 命令并流输出?

    下列的这个帖子 https stackoverflow com questions 15092961 how to continuously display python output in a webpage 我能够tail f网页的日志
  • 如何在bash中使用jq从变量中包含的json中提取值

    我正在编写一个 bash 脚本 其中存储了一个 json 值 现在我想使用 Jq 提取该 json 中的值 使用的代码是 json val code lyz1To6ZTWClDHSiaeXyxg redirect to http examp
  • 如何在 shell 脚本中并行运行多个实例以提高时间效率[重复]

    这个问题在这里已经有答案了 我正在使用 shell 脚本 它读取 16000 行的输入文件 运行该脚本需要8个多小时 我需要减少它 所以我将其划分为 8 个实例并读取数据 其中我使用 for 循环迭代 8 个文件 并在其中使用 while
  • gdb查找行号的内存地址

    假设我已将 gdb 附加到一个进程 并且在其内存布局中有一个文件和行号 我想要其内存地址 如何获取文件x中第n行的内存地址 这是在 Linux x86 上 gdb info line test c 56 Line 56 of test c

随机推荐

  • 姿态和位置,四旋翼的控制流程

    xfeff xfeff 姿态和位置计算 xff1a EKF 位置控制 xff1a PID 姿态控制 xff1a 姿态环是直接P控制 xff0c 姿态率是PID控制 主要是滤波算法和姿态算法还有PID算法 滤波算法主要是将获取到的陀螺仪和加速
  • Jetson nano 通过 vnc 实现远程桌面控制(已在nano实现)

    目录 一 linux环境 xff1a Ubuntu 18 04 二 nano设置VNC服务 三 设置开机自启动 四 设置静态IP 一 linux环境 xff1a Ubuntu 18 04 jetson nano用的是ubuntu18 04的
  • make px4_sitl_default gazebo出错

    出现错误时候 xff0c 可以在固件文件夹下先更新下 make clean sudo apt get update sudo apt get upgrade 错误1 xff1a 编译make px4 sitl default gazebo
  • PX4 与 MAVROS 实现offboard

    目录 一 虚拟机仿真环境 1 创建工作空间 2 创建ROS节点功能包 3 运行PX4的gazebo仿真 4 启动PX4与Mavros之间的连接 二 真机控制 1 硬件连接 2 软件设置 3 出现问题 Ubuntu xff1a 20 04 x
  • AprilTag_ros的使用

    目录 前言 一 usb摄像头的安装和使用 二 AprilTag ros包的安装 三 单目摄像机的标定 四 AprilTag ros的使用 五 其他 前言 平台 xff1a VM虚拟机 ROS版本 xff1a noetic Ubuntu xf
  • make px4_sitl_default gazebo 建立PX4仿真环境的各种坑

    前言 xff1a 平台 xff1a VM 虚拟机 Ubuntu18 04 gazebo9 一 执行组件更新总是各种中断 git submodule update init recursive 众所周知这是墙墙的故事 xff0c 所以进行了机
  • Jetson nano刷机安装系统

    1 下载系统镜像 可以到官网上下载镜像 xff0c 英伟达官网 在上面选择合适的镜像文件下载 2 格式化内存卡 需要下载格式化tf卡的工具SD Memory Card Formatter xff0c 读者可以也自行下载 格式化时候就像下图
  • 继电器的使用

    继电器使用 继电器基本说明 继电器就相当于一个开关 xff0c 接在任意线上 xff0c 通过控制信号下控制通断 xff1b 一般是断开状态 xff0c 此时线就断开了 xff0c 没导通 xff1b 在控制信号作用下继电器闭合 xff0c
  • 在树莓派上使用火焰,声音,震动,光敏传感器

    作为一个软件工程专业的学生 xff0c 对传感器等硬件的使用一直不太顺手 xff0c 而在树莓派使用Python的RPi GPIO xff0c 进行传感器等硬件的使用却是非常方便 而且使用树莓派这个网络功能强大的控制中心 xff0c 其在物
  • UCOSIII---信号量

    UCOSIII 信号量 概述PV原语函数接口创建信号量等待信号量释放信号量 例程注意 优先级反转概述解决方案注意事项 概述 信号量常用于任务的同步 xff0c 通过该信号 xff0c 就能够控制某个任务的执行 xff0c 这个信号具有计数值
  • 十分钟读懂『卡尔曼滤波算法』

    我是勤劳的搬运工 xff0c 转自 xff1a 1 http blog csdn net karen99 article details 7771743 2 http blog csdn net tudouniurou article de
  • Pixhawk基于Radio地面站发送指令

    xfeff xfeff px4原生固件提供offboard飞行模式 xff0c Offboard模式是使用外部电脑 xff08 软件 xff09 与pixhawk相连 xff0c 并进行控制 在室内室外都可使用该模式 xff0c 标准代码都
  • IDEA中SpringBoot出错问题

    1 新建项目时 xff0c 出现 Error java 无效的标记 parameters 或者 Error java 无效的源发行版 13 等这些问题时 xff0c 需要看下project setting中的各种配置 xff0c 注意以下图
  • 程序 = 数据结构 + 算法

    我们编写程序的目的就是与真实世界交互 xff0c 解决真实世界的问题 xff0c 帮助真实世界提高运行效率与改善运行质量 所以我们就需要对真实世界事物体的重要属性进行提炼 xff0c 并映射到程序世界中 xff0c 这就是所谓的对真实世界的
  • C++中的::

    34 34 在C 43 43 中表示作用域 xff0c 和所属关系 34 34 是运算符中等级最高的 xff0c 它分为三种 xff0c 分别如下 xff1a 一 作用域符号 xff1a 作用域符号 的前面一般是类名称 xff0c 后面一般
  • Ubuntu16桌面版安装realsense SDK

    Ubuntu16桌面版安装realsense SDK 1 下载realsense master 官网下载连接 xff1a https github com IntelRealSense librealsense 2 解压realsense
  • 自动驾驶中使用到的坐标转换

    一 简介 1 1 地心地固直角坐标系 xff08 ECEF xff09 也叫地心地固直角坐标系 其原点为地球的质心 xff0c x轴延伸通过本初子午线 xff08 0度经度 xff09 和赤道 xff08 0deglatitude xff0
  • 自动驾驶坐标转换-北东地/东北天两种导航坐标系与姿态转换

    一 坐标系 1 导航坐标系 常用的导航坐标系有北东地和东北天两种 两种坐标系的指向分别定义如下 xff1a 1 1 北东地坐标系 X轴 xff1a 指北 Y轴 xff1a 指东 Z轴 xff1a 指地 1 2 东北天坐标系 X轴 xff1a
  • DMA 中断 查询三者的区别

    1 DMA xff08 DIRECT MEMORY ACCESS xff09 即直接存储器存取 xff0c 是指外部设备不通过CPU而直接与系统内存交换数据的接口技术 要把外设的数据读入内存或把内存的数据传送到外设 xff0c 一般都要通过
  • Linux 下 i2c switch(选路芯片mux) — pca9548

    作者 xff1a 韩大卫 64 吉林师范大学 现有的关于i2c switch 资料非常少 即使阅读完官方的datasheet 也不能写出完全正确的操作 因为内核中的驱动本身不是那么完善的 还有一些资料是单片机编程的 xff0c 可惜在lin