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] //不同的時候,有相應的操作。 ********** ********************
Linux 下 i2c switch(選路芯片mux) --- pca9548
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.