54 OrangePi linux内核里的i2c控制器驱动

时间:2021-01-27 17:56:35

在全志H3平台里的”TWI Controller”就是I2C控制器. 共有4个i2c控制器

54  OrangePi linux内核里的i2c控制器驱动

  script.bin里的i2c控制器配置 :
[twi0]
twi_used = 1
twi_scl = port:PA11<2><default><default><default>
twi_sda = port:PA12<2><default><default><default>

[twi1]
twi_used = 1
twi_scl = port:PA18<3><default><default><default>
twi_sda = port:PA19<3><default><default><default>

[twi2]
twi_used = 0
twi_scl = port:PE12<3><default><default><default>
twi_sda = port:PE13<3><default><default><default>

在linux内核里控制器的驱动是由芯片厂商负责, 而且控制器的驱动都是平台驱动来的.需要用平台设备来描述i2c控制器的硬件信息,如控制器的配置寄存器基地址, 中断号等. 在内核里,可能只会驱动好部分控制器.如SOC里共有4个控制器,需要4个平台设备来分别描述每个控制器的硬件资源, 内核里有可能只写两个相关的平台设备,如需用额外两个控制器,我们则需用平台设备描述好额外两个控制器才可以.

   make menuconfig ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

Device Drivers --->
<*> I2C support --->
I2C Hardware Bus support --->
<*> SUNXI I2C controller //全志twi控制器的驱动
<*> I2C device interface //选上此项,i2c控制器驱动好后会在/dev目录下有设备文件i2c-*, 供应用程序调用控制器用. 我们也可以通过产生的设备文件得知相应的控制器是否驱动好

/////////////////////////////
控制器的驱动源码在”drivers/i2c/busses/i2c-sunxi.c”
全志控制器的驱动代码不是很规范,控制器的平台驱动与平台设备都在i2c-sunxi.c里,并没有按驱动模型分层的实现.

1589 subsys_initcall(sunxi_i2c_adap_init); //初始化函数

static int __init sunxi_i2c_adap_init(void)
{
...
sunxi_twi_device_scan(); //初始化平台设备 sunxi_twi_device[SUNXI_TWI_NUM]
twi_chan_cfg(sunxi_twi_pdata); //根据script.bin里的twi_used子键的配置来用全局变量twi_used_mask来记录相应的控制器是否使用.

#ifdef CONFIG_EVB_PLATFORM //此宏成立
for (i=0; i<SUNXI_TWI_NUM; i++) //SUNXI_TWI_NUM是表示twi控制器的个数, 现内核里宏的值是3
#else
i = CONFIG_TWI_CHAN_NUM; /* In FPGA, only one channel is available. */
#endif
{
if (twi_chan_is_enable(i)) { //如果第i个twi控制器需要使用,则注册相应的平台设备.
I2C_DBG("Sunxi I2C init channel %d \n", i);
ret = platform_device_register(&sunxi_twi_device[i]);
...
}

if (twi_used_mask) //只有一个以上的控制器的平台设备是使用,则注册控制器的平台驱动
return platform_driver_register(&sunxi_i2c_driver); // sunxi_i2c_driver是控制器的平台驱动
...

return 0;
}
内核里分别用三个数组来表示每个控制器的资源,平台数据,平台设备.
1378 static struct resource sunxi_twi_resources[SUNXI_TWI_NUM * SUNXI_TWI_RES_NUM];
1379 static struct sunxi_i2c_platform_data sunxi_twi_pdata[SUNXI_TWI_NUM];
1380 static struct platform_device sunxi_twi_device[SUNXI_TWI_NUM];
1382 static void sunxi_twi_device_scan(void)  //初始化平台设备 sunxi_twi_device[SUNXI_TWI_NUM]
1383 {
1384 int i;
...
1389
1390 for (i=0; i<SUNXI_TWI_NUM; i++) { // 初始化每个平台设备,及它的资源,平台数据。 i表示第几个控制器
//准备平台设备所用的资源. twi控制器的配置寄存器的开始地址,结束地址.
1391 sunxi_twi_resources[i * SUNXI_TWI_RES_NUM].start = SUNXI_TWI_MEM_START(i);
1392 sunxi_twi_resources[i * SUNXI_TWI_RES_NUM].end = SUNXI_TWI_MEM_END(i);
1393 sunxi_twi_resources[i * SUNXI_TWI_RES_NUM].flags = IORESOURCE_MEM;
...

1398 //准备平台设备所用的平台数据
1399 sunxi_twi_pdata[i].bus_num = i;
1400 sunxi_twi_pdata[i].frequency = SUNXI_TWI_SPEED(i); //i2c传输的标准速率100Kbsp, 高速400Kbsp.  这里的值是200Kbsp.
1401
//初始化平台设备
1402 sunxi_twi_device[i].name = SUNXI_TWI_DEV_NAME; // "twi"
1403 sunxi_twi_device[i].id = i;
1404 sunxi_twi_device[i].resource = &sunxi_twi_resources[i * SUNXI_TWI_RES_NUM];
1405 sunxi_twi_device[i].num_resources = SUNXI_TWI_RES_NUM;
1406 sunxi_twi_device[i].dev.platform_data = &sunxi_twi_pdata[i];
1407 sunxi_twi_device[i].dev.release = sunxi_i2c_release;
1408 }

///////////////////////////////////////////////////////////////

1368 static struct platform_driver sunxi_i2c_driver = { //控制器的平台驱动
1369 .probe = sunxi_i2c_probe,
1370 .remove = __devexit_p(sunxi_i2c_remove),
1371 .driver = {
1372 .name = SUNXI_TWI_DEV_NAME, // "twi",与平台设备是按名字匹配
1373 .owner = THIS_MODULE,
1374 .pm = SUNXI_I2C_DEV_PM_OPS,
1375 },
1376 };

/////////// 在linux内核里,i2c控制器驱动好后用struct i2c_adapter的一个对象来描述.
"include/linux/i2c.h"
struct i2c_adapter {
struct module *owner;
const struct i2c_algorithm *algo; //传输的协议,内核里已经实现好
void *algo_data;

...
int timeout; /* in jiffies */
int retries; // i2c协议要求发出8位数据后,应由接收方回个应答。如没有收到应答,重新尝试的次数.
struct device dev; //i2c_adapter也是基于device结构体来扩展的. 所以每个i2c_adapter的对象也可以在/sys目录下有相应的子目录

int nr; //控制器的序号,一般由控制器的平台设备的id指定
...
};


//基本上每个芯片厂家都会在i2c_adapter基础上扩展出一个自己封装的类型. 控制器的驱动是芯片厂商负责的,所以不用过多关注
67 struct sunxi_i2c {
68 int bus_num;
69 unsigned int status; /* start, running, idle */
70 unsigned int suspended:1;
71 struct i2c_adapter adap; //基于它扩展,内核里会管理adap成员的地址。只要得到此成员的地址,即可得出sunxi_i2c结构体对象的首地址
72
...
92 };


1154 static int __devinit sunxi_i2c_probe(struct platform_device *pdev)
1155 {
1156 struct sunxi_i2c *i2c = NULL;
1157 struct resource *res = NULL;
1158 struct sunxi_i2c_platform_data *pdata = NULL;
1159 int ret, irq;
1160
1161 pdata = pdev->dev.platform_data;

1166 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1167 irq = platform_get_irq(pdev, 0);
...
1172 if (!request_mem_region(res->start, resource_size(res), res->name)) {
1173 return -ENOMEM;
1174 }
1175
1176 i2c = kzalloc(sizeof(struct sunxi_i2c), GFP_KERNEL);
... //i2c_adapter对象的初始化
1182 i2c->adap.owner = THIS_MODULE;
1183 i2c->adap.nr = pdata->bus_num; //
1184 i2c->adap.retries = 3;
1185 i2c->adap.timeout = 5*HZ;
1186 i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
1187 i2c->bus_freq = pdata->frequency;
1188 i2c->irq = irq;
1189 i2c->bus_num = pdata->bus_num;
1190 i2c->status = I2C_XFER_IDLE;
1191 i2c->suspended = 0;
...
1212
1213 i2c->adap.algo = &sunxi_i2c_algorithm; //传输的具体实现方法由芯片厂商实现。
1221 i2c->adap.dev.parent = &pdev->dev;
1222
1223 if (sunxi_i2c_hw_init(i2c, pdata)) {
1224 ret = -EIO;
1225 goto ehwinit;
1226 }
1227
1228 ret = i2c_add_numbered_adapter(&i2c->adap); //里面就是调用i2c_register_adapter(&i2c->adap)注册i2c控制器对象
1229 if (ret < 0) {
1230 I2C_ERR( "[i2c%d] failed to add adapter\n", i2c->bus_num);
1231 goto eadapt;
1232 }
1233


static int i2c_register_adapter(struct i2c_adapter *adap) //注册i2c_adapter对象时触发
{
int res = 0;
...
/* Set default timeout to 1 second if not already set */
if (adap->timeout == 0)
adap->timeout = HZ;

dev_set_name(&adap->dev, "i2c-%d", adap->nr);
adap->dev.bus = &i2c_bus_type; // i2c_adapter对象的device成员挂载到名为"i2c"的总线下, 所以在"/sys/bus/i2c/"目录下会有i2c_adapter对象的子目录, 目录名为"i2c-%d"

adap->dev.type = &i2c_adapter_type;
res = device_register(&adap->dev);
...
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);

/* Notify drivers */
mutex_lock(&core_lock);
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
mutex_unlock(&core_lock);