以上為在 Linux Kernel 中的軟體架構, 其主要分為幾部分:
- I2C core 為核心程式, 也是管理程式, 做為和 kernel 之間其它驅動要收送 I2C 資炓的介面
- I2C client 為各個 I2C client 端元件的驅動式, 必須透過 I2C core 來通知 I2C master 收送資料給自己
- I2C master 為 I2C master 的驅動, 提供給 I2C core 讓 I2C client 來收送資料
- I2C multiplexer 在同一個 I2C master 之下的 bus, 不能有重覆的 I2C client address, 但有時需要使用多個相同 I2C client 時就需要 I2C multiplexer 來做切換的動作, 而在 multiplexer 中會建立多個 virtual master (adapter)
- I2C slave 其實和 I2C client 一樣, 都是 client 端驅動程式, 只是這類的驅動會使用 event 的方式來做收送資料, 這類驅動較少使用
source code directory in kernel source code root
- kernel-source/drivers/i2c
- busses 目錄放 master device driver
- muexs 目錄放 multiplexer device driver
- algos 目錄放演算法或協議 device driver
- i2c-core.c i2c 核心程式
- i2c-dev.c 建立 bus master device node 以及介面程式
- i2c-mux.c 處理 multiplexer 的核心程式
- i2c-boardinfo.c 提供建立 i2c board information 的程式 (這是較舊的做法, 現在通常使用 device tree)
使用時機
- 原則上 slave 端的元件才是我們要使用的元件, 如 EEPROM
- client 資料結構中含 adapter data
i2c_client 資料結構說明
/**
* struct i2c_client - represent an I2C slave device
* @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;
* I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking
* @addr: Address used on the I2C bus connected to the parent adapter.
* @name: Indicates the type of the device, usually a chip name that's
* generic enough to hide second-sourcing and compatible revisions.
* @adapter: manages the bus segment hosting this I2C device
* @dev: Driver model device node for the slave.
* @irq: indicates the IRQ generated by this device (if any)
* @detected: member of an i2c_driver.clients list or i2c-core's
* userspace_devices list
* @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter
* calls it to pass on slave events to the slave driver.
*
* An i2c_client identifies a single device (i.e. chip) connected to an
* i2c bus. The behaviour exposed to Linux is defined by the driver
* managing the device.
*/
PS: 這個資料結構在 client 端註冊的 probe call back function 中會傳給它
* struct i2c_client - represent an I2C slave device
* @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;
* I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking
* @addr: Address used on the I2C bus connected to the parent adapter.
* @name: Indicates the type of the device, usually a chip name that's
* generic enough to hide second-sourcing and compatible revisions.
* @adapter: manages the bus segment hosting this I2C device
* @dev: Driver model device node for the slave.
* @irq: indicates the IRQ generated by this device (if any)
* @detected: member of an i2c_driver.clients list or i2c-core's
* userspace_devices list
* @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter
* calls it to pass on slave events to the slave driver.
*
* An i2c_client identifies a single device (i.e. chip) connected to an
* i2c bus. The behaviour exposed to Linux is defined by the driver
* managing the device.
*/
PS: 這個資料結構在 client 端註冊的 probe call back function 中會傳給它
struct i2c_client {
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit */
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
unsigned short flags; /* div., see below */
unsigned short addr; /* chip address - NOTE: 7bit */
/* addresses are stored in the */
/* _LOWER_ 7 bits */
char name[I2C_NAME_SIZE];
struct i2c_adapter *adapter; /* the adapter we sit on */
struct device dev; /* the device structure */
int irq; /* irq issued by device */
struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* callback for slave mode */
#endif
};
#define to_i2c_client(d) container_of(d, struct i2c_client, dev)
PS: 其中 I2C_NAME_SIZE 目前定義為 20
i2c_driver 資料結構說明
* struct i2c_driver - represent an I2C device driver
* @class: What kind of i2c device we instantiate (for detect)
* @attach_adapter: Callback for bus addition (deprecated)
* @probe: Callback for device binding
* @remove: Callback for device unbinding
* @shutdown: Callback for device shutdown
* @alert: Alert callback, for example for the SMBus alert protocol
* @command: Callback for bus-wide signaling (optional)
* @driver: Device driver model driver
* @id_table: List of I2C devices supported by this driver
* @detect: Callback for device detection
* @address_list: The I2C addresses to probe (for detect)
* @clients: List of detected clients we created (for i2c-core use only)
* The driver.owner field should be set to the module owner of this driver.
* The driver.name field should be set to the name of this driver.
* For automatic device detection, both @detect and @address_list must be defined.
* @class should also be set, otherwise only devices forced with module parameters
* will be created. The detect function must fill at least the name field of
* the i2c_board_info structure it is handed upon successful detection, and possibly
* also the flags field. If @detect is missing, the driver will still work fine for enumerated
* devices. Detected devices simply won't be supported. This is expected
* for the many I2C/SMBus devices which can't be detected reliably, and
* the ones which can always be enumerated in practice.
* The i2c_client structure which is handed to the @detect callback is
* not a real i2c_client. It is initialized just enough so that you can
* call i2c_smbus_read_byte_data and friends on it. Don't do anything
* else with it. In particular, calling dev_dbg and friends on it is not allowed.
* struct i2c_driver - represent an I2C device driver
* @class: What kind of i2c device we instantiate (for detect)
* @attach_adapter: Callback for bus addition (deprecated)
* @probe: Callback for device binding
* @remove: Callback for device unbinding
* @shutdown: Callback for device shutdown
* @alert: Alert callback, for example for the SMBus alert protocol
* @command: Callback for bus-wide signaling (optional)
* @driver: Device driver model driver
* @id_table: List of I2C devices supported by this driver
* @detect: Callback for device detection
* @address_list: The I2C addresses to probe (for detect)
* @clients: List of detected clients we created (for i2c-core use only)
* The driver.owner field should be set to the module owner of this driver.
* The driver.name field should be set to the name of this driver.
* For automatic device detection, both @detect and @address_list must be defined.
* @class should also be set, otherwise only devices forced with module parameters
* will be created. The detect function must fill at least the name field of
* the i2c_board_info structure it is handed upon successful detection, and possibly
* also the flags field. If @detect is missing, the driver will still work fine for enumerated
* devices. Detected devices simply won't be supported. This is expected
* for the many I2C/SMBus devices which can't be detected reliably, and
* the ones which can always be enumerated in practice.
* The i2c_client structure which is handed to the @detect callback is
* not a real i2c_client. It is initialized just enough so that you can
* call i2c_smbus_read_byte_data and friends on it. Don't do anything
* else with it. In particular, calling dev_dbg and friends on it is not allowed.
struct i2c_driver {
unsigned int class;
/* Notifies the driver that a new bus has appeared. You should avoid
* using this, it will be removed in a near future. */
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
/* driver model interfaces that don't relate to enumeration */
void (*shutdown)(struct i2c_client *);
/* Alert callback, for example for the SMBus alert protocol.
* The format and meaning of the data value depends on the protocol.
* For the SMBus alert protocol, there is a single bit of data passed
* as the alert response‘s low bit (“event flag”). */
void (*alert)(struct i2c_client *, unsigned int data);
/* a ioctl like command that can be used to perform specific functions
* with the device. */
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
const struct i2c_device_id *id_table;
/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
unsigned int class;
/* Notifies the driver that a new bus has appeared. You should avoid
* using this, it will be removed in a near future. */
int (*attach_adapter)(struct i2c_adapter *) __deprecated;
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
/* driver model interfaces that don't relate to enumeration */
void (*shutdown)(struct i2c_client *);
/* Alert callback, for example for the SMBus alert protocol.
* The format and meaning of the data value depends on the protocol.
* For the SMBus alert protocol, there is a single bit of data passed
* as the alert response‘s low bit (“event flag”). */
void (*alert)(struct i2c_client *, unsigned int data);
/* a ioctl like command that can be used to perform specific functions
* with the device. */
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
const struct i2c_device_id *id_table;
/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
#define to_i2c_driver(d) container_of(d, struct i2c_driver, driver)
PS: 在 I2C client 端的驅動程式, 必需宣告這個資料, 其中 probe & remove 兩個 call back function 為必需實做的兩個 call back function, 另外 id_table 也是需要宣告的, 以下會有範例說明
I2C core 所提供的 API for client driver
#include <Linux/i2c.h>
- int i2c_register_driver(struct module *, struct i2c_driver *); // 註冊一個 client 端驅動, 通常在module_init 中呼叫
- void i2c_del_driver(struct i2c_driver *); // 移除一個 client 諯驅動, 通常在 module_exit 中呼叫
-
#define i2c_add_driver(driver) \ // 簡化上面的 API
i2c_register_driver(THIS_MODULE, driver) - module_i2c_driver(moxaeds_driver); // 簡化 module_init & module_exit
probe call back function example
static int moxaeds_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct i2c_adapter *adap=to_i2c_adapter(client->dev.parent); // my parent master
enum si5351_variant variant = (enum si5351_variant)id->driver_data; // get private data which from id table defined
{
struct i2c_adapter *adap=to_i2c_adapter(client->dev.parent); // my parent master
enum si5351_variant variant = (enum si5351_variant)id->driver_data; // get private data which from id table defined
// allocate private data and initialize it
// initialize hardware
// keep got client data, 就是將傳進來的 client 資料存在 local, 因為後面整個驅動都會用到
return 0;
}
}
remove call back function example
static int moxaeds_remove(struct i2c_client *client)
{
{
// release all resource
return 0;
}
}
id table example in driver
static const struct i2c_device_id moxaeds_id[] = {
{"moxaeds-i2c-mux", 0 /* private data */}, // 若有很多個, 可以再加
{} // 一定要加這個為結束
};
MODULE_DEVICE_TABLE(i2c, moxaeds_id);
{"moxaeds-i2c-mux", 0 /* private data */}, // 若有很多個, 可以再加
{} // 一定要加這個為結束
};
MODULE_DEVICE_TABLE(i2c, moxaeds_id);
device tree example in driver
static const struct of_device_id si5351_dt_ids[] = {
{ .compatible = "silabs,si5351a", .data = (void *)SI5351_VARIANT_A /* private data */, },
{ .compatible = "silabs,si5351a-msop", .data = (void *)SI5351_VARIANT_A3 /* private data */, },
{ .compatible = "silabs,si5351b", .data = (void *)SI5351_VARIANT_B /* private data */, },
{ .compatible = "silabs,si5351c", .data = (void *)SI5351_VARIANT_C /* private data */, },
{ } // 一定要加這個為結束
};
{ .compatible = "silabs,si5351a", .data = (void *)SI5351_VARIANT_A /* private data */, },
{ .compatible = "silabs,si5351a-msop", .data = (void *)SI5351_VARIANT_A3 /* private data */, },
{ .compatible = "silabs,si5351b", .data = (void *)SI5351_VARIANT_B /* private data */, },
{ .compatible = "silabs,si5351c", .data = (void *)SI5351_VARIANT_C /* private data */, },
{ } // 一定要加這個為結束
};
MODULE_DEVICE_TABLE(of, si5351_dt_ids);
device tree example in device tree file
i2c0: i2c@11000 {
pinctrl-names = "default";
pinctrl-0 = <&i2c0_pins>;
// timeout-ms = <3000>;
/* hardware change to connect after FPAG#9 bus 15
eeprom@50 {
compatible = "atmel,24c64"; // 這是上面 driver 中宣告的 device tree, ',' 之後名字最多20字母
pagesize = <32>;
reg = <0x50>; // 這是 i2c client address
marvell,board_id_reg = <0x7>;
};
rtc@6f {
compatible = "isl,isl1208"; // 這是上面 driver 中宣告的 device tree
reg = <0x6f>; // 這是 i2c client address
};
*/
moxaeds-i2c-mux@08 {
compatible = "moxa,moxaeds-i2c-mux"; // 這是上面 driver 中宣告的 device tree
reg = <0x08>; // 這是 i2c client address
};
};
i2c0: i2c@11000 {
pinctrl-names = "default";
pinctrl-0 = <&i2c0_pins>;
// timeout-ms = <3000>;
/* hardware change to connect after FPAG#9 bus 15
eeprom@50 {
compatible = "atmel,24c64"; // 這是上面 driver 中宣告的 device tree, ',' 之後名字最多20字母
pagesize = <32>;
reg = <0x50>; // 這是 i2c client address
marvell,board_id_reg = <0x7>;
};
rtc@6f {
compatible = "isl,isl1208"; // 這是上面 driver 中宣告的 device tree
reg = <0x6f>; // 這是 i2c client address
};
*/
moxaeds-i2c-mux@08 {
compatible = "moxa,moxaeds-i2c-mux"; // 這是上面 driver 中宣告的 device tree
reg = <0x08>; // 這是 i2c client address
};
};
另一個 device tree example in device tree file
&i2c0 {
status = "okay";
clock-frequency = <100000>;
si5351: clock-generator {
compatible = "silabs,si5351a-msop";
reg = <0x60>;
#address-cells = <1>;
#size-cells = <0>;
#clock-cells = <1>;
/* connect xtal input to 25MHz reference */
clocks = <&ref25>;
clock-names = "xtal";
/* connect xtal input as source of pll0 and pll1 */
silabs,pll-source = <0 0>, <1 0>;
clkout0 {
reg = <0>;
silabs,drive-strength = <8>;
silabs,multisynth-source = <0>;
silabs,clock-source = <0>;
silabs,pll-master;
};
clkout2 {
reg = <2>;
silabs,drive-strength = <8>;
silabs,multisynth-source = <1>;
silabs,clock-source = <0>;
silabs,pll-master;
};
};
};
&i2c0 {
status = "okay";
clock-frequency = <100000>;
si5351: clock-generator {
compatible = "silabs,si5351a-msop";
reg = <0x60>;
#address-cells = <1>;
#size-cells = <0>;
#clock-cells = <1>;
/* connect xtal input to 25MHz reference */
clocks = <&ref25>;
clock-names = "xtal";
/* connect xtal input as source of pll0 and pll1 */
silabs,pll-source = <0 0>, <1 0>;
clkout0 {
reg = <0>;
silabs,drive-strength = <8>;
silabs,multisynth-source = <0>;
silabs,clock-source = <0>;
silabs,pll-master;
};
clkout2 {
reg = <2>;
silabs,drive-strength = <8>;
silabs,multisynth-source = <1>;
silabs,clock-source = <0>;
silabs,pll-master;
};
};
};
上面所提 i2c_driver 資料結構範例
static struct i2c_driver moxaeds_driver = {
.driver = {
.name = "moxaeds-i2c-mux",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(si5351_dt_ids), // my device tree
},
.probe = moxaeds_probe, // 參考前面的範
.remove = moxaeds_remove, // 參考前面的範
.id_table = moxaeds_id, // my id table
};
.driver = {
.name = "moxaeds-i2c-mux",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(si5351_dt_ids), // my device tree
},
.probe = moxaeds_probe, // 參考前面的範
.remove = moxaeds_remove, // 參考前面的範
.id_table = moxaeds_id, // my id table
};
module_init & module_exit example
#if 0 // if you want to debug module initialize function enable here
static int __init moxaeds_init(void)
{
dgprintk(HIGH_DEBUG_LEVEL, "\n");
return i2c_add_driver(&moxaeds_driver);
}
module_init(moxaeds_init);
static void __exit moxaeds_exit(void)
{
dgprintk(HIGH_DEBUG_LEVEL, "\n");
i2c_del_driver(&moxaeds_driver);
}
module_exit(moxaeds_exit);
#else
module_i2c_driver(moxaeds_driver); // to use i2c module initialize macro
#endif
static int __init moxaeds_init(void)
{
dgprintk(HIGH_DEBUG_LEVEL, "\n");
return i2c_add_driver(&moxaeds_driver);
}
module_init(moxaeds_init);
static void __exit moxaeds_exit(void)
{
dgprintk(HIGH_DEBUG_LEVEL, "\n");
i2c_del_driver(&moxaeds_driver);
}
module_exit(moxaeds_exit);
#else
module_i2c_driver(moxaeds_driver); // to use i2c module initialize macro
#endif
如何傳送資料在 client 端驅動
- call i2c_transfer() or __i2c_transfer(), 前面一個會幫忙做 lock bus 的動作, 可避免同時有在存取相同的 bus master, 而後面的則不會, 必需使用者自己做 lock 的動作
- 範例如下:
struct i2c_msg msg[2];
u8 tbuf[4], rbuf[4];
int ret;
msg[0].addr = moxaeds_client_fpga->addr;
msg[0].flags = 0;
msg[0].len = 1;
msg[0].buf = tbuf;
tbuf[0] = FPGA_SET_BIT_MAP_REG_NO;
msg[1].addr = moxaeds_client_fpga->addr;
msg[1].flags = I2C_M_RD;
msg[1].len = 2;
msg[1].buf = rbuf;
ret = i2c_transfer(moxaeds_master_adap, msg, 2); // called i2c_transfer(), not __i2c_transfer(),
if ( ret != 2 ) {
myprintk("Read bit map register fail [%d] !\n", ret);
return 0;
}
return sprintf(buf, "0x%02x%02x", rbuf[0], rbuf[1]);
// 其中在 i2c_transfer 中第一個參數 moxaeds_master_adap, 在前面所提 probe call back function 所傳進來的 client 中其中的 member
沒有留言:
張貼留言