本文以s3c2440的I2C子系统为例, 分析其代码实现
本人学习驱动不久, 如有瑕疵纰漏, 欢迎指教, 谢谢
从硬件的角度看, I2C子系统由总线适配器和挂在总线上的设备组成
因此, 很容易想到, Linux的I2C子系统至少要提供:
总线上设备的支持, 以及其驱动
总线适配器的支持, 以及其驱动
1. S3C2440的I2C总线作为一个平台设备, 来看下添加平台设备的代码: /arch/arm/mach-s3c2440/mach-smdk2440.c
- static struct platform_device *smdk2440_devices[] __initdata = {
- &s3c_device_usb,
- &s3c_device_lcd,
- &s3c_device_wdt,
- &s3c_device_i2c0,
- &s3c_device_iis,
- };
- static void __init smdk2440_machine_init(void)
- {
- s3c24xx_fb_set_platdata(&smdk2440_fb_info);
- s3c_i2c0_set_platdata(NULL);
- platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
- smdk_machine_init();
- }
static struct platform_device *smdk2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
};
static void __init smdk2440_machine_init(void)
{
s3c24xx_fb_set_platdata(&smdk2440_fb_info);
s3c_i2c0_set_platdata(NULL);
platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
smdk_machine_init();
}
其中, s3c_device_i2c0即为I2C的平台设备, 看下它是怎么定义的: /arch/arm/plat-s3c/dev-i2c0.c
- static struct resource s3c_i2c_resource[] = {
- [0] = {
- .start = S3C_PA_IIC,
- .end = S3C_PA_IIC + SZ_4K - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = IRQ_IIC,
- .end = IRQ_IIC,
- .flags = IORESOURCE_IRQ,
- },
- };
- struct platform_device s3c_device_i2c0 = {
- .name = "s3c2410-i2c",
- #ifdef CONFIG_S3C_DEV_I2C1
- .id = 0,
- #else
- .id = -1,
- #endif
- .num_resources = ARRAY_SIZE(s3c_i2c_resource),
- .resource = s3c_i2c_resource,
- };
static struct resource s3c_i2c_resource[] = {
[0] = {
.start = S3C_PA_IIC,
.end = S3C_PA_IIC + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_IIC,
.end = IRQ_IIC,
.flags = IORESOURCE_IRQ,
},
};
struct platform_device s3c_device_i2c0 = {
.name = "s3c2410-i2c",
#ifdef CONFIG_S3C_DEV_I2C1
.id = 0,
#else
.id = -1,
#endif
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource = s3c_i2c_resource,
};
设备名称为s3c2410-i2c, 同时定义了两个资源, 一段IO内存, 一个中断号
在smdk2440_machine_init中, 即初始化过程中, 调用了s3c_i2c0_set_platdata(NULL)
- static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {
- .flags = 0,
- .slave_addr = 0x10,
- .bus_freq = 100*1000,
- .max_freq = 400*1000,
- .sda_delay = S3C2410_IICLC_SDA_DELAY5 | S3C2410_IICLC_FILTER_ON,
- };
- 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;
- }
static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {
.flags = 0,
.slave_addr = 0x10,
.bus_freq = 100*1000,
.max_freq = 400*1000,
.sda_delay = S3C2410_IICLC_SDA_DELAY5 | S3C2410_IICLC_FILTER_ON,
};
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;
}
阅读s3c_i2c0_set_platdata函数, 是对s3c_device_i2c0添加一些设备参数(default_i2c_data0)
简单的看下系统默认提供的这套参数, slave_addr应该是作为从机的地址, bus_freq应该是总线频率, 为100kHz, ...
所以, 设备结构体s3c_device_i2c0最后应该是这个样子:
- struct platform_device s3c_device_i2c0 = {
- .name = "s3c2410-i2c",
- .id = -1,
- .num_resources = ARRAY_SIZE(s3c_i2c_resource),
- .resource = s3c_i2c_resource,
- };
- s3c_device_i2c0.dev.platform_data = {
- .flags = 0,
- .slave_addr = 0x10,
- .bus_freq = 100*1000,
- .max_freq = 400*1000,
- .sda_delay = S3C2410_IICLC_SDA_DELAY5 | S3C2410_IICLC_FILTER_ON,
- .cfg_gpio = s3c_i2c0_cfg_gpio;
- };
struct platform_device s3c_device_i2c0 = {
.name = "s3c2410-i2c",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource = s3c_i2c_resource,
};
s3c_device_i2c0.dev.platform_data = {
.flags = 0,
.slave_addr = 0x10,
.bus_freq = 100*1000,
.max_freq = 400*1000,
.sda_delay = S3C2410_IICLC_SDA_DELAY5 | S3C2410_IICLC_FILTER_ON,
.cfg_gpio = s3c_i2c0_cfg_gpio;
};
其中有个函数指针cfg_gpio, 指向了s3c_i2c0_cfg_gpio, 作用是配置I2C相应IO接口:
- void s3c_i2c0_cfg_gpio(struct platform_device *dev)
- {
- s3c2410_gpio_cfgpin(S3C2410_GPE15, S3C2410_GPE15_IICSDA);
- s3c2410_gpio_cfgpin(S3C2410_GPE14, S3C2410_GPE14_IICSCL);
- }
void s3c_i2c0_cfg_gpio(struct platform_device *dev)
{
s3c2410_gpio_cfgpin(S3C2410_GPE15, S3C2410_GPE15_IICSDA);
s3c2410_gpio_cfgpin(S3C2410_GPE14, S3C2410_GPE14_IICSCL);
}
自此, I2C作为平台设备, 被添加到了平台总线上.
2. 设备添加完了, 就该驱动了, 来看下I2C设备驱动定义: /drivers/i2c/buses/i2c-s3c2410.c
- static struct platform_driver s3c2410_i2c_driver = {
- .probe = s3c24xx_i2c_probe,
- .remove = s3c24xx_i2c_remove,
- .suspend_late = s3c24xx_i2c_suspend_late,
- .resume = s3c24xx_i2c_resume,
- .driver = {
- .owner = THIS_MODULE,
- .name = "s3c2410-i2c",
- },
- };
static struct platform_driver s3c2410_i2c_driver = {
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
.suspend_late = s3c24xx_i2c_suspend_late,
.resume = s3c24xx_i2c_resume,
.driver = {
.owner = THIS_MODULE,
.name = "s3c2410-i2c",
},
};
驱动名字为s3c2410-i2c, 和设备名字一样
然后将注册驱动, 将驱动添加到平台总线上:
- static int __init i2c_adap_s3c_init(void)
- {
- int ret;
- ret = platform_driver_register(&s3c2410_i2c_driver);
- if (ret == 0) {
- ret = platform_driver_register(&s3c2440_i2c_driver);
- if (ret)
- platform_driver_unregister(&s3c2410_i2c_driver);
- }
- return ret;
- }
static int __init i2c_adap_s3c_init(void)
{
int ret;
ret = platform_driver_register(&s3c2410_i2c_driver);
if (ret == 0) {
ret = platform_driver_register(&s3c2440_i2c_driver);
if (ret)
platform_driver_unregister(&s3c2410_i2c_driver);
}
return ret;
}
回想一下, 当添加一个平台设备或者平台驱动时, 都会调用总线的match函数来将设备和驱动配对
而平台总线的match是根据设备和驱动的名字, 当相同时, 即匹配成功, 并调用驱动的probe函数
那么, 接着来看下s3c24xx_i2c_probe函数, 为了看着清晰些, 我把错误检测和错误恢复的代码删了
- /* s3c24xx_i2c_probe
- *
- * called by the bus driver when a suitable device is found
- */
- static int s3c24xx_i2c_probe(struct platform_device *pdev)
- {
- struct s3c24xx_i2c *i2c;
- struct s3c2410_platform_i2c *pdata;
- struct resource *res;
- int ret;
- pdata = pdev->dev.platform_data;
- i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
- 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;
- spin_lock_init(&i2c->lock);
- init_waitqueue_head(&i2c->wait);
- /* find the clock and enable it */
- i2c->dev = &pdev->dev;
- i2c->clk = clk_get(&pdev->dev, "i2c");
- dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
- clk_enable(i2c->clk);
- /* map the registers */
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,
- pdev->name);
- i2c->regs = ioremap(res->start, (res->end-res->start)+1);
- dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
- i2c->regs, i2c->ioarea, res);
- /* setup info block for the i2c core */
- i2c->adap.algo_data = i2c;
- i2c->adap.dev.parent = &pdev->dev;
- /* initialise the i2c controller */
- ret = s3c24xx_i2c_init(i2c);
- /* find the IRQ for this unit (note, this relies on the init call to
- * ensure no current IRQs pending
- */
- i2c->irq = ret = platform_get_irq(pdev, 0);
- ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
- dev_name(&pdev->dev), i2c);
- ret = s3c24xx_i2c_register_cpufreq(i2c);
- /* Note, previous versions of the driver used i2c_add_adapter()
- * to add the bus at any number. We now pass the bus number via
- * the platform data, so if unset it will now default to always
- * being bus 0.
- */
- i2c->adap.nr = pdata->bus_num;
- ret = i2c_add_numbered_adapter(&i2c->adap);
- platform_set_drvdata(pdev, i2c);
- dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
- return 0;
- }
/* s3c24xx_i2c_probe
*
* called by the bus driver when a suitable device is found
*/
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c;
struct s3c2410_platform_i2c *pdata;
struct resource *res;
int ret;
pdata = pdev->dev.platform_data;
i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
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;
spin_lock_init(&i2c->lock);
init_waitqueue_head(&i2c->wait);
/* find the clock and enable it */
i2c->dev = &pdev->dev;
i2c->clk = clk_get(&pdev->dev, "i2c");
dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
clk_enable(i2c->clk);
/* map the registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,
pdev->name);
i2c->regs = ioremap(res->start, (res->end-res->start)+1);
dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
i2c->regs, i2c->ioarea, res);
/* setup info block for the i2c core */
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;
/* initialise the i2c controller */
ret = s3c24xx_i2c_init(i2c);
/* find the IRQ for this unit (note, this relies on the init call to
* ensure no current IRQs pending
*/
i2c->irq = ret = platform_get_irq(pdev, 0);
ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
dev_name(&pdev->dev), i2c);
ret = s3c24xx_i2c_register_cpufreq(i2c);
/* Note, previous versions of the driver used i2c_add_adapter()
* to add the bus at any number. We now pass the bus number via
* the platform data, so if unset it will now default to always
* being bus 0.
*/
i2c->adap.nr = pdata->bus_num;
ret = i2c_add_numbered_adapter(&i2c->adap);
platform_set_drvdata(pdev, i2c);
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
return 0;
}
比较常规的probe, 使能时钟, 申请IO内存, 映射IO内存, 申请中断就不说了, 删去, 得到以下:
- static int s3c24xx_i2c_probe(struct platform_device *pdev)
- {
- struct s3c24xx_i2c *i2c;
- struct s3c2410_platform_i2c *pdata;
- struct resource *res;
- int ret;
- pdata = pdev->dev.platform_data;
- i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
- 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;
- /* setup info block for the i2c core */
- i2c->adap.algo_data = i2c;
- i2c->adap.dev.parent = &pdev->dev;
- /* initialise the i2c controller */
- ret = s3c24xx_i2c_init(i2c);
- ret = s3c24xx_i2c_register_cpufreq(i2c);
- /* Note, previous versions of the driver used i2c_add_adapter()
- * to add the bus at any number. We now pass the bus number via
- * the platform data, so if unset it will now default to always
- * being bus 0.
- */
- i2c->adap.nr = pdata->bus_num;
- ret = i2c_add_numbered_adapter(&i2c->adap);
- platform_set_drvdata(pdev, i2c);
- return 0;
- }
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c;
struct s3c2410_platform_i2c *pdata;
struct resource *res;
int ret;
pdata = pdev->dev.platform_data;
i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
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;
/* setup info block for the i2c core */
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;
/* initialise the i2c controller */
ret = s3c24xx_i2c_init(i2c);
ret = s3c24xx_i2c_register_cpufreq(i2c);
/* Note, previous versions of the driver used i2c_add_adapter()
* to add the bus at any number. We now pass the bus number via
* the platform data, so if unset it will now default to always
* being bus 0.
*/
i2c->adap.nr = pdata->bus_num;
ret = i2c_add_numbered_adapter(&i2c->adap);
platform_set_drvdata(pdev, i2c);
return 0;
}
是不是清爽多了
函数定义了一个struct s3c24xx_i2c *i2c, 然后对其初始化
而且可以看出, 主要是对i2c->adap进行初始化
i2c->adap指向一个struct i2c_adapter结构体, 可以翻译为i2c适配器, 来看下
- /*
- * i2c_adapter is the structure used to identify a physical i2c bus along
- * with the access algorithms necessary to access it.
- */
- struct i2c_adapter {
- struct module *owner;
- unsigned int id;
- unsigned int class; /* classes to allow probing for */
- const struct i2c_algorithm *algo; /* the algorithm to access the bus */
- void *algo_data;
- /* --- administration stuff. */
- int (*client_register)(struct i2c_client *);
- int (*client_unregister)(struct i2c_client *);
- /* data fields that are valid for all devices */
- u8 level; /* nesting level for lockdep */
- struct mutex bus_lock;
- struct mutex clist_lock;
- int timeout; /* in jiffies */
- int retries;
- struct device dev; /* the adapter device */
- int nr;
- struct list_head clients; /* DEPRECATED */
- char name[48];
- struct completion dev_released;
- };
/*
* i2c_adapter is the structure used to identify a physical i2c bus along
* with the access algorithms necessary to access it.
*/
struct i2c_adapter {
struct module *owner;
unsigned int id;
unsigned int class; /* classes to allow probing for */
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
void *algo_data;
/* --- administration stuff. */
int (*client_register)(struct i2c_client *);
int (*client_unregister)(struct i2c_client *);
/* data fields that are valid for all devices */
u8 level; /* nesting level for lockdep */
struct mutex bus_lock;
struct mutex clist_lock;
int timeout; /* in jiffies */
int retries;
struct device dev; /* the adapter device */
int nr;
struct list_head clients; /* DEPRECATED */
char name[48];
struct completion dev_released;
};
struct i2c_adapter中还有个const struct i2c_algorithm *algo
将i2c_adapter简称为adp, i2c_algorithm简称为algo
具体讲一下这两个数据结构有什么用:
I2C子系统主要有三部分组成: core, adap, algo
core是i2c子系统的核心层, 将与具体硬件相关的代码交给adap
而adap含有一个algo, algo定义了与具体硬件通讯的代码所以
如果有一个应用程序希望与i2c设备通讯, 将依次通过core, adap, algo, 最终将数据发到总线上
- static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
- {
- unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;
- struct s3c2410_platform_i2c *pdata;
- unsigned int freq;
- /* get the plafrom data */
- pdata = i2c->dev->platform_data;
- /* inititalise the gpio */
- if (pdata->cfg_gpio)
- pdata->cfg_gpio(to_platform_device(i2c->dev));
- /* write slave address */
- writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);
- dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);
- writel(iicon, i2c->regs + S3C2410_IICCON);
- /* we need to work out the divisors for the clock... */
- if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {
- writel(0, i2c->regs + S3C2410_IICCON);
- dev_err(i2c->dev, "cannot meet bus frequency required\n");
- return -EINVAL;
- }
- /* todo - check that the i2c lines aren't being dragged anywhere */
- dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);
- dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);
- /* check for s3c2440 i2c controller */
- if (s3c24xx_i2c_is2440(i2c)) {
- dev_dbg(i2c->dev, "S3C2440_IICLC=%08x\n", pdata->sda_delay);
- writel(pdata->sda_delay, i2c->regs + S3C2440_IICLC);
- }
- return 0;
- }
static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
{
unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;
struct s3c2410_platform_i2c *pdata;
unsigned int freq;
/* get the plafrom data */
pdata = i2c->dev->platform_data;
/* inititalise the gpio */
if (pdata->cfg_gpio)
pdata->cfg_gpio(to_platform_device(i2c->dev));
/* write slave address */
writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);
dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);
writel(iicon, i2c->regs + S3C2410_IICCON);
/* we need to work out the divisors for the clock... */
if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {
writel(0, i2c->regs + S3C2410_IICCON);
dev_err(i2c->dev, "cannot meet bus frequency required\n");
return -EINVAL;
}
/* todo - check that the i2c lines aren't being dragged anywhere */
dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);
dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);
/* check for s3c2440 i2c controller */
if (s3c24xx_i2c_is2440(i2c)) {
dev_dbg(i2c->dev, "S3C2440_IICLC=%08x\n", pdata->sda_delay);
writel(pdata->sda_delay, i2c->regs + S3C2440_IICLC);
}
return 0;
}
主要是初始化硬件寄存器, 没什么好说的, 参考s3c2440手册
继续看probe初始化过程
i2c->adap.nr = pdata->bus_num;
ret = i2c_add_numbered_adapter(&i2c->adap);
- 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;
- }
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)
- static int i2c_register_adapter(struct i2c_adapter *adap)
- {
- int res = 0, dummy;
- /* Can't register until after driver model init */
- if (unlikely(WARN_ON(!i2c_bus_type.p)))
- return -EAGAIN;
- mutex_init(&adap->bus_lock);
- mutex_init(&adap->clist_lock);
- INIT_LIST_HEAD(&adap->clients);
- mutex_lock(&core_lock);
- /* Add the adapter to the driver core.
- * If the parent pointer is not set up,
- * we add this adapter to the host bus.
- */
- if (adap->dev.parent == NULL) {
- adap->dev.parent = &platform_bus;
- pr_debug("I2C adapter driver [%s] forgot to specify "
- "physical device\n", adap->name);
- }
- dev_set_name(&adap->dev, "i2c-%d", adap->nr);
- adap->dev.release = &i2c_adapter_dev_release;
- adap->dev.class = &i2c_adapter_class;
- res = device_register(&adap->dev);
- if (res)
- goto out_list;
- dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
- /* create pre-declared device nodes for new-style drivers */
- if (adap->nr < __i2c_first_dynamic_bus_num)
- i2c_scan_static_board_info(adap);
- /* Notify drivers */
- dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,
- i2c_do_add_adapter);
- out_unlock:
- mutex_unlock(&core_lock);
- return res;
- out_list:
- idr_remove(&i2c_adapter_idr, adap->nr);
- goto out_unlock;
- }
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = 0, dummy;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p)))
return -EAGAIN;
mutex_init(&adap->bus_lock);
mutex_init(&adap->clist_lock);
INIT_LIST_HEAD(&adap->clients);
mutex_lock(&core_lock);
/* Add the adapter to the driver core.
* If the parent pointer is not set up,
* we add this adapter to the host bus.
*/
if (adap->dev.parent == NULL) {
adap->dev.parent = &platform_bus;
pr_debug("I2C adapter driver [%s] forgot to specify "
"physical device\n", adap->name);
}
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
adap->dev.release = &i2c_adapter_dev_release;
adap->dev.class = &i2c_adapter_class;
res = device_register(&adap->dev);
if (res)
goto out_list;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
/* create pre-declared device nodes for new-style drivers */
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
/* Notify drivers */
dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,
i2c_do_add_adapter);
out_unlock:
mutex_unlock(&core_lock);
return res;
out_list:
idr_remove(&i2c_adapter_idr, adap->nr);
goto out_unlock;
}
其中, 最关键的是i2c_scan_static_board_info(adap);
- static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
- {
- struct i2c_devinfo *devinfo;
- mutex_lock(&__i2c_board_lock);
- list_for_each_entry(devinfo, &__i2c_board_list, list) {
- if (devinfo->busnum == adapter->nr
- && !i2c_new_device(adapter,
- &devinfo->board_info))
- printk(KERN_ERR "i2c-core: can't create i2c%d-%04x\n",
- i2c_adapter_id(adapter),
- devinfo->board_info.addr);
- }
- mutex_unlock(&__i2c_board_lock);
- }
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo *devinfo;
mutex_lock(&__i2c_board_lock);
list_for_each_entry(devinfo, &__i2c_board_list, list) {
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,
&devinfo->board_info))
printk(KERN_ERR "i2c-core: can't create i2c%d-%04x\n",
i2c_adapter_id(adapter),
devinfo->board_info.addr);
}
mutex_unlock(&__i2c_board_lock);
}
此函数扫描所有预先定义好的设备(挂在总线上的设备), 然后添加设备(i2c_new_device)
来看下i2c的从机设备是如何添加的:
- struct i2c_client *
- i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
- {
- struct i2c_client *client;
- int status;
- client = kzalloc(sizeof *client, GFP_KERNEL);
- if (!client)
- return NULL;
- client->adapter = adap;
- client->dev.platform_data = info->platform_data;
- if (info->archdata)
- client->dev.archdata = *info->archdata;
- client->flags = info->flags;
- client->addr = info->addr;
- client->irq = info->irq;
- strlcpy(client->name, info->type, sizeof(client->name));
- /* a new style driver may be bound to this device when we
- * return from this function, or any later moment (e.g. maybe
- * hotplugging will load the driver module). and the device
- * refcount model is the standard driver model one.
- */
- status = i2c_attach_client(client);
- if (status < 0) {
- kfree(client);
- client = NULL;
- }
- return client;
- }
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
int status;
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
client->adapter = adap;
client->dev.platform_data = info->platform_data;
if (info->archdata)
client->dev.archdata = *info->archdata;
client->flags = info->flags;
client->addr = info->addr;
client->irq = info->irq;
strlcpy(client->name, info->type, sizeof(client->name));
/* a new style driver may be bound to this device when we
* return from this function, or any later moment (e.g. maybe
* hotplugging will load the driver module). and the device
* refcount model is the standard driver model one.
*/
status = i2c_attach_client(client);
if (status < 0) {
kfree(client);
client = NULL;
}
return client;
}
可以看出, i2c的从机设备被定义为一个i2c_client结构, addr为从机地址, irq为中断号
接着调用i2c_attach_client
- int i2c_attach_client(struct i2c_client *client)
- {
- struct i2c_adapter *adapter = client->adapter;
- int res;
- /* Check for address business */
- res = i2c_check_addr(adapter, client->addr);
- if (res)
- return res;
- client->dev.parent = &client->adapter->dev;
- client->dev.bus = &i2c_bus_type;
- if (client->driver)
- client->dev.driver = &client->driver->driver;
- if (client->driver && !is_newstyle_driver(client->driver)) {
- client->dev.release = i2c_client_release;
- client->dev.uevent_suppress = 1;
- } else
- client->dev.release = i2c_client_dev_release;
- dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adapter),
- client->addr);
- res = device_register(&client->dev);
- if (res)
- goto out_err;
- mutex_lock(&adapter->clist_lock);
- list_add_tail(&client->list, &adapter->clients);
- mutex_unlock(&adapter->clist_lock);
- dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
- client->name, dev_name(&client->dev));
- if (adapter->client_register) {
- if (adapter->client_register(client)) {
- dev_dbg(&adapter->dev, "client_register "
- "failed for client [%s] at 0x%02x\n",
- client->name, client->addr);
- }
- }
- return 0;
- out_err:
- dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "
- "(%d)\n", client->name, client->addr, res);
- return res;
- }
int i2c_attach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
int res;
/* Check for address business */
res = i2c_check_addr(adapter, client->addr);
if (res)
return res;
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
if (client->driver)
client->dev.driver = &client->driver->driver;
if (client->driver && !is_newstyle_driver(client->driver)) {
client->dev.release = i2c_client_release;
client->dev.uevent_suppress = 1;
} else
client->dev.release = i2c_client_dev_release;
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adapter),
client->addr);
res = device_register(&client->dev);
if (res)
goto out_err;
mutex_lock(&adapter->clist_lock);
list_add_tail(&client->list, &adapter->clients);
mutex_unlock(&adapter->clist_lock);
dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
client->name, dev_name(&client->dev));
if (adapter->client_register) {
if (adapter->client_register(client)) {
dev_dbg(&adapter->dev, "client_register "
"failed for client [%s] at 0x%02x\n",
client->name, client->addr);
}
}
return 0;
out_err:
dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "
"(%d)\n", client->name, client->addr, res);
return res;
}
i2c_attach_client做了添加设备(device_register)和将此client加入到其所在adp的clients链表中
自此, 总线驱动就添加完了, 总结一下, 具体经过以下步骤:
添加总线驱动 -> probe -> 创建适配器结构 -> 初始化适配器硬件 -> 扫描从机设备并添加
再来看下algo
在probe中, 有i2c->adap.algo = &s3c24xx_i2c_algorithm;
- static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
- .master_xfer = s3c24xx_i2c_xfer,
- .functionality = s3c24xx_i2c_func,
- };
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
其中, master_xfer即为与具体硬件通讯的收发数据函数
- static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
- struct i2c_msg *msgs, int num)
- {
- struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
- int retry;
- int ret;
- for (retry = 0; retry < adap->retries; retry++) {
- ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
- if (ret != -EAGAIN)
- return ret;
- dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
- udelay(100);
- }
- return -EREMOTEIO;
- }
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
struct i2c_msg *msgs, int num)
{
struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
int retry;
int ret;
for (retry = 0; retry < adap->retries; retry++) {
ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
if (ret != -EAGAIN)
return ret;
dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);
udelay(100);
}
return -EREMOTEIO;
}
参数i2c_msg封装了buf以及flags, 用以指定读或者写或者其他操作:
- struct i2c_msg {
- __u16 addr; /* slave address */
- __u16 flags;
- #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
- #define I2C_M_RD 0x0001 /* read data, from slave to master */
- #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
- #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
- #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
- #define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
- #define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
- __u16 len; /* msg length */
- __u8 *buf; /* pointer to msg data */
- };
struct i2c_msg {
__u16 addr; /* slave address */
__u16 flags;
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_RD 0x0001 /* read data, from slave to master */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
__u16 len; /* msg length */
__u8 *buf; /* pointer to msg data */
};
3. 从机设备
来看下如何静态添加一个设备
在arch/arm/mach-s3c2440/mach-anubis.c中, 可以看到一个例子:
- /* I2C devices. */
- static struct i2c_board_info anubis_i2c_devs[] __initdata = {
- {
- I2C_BOARD_INFO("tps65011", 0x48),
- .irq = IRQ_EINT20,
- }
- };
/* I2C devices. */
static struct i2c_board_info anubis_i2c_devs[] __initdata = {
{
I2C_BOARD_INFO("tps65011", 0x48),
.irq = IRQ_EINT20,
}
};
tps65011是设备名称, 0x48为从机地址
还有个中断, 连接在2440的EINT20上
可以查一下这个芯片, 是个锂电的电源管理芯片
那个中断用于当电量少于10%时报警
4. 从机设备驱动
来看一个I2C EEPROM设备驱动: /drivers/i2c/chips/ ds1682.c
- static struct i2c_driver ds1682_driver = {
- .driver = {
- .name = "ds1682",
- },
- .probe = ds1682_probe,
- .remove = ds1682_remove,
- .id_table = ds1682_id,
- };
- static int __init ds1682_init(void)
- {
- return i2c_add_driver(&ds1682_driver);
- }
static struct i2c_driver ds1682_driver = {
.driver = {
.name = "ds1682",
},
.probe = ds1682_probe,
.remove = ds1682_remove,
.id_table = ds1682_id,
};
static int __init ds1682_init(void)
{
return i2c_add_driver(&ds1682_driver);
}
- i2c_add_driver会遍历I2C总线上的设备, 匹配设备与驱动, 并调用probe
- <pre name="code" class="cpp">/*
- * Called when a ds1682 device is matched with this driver
- */
- static int ds1682_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
- {
- int rc;
- if (!i2c_check_functionality(client->adapter,
- I2C_FUNC_SMBUS_I2C_BLOCK)) {
- dev_err(&client->dev, "i2c bus does not support the ds1682\n");
- rc = -ENODEV;
- goto exit;
- }
- rc = sysfs_create_group(&client->dev.kobj, &ds1682_group);
- if (rc)
- goto exit;
- rc = sysfs_create_bin_file(&client->dev.kobj, &ds1682_eeprom_attr);
- if (rc)
- goto exit_bin_attr;
- return 0;
- exit_bin_attr:
- sysfs_remove_group(&client->dev.kobj, &ds1682_group);
- exit:
- return rc;
- }</pre>由于EEPROM比较简单, 不需要什么硬件初始化所以我们看到, 这个probe没有做硬件初始化probe创建了在sysfs中的自己的设备目录下创建了一些属性: ds1682_group和ds1682_eeprom_attr<br>
- <pre name="code" class="cpp">static SENSOR_DEVICE_ATTR_2(elapsed_time, S_IRUGO | S_IWUSR, ds1682_show,
- ds1682_store, 4, DS1682_REG_ELAPSED);
- static SENSOR_DEVICE_ATTR_2(alarm_time, S_IRUGO | S_IWUSR, ds1682_show,
- ds1682_store, 4, DS1682_REG_ALARM);
- static SENSOR_DEVICE_ATTR_2(event_count, S_IRUGO | S_IWUSR, ds1682_show,
- ds1682_store, 2, DS1682_REG_EVT_CNTR);
- static const struct attribute_group ds1682_group = {
- .attrs = (struct attribute *[]) {
- &sensor_dev_attr_elapsed_time.dev_attr.attr,
- &sensor_dev_attr_alarm_time.dev_attr.attr,
- &sensor_dev_attr_event_count.dev_attr.attr,
- NULL,
- },
- };
- static struct bin_attribute ds1682_eeprom_attr = {
- .attr = {
- .name = "eeprom",
- .mode = S_IRUGO | S_IWUSR,
- },
- .size = DS1682_EEPROM_SIZE,
- .read = ds1682_eeprom_read,
- .write = ds1682_eeprom_write,
- };</pre>根据这些属性的定义, 程序在ds1682的设备目录下建立了以下文件:
- <pre></pre>
- <p></p>
- <blockquote style="margin: 0pt 0pt 0pt 40px; border: medium none; padding: 0px;">
- <p></p><pre name="code" class="cpp" style="background-color: rgb(255, 255, 255);"><pre name="code" class="cpp" style="background-color: rgb(255, 255, 255);">elapsed_time, alarm_time, event_count, eeprom
- </pre>
- <pre></pre>
- <p></p>
- </pre></blockquote>
- 其中eeprom文件为一个二进制文件, 直接将整片存储空间映射到该文件, 方便用户空间对其读写<br>
- <p><br>
- 再来看下前三个属性的读写方法: ds1682_show, ds1682_store<br>
- </p><pre name="code" class="cpp">/*
- * Generic counter attributes
- */
- static ssize_t ds1682_show(struct device *dev, struct device_attribute *attr,
- char *buf)
- {
- struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
- struct i2c_client *client = to_i2c_client(dev);
- __le32 val = 0;
- int rc;
- dev_dbg(dev, "ds1682_show() called on %s\n", attr->attr.name);
- /* Read the register */
- rc = i2c_smbus_read_i2c_block_data(client, sattr->index, sattr->nr,
- (u8 *) & val);
- if (rc < 0)
- return -EIO;
- /* Special case: the 32 bit regs are time values with 1/4s
- * resolution, scale them up to milliseconds */
- if (sattr->nr == 4)
- return sprintf(buf, "%llu\n",
- ((unsigned long long)le32_to_cpu(val)) * 250);
- /* Format the output string and return # of bytes */
- return sprintf(buf, "%li\n", (long)le32_to_cpu(val));
- }
- static ssize_t ds1682_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
- {
- struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
- struct i2c_client *client = to_i2c_client(dev);
- char *endp;
- u64 val;
- __le32 val_le;
- int rc;
- dev_dbg(dev, "ds1682_store() called on %s\n", attr->attr.name);
- /* Decode input */
- val = simple_strtoull(buf, &endp, 0);
- if (buf == endp) {
- dev_dbg(dev, "input string not a number\n");
- return -EINVAL;
- }
- /* Special case: the 32 bit regs are time values with 1/4s
- * resolution, scale input down to quarter-seconds */
- if (sattr->nr == 4)
- do_div(val, 250);
- /* write out the value */
- val_le = cpu_to_le32(val);
- rc = i2c_smbus_write_i2c_block_data(client, sattr->index, sattr->nr,
- (u8 *) & val_le);
- if (rc < 0) {
- dev_err(dev, "register write failed; reg=0x%x, size=%i\n",
- sattr->index, sattr->nr);
- return -EIO;
- }
- return count;
- }</pre>show函数中根据属性的编号, 读取ds1682的寄存器, 获取其相应属性:<br>
- <blockquote style="margin: 0pt 0pt 0pt 40px; border: medium none; padding: 0px;">
- <p>rc = i2c_smbus_read_i2c_block_data(client, sattr->index, sattr->nr, (u8 *) & val);</p>
- </blockquote>
- <p>读写是不很方便呢, 这里show只调用了i2c_smbus_read_i2c_block_data, 并告诉它地址和长度即可<br>
- 而i2c_smbus_read_i2c_block_data是i2c_core提供的功能<br>
- 这里可以看到这样抽象的好处了, 编写i2c设备驱动的人, 不需要关心适配器是什么或者怎么实现的<br>
- <br>
- store函数也类似<br>
- <br>
- 再来看下eeprom属性的读写方法:<br>
- </p><pre name="code" class="cpp">/*
- * User data attribute
- */
- static ssize_t ds1682_eeprom_read(struct kobject *kobj, struct bin_attribute *attr,
- char *buf, loff_t off, size_t count)
- {
- struct i2c_client *client = kobj_to_i2c_client(kobj);
- int rc;
- dev_dbg(&client->dev, "ds1682_eeprom_read(p=%p, off=%lli, c=%zi)\n",
- buf, off, count);
- if (off >= DS1682_EEPROM_SIZE)
- return 0;
- if (off + count > DS1682_EEPROM_SIZE)
- count = DS1682_EEPROM_SIZE - off;
- rc = i2c_smbus_read_i2c_block_data(client, DS1682_REG_EEPROM + off,
- count, buf);
- if (rc < 0)
- return -EIO;
- return count;
- }
- static ssize_t ds1682_eeprom_write(struct kobject *kobj, struct bin_attribute *attr,
- char *buf, loff_t off, size_t count)
- {
- struct i2c_client *client = kobj_to_i2c_client(kobj);
- dev_dbg(&client->dev, "ds1682_eeprom_write(p=%p, off=%lli, c=%zi)\n",
- buf, off, count);
- if (off >= DS1682_EEPROM_SIZE)
- return -ENOSPC;
- if (off + count > DS1682_EEPROM_SIZE)
- count = DS1682_EEPROM_SIZE - off;
- /* Write out to the device */
- if (i2c_smbus_write_i2c_block_data(client, DS1682_REG_EEPROM + off,
- count, buf) < 0)
- return -EIO;
- return count;
- }</pre>这个就更简单了, 像不像普通字符设备的read和write<br>
- <br>
- 自此, 除了I2C Core的代码没有分析, 其他的都分析完了<br>