linux驱动学习笔记(九)I2C
前言i2c协议是由 数据线SDA和时钟线SCL构成的串行总线,是半双工通信方式,一个I2c接口可以挂载多个从设备,但是每个从设备都会有唯一的器件地址, I2C速度在标准模式下为100k,快速模式下可以达到400k,高速模式下可以达到3.4M时序介绍空闲状态SDA与SCL两条信号线同时处于高电平状态起始信号SCL为高的时候 SDA为下降沿停止信号SCL为高的时候 SDA为上升沿数据传输SCL为低时候
前言
i2c协议是由 数据线SDA和时钟线SCL构成的串行总线,是半双工通信方式,一个I2c接口可以挂载多个从设备,但是每个从设备都会有唯一的器件地址, I2C速度在标准模式下为100k,快速模式下可以达到400k,高速模式下可以达到3.4M
时序介绍
-
空闲状态
SDA与SCL两条信号线同时处于高电平状态 -
起始信号
SCL为高的时候 SDA为下降沿
-
停止信号
SCL为高的时候 SDA为上升沿
-
数据传输
SCL为低时候 SDA数据可以变化。SCL为高的时候要求数据稳定 -
应答信号
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驱动体系流程
- 初始化I2C适配器(入口地址 中断号等),驱动CPU控制适配器从硬件上产生信号 以及处理信号
- 编写I2C适配器的通信方法 xxx_xfer函数填充master_xfer数据结构的master_xfer指针,并把该数据结构放到i2c_adapter的algo指针上。
- 实现I2C设备驱动中的i2c_driver接口。将设备挂载在总线上
- 实现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 = ®,
},
[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");
更多推荐
所有评论(0)