基于RK3399的Linux驱动开发 -- I2C驱动框架

文章目录

  • 一、概述
  • 二、core
    • 1、core接口
      • `i2c_init`
      • `i2c_device_match`
      • `i2c_device_probe`
    • 2、对接adapter的接口
      • `i2c_adapter`
      • `i2c_client`
      • `i2c_add_adapter`
      • `__i2c_add_numbered_adapter`
      • `i2c_register_adapter`
      • `of_i2c_register_devices`
      • `of_i2c_register_device`
      • `i2c_new_device`
    • 3、对接client的接口
      • `i2c_driver`
      • `i2c_register_driver`
  • 三、adapter
      • `rk3x_i2c_soc_data`
      • `rk3x_i2c`
      • adapter驱动骨架
      • `rk3x_i2c_probe`
  • 四、client
      • client驱动骨架
      • `module_i2c_driver`
  • 五、时序图
    • 1、i2c总线注册
    • 2、adapter注册
    • 3、client驱动注册
    • 4、i2c_client和i2c_driver匹配

一、概述

本文讲述如何从源代码分析i2c驱动架构,i2c的主体包含i2c总线、adapter设备和client设备。总线由i2c-core.c实现;adapter是具体的芯片i2c控制器,rk3399的adapter驱动由i2c-rk3x.c实现;client则是具体的i2c设备,如使用i2c通信的电源管理芯片rk808的驱动为rk808.c。

二、core

core主要实现一些与具体soc平台和具体i2c设备无关的接口,如adapter的注册、i2c设备注册、i2c总线初始化、和i2c设备读写功能等。读写通过底层回调接口实现。

1、core接口

i2c_init

这个接口主要向系统初始化i2c总线,设置总线操作的一些回调

struct bus_type i2c_bus_type = {
	.name		= "i2c",
	.match		= i2c_device_match,
	.probe		= i2c_device_probe,
	.remove		= i2c_device_remove,
	.shutdown	= i2c_device_shutdown,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);

static int __init i2c_init(void)
{
	int retval;

	/* 注册i2c总线 */
	retval = bus_register(&i2c_bus_type);
}

i2c_device_match

这个回调主要用于匹配i2c总线上的设备和驱动

static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
	/* 获取对应的i2c_client */
	struct i2c_client	*client = i2c_verify_client(dev);
	struct i2c_driver	*driver;
	
	/* 使用of_match_table尽心匹配, */
	if (of_driver_match_device(dev, drv))
		return 1;
	
	/* 获取i2c驱动实例 */
	driver = to_i2c_driver(drv);
	/* 使用dts具体i2c设备节点的compatible属性后半部和i2c_driver的id_table对应字段做对比,一样就返回真。
	如rk808的compatible为"rockchip,rk808",而rk808驱动的id_table包含"rk808" */
	if (driver->id_table)
		return i2c_match_id(driver->id_table, client) != NULL;		
}

i2c_device_probe

这个回调主要在驱动和设备匹配成功后,调用驱动的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->irq) {
		int irq = -ENOENT;
		
		if (dev->of_node) {
			irq = of_irq_get_byname(dev->of_node, "irq");
		}
		/* 获取中断源 */
		client->irq = irq;
	}
	
	/* 获取client的驱动, 并执行驱动的probe回调,比如rk808的rk808_probe函数 */
	driver = to_i2c_driver(dev->driver);
	status = driver->probe(client, i2c_match_id(driver->id_table, client));	
}

2、对接adapter的接口

i2c_adapter

这个结构描述一个i2c适配器,指的就是soc上的具体i2c控制器,控制器驱动需要实现i2c读写时需要用到的核心算法回调

struct i2c_adapter {
	const struct i2c_algorithm *algo; /* the algorithm to access the bus */
	void *algo_data;
	struct device dev;		/* the adapter device */
};

i2c_client

这个结构描述具体的i2c设备,它一般会包含地址和关联的适配器指针

struct i2c_client {
	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		*/				
};

i2c_add_adapter

这个接口实现如何向i2c总线添加一个适配器,并根据dts向总线添加一些列的i2c设备

int i2c_add_adapter(struct i2c_adapter *adapter)
{
	if (dev->of_node) {
		/* 根据dts的alias获取后面的索引数字,可以查看rk3399.dtsi的alias定义 */
		id = of_alias_get_id(dev->of_node, "i2c");
		if (id >= 0) {
			adapter->nr = id;
			return __i2c_add_numbered_adapter(adapter);
		}
	}
}

__i2c_add_numbered_adapter

static int __i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
	return i2c_register_adapter(adap);
}

i2c_register_adapter

该函数首先注册adapter设备,并根据adapter的dts节点描述遍历adapter上挂载的i2c设备,并一一添加到i2c总线

static int i2c_register_adapter(struct i2c_adapter *adap)
{
	/* 设置设备名称 */
	dev_set_name(&adap->dev, "i2c-%d", adap->nr);
	/* 把adapter设备注册到i2c总线,因为adapter不是client所以,匹配总是失败 */
	adap->dev.bus = &i2c_bus_type;
	adap->dev.type = &i2c_adapter_type;
	res = device_register(&adap->dev);
	
	/* 根据dts描述注册adapter上的所有i2c设备 */
	of_i2c_register_devices(adap);	
}

of_i2c_register_devices

static void of_i2c_register_devices(struct i2c_adapter *adap)
{
	struct device_node *node;
	
	for_each_available_child_of_node(adap->dev.of_node, node) {
		if (of_node_test_and_set_flag(node, OF_POPULATED))
			continue;
		/* 注册单个i2c设备 */	
		of_i2c_register_device(adap, node);
	}
}

of_i2c_register_device

static struct i2c_client *of_i2c_register_device(struct i2c_adapter *adap,
						 struct device_node *node)
{
	struct i2c_client *result;
	struct i2c_board_info info = {};
	struct dev_archdata dev_ad = {};
	const __be32 *addr_be;
	u32 addr;
	
	/* 解析设备名字 */
	if (of_modalias_node(node, info.type, sizeof(info.type)) < 0) {
	}
	
	/* 解析地址 */
	addr_be = of_get_property(node, "reg", &len);
	addr = be32_to_cpup(addr_be);

	info.addr = addr;
	info.of_node = of_node_get(node);
	info.archdata = &dev_ad;
	
	/* 创建新的i2c client并注册到i2c总线 */
	result = i2c_new_device(adap, &info);
}

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);
	/* 关联client设备挂载的adapter设备 */
	client->adapter = adap;
	/* 设置地址 */
	client->addr = info->addr;
	/* 设置中断源*/
	client->irq = info->irq;
	/* 设置父设备节点为关联的adapter设备 */
	client->dev.parent = &client->adapter->dev;
	client->dev.bus = &i2c_bus_type;
	/* 设置类型,i2c_verify_client要用 */
	client->dev.type = &i2c_client_type;
	client->dev.of_node = info->of_node;
	/* 正式注册到i2c总线 */
	status = device_register(&client->dev);
}

3、对接client的接口

i2c_driver

这个结构描述一个i2c驱动,是我们主要需要实现和填充的

struct i2c_driver {
	int (*probe)(struct i2c_client *, const struct i2c_device_id *);
	int (*remove)(struct i2c_client *);
	struct device_driver driver;
	const struct i2c_device_id *id_table;
};

i2c_register_driver

这个接口实现如何向i2c总线注册一个i2c驱动

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
	int res;

	driver->driver.owner = owner;
	/* 设置其关联i2c总线 */
	driver->driver.bus = &i2c_bus_type;
	/* 一个驱动可以服务相同类型的设备 */
	INIT_LIST_HEAD(&driver->clients);
	/* 正式向i2c总线注册i2c client设备 */
	res = driver_register(&driver->driver);
}

三、adapter

adapter的驱动以rk3399的i2c为例子

rk3x_i2c_soc_data

struct rk3x_i2c_soc_data {
	int grf_offset;
	int (*calc_timings)(unsigned long, struct i2c_timings *,
			    struct rk3x_i2c_calced_timings *);
};

rk3x_i2c

struct rk3x_i2c {
	struct i2c_adapter adap;
	struct device *dev;
	struct rk3x_i2c_soc_data *soc_data;
};

adapter驱动骨架

static const struct i2c_algorithm rk3x_i2c_algorithm = {
	/* 具体的i2c数据传输回调函数 */
	.master_xfer		= rk3x_i2c_xfer,
	.functionality		= rk3x_i2c_func,
};

static const struct rk3x_i2c_soc_data rk3399_soc_data = {
	.grf_offset = -1,
	.calc_timings = rk3x_i2c_v1_calc_timings,
};

static const struct of_device_id rk3x_i2c_match[] = {
	{	
		.compatible = "rockchip,rk3399-i2c",
		.data = (void *)&rk3399_soc_data
	},
	{},
};

static struct platform_driver rk3x_i2c_driver = {
	.probe   = rk3x_i2c_probe,
	.remove  = rk3x_i2c_remove,
	.driver  = {
		.name  = "rk3x-i2c",
		.of_match_table = rk3x_i2c_match,
		.pm = &rk3x_i2c_pm_ops,
	},
};

module_platform_driver(rk3x_i2c_driver);

rk3x_i2c_probe

static int rk3x_i2c_probe(struct platform_device *pdev)
{
	struct rk3x_i2c *i2c;
	struct resource *mem;
	int ret = 0;
	int irq;
	
	i2c = devm_kzalloc(&pdev->dev, sizeof(struct rk3x_i2c), GFP_KERNEL);
	/* 匹配具体平台后设置soc_data */
	match = of_match_node(rk3x_i2c_match, np);
	i2c->soc_data = (struct rk3x_i2c_soc_data *)match->data;
	/* 实现i2c总线算法 */
	i2c->adap.algo = &rk3x_i2c_algorithm;
	/* 获取并映射寄存器资源 */
	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
	/* 获取中断并设置中断处理函数 */
	irq = platform_get_irq(pdev, 0);
	ret = devm_request_irq(&pdev->dev, irq, rk3x_i2c_irq,
			       0, dev_name(&pdev->dev), i2c);
	/* 获取和准备时钟 */		       
	if (i2c->soc_data->calc_timings == rk3x_i2c_v0_calc_timings) {
	} else {
		i2c->clk = devm_clk_get(&pdev->dev, "i2c");
		i2c->pclk = devm_clk_get(&pdev->dev, "pclk");	
	}
	ret = clk_prepare(i2c->clk);
	ret = clk_prepare(i2c->pclk);
	
	/* 向core层添加adapter */
	ret = i2c_add_adapter(&i2c->adap);
}

四、client

client部分的驱动以rk808电源管理芯片为例子

client驱动骨架

static const struct of_device_id rk808_of_match[] = {
	/* 跟dts文件的i2c上挂载的设备节点一致 */
	{ .compatible = "rockchip,rk808" },
	{ },
};
MODULE_DEVICE_TABLE(of, rk808_of_match);

static const struct i2c_device_id rk808_ids[] = {
	{ "rk808" },
	{ },
};
MODULE_DEVICE_TABLE(i2c, rk808_ids);

static struct i2c_driver rk808_i2c_driver = {
	.driver = {
		.name = "rk808",
		.of_match_table = rk808_of_match,
		.pm = &rk808_pm_ops,
	},
	.probe    = rk808_probe,
	.remove   = rk808_remove,
	.id_table = rk808_ids,
};

module_i2c_driver(rk808_i2c_driver);

module_i2c_driver

这个宏展开后最终会调用i2c_register_driver向i2c总线添加i2c_driver

#define module_i2c_driver(__i2c_driver) \
	module_driver(__i2c_driver, i2c_add_driver, \
			i2c_del_driver)

#define module_driver(__driver, __register, __unregister, ...) \
static int __init __driver##_init(void) \
{ \
	return __register(&(__driver) , ##__VA_ARGS__); \
} \
module_init(__driver##_init); \
static void __exit __driver##_exit(void) \
{ \
	__unregister(&(__driver) , ##__VA_ARGS__); \
} \
module_exit(__driver##_exit);

#define i2c_add_driver(driver) \
	i2c_register_driver(THIS_MODULE, driver)

五、时序图

1、i2c总线注册

i2c_core base/bus bus_register 向系统注册i2c 总线,设置mat ch和probe回调 i2c_core base/bus

2、adapter注册

i2c_rk3x i2c_core base/core 获取寄存器、中断和时钟资源 i2c_add_adapter 向i2c总线注册 adapter设备 __i2c_add_numbered_adapter i2c_register_adapter device_register 正式把adapt er设备注册到总线 of_i2c_register_devices of_i2c_register_device i2c_new_device device_register 把adapter上 挂载的设备注册到总 线 i2c_rk3x i2c_core base/core

3、client驱动注册

rk808 i2c_core base/dirver i2c_register_driver 向i2c总线注册c lient设备的驱动 driver_register 这里触发系统驱动框架 调用i2c总线mat ch和probe回调 rk808 i2c_core base/dirver

4、i2c_client和i2c_driver匹配

通过driver_register注册i2c_driver会触发系统驱动框架调用i2c总线的match和probe

base/dd i2c_core of/device rk808 i2c_device_match of_match_device i2c_match_id 对比i2c_driv er的id_tabl es只有存在才返回真 i2c_device_probe dd通过driver_p robe_device调用 rk808_probe base/dd i2c_core of/device rk808

你可能感兴趣的:(Linux驱动开发)