相关文章
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.pdf
- Linux文档
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[] = {
{ .compatible = "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。