資料結構
struct i2c_adapter {
struct module *owner;
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 */
struct rt_mutex bus_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;
};
#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) // d is device
struct module *owner;
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 */
struct rt_mutex bus_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;
};
#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev) // d is device
algo 資料結構
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,
smbus_xfer. If set to NULL, the SMBus protocol is
using common I2C messages */
/* master_xfer should return the number of messages
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 *);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
int (*reg_slave)(struct i2c_client *client);
int (*unreg_slave)(struct i2c_client *client);
#endif
};
/* If an adapter algorithm can't do I2C-level access, set master_xfer
to NULL. If an adapter algorithm can do SMBus access,
smbus_xfer. If set to NULL, the SMBus protocol is
using common I2C messages */
/* master_xfer should return the number of messages
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 *);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
int (*reg_slave)(struct i2c_client *client);
int (*unreg_slave)(struct i2c_client *client);
#endif
};
其中的 master_xfer & smbus_xfer 就是給 client 驅動所呼叫的 call back function 來收送資料
I2C core 所提供 Kernel API
- #include <linux/i2c.h> // 驅動需要的 include file
- static inline void *i2c_get_adapdata(const struct i2c_adapter *dev) // 使用 i2c_adapter 的資料結構來取得你自行設定的私有資料結構
- static inline void i2c_set_adapdata(struct i2c_adapter *dev, void *data)// 使用 i2c_adapter 的資料結構來設定你自行設定的私有資料結構
- void i2c_lock_adapter(struct i2c_adapter *); // 鎖定這個 adapter bus, 以確保只有一個人在收送資料, 這是一個很重要的動作, 因為 I2C
- void i2c_unlock_adapter(struct i2c_adapter *); // 解鎖 adapter bus, 這必需和上面的鎖定成對使用
- static inline struct i2c_adapter *i2c_parent_is_i2c_adapter(const struct i2c_adapter *adapter) // 取得上一層的 adapter data, 這在如果有多工器時會更需要用到
- extern int i2c_add_adapter(struct i2c_adapter *); // 增加一個 adapter or master, 這不指定 bus number, 由系統自動 assign
- extern void i2c_del_adapter(struct i2c_adapter *); // 移除一個 adapter
- extern int i2c_add_numbered_adapter(struct i2c_adapter *); // 增加一個指定 bus number 的 adapter
- int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); // 這是給 client 驅動所呼叫的的程式來要求 adapter 收送資料給 client, 而 master 則是轉成呼叫上面所提的 master_xfer & smbus_xfer call back function
範例 - id table
static const struct of_device_id mv64xxx_i2c_of_match_table[] = {
{ .compatible = "allwinner,sun4i-a10-i2c", .data = &mv64xxx_i2c_regs_sun4i},
{ .compatible = "allwinner,sun6i-a31-i2c", .data = &mv64xxx_i2c_regs_sun4i},
{ .compatible = "marvell,mv64xxx-i2c", .data = &mv64xxx_i2c_regs_mv64xxx},
{ .compatible = "marvell,mv78230-i2c", .data = &mv64xxx_i2c_regs_mv64xxx},
{ .compatible = "marvell,mv78230-a0-i2c", .data = &mv64xxx_i2c_regs_mv64xxx},
{} // 一定要有這個做為結束
};
MODULE_DEVICE_TABLE(of, mv64xxx_i2c_of_match_table);
static const struct of_device_id mv64xxx_i2c_of_match_table[] = {
{ .compatible = "allwinner,sun4i-a10-i2c", .data = &mv64xxx_i2c_regs_sun4i},
{ .compatible = "allwinner,sun6i-a31-i2c", .data = &mv64xxx_i2c_regs_sun4i},
{ .compatible = "marvell,mv64xxx-i2c", .data = &mv64xxx_i2c_regs_mv64xxx},
{ .compatible = "marvell,mv78230-i2c", .data = &mv64xxx_i2c_regs_mv64xxx},
{ .compatible = "marvell,mv78230-a0-i2c", .data = &mv64xxx_i2c_regs_mv64xxx},
{} // 一定要有這個做為結束
};
MODULE_DEVICE_TABLE(of, mv64xxx_i2c_of_match_table);
adapter algo example
static int
mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
// just do it
// return how many messages are sent
}
static u32
mv64xxx_i2c_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm mv64xxx_i2c_algo = {
.master_xfer = mv64xxx_i2c_xfer,
.functionality = mv64xxx_i2c_functionality,
};
mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
struct mv64xxx_i2c_data *drv_data = i2c_get_adapdata(adap);
// just do it
// return how many messages are sent
}
static u32
mv64xxx_i2c_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
}
static const struct i2c_algorithm mv64xxx_i2c_algo = {
.master_xfer = mv64xxx_i2c_xfer,
.functionality = mv64xxx_i2c_functionality,
};
example – probe call back function
static int mv64xxx_i2c_probe(struct platform_device *pd) {
// prepare i2c_adapter data structure
adapter.name= “myname”;
adapter.timeout = msecs_to_jiffies(timeout);
adapter.dev.parent = &pd->dev;
adapter.algo = &mv64xxx_i2c_algo;
adapter.owner = THIS_MODULE;
adapter.class = I2C_CLASS_DEPRECATED;
adapter.nr = pd->id; // bus number
adapter.dev.of_node = pd->dev.of_node;
// call i2c_add_numbered_adapter(&adapter)
// prepare i2c_adapter data structure
adapter.name= “myname”;
adapter.timeout = msecs_to_jiffies(timeout);
adapter.dev.parent = &pd->dev;
adapter.algo = &mv64xxx_i2c_algo;
adapter.owner = THIS_MODULE;
adapter.class = I2C_CLASS_DEPRECATED;
adapter.nr = pd->id; // bus number
adapter.dev.of_node = pd->dev.of_node;
// call i2c_add_numbered_adapter(&adapter)
}
example – remove call back function & driver register
static int mv64xxx_i2c_remove(struct platform_device *dev) {};
static struct platform_driver mv64xxx_i2c_driver = {
.probe = mv64xxx_i2c_probe,
.remove = mv64xxx_i2c_remove,
.driver = {
.name = MV64XXX_I2C_CTLR_NAME,
.of_match_table = mv64xxx_i2c_of_match_table,
},
};
module_platform_driver(mv64xxx_i2c_driver);
static struct platform_driver mv64xxx_i2c_driver = {
.probe = mv64xxx_i2c_probe,
.remove = mv64xxx_i2c_remove,
.driver = {
.name = MV64XXX_I2C_CTLR_NAME,
.of_match_table = mv64xxx_i2c_of_match_table,
},
};
module_platform_driver(mv64xxx_i2c_driver);
如何加入一個 slave device
- 可以在 device tree 中描述,並準備好驅動程式即可,以下為範例:
i2c0: i2c@11000 {
pinctrl-names = "default";
pinctrl-0 = <&i2c0_pins>;
// timeout-ms = <3000>;
eeprom@50 {
compatible = "atmel,24c64";
pagesize = <32>;
reg = <0x50>;
marvell,board_id_reg = <0x7>;
};
rtc@6f {
compatible = "isl,isl1208";
reg = <0x6f>;
};
moxaeds-i2c-mux@08 {
compatible = "moxa,moxaeds-i2c-mux";
reg = <0x08>;
};
};
- 可以在 adapter (master) 驅動中加入,以下為範例:
static int moxaeds_create_client(struct moxaeds_priv_struct *priv, u32 addr, const char *type, int index)
{
struct i2c_board_info info;
struct dev_archdata dev_ad;
memset(&info, 0, sizeof(info));
memset(&dev_ad, 0, sizeof(dev_ad));
info.addr = addr;
if ( addr > 0x7f )
info.flags |= I2C_CLIENT_TEN; // for 10 bits address slave device
strncpy(info.type, type, I2C_NAME_SIZE);
info.archdata = &dev_ad;
priv->client[index] = i2c_new_device(priv->madap, &info);
if ( priv->client[index] == NULL )
return -ENODEV;
return 0;
}
ret = moxaeds_create_client(fpga9_son_priv[busnum], 0x50, "24c64", EEPROM_CLIENT);