您的位置:首页 > 其它

I2C驱动的框架实现分析

2017-09-07 15:46 387 查看
前面讲了一下I2C的基本概念以及一个怎么去运用它的一个流程,但是没有做具体的分析,这篇博客转自http://m.blog.csdn.net/Guet_Kite/article/details/77870905,写得很明了,于是忍不住就转载啦。



首先这张图片很好的说明了我们内核的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总线传输函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  I2C