前言

i2c协议是由 数据线SDA和时钟线SCL构成的串行总线,是半双工通信方式,一个I2c接口可以挂载多个从设备,但是每个从设备都会有唯一的器件地址, I2C速度在标准模式下为100k,快速模式下可以达到400k,高速模式下可以达到3.4M

时序介绍

  1. 空闲状态
    SDA与SCL两条信号线同时处于高电平状态

  2. 起始信号
    SCL为高的时候 SDA为下降沿
    在这里插入图片描述

  3. 停止信号
    SCL为高的时候 SDA为上升沿
    在这里插入图片描述

  4. 数据传输
    SCL为低时候 SDA数据可以变化。SCL为高的时候要求数据稳定

  5. 应答信号
    I2C发送了八位数据之后,接收方就会输出一个ACK信号,当SDA为高时,表示非应答信号为NACK,当SDA为低电平时 表示为ACK信号。收到最后一个字节时候可以不发送ACK,直接停止信号。

一般就是: START+(ADDR+RW)(7+1位)+ ACK + DATA(8位) +ACK +DATA(8位) +ACK + STOP
ADDR是I2C的器件地址一般为7位 第八位RW 为读写位 1为读 0为写

linux I2C结构

在之前platform中,说到 linux内核驱动通过总线进行设备和驱动进行匹配,platform 是虚拟出来的一条总线,而I2C是一条实际的总线。

I2C核心

I2C核心层 提供了总线驱动和设备驱动注册,注销,通信方法等

I2C总线驱动

是针对硬件结构中适配器端的实现,可由CPU控制。包括适配器数据结构i2c_adapter和适配器的通信方法数据结构i2c_algorithm以及控制I2C适配器产生通信信号的函数,经由总线驱动的代码,适配器可以产生I2C时序。

I2C设备驱动

针对具体外围器件,根据I2C总线适配器去与器件进行通信,交换数据等。I2C设备驱动主要包含数据结构i2c_driver和i2c_client, 我们需要根据具体设备实现其中的成员函数。

I2C数据结构介绍

i2c_adapter

i2c_adapter对应于物理上的一个适配器

struct i2c_adapter {
	struct module *owner;	//所有者
	unsigned int class;		  //适配器支持的从设备的类型
	const struct i2c_algorithm *algo; //适配器与从设备的通信算法
	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;		//适配器设备对应的device

	int nr;//适配器编号
	char name[48];//适配器名字
	struct completion dev_released;//完成量

	struct mutex userspace_clients_lock;
	struct list_head userspace_clients;//挂载成功的从设备i2c_client的一个链表头

	struct i2c_bus_recovery_info *bus_recovery_info;//i2c总线恢复信息
	const struct i2c_adapter_quirks *quirks;
};

i2c_algorithm

i2c_algorithm对应一套通信方法master_xfer() 用于产生I2C访问周期需要的信号, 以i2c_msg(即I2C消息) 为单位

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, set
	   smbus_xfer. If set to NULL, the SMBus protocol is simulated
	   using common I2C messages */
	/* master_xfer should return the number of messages successfully
	   processed, or a negative value on error */
	 //I2C读写通信方法函数指针
	int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
			   int num);
	//smbus协议通信方法函数指针,与I2C总线相比, 在访问时序上也有一定的差异
	int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
			   unsigned short flags, char read_write,
			   u8 command, int size, union i2c_smbus_data *data);

	//确定适配器要支持什么通信方法
	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
};
struct i2c_msg {
	__u16 addr;	/*从设备地址*/
	__u16 flags;/*标志位 下面这一堆*/
#define I2C_M_TEN		0x0010	/* 表示从设备10位地址*/
#define I2C_M_RD		0x0001	/* 表示本次通信i2c控制器是处于接收方,否则就是发送方*/
#define I2C_M_STOP		0x8000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_NOSTART */
#define I2C_M_REV_DIR_ADDR	0x2000	/* 表示需要将读写标志位反转过来 */
#define I2C_M_IGNORE_NAK	0x1000	/* 意味当前i2c_msg忽略I2C器件的ack和nack信号 */
#define I2C_M_NO_RD_ACK		0x0800	/* 表示在读操作中主机不用ACK  */
#define I2C_M_RECV_LEN		0x0400	/* length will be first received byte */
	__u16 len;		/* 数据长度	*/
	__u8 *buf;		/* 数据缓冲区指针 */
};

i2c_driver

i2c_driver对应于一套驱动方法,一个i2c_driver可以支持多个同类型的i2c_client。

struct i2c_driver {
	unsigned int class;//i2c设备驱动所支持的i2c设备的类型  

	/* 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;//用来匹配适配器的函数 adapter

	/* Standard driver model interfaces */
	int (*probe)(struct i2c_client *, const struct i2c_device_id *);// 设备驱动层的probe函数
	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;//i2c设备驱动所对应的device_driver
	const struct i2c_device_id *id_table;//设备驱动层用来匹配设备的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;
};

i2c_client

struct i2c_client {
	unsigned short flags;//  描述i2c从设备特性的标志位  
	unsigned short addr;//i2c 从设备的地址
					/* addresses are stored in the	*/
					/* _LOWER_ 7 bits		*/
	char name[I2C_NAME_SIZE];//从设备名字
	struct i2c_adapter *adapter;//匹配成功的适配器
	struct device dev;//从设备对应的device
	int irq;//从设备中断引脚
	struct list_head detected;
#if IS_ENABLED(CONFIG_I2C_SLAVE)
	i2c_slave_cb_t slave_cb;//从设备模式
#endif
};

i2c_board_info

struct i2c_board_info {
	char		type[I2C_NAME_SIZE];
	unsigned short	flags;//用来初始化i2c_client.flags
	unsigned short	addr;//用来初始化 i2c_client.addr
	void		*platform_data;
	struct dev_archdata	*archdata;
	struct device_node *of_node;
	struct fwnode_handle *fwnode;
	int		irq;//用来初始化i2c_client.irq
};

I2C驱动体系流程

  1. 初始化I2C适配器(入口地址 中断号等),驱动CPU控制适配器从硬件上产生信号 以及处理信号
  2. 编写I2C适配器的通信方法 xxx_xfer函数填充master_xfer数据结构的master_xfer指针,并把该数据结构放到i2c_adapter的algo指针上。
  3. 实现I2C设备驱动中的i2c_driver接口。将设备挂载在总线上
  4. 实现I2C设备所对应类型的具体驱动

i2c-core.c

核心函数
/*
 *添加删除I2C适配器	
 */
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
int i2c_add_adapter(struct i2c_adapter *adap);
void i2c_del_adapter(struct i2c_adapter *adap);
/*
 * 添加删除I2C设备驱动
 */
int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
void i2c_del_driver(struct i2c_driver *driver);
#define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE, driver)
/*
	I2C通信方法
*/
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num);
int i2c_master_send(struct i2c_client *client,const char *buf ,int count);//只读一次,底层是i2c_transfer + I2C_msg实现的
int i2c_master_recv(struct i2c_client *client, char *buf ,int count);//只写一次,底层是i2c_transfer + I2C_msg实现的

总线注册

struct bus_type i2c_bus_type = {
	.name		= "i2c",
	.match		= i2c_device_match,
	.probe		= i2c_device_probe,
	.remove		= i2c_device_remove,
	.shutdown	= i2c_device_shutdown,
};
match函数
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
	struct i2c_client	*client = i2c_verify_client(dev);
	struct i2c_driver	*driver;

	if (!client)
		return 0;

	/* Attempt an OF style match */
	if (of_driver_match_device(dev, drv))
		return 1;

	/* Then ACPI style match */
	if (acpi_driver_match_device(dev, drv))
		return 1;

	driver = to_i2c_driver(drv);
	/* match on an id table if there is one */
	if (driver->id_table)
		return i2c_match_id(driver->id_table, client) != NULL;

	return 0;
}

probe函数
static int i2c_device_probe(struct device *dev)
{
	struct i2c_client	*client = i2c_verify_client(dev); //  通过device指针获取到对应的i2c_client指针
	struct i2c_driver	*driver;
	int status;

	if (!client)
		return 0;
	/*获取中断*/
	if (!client->irq && dev->of_node) {
		int irq = of_irq_get(dev->of_node, 0);

		if (irq == -EPROBE_DEFER)
			return irq;
		if (irq < 0)
			irq = 0;

		client->irq = irq;
	}

	driver = to_i2c_driver(dev->driver);//通过device->driver指针获取到对应的i2c_driver指针
	if (!driver->probe || !driver->id_table)
		return -ENODEV;

	if (!device_can_wakeup(&client->dev))
		device_init_wakeup(&client->dev,
					client->flags & I2C_CLIENT_WAKE);
	dev_dbg(dev, "probe\n");

	status = of_clk_set_defaults(dev->of_node, false);
	if (status < 0)
		return status;

	status = dev_pm_domain_attach(&client->dev, true);
	if (status != -EPROBE_DEFER) {
	/*调用设备驱动层的probe函数*/
		status = driver->probe(client, i2c_match_id(driver->id_table,
					client));
		if (status)
			dev_pm_domain_detach(&client->dev, true);
	}

	return status;
}
i2c_register_adapter函数
static int i2c_register_adapter(struct i2c_adapter *adap)
{
	int res = 0;

	/*驱动模型初始化之后才能注册*/
	if (unlikely(WARN_ON(!i2c_bus_type.p))) {
		res = -EAGAIN;
		goto out_list;
	}

	/*i2c_adapter(适配器)没名字*/
	if (unlikely(adap->name[0] == '\0')) {
		pr_err("i2c-core: Attempt to register an adapter with "
		       "no name!\n");
		return -EINVAL;
	}
	/*i2c_adapter(适配器)没设置通信方法*/
	if (unlikely(!adap->algo)) {
		pr_err("i2c-core: Attempt to register adapter '%s' with "
		       "no algo!\n", adap->name);
		return -EINVAL;
	}

	rt_mutex_init(&adap->bus_lock);
	mutex_init(&adap->userspace_clients_lock);
	INIT_LIST_HEAD(&adap->userspace_clients);//初始化i2c_adapter->userspace_clients链表

	/* 设置超时时间为1S */
	if (adap->timeout == 0)
		adap->timeout = HZ;
	/*适配器设备名 在/sys/bus/i2c/devices中可以看到*/
	dev_set_name(&adap->dev, "i2c-%d", adap->nr);
	adap->dev.bus = &i2c_bus_type;//设置设备的总线类型
	adap->dev.type = &i2c_adapter_type;//设置设备的设备类型
	res = device_register(&adap->dev);//注册设备  如果前面没有指定父设备那么创建的设备文件是: /sys/devices/i2c-%d
	if (res)
		goto out_list;

	dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);

	pm_runtime_no_callbacks(&adap->dev);

#ifdef CONFIG_I2C_COMPAT
	res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
				       adap->dev.parent);
	if (res)
		dev_warn(&adap->dev,
			 "Failed to create compatibility class link\n");
#endif
	...
exit_recovery:
	/* create pre-declared device nodes */
	of_i2c_register_devices(adap);
	acpi_i2c_register_devices(adap);
	acpi_i2c_install_space_handler(adap);

	if (adap->nr < __i2c_first_dynamic_bus_num)
		i2c_scan_static_board_info(adap);//扫描__i2c_board_list链表上挂接的所有的i2c从设备信息并与适配器进行匹配,匹配成功创建从设备

	/* Notify drivers */
	mutex_lock(&core_lock);
	bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
	mutex_unlock(&core_lock);

	return 0;

out_list:
	mutex_lock(&core_lock);
	idr_remove(&i2c_adapter_idr, adap->nr);
	mutex_unlock(&core_lock);
	return res;
}
i2c_scan_static_board_info函数
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
	struct i2c_devinfo	*devinfo;

	down_read(&__i2c_board_lock);
	/*遍历 __i2c_board_list 链表上的所有i2c_devinfo 结构体*/
	list_for_each_entry(devinfo, &__i2c_board_list, list) { 
		if (devinfo->busnum == adapter->nr/*比较 i2c_devinfo->busnum 与 适配器的编号是否匹配*/
				/*如果匹配就会调用 i2c_new_device 函数进行注册添加新的次设备 i2c_client*/
				&& !i2c_new_device(adapter,
						&devinfo->board_info))
			dev_err(&adapter->dev,
				"Can't create device at 0x%02x\n",
				devinfo->board_info.addr);
	}
	up_read(&__i2c_board_lock);
}
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;
	/* 对i2c_client结构体变量进行填充*/
	client->adapter = adap;//从设备通过client->adapter指针去指向与它匹配成功的适配器i2c_adapter
	client->dev.platform_data = info->platform_data;//将传进来的i2c_board_info结构体作为i2c从设备的platform平台数据
	if (info->archdata)
		client->dev.archdata = *info->archdata;
	client->flags = info->flags;//从设备标志位
	client->addr = info->addr;//从设备地址
	client->irq = info->irq;//中断号

	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 */
	status = i2c_check_addr_busy(adap, client->addr);
	if (status)
		goto out_err;

	client->dev.parent = &client->adapter->dev;//指定从设备的爹是与它成功匹配的适配器对应的设备
	client->dev.bus = &i2c_bus_type;//从设备总线类型
	client->dev.type = &i2c_client_type;//从设备设备类型
	client->dev.of_node = info->of_node;//设备树节点
	client->dev.fwnode = info->fwnode;

	i2c_dev_set_name(adap, client);//次设备名字,/sys/bus/i2c/devices/i2c-0/中可查看挂载在适配器0下的设备名字
	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;

out_err:
	dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
		"(%d)\n", client->name, client->addr, status);
out_err_silent:
	kfree(client);
	return NULL;
}
i2c_register_driver函数
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
	int res;

	/* 驱动模型初始化之后才能注册 */
	if (unlikely(WARN_ON(!i2c_bus_type.p)))
		return -EAGAIN;

	/* add the driver to the list of i2c drivers in the driver core */
	driver->driver.owner = owner;//标定从设备所属模块
	driver->driver.bus = &i2c_bus_type;//

	/* When registration returns, the driver core
	 * will have called probe() for all matching-but-unbound devices.
	 */
	res = driver_register(&driver->driver);//注册从设备
	if (res)
		return res;

	pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
	//把这个I2C设备添加到i2c_scan_static_board_info所查询的链表中
	INIT_LIST_HEAD(&driver->clients);
	/* Walk the adapters that are already present */
	i2c_for_each_dev(driver, __process_new_driver);

	return 0;
}

i2c-imx.c

正点原子IMX6ULL开发板的 i2c的适配器驱动由恩智浦编写好了

	i2c1: i2c@021a0000 {
		#address-cells = <1>;
		#size-cells = <0>;
		compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
		reg = <0x021a0000 0x4000>;
		interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
		clocks = <&clks IMX6UL_CLK_I2C1>;
		status = "disabled";
	};

根据i2c的compatible 的"fsl,imx21-i2c"可以搜到 恩智浦的I2C适配器驱动在drivers/i2c/busses/i2c-imx.c中编写。

代码分析
static struct platform_device_id imx_i2c_devtype[] = {
	{
		.name = "imx1-i2c",
		.driver_data = (kernel_ulong_t)&imx1_i2c_hwdata,
	}, {
		.name = "imx21-i2c",
		.driver_data = (kernel_ulong_t)&imx21_i2c_hwdata,
	}, {
		/* sentinel */
	}
};
MODULE_DEVICE_TABLE(platform, imx_i2c_devtype);

static const struct of_device_id i2c_imx_dt_ids[] = {
	{ .compatible = "fsl,imx1-i2c", .data = &imx1_i2c_hwdata, },
	{ .compatible = "fsl,imx21-i2c", .data = &imx21_i2c_hwdata, },
	{ .compatible = "fsl,vf610-i2c", .data = &vf610_i2c_hwdata, },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, i2c_imx_dt_ids);
static struct platform_driver i2c_imx_driver = {
	.probe = i2c_imx_probe,
	.remove = i2c_imx_remove,
	.driver	= {
		.name = DRIVER_NAME,
		.owner = THIS_MODULE,
		.of_match_table = i2c_imx_dt_ids,
		.pm = IMX_I2C_PM,
	},
	.id_table	= imx_i2c_devtype,
};

static int __init i2c_adap_imx_init(void)
{
	return platform_driver_register(&i2c_imx_driver);
}
subsys_initcall(i2c_adap_imx_init);//module_init是因为编译在内核中的模块必须按照指定顺序进行加载在include/linux/init.h定义

static void __exit i2c_adap_imx_exit(void)
{
	platform_driver_unregister(&i2c_imx_driver);
}
module_exit(i2c_adap_imx_exit);

由此可见i2c适配器驱动也是挂载在标准platform上的,当从设备树中compatible 中匹配成功就运行probe函数

i2c_imx_probe
static int i2c_imx_probe(struct platform_device *pdev)
{
	const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids,
							   &pdev->dev);
	struct imx_i2c_struct *i2c_imx;
	struct resource *res;
	struct imxi2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
	void __iomem *base;
	int irq, ret;
	dma_addr_t phy_addr;

	dev_dbg(&pdev->dev, "<%s>\n", __func__);

	irq = platform_get_irq(pdev, 0);//获取中断号
	if (irq < 0) {
		dev_err(&pdev->dev, "can't get irq number\n");
		return irq;
	}
	/*获取I2C寄存器的基地址*/
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	/*映射I2C寄存器的基地址的虚拟地址*/
	base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(base))
		return PTR_ERR(base);

	phy_addr = (dma_addr_t)res->start;
	i2c_imx = devm_kzalloc(&pdev->dev, sizeof(*i2c_imx), GFP_KERNEL);//为定义的适配器设备结构体进行分配内存
	if (!i2c_imx)
		return -ENOMEM;

	if (of_id)
		i2c_imx->hwdata = of_id->data;
	else
		i2c_imx->hwdata = (struct imx_i2c_hwdata *)
				platform_get_device_id(pdev)->driver_data;

	/* Setup i2c_imx driver structure */
	strlcpy(i2c_imx->adapter.name, pdev->name, sizeof(i2c_imx->adapter.name));//适配器名字
	i2c_imx->adapter.owner		= THIS_MODULE;
	i2c_imx->adapter.algo		= &i2c_imx_algo;//通信方法
	i2c_imx->adapter.dev.parent	= &pdev->dev;
	i2c_imx->adapter.nr		= pdev->id;//适配器编号
	i2c_imx->adapter.dev.of_node	= pdev->dev.of_node;
	i2c_imx->base			= base;//基地址映射的虚拟地址

	/* 获取I2C 时钟*/
	i2c_imx->clk = devm_clk_get(&pdev->dev, NULL);
	if (IS_ERR(i2c_imx->clk)) {
		dev_err(&pdev->dev, "can't get I2C clock\n");
		return PTR_ERR(i2c_imx->clk);
	}

	ret = clk_prepare_enable(i2c_imx->clk);
	if (ret) {
		dev_err(&pdev->dev, "can't enable I2C clock\n");
		return ret;
	}
	/* 申请中断*/
	ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr,
			       IRQF_NO_SUSPEND, pdev->name, i2c_imx);
	if (ret) {
		dev_err(&pdev->dev, "can't claim irq %d\n", irq);
		goto clk_disable;
	}

	/*初始化等待队列头,*/
	init_waitqueue_head(&i2c_imx->queue);

	/* 设置适配器的数据*/
	i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);

	/* 设置I2C通信速率为100K*/
	i2c_imx->bitrate = IMX_I2C_BIT_RATE;
	ret = of_property_read_u32(pdev->dev.of_node,
				   "clock-frequency", &i2c_imx->bitrate);
	if (ret < 0 && pdata && pdata->bitrate)
		i2c_imx->bitrate = pdata->bitrate;

	/* 初始化I2C1的控制寄存器*/
	imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
			i2c_imx, IMX_I2C_I2CR);
	imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);

	/* 添加一个I2C适配器 */
	ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
	if (ret < 0) {
		dev_err(&pdev->dev, "registration failed\n");
		goto clk_disable;
	}

	/* Set up platform driver data */
	platform_set_drvdata(pdev, i2c_imx);
	clk_disable_unprepare(i2c_imx->clk);

	dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);
	dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res);
	dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",
		i2c_imx->adapter.name);
	dev_info(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n");

	/*申请一个DMA */
	i2c_imx_dma_request(i2c_imx, phy_addr);

	return 0;   /* Return OK */

clk_disable:
	clk_disable_unprepare(i2c_imx->clk);
	return ret;
}
i2c_imx_algo
static struct i2c_algorithm i2c_imx_algo = {
	.master_xfer	= i2c_imx_xfer,
	.functionality	= i2c_imx_func,
};

设置完后,drivers/i2c/i2c-core.c中的__i2c_transfer中调用这个master_xfer 函数指针时候就运行了i2c_imx_xfer函数,也就是我们写设备驱动时候使用的i2c_transfer最终会调用i2c_imx_xfer,

i2c_imx_xfer
static int i2c_imx_xfer(struct i2c_adapter *adapter,
						struct i2c_msg *msgs, int num)
{
	unsigned int i, temp;
	int result;
	bool is_lastmsg = false;
	struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);

	dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);

	/* 开始I2C通信 */
	result = i2c_imx_start(i2c_imx);
	if (result)
		goto fail0;

	/* 读写I2C*/
	for (i = 0; i < num; i++) {
		if (i == num - 1)
			is_lastmsg = true;

		if (i) {
			dev_dbg(&i2c_imx->adapter.dev,
				"<%s> repeated start\n", __func__);
			temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
			temp |= I2CR_RSTA;
			imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
			result =  i2c_imx_bus_busy(i2c_imx, 1);
			if (result)
				goto fail0;
		}
		dev_dbg(&i2c_imx->adapter.dev,
			"<%s> transfer message: %d\n", __func__, i);
		...
		if (msgs[i].flags & I2C_M_RD)
			result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);
		else {
			if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD)
				result = i2c_imx_dma_write(i2c_imx, &msgs[i]);
			else
				result = i2c_imx_write(i2c_imx, &msgs[i]);
		}
		if (result)
			goto fail0;
	}

fail0:
	/* I2C停止 */
	i2c_imx_stop(i2c_imx);

	dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
		(result < 0) ? "error" : "success msg",
			(result < 0) ? result : num);
	return (result < 0) ? result : num;
}

I2C设备驱动

还是拿正点原子平台的AP3216C来举例😁
设备树

ap3216c_test@1e {
		compatible = "ap3216c-test";
		reg = <0x1e>;
	};
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/i2c.h>

#define AP3216C_NAME "ap3216c_test"
#define AP3216C_MINOR 211

#define AP3216C_ADDR    	0X1E	/* AP3216C器件地址  */

/* AP3316C寄存器 */
#define AP3216C_SYSTEMCONG	0x00	/* 配置寄存器       */
#define AP3216C_INTSTATUS	0X01	/* 中断状态寄存器   */
#define AP3216C_INTCLEAR	0X02	/* 中断清除寄存器   */
#define AP3216C_IRDATALOW	0x0A	/* IR数据低字节     */
#define AP3216C_IRDATAHIGH	0x0B	/* IR数据高字节     */
#define AP3216C_ALSDATALOW	0x0C	/* ALS数据低字节    */
#define AP3216C_ALSDATAHIGH	0X0D	/* ALS数据高字节    */
#define AP3216C_PSDATALOW	0X0E	/* PS数据低字节     */
#define AP3216C_PSDATAHIGH	0X0F	/* PS数据高字节     */

struct ap3216c_info {
    int enable;
};

struct ap3216c_dev {
    struct miscdevice misc_dev;
    struct i2c_client *client;
    struct ap3216c_info info;
};

static int read_ir_data(struct i2c_client *client ,u16 *buf);
static int read_als_data(struct i2c_client *client ,u16 *buf);
static int read_ps_data(struct i2c_client *client ,u16 *buf);
static int ap3216c_enable(struct i2c_client *client,int enable);
static ssize_t ir_show(struct device *dev, struct device_attribute *attr, char *buf){
    struct ap3216c_dev *devs = (struct ap3216c_dev *)dev_get_drvdata(dev);
    u16 data;
    if(devs->info.enable == 0){
        return sprintf(buf, "%s\n","please turn on the devices!");
    }
    if (read_ir_data(devs->client, &data))
        return sprintf(buf, "%d\n", -1);
    return sprintf(buf, "%d\n", data);

}
static DEVICE_ATTR_RO(ir);
static ssize_t als_show(struct device *dev, struct device_attribute *attr, char *buf){
    struct ap3216c_dev *devs = (struct ap3216c_dev *)dev_get_drvdata(dev);
    u16 data;
    if(devs->info.enable == 0){
        return sprintf(buf, "%s\n","please turn on the devices!");
    }
    if (read_als_data(devs->client, &data))
        return sprintf(buf, "%d\n", -1);
    return sprintf(buf, "%d\n", data);
}
static DEVICE_ATTR_RO(als);
static ssize_t ps_show(struct device *dev, struct device_attribute *attr, char *buf){
    struct ap3216c_dev *devs = (struct ap3216c_dev *)dev_get_drvdata(dev);
    u16 data;
    if(devs->info.enable == 0){
        return sprintf(buf, "%s\n","please turn on the devices!");
    }
    if (read_ps_data(devs->client, &data))
        return sprintf(buf, "%d\n", -1);
    return sprintf(buf, "%d\n", data);
}
static DEVICE_ATTR_RO(ps);
static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf){
    struct ap3216c_dev *devs = (struct ap3216c_dev *)dev_get_drvdata(dev);
	return sprintf(buf, "%s\n",(devs->info.enable==0)?"unenabled":"enabled");
}
static ssize_t enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len){
    struct ap3216c_dev *devs = (struct ap3216c_dev *)dev_get_drvdata(dev);
    sscanf(buf, "%d",&devs->info.enable);	
    ap3216c_enable(devs->client,devs->info.enable);
	return len;
}
static DEVICE_ATTR_RW(enable);
static struct attribute *ap3216c_class_attrs[] = {
	&dev_attr_ir.attr,
    &dev_attr_als.attr,
    &dev_attr_ps.attr,
    &dev_attr_enable.attr,
	NULL,
};
static int ap3216c_read_regs(struct i2c_client *client,u8 reg,u8 *buf){
    struct i2c_msg msg[2] = {
        [0] = {
            .addr = client->addr,
            .flags = 0,
            .len  = 1,
            .buf  = &reg,
        },
        [1] = {
            .addr = client->addr,
            .flags = I2C_M_RD,
            .len  = 1,
            .buf  = buf,
        },
    };
    if(i2c_transfer(client->adapter,msg,2)!=2){
        printk("i2c read error\n");
        return -1;
    }
    return 0;
}
static int ap3216c_write_regs(struct i2c_client *client,u8 reg,u8 data){
    u8 buf[2] = {reg,data};
    struct i2c_msg msg ={
        .addr = client->addr,
        .flags = 0,
        .len  = 2,
        .buf  = buf,

    };
    if(i2c_transfer(client->adapter,&msg,1)!=1){
        printk("i2c write error\n");
        return -1;
    }
    return 0;
}
static int read_ir_data(struct i2c_client *client ,u16 *buf)
{
    u8 low_buf,high_buf;
    if(ap3216c_read_regs(client,AP3216C_IRDATALOW,&low_buf) || 
        ap3216c_read_regs(client,AP3216C_IRDATAHIGH,&high_buf))
        return -1;
    if(low_buf & 0x80){
        *buf = ((u16)high_buf<<2) | (low_buf & 0x03);
    }else{
        return -1;
    }
    return 0;
}
static int read_als_data(struct i2c_client *client ,u16 *buf)
{
    u8 low_buf,high_buf;
    if(ap3216c_read_regs(client,AP3216C_ALSDATALOW,&low_buf) || 
        ap3216c_read_regs(client,AP3216C_ALSDATAHIGH,&high_buf))
        return -1;
    if(low_buf & 0x80){
        *buf = ((u16)high_buf<<2) | (low_buf & 0x03);
    }else{
        return -1;
    }
    return 0;
}
static int read_ps_data(struct i2c_client *client ,u16 *buf)
{
    u8 low_buf,high_buf;
    if(ap3216c_read_regs(client,AP3216C_PSDATALOW,&low_buf) || 
        ap3216c_read_regs(client,AP3216C_PSDATAHIGH,&high_buf))
        return -1;
    printk("low_buf[%d]  high_buf[%d]\n",low_buf,high_buf);
    if(low_buf & 0x80){
        *buf = ((u16)high_buf<<2) | (low_buf & 0x03);
    }else{
        return -1;
    }
    return 0;

}
static int ap3216c_enable(struct i2c_client *client,int enable)
{
    if(!enable)
    {
        if (ap3216c_write_regs(client, AP3216C_SYSTEMCONG, 0x00))
            return -1;
    }else{
        /* reset*/
        if (ap3216c_write_regs(client, AP3216C_SYSTEMCONG, 0x04))
            return -1;
        mdelay(50);
        /* Enable IR, ALS and PS */
        if (ap3216c_write_regs(client, AP3216C_SYSTEMCONG, 0x03))
            return -1;
    }
    return 0;
}
static const struct attribute_group ap3216c_group = {
	.attrs = ap3216c_class_attrs,
};

static const struct attribute_group *ap3216c_groups[] = {
	&ap3216c_group,
	NULL,
};
static struct file_operations ap3216c_fops = {
	.owner = THIS_MODULE,
};
struct ap3216c_dev ap_dev = {
    .misc_dev = {
        .name = AP3216C_NAME,
        .minor = AP3216C_MINOR,
        .groups = ap3216c_groups,
        .fops = &ap3216c_fops,
    },
    .client = NULL,
    .info = {
        .enable = 0,
    },
};
int ap3216c_probe(struct i2c_client *client, const struct i2c_device_id *dev_id){
    printk("match success!!\n");
    misc_register(&ap_dev.misc_dev);
    ap_dev.client = client;
    dev_set_drvdata(ap_dev.misc_dev.this_device,&ap_dev);
    return 0;
}
int ap3216c_remove(struct i2c_client *client){
    misc_deregister(&ap_dev.misc_dev);
    return 0;
}
/* 传统匹配方式ID列表 */
static const struct i2c_device_id ap3216c_id[] = {
	{"ap3216c-test", 0},  

};
/* 设备树匹配列表 */
static const struct of_device_id ap3216c_of_match[] = {
	{ .compatible = "ap3216c-test" },

};
/* i2c驱动结构体 */	
static struct i2c_driver ap3216c_driver = {
	.probe = ap3216c_probe,
	.remove = ap3216c_remove,
	.driver = {
			.owner = THIS_MODULE,
		   	.name = "ap3216c_test",
		   	.of_match_table = ap3216c_of_match, 
		   },
	.id_table = ap3216c_id,
};
		   
static int __init ap3216c_init(void)
{
	int ret = 0;

	ret = i2c_add_driver(&ap3216c_driver);
	return ret;
}


static void __exit ap3216c_exit(void)
{
	i2c_del_driver(&ap3216c_driver);
}

module_init(ap3216c_init);
module_exit(ap3216c_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("bin");
Logo

更多推荐