Linux 下 i2c switch(選路芯片mux) --- pca9548

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] 


//不同的時候,有相應的操作。

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

發佈了29 篇原創文章 · 獲贊 28 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章