I2C驱动(十一)--gpio模拟的i2c总线驱动i2c-gpio.c分析
- 互联网
- 2025-09-18 19:39:01

相关文章
I2C驱动(一) – I2C协议 I2C驱动(二) – SMBus协议 I2C驱动(三) – 驱动中的几个重要结构 I2C驱动(四) – I2C-Tools介绍 I2C驱动(五) – 通用驱动i2c-dev.c分析 I2C驱动(六) – I2C驱动程序模型 I2C驱动(七) – 编写I2C设备驱动之i2c_driver I2C驱动(八) – 编写I2C设备驱动之i2c_client I2C驱动(九) – i2c_adapter控制器驱动框架编写 I2C驱动(十) – i2c_adapter控制器驱动完善与上机实验
文章目录 相关文章参考资料一、平台-总线-设备驱动模型二、设备树分析三、驱动程序分析3.1 i2c-gpio驱动层次3.2 `bit_xfer`传输函数分析 四、怎么使用i2c-gpio五、总结参考资料 i2c_spec.pdfLinux文档 Linux-4.9.88\Documentation\devicetree\bindings\i2c\i2c-gpio.txt Linux驱动源码 Linux-4.9.88\drivers\i2c\busses\i2c-gpio.c 一、平台-总线-设备驱动模型
i2c-gpio.c也是基于万能框架:平台-总线-设备模型来写的。platform_device部分来自设备树,platform_driver就是i2c-gpio.c驱动。下面分析两边的代码。
二、设备树分析设备树节点如下:
i2c_gpio: i2c-gpio { compatible = "i2c-gpio"; #address-cells = <1>; #size-cells = <0>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_i2c_gpio>; gpios = < &gpio5 1 GPIO_ACTIVE_HIGH /* SDA */ &gpio5 0 GPIO_ACTIVE_HIGH /* SCL */ >; i2c-gpio,delay-us = <5>; /* ~100 kHz */ status = "okay"; ds1339: rtc@68 { compatible = "dallas,ds1339"; reg = <0x68>; status = "disabled"; }; }; compatible 属性用于和i2c-gpio.c程序中.of_match_table结构中的compatible 进行比较。#address-cells 和 #size-cells 属性用来指定 reg属性的地址和大小用多少个32位数据表示。reg属性在子节点中用了表示i2c设备地址。pinctrl-names 和 pinctrl-0属性表示使用pinctrl将引脚配置成gpio模式。gpios 属性用于指定gpio引脚i2c-gpio,delay-us属性表示时钟频率status 属性节点使能状态ds1339: rtc@68表示的是这个i2c总线下挂的设备,地址是0x68。 三、驱动程序分析 3.1 i2c-gpio驱动层次从入口函数开始,入口函数注册了一个platform_driver结构。
tatic int __init i2c_gpio_init(void) { ... ret = platform_driver_register(&i2c_gpio_driver); ... }platform_driver结构包含了of_match_table数组,probe函数。
static struct platform_driver i2c_gpio_driver = { .driver = { .name = "i2c-gpio", .of_match_table = of_match_ptr(i2c_gpio_dt_ids), //和设备树比较 }, .probe = i2c_gpio_probe, //匹配成功调用 .remove = i2c_gpio_remove, //做一些和probe相反的工作 };of_match_table数组中的compatible 和设备树匹配成功,调用probe函数。
static const struct of_device_id i2c_gpio_dt_ids[] = { { patible = "i2c-gpio", }, //与设备树的compatible 比较 { /* sentinel */ } };来看probe函数:
of_i2c_gpio_get_pins从设备树获取gpio引脚。adap = &priv->adap;分配的i2c_adapter。of_i2c_gpio_get_props从设备树获取属性值,用来设置硬件参数和i2c_adapter结构。i2c_bit_add_numbered_bus注册i2c_adapter,这个是重点,这里面会有算法部分设置,下面继续分析。 static int i2c_gpio_probe(struct platform_device *pdev) { ... unsigned int sda_pin, scl_pin; //sda 和 scl引脚 int ret; /* First get the GPIO pins; if it fails, we'll defer the probe. */ if (pdev->dev.of_node) { /* 从设备树中获取 sda 和 scl */ ret = of_i2c_gpio_get_pins(pdev->dev.of_node, &sda_pin, &scl_pin); if (ret) return ret; } else { if (!dev_get_platdata(&pdev->dev)) return -ENXIO; pdata = dev_get_platdata(&pdev->dev); sda_pin = pdata->sda_pin; scl_pin = pdata->scl_pin; } /*devm_gpio_request 可以自动处理清理工作 */ ret = devm_gpio_request(&pdev->dev, sda_pin, "sda"); if (ret) { if (ret == -EINVAL) ret = -EPROBE_DEFER; /* Try again later */ return ret; } ret = devm_gpio_request(&pdev->dev, scl_pin, "scl"); if (ret) { if (ret == -EINVAL) ret = -EPROBE_DEFER; /* Try again later */ return ret; } priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; /* 分配了i2c_adapter */ adap = &priv->adap; bit_data = &priv->bit_data; pdata = &priv->pdata; if (pdev->dev.of_node) { pdata->sda_pin = sda_pin; pdata->scl_pin = scl_pin; /*从设备树获取属性*/ of_i2c_gpio_get_props(pdev->dev.of_node, pdata); } else { memcpy(pdata, dev_get_platdata(&pdev->dev), sizeof(*pdata)); } /* 根据获取的设备树属性值设置开漏情况 */ if (pdata->sda_is_open_drain) { gpio_direction_output(pdata->sda_pin, 1); bit_data->setsda = i2c_gpio_setsda_val; } else { gpio_direction_input(pdata->sda_pin); bit_data->setsda = i2c_gpio_setsda_dir; } if (pdata->scl_is_open_drain || pdata->scl_is_output_only) { gpio_direction_output(pdata->scl_pin, 1); bit_data->setscl = i2c_gpio_setscl_val; } else { gpio_direction_input(pdata->scl_pin); bit_data->setscl = i2c_gpio_setscl_dir; } /* 根据获取的设备树属性值设置时间参数 */ if (!pdata->scl_is_output_only) bit_data->getscl = i2c_gpio_getscl; bit_data->getsda = i2c_gpio_getsda; if (pdata->udelay) bit_data->udelay = pdata->udelay; else if (pdata->scl_is_output_only) bit_data->udelay = 50; /* 10 kHz */ else bit_data->udelay = 5; /* 100 kHz */ if (pdata->timeout) bit_data->timeout = pdata->timeout; else bit_data->timeout = HZ / 10; /* 100 ms */ bit_data->data = pdata; /* 根据设备树解析的值设置i2c_adapter */ adap->owner = THIS_MODULE; if (pdev->dev.of_node) strlcpy(adap->name, dev_name(&pdev->dev), sizeof(adap->name)); else snprintf(adap->name, sizeof(adap->name), "i2c-gpio%d", pdev->id); adap->algo_data = bit_data; adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; adap->dev.parent = &pdev->dev; adap->dev.of_node = pdev->dev.of_node; adap->nr = pdev->id; /* 注册i2c_adapter */ ret = i2c_bit_add_numbered_bus(adap); ... }进入i2c_bit_add_numbered_bus看看,这个函数在drivers\i2c\algos\i2c-algo-bit.c中定义,他调用__i2c_bit_add_bus,__i2c_bit_add_bus里面设置了adap->algo = &i2c_bit_algo;,这就是i2c核心算法结构,下面继续看i2c_bit_algo。
int i2c_bit_add_numbered_bus(struct i2c_adapter *adap) { return __i2c_bit_add_bus(adap, i2c_add_numbered_adapter); } static int __i2c_bit_add_bus(struct i2c_adapter *adap, int (*add_adapter)(struct i2c_adapter *)) { ... /* 核心算法 */ adap->algo = &i2c_bit_algo; ... }i2c_bit_algo结构中bit_xfer就是gpio模拟i2c_adapter的传输函数。
const struct i2c_algorithm i2c_bit_algo = { .master_xfer = bit_xfer, .functionality = bit_func, };从上面的分析,可以知道I2C-GPIO的驱动层次如下:
3.2 bit_xfer传输函数分析传输函数是根据i2c协议来完成的,i2c_start发起一个start信号,接着根据i2c_msg 来判断读写,readbytes读一个字节,sendbytes写一个字节。结束后i2c_stop发出停止信号。
static int bit_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) { struct i2c_msg *pmsg; struct i2c_algo_bit_data *adap = i2c_adap->algo_data; int i, ret; unsigned short nak_ok; if (adap->pre_xfer) { ret = adap->pre_xfer(i2c_adap); if (ret < 0) return ret; } bit_dbg(3, &i2c_adap->dev, "emitting start condition\n"); i2c_start(adap); //发出Start信号 for (i = 0; i < num; i++) { pmsg = &msgs[i]; //循环取出i2c_msg nak_ok = pmsg->flags & I2C_M_IGNORE_NAK; if (!(pmsg->flags & I2C_M_NOSTART)) { if (i) { bit_dbg(3, &i2c_adap->dev, "emitting " "repeated start condition\n"); i2c_repstart(adap); } ret = bit_doAddress(i2c_adap, pmsg); if ((ret != 0) && !nak_ok) { bit_dbg(1, &i2c_adap->dev, "NAK from " "device addr 0x%02x msg #%d\n", msgs[i].addr, i); goto bailout; } } if (pmsg->flags & I2C_M_RD) { /* read bytes into buffer(读一个字节)*/ ret = readbytes(i2c_adap, pmsg); if (ret >= 1) bit_dbg(2, &i2c_adap->dev, "read %d byte%s\n", ret, ret == 1 ? "" : "s"); if (ret < pmsg->len) { if (ret >= 0) ret = -EIO; goto bailout; } } else { /* write bytes from buffer (写一个字节)*/ ret = sendbytes(i2c_adap, pmsg); if (ret >= 1) bit_dbg(2, &i2c_adap->dev, "wrote %d byte%s\n", ret, ret == 1 ? "" : "s"); if (ret < pmsg->len) { if (ret >= 0) ret = -EIO; goto bailout; } } } ret = i; bailout: bit_dbg(3, &i2c_adap->dev, "emitting stop condition\n"); i2c_stop(adap); if (adap->post_xfer) adap->post_xfer(i2c_adap); return ret; }字节读写再细分到位操作,在i2c_outb函数中实现。
static int i2c_outb(struct i2c_adapter *i2c_adap, unsigned char c) { int i; int sb; int ack; struct i2c_algo_bit_data *adap = i2c_adap->algo_data; /* assert: scl is low */ for (i = 7; i >= 0; i--) { sb = (c >> i) & 1; setsda(adap, sb); udelay((adap->udelay + 1) / 2); if (sclhi(adap) < 0) { /* timed out */ bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, " "timeout at bit #%d\n", (int)c, i); return -ETIMEDOUT; } /* FIXME do arbitration here: * if (sb && !getsda(adap)) -> ouch! Get out of here. * * Report a unique code, so higher level code can retry * the whole (combined) message and *NOT* issue STOP. */ scllo(adap); } sdahi(adap); if (sclhi(adap) < 0) { /* timeout */ bit_dbg(1, &i2c_adap->dev, "i2c_outb: 0x%02x, " "timeout at ack\n", (int)c); return -ETIMEDOUT; } /* read ack: SDA should be pulled down by slave, or it may * NAK (usually to report problems with the data we wrote). */ ack = !getsda(adap); /* ack: sda is pulled low -> success */ bit_dbg(2, &i2c_adap->dev, "i2c_outb: 0x%02x %s\n", (int)c, ack ? "A" : "NA"); scllo(adap); return ack; /* assert: scl is low (sda undef) */ } 四、怎么使用i2c-gpio设置设备树,在里面添加一个节点即可,示例代码看上面"设备树分析"部分,也可以参考 Linux-4.9.88\Documentation\devicetree\bindings\i2c\i2c-gpio.txt。
五、总结本文分析了gpio模拟的i2c_adapter驱动程序i2c-gpio.c。
I2C驱动(十一)--gpio模拟的i2c总线驱动i2c-gpio.c分析由讯客互联互联网栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“I2C驱动(十一)--gpio模拟的i2c总线驱动i2c-gpio.c分析”