I2C驱动的框架实现分析
2017-09-07 15:46
423 查看
前面讲了一下I2C的基本概念以及一个怎么去运用它的一个流程,但是没有做具体的分析,这篇博客转自http://m.blog.csdn.net/Guet_Kite/article/details/77870905,写得很明了,于是忍不住就转载啦。
首先这张图片很好的说明了我们内核的I2C驱动的一个框架,现在从上到下看起,先看我们的平台文件:mach-smdk2440.c,这里内核为3.0.54版本。
在smdk2440_map_io函数里面,调用了s3c24xx_init_io函数:
其中s3c_init_cpu的第二个参数cpu_ids在:
因为我们的板子是2440的,因此就截取了这个字段。然后继续追踪s3c_init_cpu函数:
可以看见最后一行调用了map_io函数,就是前面的s3c2440_map_io函数。这里在刚才的cpu_table结构体中说明的。
自然进入s3c24x_map_io函数:
最终在这里将s3c_device_i2c0结构体的名字改为了s3c2440-i2c,然后回到我们的平台文件mach-smdk2440.c文件中,进入smdk2440_machine_init函数,这里通过s3c_i2c0_set_platdata函数,设置default_i2c_data结构体的bus_num为0,以及设置i2c的IO口,npd->cfg_gpio = s3c_i2c0_cfg_gpio,接着就调用paltform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices)),在platform平台下进行设备注册,设备名字为“s3c2440-i2c”
到这里,平台设备已经注册完毕,接下来就是驱动咯:i2c-s3c2410.c
然后可以看见我们的名字“s3c2440-i2c”,和上面的设备匹配成功,调用probe函数:
因为代码太长,这里给出部分重要信息代码。
i2c->adap.algo = &s3c24xx_i2c_algorithm;
其中:
正如开始的图,i2c_adapter和i2c_algorithm都是操作i2c_bus的结构体,前者定义一个i2c模块,后者顶一个操作模块的方法,或者前者对应于物理上的一个适配器,而后者对应一套通信方法。
i2c_algorithm是i2c的底层实现函数,已封装,.master_xfer用于I2C总线传输,传送给它的i2c_msg数组中每个I2C消息,.functionality用于返回algorithm所支持的通信协议,如I2C_FUNC_I2C、I2C_FUNC_10BIT_ADDR、I2C_FUNC_SMBUS_READ_BYTE、I2C_FUNC_SMBUS_WRITE等。
ret=i2c_add_numbered_adapter(&i2c->ddap);
可以看见最后调用i2c_register_adapter(adap)函数,在i2c_bus总线上进行注册,名字为dev_set_name(&adap->dev, “i2c-%d”, adap->nr); 再说下i2c_adapter与i2c_client的关系:
i2c_client依附于i2c_adapter,由于一个适配器上可以连接多个I2C设备,所以一个i2c_adapter也可以被多个i2c_client依附,i2c_adapter中包含依附于它的i2c_client的链表。
i2c的框架就到这里,下面以我们的eeprom为例看下i2c的实现:
这里调用i2c_add_driver函数在i2c_bus总线下注册,然后看下他的读写函数,
这里面调用i2c_transfer函数,里面以i2c_msg为单位通信:代码就不搬上来了这里就是ret=adap->algo->master_xfer(adap, msgs, num); 这个就是前面的.master_xfer函数,实现i2c总线传输函数。
首先这张图片很好的说明了我们内核的I2C驱动的一个框架,现在从上到下看起,先看我们的平台文件:mach-smdk2440.c,这里内核为3.0.54版本。
static struct platform_device *smdk2440_devices[] __initdata = { &s3c_device_ohci, &s3c_device_lcd, &s3c_device_wdt, &s3c_device_i2c0, &s3c_device_iis, &s3c_device_dm9000, &s3c_device_adc, &s3c_device_ts, &uda1340_codec, &s3c24xx_uda134x, &samsung_asoc_dma, &s3c_device_timer, &smdk2440_beeper_device, &s3c_device_rtc, &s3c_device_usbgadget, &s3c_device_sdi, }; static void __init smdk2440_map_io(void) { s3c24xx_init_io(smdk2440_iodesc, ARRAY_SIZE(smdk2440_iodesc)); s3c24xx_init_clocks(12000000); s3c24xx_init_uarts(smdk2440_uartcfgs, ARRAY_SIZE(smdk2440_uartcfgs)); usb_s3c2440_init(); } static void __init smdk2440_machine_init(void) { s3c24xx_fb_set_platdata(&smdk2440_fb_info); s3c_i2c0_set_platdata(NULL); s3c24xx_ts_set_platdata(&smdk2440_ts_cfg); gpio_request(S3C2410_GPB(0),"beeper"); s3c_gpio_setpull(S3C2410_GPB(0), S3C_GPIO_PULL_NONE); s3c_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPB0_TOUT0); i2c_register_board_info(0, smdk2440_i2c_devs, ARRAY_SIZE(smdk2440_i2c_devs)); platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices)); smdk_machine_init(); s3c24xx_udc_set_platdata(&s3c_udc_cfg); }
在smdk2440_map_io函数里面,调用了s3c24xx_init_io函数:
void __init s3c24xx_init_io(struct map_desc *mach_desc, int size) { unsigned long idcode = 0x0; /* initialise the io descriptors we need for initialisation */ iotable_init(mach_desc, size); iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc)); if (cpu_architecture() >= CPU_ARCH_ARMv5) { idcode = s3c24xx_read_idcode_v5(); } else { idcode = s3c24xx_read_idcode_v4(); } arm_pm_restart = s3c24xx_pm_restart; s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids)); }
其中s3c_init_cpu的第二个参数cpu_ids在:
static struct cpu_table cpu_ids[] __initdata = { { .idcode = 0x32440000, .idmask = 0xffffffff, .map_io = s3c2440_map_io, .init_clocks = s3c244x_init_clocks, .init_uarts = s3c244x_init_uarts, .init = s3c2440_init, .name = name_s3c2440 }, ................ }
因为我们的板子是2440的,因此就截取了这个字段。然后继续追踪s3c_init_cpu函数:
void __init s3c_init_cpu(unsigned long idcode, struct cpu_table *cputab, unsigned int cputab_size) { cpu = s3c_lookup_cpu(idcode, cputab, cputab_size); if (cpu == NULL) { printk(KERN_ERR "Unknown CPU type 0x%08lx\n", idcode); panic("Unknown S3C24XX CPU"); } printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode); if (cpu->map_io == NULL || cpu->init == NULL) { printk(KERN_ERR "CPU %s support not enabled\n", cpu->name); panic("Unsupported Samsung CPU"); } cpu->map_io(); }
可以看见最后一行调用了map_io函数,就是前面的s3c2440_map_io函数。这里在刚才的cpu_table结构体中说明的。
void __init s3c2440_map_io(void) { s3c244x_map_io(); s3c24xx_gpiocfg_default.set_pull = s3c_gpio_setpull_1up; s3c24xx_gpiocfg_default.get_pull = s3c_gpio_getpull_1up; }
自然进入s3c24x_map_io函数:
void __init s3c244x_map_io(void) { /* register our io-tables */ iotable_init(s3c244x_iodesc, ARRAY_SIZE(s3c244x_iodesc)); /* rename any peripherals used differing from the s3c2410 */ s3c_device_sdi.name = "s3c2440-sdi"; s3c_device_i2c0.name = "s3c2440-i2c"; s3c_nand_setname("s3c2440-nand"); s3c_device_ts.name = "s3c2440-ts"; s3c_device_usbgadget.name = "s3c2440-usbgadget"; }
最终在这里将s3c_device_i2c0结构体的名字改为了s3c2440-i2c,然后回到我们的平台文件mach-smdk2440.c文件中,进入smdk2440_machine_init函数,这里通过s3c_i2c0_set_platdata函数,设置default_i2c_data结构体的bus_num为0,以及设置i2c的IO口,npd->cfg_gpio = s3c_i2c0_cfg_gpio,接着就调用paltform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices)),在platform平台下进行设备注册,设备名字为“s3c2440-i2c”
void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd) { struct s3c2410_platform_i2c *npd; if (!pd) pd = &default_i2c_data0; npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL); if (!npd) printk(KERN_ERR "%s: no memory for platform data\n", __func__); else if (!npd->cfg_gpio) npd->cfg_gpio = s3c_i2c0_cfg_gpio; s3c_device_i2c0.dev.platform_data = npd; }
到这里,平台设备已经注册完毕,接下来就是驱动咯:i2c-s3c2410.c
static struct platform_device_id s3c24xx_driver_ids[] = { { .name = "s3c2410-i2c", .driver_data = TYPE_S3C2410, }, { .name = "s3c2440-i2c", .driver_data = TYPE_S3C2440, }, { }, }; MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); static struct platform_driver s3c24xx_i2c_driver = { .probe = s3c24xx_i2c_probe, .remove = s3c24xx_i2c_remove, .id_table = s3c24xx_driver_ids, .driver = { .owner = THIS_MODULE, .name = "s3c-i2c", .pm = S3C24XX_DEV_PM_OPS, }, }; static int __init i2c_adap_s3c_init(void) { return platform_driver_register(&s3c24xx_i2c_driver); }
然后可以看见我们的名字“s3c2440-i2c”,和上面的设备匹配成功,调用probe函数:
static int s3c24xx_i2c_probe(struct platform_device *pdev) { .................... strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name)); i2c->adap.owner = THIS_MODULE; i2c->adap.algo = &s3c24xx_i2c_algorithm; i2c->adap.retries = 2; i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; i2c->tx_setup = 50; ................. ret = i2c_add_numbered_adapter(&i2c->adap); }
因为代码太长,这里给出部分重要信息代码。
i2c->adap.algo = &s3c24xx_i2c_algorithm;
其中:
static const struct i2c_algorithm s3c24xx_i2c_algorithm = { .master_xfer = s3c24xx_i2c_xfer, .functionality = s3c24xx_i2c_func, };
正如开始的图,i2c_adapter和i2c_algorithm都是操作i2c_bus的结构体,前者定义一个i2c模块,后者顶一个操作模块的方法,或者前者对应于物理上的一个适配器,而后者对应一套通信方法。
i2c_algorithm是i2c的底层实现函数,已封装,.master_xfer用于I2C总线传输,传送给它的i2c_msg数组中每个I2C消息,.functionality用于返回algorithm所支持的通信协议,如I2C_FUNC_I2C、I2C_FUNC_10BIT_ADDR、I2C_FUNC_SMBUS_READ_BYTE、I2C_FUNC_SMBUS_WRITE等。
ret=i2c_add_numbered_adapter(&i2c->ddap);
int i2c_add_numbered_adapter(struct i2c_adapter *adap) { int id; int status; if (adap->nr & ~MAX_ID_MASK) return -EINVAL; retry: if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) return -ENOMEM; mutex_lock(&core_lock); /* "above" here means "above or equal to", sigh; * we need the "equal to" result to force the result */ status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id); if (status == 0 && id != adap->nr) { status = -EBUSY; idr_remove(&i2c_adapter_idr, id); } mutex_unlock(&core_lock); if (status == -EAGAIN) goto retry; if (status == 0) status = i2c_register_adapter(adap); return status; }
可以看见最后调用i2c_register_adapter(adap)函数,在i2c_bus总线上进行注册,名字为dev_set_name(&adap->dev, “i2c-%d”, adap->nr); 再说下i2c_adapter与i2c_client的关系:
i2c_client依附于i2c_adapter,由于一个适配器上可以连接多个I2C设备,所以一个i2c_adapter也可以被多个i2c_client依附,i2c_adapter中包含依附于它的i2c_client的链表。
i2c的框架就到这里,下面以我们的eeprom为例看下i2c的实现:
static struct i2c_driver at24_driver = { .driver = { .name = "at24", .owner = THIS_MODULE, }, .probe = at24_probe, .remove = __devexit_p(at24_remove), .id_table = at24_ids, }; static int __init at24_init(void) { if (!io_limit) { pr_err("at24: io_limit must not be 0!\n") return -EINVAL; } io_limit = rounddown_pow_of_two(io_limit); return i2c_add_driver(&at24_driver); }
这里调用i2c_add_driver函数在i2c_bus总线下注册,然后看下他的读写函数,
static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf, unsigned offset, size_t count) { struct i2c_msg msg[2]; u8 msgbuf[2]; struct i2c_client *client; unsigned long timeout, read_time; int status, i; memset(msg, 0, sizeof(msg)); client = at24_translate_offset(at24, &offset); if (count > io_limit) count = io_limit; switch (at24->use_smbus) { case I2C_SMBUS_I2C_BLOCK_DATA: /* Smaller eeproms can work given some SMBus extension calls */ if (count > I2C_SMBUS_BLOCK_MAX) count = I2C_SMBUS_BLOCK_MAX; break; case I2C_SMBUS_WORD_DATA: count = 2; break; case I2C_SMBUS_BYTE_DATA: count = 1; break default: i = 0; if (at24->chip.flags & AT24_FLAG_ADDR16) msgbuf[i++] = offset >> 8; msgbuf[i++] = offset; msg[0].addr = client->addr; msg[0].buf = msgbuf; msg[0].len = i; msg[1].addr = client->addr; msg[1].flags = I2C_M_RD; msg[1].buf = buf; msg[1].len = count } timeout = jiffies + msecs_to_jiffies(write_timeout); do { read_time = jiffies; switch (at24->use_smbus) { case I2C_SMBUS_I2C_BLOCK_DATA: status = i2c_smbus_read_i2c_block_data(client, offset, count, buf); break; case I2C_SMBUS_WORD_DATA: status = i2c_smbus_read_word_data(client, offset); if (status >= 0) { buf[0] = status & 0xff; buf[1] = status >> 8; status = count; } break; case I2C_SMBUS_BYTE_DATA: status = i2c_smbus_read_byte_data(client, offset); if (status >= 0) { buf[0] = status; status = count; } break; default: status = i2c_transfer(client->adapter, msg, 2); if (status == 2) status = count; } dev_dbg(&client->dev, "read %zu@%d --> %d (%ld)\n", count, offset, status, jiffies); if (status == count) return count; /* REVISIT: at HZ=100, this is sloooow */ msleep(1); } while (time_before(read_time, timeout)); return -ETIMEDOUT; }
这里面调用i2c_transfer函数,里面以i2c_msg为单位通信:代码就不搬上来了这里就是ret=adap->algo->master_xfer(adap, msgs, num); 这个就是前面的.master_xfer函数,实现i2c总线传输函数。
相关文章推荐
- Linux I2C驱动分析与实现(一)
- I2C驱动框架分析
- 嵌入式Linux驱动笔记(十)------通俗易懂式分析了解i2c框架
- 【Linux高级驱动】I2C驱动框架分析
- 基于事件驱动的领域模型实现框架 - 分析框架如何解决各种典型业务逻辑场景
- Linux I2C驱动分析与实现(一)
- Linux I2C驱动分析与实现(二)
- Linux2.6.37 I2C驱动框架分析(一)
- Linux2.6.37 I2C驱动框架分析(三)
- i2c 驱动举例框架分析
- 学习笔记 --- LINUX I2C总线驱动框架分析
- Linux2.6.37 I2C驱动框架分析(五)
- Linux I2C驱动分析与实现
- Linux I2C驱动分析与实现--例子
- Linux2.6.37 I2C驱动框架分析(六)
- Linux I2C驱动分析与实现(二)
- 基于事件驱动的领域模型实现框架 - 分析框架如何解决各种典型业务逻辑场景
- linux设备驱动之 i2c设备驱动 at24c08驱动程序分析【全部地址的操作】
- SylixOS I2C驱动框架浅析
- 基于MTD的NANDFLASH设备驱动底层实现原理分析(一)