赞
踩
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为写
在之前platform中,说到 linux内核驱动通过总线进行设备和驱动进行匹配,platform 是虚拟出来的一条总线,而I2C是一条实际的总线。
I2C核心层 提供了总线驱动和设备驱动注册,注销,通信方法等
是针对硬件结构中适配器端的实现,可由CPU控制。包括适配器数据结构i2c_adapter和适配器的通信方法数据结构i2c_algorithm以及控制I2C适配器产生通信信号的函数,经由总线驱动的代码,适配器可以产生I2C时序。
针对具体外围器件,根据I2C总线适配器去与器件进行通信,交换数据等。I2C设备驱动主要包含数据结构i2c_driver和i2c_client, 我们需要根据具体设备实现其中的成员函数。
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对应一套通信方法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_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; };
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
};
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适配器 */ 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,
};
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; }
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; }
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; }
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); }
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; }
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; }
正点原子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函数
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; }
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,
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; }
还是拿正点原子平台的AP3216C来举例声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。