一、i2c驱动代码分析
在Linux驱动.之I2C,iic驱动层(二),进行了框架分析,这篇,将分析具体device的设备驱动代码
摘抄自,正点原子
I2C主要以两种功能模式运行:标准模式和快速模式。
•在标准模式下,I2C支持高达100 kbits/s的数据传输速率。
•在快速模式下,可以实现高达400 kbits/s的数据传输速率。
在前面讲 platform 的时候就说过,platform 是虚拟出来的一条总线,目的是为了实现总线、设备、驱动框架。对于 I2C 而言,不需要虚拟出一条总线,直接使用 I2C总线即可。I2C 总线驱动重点是 I2C 适配器(也就是 SOC 的 I2C 接口控制器)驱动。
在Linux设计中,将I2C代码框架分为三个部分:I2C总线、I2C核心、I2C驱动。
I2C核心(i2c-core): 主要定义i2c驱动所用到的通用API,高内聚的代码会放到i2c-core.c。
I2C总线驱动(i2c adapter): 根据平台定制的i2c驱动,其中包含i2c传输的算法设计。主要工作负责生成i2c_client,注册适配器,以及i2c_client与i2c_driver的匹配。
I2C设备驱动(i2c client driver): 驱动I2C设备的代码。I2C设备驱动定义了外设的交互方式,与不同的I2C外设需要不同的设备驱动。I2C设备驱动对上和用户应用程序打交道,对下和I2C核心对接。
对于i2c-core而言,不需要我们实现,这个是linux内核中已经有的,属于标准内核代码。
i2c adapter,则是SOC芯片的I2C控制器驱动,这个根据不同的平台,I2C控制器会有差异,所以这部分代码是SOC厂商会实现,也不需要开发人员写,但我们需要了解一下也是可以的。
i2c client driver,则是具体的I2C设备驱动,一般驱动开发人员需要编写这部分代码。
1、I2C总线驱动
我们先来看一下i2c adapter的有关内容。对于i2c adapter相关驱动代码已经有NXP实现,相关内容如下。
I2C控制器的设备树节点如下:
路径:arch/arm/boot/dts/imx6ull.dtsi
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";
};
i2c2: i2c@021a4000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
reg = <0x021a4000 0x4000>;
interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_I2C2>;
status = "disabled";
};
i2c3: i2c@021a8000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
reg = <0x021a8000 0x4000>;
interrupts = <GIC_SPI 38 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_I2C3>;
status = "disabled";
};
上述代码内容就是IMX6ULL中的3个I2C,I2C1、I2C2和I2C3的地址分别是0x021a0000、0x021a4000、0x021a8000。根据compatible属性,我们可以找到对于的驱动程序,搜索 “fsl,imx6ul-i2c”, “fsl,imx21-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);
文件i2c-imx.c中的内容就是NXP原厂针对IMX6ULL等SOC中I2C控制器的驱动i2c adapter。我们不深入研究i2c adapter,因为这是SOC原厂会完成这部分驱动的编写。
i2c adapter用到两个重要的数据结构:i2c_adapter 和 i2c_algorithm,Linux 内核将 SOC 的 I2C 适配器(控制器)抽象成 i2c_adapter,i2c_adapter 结构体内容如下:
路径:
/*i2c_adapter是用于识别物理i2c总线以及访问该总线所需的访问算法的结构。
*/
struct i2c_adapter {
struct module *owner;
unsigned int class; /*允许探测的类*/
const struct i2c_algorithm *algo; /*访问总线的算法*/
void *algo_data;
/*对所有设备有效的数据字段*/
struct rt_mutex bus_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /*适配器设备*/
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;
};
第 7 行,i2c_algorithm 类型的指针变量 algo,对于一个 I2C 适配器,肯定要对外提供读写 API 函数,设备驱动程序可以使用这些 API 函数来完成读写操作。i2c_algorithm 就是 I2C 适配器与 IIC 设备进行通信的方法。
i2c_algorithm 结构体定义内容如下:
路径:include/linux/i2c.h
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);
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
};
第 20 行,master_xfer 就是 I2C 适配器的传输函数,可以通过此函数来完成与 IIC 设备之间的通信。
第 22 行,smbus_xfer 就是 SMBUS 总线的传输函数。
重点来看一下 i2c_imx_xfer 函数,因为最终就是通过此函数来完成与 I2C 设备通信的,此函数内容如下:
路径:drivers/i2c/busses/i2c-imx.c
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__);
/* Start I2C transfer */
result = i2c_imx_start(i2c_imx);
if (result)
goto fail0;
/* read/write data */
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);
/* write/read data */
#ifdef CONFIG_I2C_DEBUG_BUS
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
dev_dbg(&i2c_imx->adapter.dev,
"<%s> CONTROL: IEN=%d, IIEN=%d, MSTA=%d, MTX=%d, TXAK=%d, RSTA=%d\n",
__func__,
(temp & I2CR_IEN ? 1 : 0), (temp & I2CR_IIEN ? 1 : 0),
(temp & I2CR_MSTA ? 1 : 0), (temp & I2CR_MTX ? 1 : 0),
(temp & I2CR_TXAK ? 1 : 0), (temp & I2CR_RSTA ? 1 : 0));
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
dev_dbg(&i2c_imx->adapter.dev,
"<%s> STATUS: ICF=%d, IAAS=%d, IBB=%d, IAL=%d, SRW=%d, IIF=%d, RXAK=%d\n",
__func__,
(temp & I2SR_ICF ? 1 : 0), (temp & I2SR_IAAS ? 1 : 0),
(temp & I2SR_IBB ? 1 : 0), (temp & I2SR_IAL ? 1 : 0),
(temp & I2SR_SRW ? 1 : 0), (temp

&spm=1001.2101.3001.5002&articleId=141003881&d=1&t=3&u=6ae3aff6b96b4a6ba3784b8c020b0f79)
2508

被折叠的 条评论
为什么被折叠?



