i2c架构图
i2c控制器注册
i2c控制器通过适配器来描述,即一个i2c控制器对应一个i2c适配器,一个i2c控制器也对应着一条i2c总线。
1.分配私有结构体struct rk3x_i2c
2.填充struct i2c_adapter,调用i2c_add_adapter注册adpater设备。对于非smbus功能控制器,控制器收发功能通过master_xfer控制。
static const struct i2c_algorithm rk3x_i2c_algorithm = {
.master_xfer = rk3x_i2c_xfer,
.functionality = rk3x_i2c_func,
};
......
strlcpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &rk3x_i2c_algorithm;
i2c->adap.retries = 3;
i2c->adap.dev.of_node = np;
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;
......
i2c_add_adapter:
1.adapter->nr未赋值则分配后注册,若已赋值则按已赋值id进行注册。
2.of_i2c_register_devices注册挂在i2c总线下的i2c设备(设备树实例化i2c_client)。3.i2c_scan_static_board_info扫描__i2c_board_list注册属于该总线下的i2c设备(i2c_register_board_info方式实例化i2c_client)。
4.bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter)扫描i2c总线上的驱动,通过驱动提供的探测函数探测总线下是否存在设备,若存在注册i2c设备(驱动探测i2c设备实例化i2c_client)
/*
* i2c_adapter is the structure used to identify a physical i2c bus along
* with the access algorithms necessary to access it.
*/
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 */
const struct i2c_lock_operations *lock_ops;
struct rt_mutex bus_lock;
struct rt_mutex mux_lock;
int timeout; //默认HZ /* 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;
struct irq_domain *host_notify_domain;
};
i2c设备注册
instantiate an i2c device,i2c设备的注册过程是通过实例化一个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.
*/
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 init_irq; /* irq set at initialization */
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
};
一、实例化i2c_client的几种方法(最终调用的都是i2c_new_device(4.19版本)/i2c_new_client_device(5.10版本))
1.设备树,通过在对应的i2c总线下创建i2c设备节点。
2.调用i2c_register_board_info将i2c设备信息挂载到__i2c_board_list链表上,后续adapter注册会扫描__i2c_board_list上的i2c设备信息进行实例化i2c设备
1) i2c_register_board_info定义I2C器件信息(Name,Address,etc.)
static struct i2c_board_info __initdata pi2c_board_info[] = {
{
I2C_BOARD_INFO("max1586", 0x14),
.platform_data = &max1587a_info,
},
};
i2c_register_board_info(1, ARRAY_AND_SIZE(pi2c_board_info));
/* 1表示该I2C设备挂在I2C-1上 ,注册I2C adapt时相应的id = 1 */
3.显示调用i2c_new_device(4.19版本)/i2c_new_client_device(5.10版本)实例化i2c_client
struct i2c_board_info ser_info = {
.of_node = rxport->remote_of_node,
};
......
ser_info.addr = rxport->ser_alias;
i2c_new_client_device(priv->client->adapter, &ser_info);
......
4.驱动提供探测函数主动探测adapter上的i2c设备,探测到相应设备则进行实例化(smbus类i2c设备才有此功能,i2c驱动注册或i2c适配器注册时会调用bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter)去处理)
static struct i2c_driver lm90_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "lm90",
.of_match_table = of_match_ptr(lm90_of_match),
},
.probe = lm90_probe,
.alert = lm90_alert,
.id_table = lm90_id,
.detect = lm90_detect,
.address_list = normal_i2c,
};
5.用户空间实例化
1)创建 i2c 设备
echo i2c_test 0x48 > /sys/bus/i2c/devices/i2c-6/new_device
2)删除设备
echo 0x48 > /sys/bus/i2c/devices/i2c-6/delete_device
二、相关i2c_client实例化接口(i2c设备注册)
i2c_new_device(4.19版本)/i2c_new_client_device(5.10)
struct i2c_client *i2c_new_dummy(struct i2c_adapter *adapter, u16 address)(4.9)//info.type="dummy"
struct i2c_client *i2c_new_dummy_device(struct i2c_adapter *adapter, u16 address)(5.10)
struct i2c_client *i2c_new_probed_device(struct i2c_adapter *adap,
struct i2c_board_info *info,
unsigned short const *addr_list,
int (*probe)(struct i2c_adapter *, unsigned short addr))(内核4.9版本)
struct i2c_client *i2c_new_scanned_device(struct i2c_adapter *adap,
struct i2c_board_info *info,
unsigned short const *addr_list,
int (*probe)(struct i2c_adapter *adap, unsigned short addr))(内核5.10)
i2c驱动注册
一、驱动注册方法
1.module_i2c_driver注册
static struct i2c_driver ds90ub953_driver = {
.probe_new = ub953_probe,
.remove = ub953_remove,
.id_table = ub953_id,
.driver = {
.name = "ds90ub953",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ub953_dt_ids),
},
};
module_i2c_driver(ds90ub953_driver);
2.编写module_init函数,手动调用i2c_add_driver
static struct i2c_driver techpoint_i2c_driver = {
.driver = {
.name = TECHPOINT_NAME,
.pm = &techpoint_pm_ops,
.of_match_table = of_match_ptr(techpoint_of_match),
},
.probe = &techpoint_probe,
.remove = &techpoint_remove,
.id_table = techpoint_match_id,
};
static int __init sensor_mod_init(void)
{
return i2c_add_driver(&techpoint_i2c_driver);
}
static void __exit sensor_mod_exit(void)
{
i2c_del_driver(&techpoint_i2c_driver);
}
device_initcall_sync(sensor_mod_init);
module_exit(sensor_mod_exit);
二、实例化i2c_client与driver匹配过程
1.driver_register和device_register最终都会调用driver_match_device,
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
2.i2c_bus_type的match函数为i2c_device_match,有三个匹配过程,任何一个成功即认为匹配成功
a.i2c_of_match_device//设备树匹配
b.acpi_driver_match_device //ACPI style match
c.i2c_match_id//driver.id_table.name跟client.name比较
i2c内核层i2c通信方法
1.regmap方式访问
struct regmap *__devm_regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name);
#define devm_regmap_init_i2c(i2c, config) \
__regmap_lockdep_wrapper(__devm_regmap_init_i2c, #config, \
i2c, config)
2.调用封装之后的接口
//发起单个struct i2c_msg传输,读数据,rk356x adapter驱动单个读会发i2c启动写,把reg地址写成0x00,然后再i2c启动读,tda4直接启动读。
static inline int i2c_master_recv(const struct i2c_client *client,
char *buf, int count)//从client中取地址,取adapter
static inline int i2c_master_send(const struct i2c_client *client,
const char *buf, int count)//发起单个struct i2c_msg传输,发送数据
3.直接调用底层收发接口
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num)//发起多个struct i2c_msg传输,读数据或者发数据
i2c控制器做slave
在i2c控制器支持slave模式得情况下,系统可以做为slave被外部master访问
一、设备树节点修改方法
i2c slave设备实例化需要info->flags |= I2C_CLIENT_SLAVE
struct i2c_board_info info;
......
info->flags |= I2C_CLIENT_SLAVE;
......
client = i2c_new_device(adap, &info);
所以以设备树方式实例化i2c client,reg i2C地址bit30置1,of_i2c_get_board_info时会判断reg bit 30是否置1,置1才会info->flags |= I2C_CLIENT_SLAVE
&i2c1 {
status = "okay";
pinctrl-0 = <&i2c1_xfer>;
slave-testunit: i2cslave@40000044 {
compatible = "slave-testunit";
reg = <0x40000044>;
二、adapter注册方式
i2c控制器如果支持作为从设备,则需要adap.algo支持reg_slave,在i2c slave设备驱动程序中会调用i2c_slave_register,该函数会调用algo->reg_slave
static const struct i2c_algorithm cdns_i2c_algo = {
.master_xfer = cdns_i2c_master_xfer,
.functionality = cdns_i2c_func,
#if IS_ENABLED(CONFIG_I2C_SLAVE)
.reg_slave = cdns_reg_slave,
.unreg_slave = cdns_unreg_slave,
#endif
};
三、soc作为i2c从设备驱动
1、CONFIG_I2C_SLAVE打开,内核框架涉及文件drivers/i2c//i2c-core-slave.c
2、测试驱动见drivers/i2c//i2c-slave-testunit.c
3、详细过程
驱动注册函数
int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb)
i2c_slave_register解析:
1、检查i2c client是否是slave client,地址是否合法,algo->reg_slave函数是否存在
2、client->slave_cb = slave_cb;
3、client->adapter->algo->reg_slave(client);
reg_slave函数:
1、判断是否已经控制器绑定了slave client,不做重复绑定
struct cdns_i2c *id = container_of(slave->adapter, struct cdns_i2c,
adap);
if (id->slave)
return -EBUSY;
2、slave client存入i2c 控制器私有结构体
/* Store slave information */
id->slave = slave;
3、i2c控制器切换成i2c slave模式 ,slave client的addr写入控制器地址寄存器,使能收发中断
case CDNS_I2C_MODE_SLAVE:
/* Enable i2c slave */
cdns_i2c_writereg(id->ctrl_reg_diva_divb &
CDNS_I2C_CR_SLAVE_EN_MASK,
CDNS_I2C_CR_OFFSET);
/* Setting slave address */
cdns_i2c_writereg(id->slave->addr & CDNS_I2C_ADDR_MASK,
CDNS_I2C_ADDR_OFFSET);
/* Enable slave send/receive interrupts */
cdns_i2c_writereg(CDNS_I2C_IXR_SLAVE_INTR_MASK,
CDNS_I2C_IER_OFFSET);
break;
slave_cb函数:
1、i2c控制器驱动注册的中断函数会调用i2c_slave_event
static void cdns_i2c_slave_rcv_data(struct cdns_i2c *id)
{
u8 bytes;
unsigned char data;
/* Prepare backend for data reception */
if (id->slave_state == CDNS_I2C_SLAVE_STATE_IDLE) {
id->slave_state = CDNS_I2C_SLAVE_STATE_RECV;
i2c_slave_event(id->slave, I2C_SLAVE_WRITE_REQUESTED, NULL);
}
/* Fetch number of bytes to receive */
bytes = cdns_i2c_readreg(CDNS_I2C_XFER_SIZE_OFFSET);
/* Read data and send to backend */
while (bytes--) {
data = cdns_i2c_readreg(CDNS_I2C_DATA_OFFSET);
i2c_slave_event(id->slave, I2C_SLAVE_WRITE_RECEIVED, &data);
}
}
static void cdns_i2c_slave_send_data(struct cdns_i2c *id)
{
u8 data;
/* Prepare backend for data transmission */
if (id->slave_state == CDNS_I2C_SLAVE_STATE_IDLE) {
id->slave_state = CDNS_I2C_SLAVE_STATE_SEND;
i2c_slave_event(id->slave, I2C_SLAVE_READ_REQUESTED, &data);
} else {
i2c_slave_event(id->slave, I2C_SLAVE_READ_PROCESSED, &data);
}
/* Send data over bus */
cdns_i2c_writereg(data, CDNS_I2C_DATA_OFFSET);
}
/**
* cdns_i2c_slave_isr - Interrupt handler for the I2C device in slave role
* @ptr: Pointer to I2C device private data
*
* This function handles the data interrupt and transfer complete interrupt of
* the I2C device in slave role.
*
* Return: IRQ_HANDLED always
*/
static irqreturn_t cdns_i2c_slave_isr(void *ptr)
{
struct cdns_i2c *id = ptr;
unsigned int isr_status, i2c_status;
/* Fetch the interrupt status */
isr_status = cdns_i2c_readreg(CDNS_I2C_ISR_OFFSET);
cdns_i2c_writereg(isr_status, CDNS_I2C_ISR_OFFSET);
/* Ignore masked interrupts */
isr_status &= ~cdns_i2c_readreg(CDNS_I2C_IMR_OFFSET);
/* Fetch transfer mode (send/receive) */
i2c_status = cdns_i2c_readreg(CDNS_I2C_SR_OFFSET);
/* Handle data send/receive */
if (i2c_status & CDNS_I2C_SR_RXRW) {
/* Send data to master */
if (isr_status & CDNS_I2C_IXR_DATA)
cdns_i2c_slave_send_data(id);
if (isr_status & CDNS_I2C_IXR_COMP) {
id->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
i2c_slave_event(id->slave, I2C_SLAVE_STOP, NULL);
}
} else {
/* Receive data from master */
if (isr_status & CDNS_I2C_IXR_DATA)
cdns_i2c_slave_rcv_data(id);
if (isr_status & CDNS_I2C_IXR_COMP) {
cdns_i2c_slave_rcv_data(id);
id->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
i2c_slave_event(id->slave, I2C_SLAVE_STOP, NULL);
}
}
/* Master indicated xfer stop or fifo underflow/overflow */
if (isr_status & (CDNS_I2C_IXR_NACK | CDNS_I2C_IXR_RX_OVF |
CDNS_I2C_IXR_RX_UNF | CDNS_I2C_IXR_TX_OVF)) {
id->slave_state = CDNS_I2C_SLAVE_STATE_IDLE;
i2c_slave_event(id->slave, I2C_SLAVE_STOP, NULL);
cdns_i2c_writereg(CDNS_I2C_CR_CLR_FIFO, CDNS_I2C_CR_OFFSET);
}
return IRQ_HANDLED;
}
enum i2c_slave_event {
I2C_SLAVE_READ_REQUESTED,
I2C_SLAVE_WRITE_REQUESTED,
I2C_SLAVE_READ_PROCESSED,
I2C_SLAVE_WRITE_RECEIVED,
I2C_SLAVE_STOP,
};
int i2c_slave_register(struct i2c_client *client, i2c_slave_cb_t slave_cb);
int i2c_slave_unregister(struct i2c_client *client);
bool i2c_detect_slave_mode(struct device *dev);
static inline int i2c_slave_event(struct i2c_client *client,
enum i2c_slave_event event, u8 *val)
{
return client->slave_cb(client, event, val);
}
2、slave i2c设备驱动根据收到的event做相应的处理
static int i2c_slave_testunit_slave_cb(struct i2c_client *client,
enum i2c_slave_event event, u8 *val)
{
struct testunit_data *tu = i2c_get_clientdata(client);
int ret = 0;
switch (event) {
case I2C_SLAVE_WRITE_RECEIVED:
if (test_bit(TU_FLAG_IN_PROCESS, &tu->flags))
return -EBUSY;
if (tu->reg_idx < TU_NUM_REGS)
tu->regs[tu->reg_idx] = *val;
else
ret = -EMSGSIZE;
if (tu->reg_idx <= TU_NUM_REGS)
tu->reg_idx++;
/* TU_REG_CMD always written at this point */
if (tu->regs[TU_REG_CMD] >= TU_NUM_CMDS)
ret = -EINVAL;
break;
case I2C_SLAVE_STOP:
if (tu->reg_idx == TU_NUM_REGS) {
set_bit(TU_FLAG_IN_PROCESS, &tu->flags);
queue_delayed_work(system_long_wq, &tu->worker,
msecs_to_jiffies(10 * tu->regs[TU_REG_DELAY]));
}
fallthrough;
case I2C_SLAVE_WRITE_REQUESTED:
tu->reg_idx = 0;
break;
case I2C_SLAVE_READ_REQUESTED:
case I2C_SLAVE_READ_PROCESSED:
*val = TU_CUR_VERSION;
break;
}
return ret;
}
i2c master应用层操作方法
上层应用通过调用i2c_dev.c的驱动操作i2c
两种方法
1.open之后调用ioctl(fd, I2C_RDWR, &i2c_msg)进行读写
2.open之后调用ioctl(fd, I2C_SLAVE_FORCE, address),然后调用read/write进行读写
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <string.h>
#include <unistd.h>#define I2C_DEVICE "/dev/i2c-1" // I2C设备文件路径
#define I2C_ADDRESS 0x50 // I2C设备地址// 使用ioctl函数i2c读数据
int i2c_read(int fd, unsigned char slave_addr, unsigned char reg_addr)
{
unsigned char data[1];
struct i2c_rdwr_ioctl_data i2c_msg;
struct i2c_msg messages[] = {
[0] = {
.addr = slave_addr,
.flags = 0,
.len = sizeof(reg_addr),
.buf = ®_addr,
},
[1] = {
.addr = slave_addr,
.flags = I2C_M_RD,
.len = sizeof(data),
.buf = data,
},
};
i2c_msg.msgs = messages;
i2c_msg.nmsgs = 2;int ret = ioctl(fd, I2C_RDWR, &i2c_msg);
if (ret < 0)
{
printf("I2C read error\n");
return ret;
}
return data[0];
}// 使用ioctl函数i2c写数据
void i2c_write(int fd, unsigned char slave_addr, int reg_addr, unsigned char *data, int len)
{
unsigned char buf[256];
struct i2c_rdwr_ioctl_data i2c_msg;
struct i2c_msg messages[] = {
[0] = {
.addr = slave_addr,
.flags = 0,
.len = len + 1,
.buf = buf,
},
};
buf[0] = reg_addr;
memcpy(&buf[1], data, len);
i2c_msg.msgs = messages;
i2c_msg.nmsgs = 1;int ret = ioctl(fd, I2C_RDWR, &i2c_msg);
if (ret < 0)
{
printf("I2C write error\n");
}
}// 使用read和write驱动i2c通信
void i2c_kernel_write(int fd, int reg_addr, unsigned char data)
{
unsigned char wr_data[2];
wr_data[0] = reg_addr;
wr_data[1] = data;int ret = write(fd, wr_data, sizeof(wr_data));
if (ret < 0)
{
printf("I2C write error\n");
}
}
int i2c_kernel_read(int fd, unsigned char reg_addr)
{
int ret;
unsigned char rd_data[1];
rd_data[0] = reg_addr;write(fd, rd_data, sizeof(rd_data));
read(fd, rd_data, sizeof(rd_data));printf("I2C read data: %x\n", rd_data[0]);
ret = rd_data[0];
return ret;
}int main(int argc, char *argv[])
{
int fd;
int REG_VALUE;
fd = open("I2C_DEVICE", O_RDWR);
if (fd < 0)
{
printf("Failed to open I2C device\n");
return -1;
}
i2c_write(fd, I2C_ADDRESS, 0x00, 0x55, 1);REG_VALUE = i2c_read(fd, I2C_ADDRESS, 0x00);
printf("REG_VALUE: %d\n", REG_VALUE);
// 使用read和write驱动i2c通信
// ioctl(fd,I2C_SLAVE_FORCE,I2C_ADDRESS);
// i2c_kernel_write(fd, 0x00, 0x55);
// REG_VALUE = i2c_kernel_read(fd, 0x00);
// printf("REG_VALUE: %d\n", REG_VALUE);close(fd);
return 0;
}