从kernel/drivers/i2c/busses/i2c-rockchip.c开始分析
根据I2C总线模型,首先分配一个 rockchip_i2c_driver 结构体
static const struct of_device_id rockchip_i2c_of_match[] = {
{ .compatible = "rockchip,rk30-i2c", .data = NULL, },
{},
};
MODULE_DEVICE_TABLE(of, rockchip_i2c_of_match);
static struct platform_driver rockchip_i2c_driver = {
.probe = rockchip_i2c_probe,
.remove = rockchip_i2c_remove,
.driver = {
.owner = THIS_MODULE,
.name = "rockchip_i2c",
.pm = ROCKCHIP_I2C_PM_OPS,
.of_match_table = of_match_ptr(rockchip_i2c_of_match),
},
};
与设备树匹配成功后调用probe函数
subsys_initcall(rockchip_i2c_init_driver);
subsys_initcall(rockchip_i2c_init_driver);
platform_driver_register(&rockchip_i2c_driver);
static int rockchip_i2c_probe(struct platform_device *pdev)
i2c = devm_kzalloc(&pdev->dev, sizeof(struct rockchip_i2c), GFP_KERNEL);//分配内存
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2c->regs = devm_ioremap_resource(&pdev->dev, res); //获取资源
===================================================================================
ret = i2c_add_adapter(&i2c->adap); //这个比较重要
i2c_register_adapter(adapter);
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
list_for_each_entry(devinfo, &__i2c_board_list, list) //遍历链表
//遍历一个一个从机准备的信息,匹配busnum ,成功后调用i2c_new_device来创建 i2c_client
//i2c->adap.nr = i2c->pdata->bus_num
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,
&devinfo->board_info))
client = kzalloc(sizeof *client, GFP_KERNEL); //分配内存
===================================================================================
struct i2c_client
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 i2c_driver *driver; /* and our access routines
struct device dev; /* the device structure
int irq; /* irq issued by device
struct list_head detected;
};
在简单分析i2c_new_device
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
int status;
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
client->adapter = adap;
//info 从机真正的信息
client->dev.platform_data = info->platform_data;
if (info->archdata)
client->dev.archdata = *info->archdata;
client->flags = info->flags;
client->addr = info->addr; //从机地址
client->irq = info->irq; //从机对应的外部中断号或者外部中断对应的GPIO
strlcpy(client->name, info->type, sizeof(client->name)); //与匹配相关的名字
/* Check for address validity */
status = i2c_check_client_addr_validity(client);
if (status) {
dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
goto out_err_silent;
}
/* Check for address business */
#if 0
status = i2c_check_addr_busy(adap, client->addr);
if (status)
goto out_err;
#else
/* ddl@rock-chips.com : Devices which have some i2c addr can work in same i2c bus,
if devices havn't work at the same time.*/
status = i2c_check_addr_ex(adap, client->addr);
if (status != 0)
dev_err(&adap->dev, "%d i2c clients have been registered at 0x%02x",
status, client->addr);
#endif
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type; //匹配成功后调用i2c_device_probe
//查看匹配规则 后面列出
static int i2c_device_probe(struct device *dev)
{
struct i2c_client *client = i2c_verify_client(dev);
struct i2c_driver *driver;
int status;
if (!client)
return 0;
driver = to_i2c_driver(dev->driver);
if (!driver->probe || !driver->id_table)
return -ENODEV;
client->driver = driver;
if (!device_can_wakeup(&client->dev))
device_init_wakeup(&client->dev,
client->flags & I2C_CLIENT_WAKE);
dev_dbg(dev, "probe\n");
status = driver->probe(client, i2c_match_id(driver->id_table, client)); //匹配的最后一个有个哨兵
if (status) {
client->driver = NULL;
i2c_set_clientdata(client, NULL);
}
return status;
}
client->dev.type = &i2c_client_type;
client->dev.of_node = info->of_node;
ACPI_HANDLE_SET(&client->dev, info->acpi_node.handle);
/* For 10-bit clients, add an arbitrary offset to avoid collisions */
/* ddl@rock-chips.com : Devices which have some i2c addr can work in same i2c bus,
if devices havn't work at the same time.*/
#if 0
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
client->addr | ((client->flags & I2C_CLIENT_TEN)
? 0xa000 : 0));
#else
status = device_register(&client->dev); //注册
if (status)
goto out_err;
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
client->name, dev_name(&client->dev));
return client;
}
EXPORT_SYMBOL_GPL(i2c_new_device);
匹配规则:i2c_device_match
struct rockchip_i2c {
spinlock_t lock;
wait_queue_head_t wait;
unsigned int suspended:1;
struct i2c_msg *msg; //IIC要传输的数据,
unsigned int is_busy;
int error;
unsigned int msg_ptr;
unsigned int irq; //中断号
enum rockchip_i2c_state state;
unsigned int complete_what;
unsigned long clkrate;
void __iomem *regs; //通过platform_get_resource拿到物理基地址,映射完后赋值
struct clk *clk;
struct device *dev;
struct resource *ioarea;
struct i2c_adapter adap; //读写数据的算法
struct mutex suspend_lock;
unsigned long scl_rate;
unsigned long i2c_rate;
unsigned int addr;
unsigned char addr_1st, addr_2nd;
unsigned int mode;
unsigned int count;
unsigned int check_idle;
int sda_gpio, scl_gpio;
struct pinctrl_state *gpio_state;
};
struct i2c_msg
struct i2c_msg {
__u16 addr;
__u16 flags;
#define I2C_M_TEN 0x10
#define I2C_M_RD 0x01
#define I2C_M_NOSTART 0x4000
#define I2C_M_REV_DIR_ADDR 0x2000
#define I2C_M_IGNORE_NAK 0x1000
#define I2C_M_NO_RD_ACK 0x0800
__u16 len;
__u8 *buf;
};
总结:系统维护了一个__i2c_board_list为头结点的双向循环链表。