版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_16777851/article/details/88919507
由于I2C总线控制器通常是在内存(寄存器)上的, 所以它本身也连接在platform总线上, 要通过platform_driver和platform_device的匹配来执行。 因此尽管I2C适配器给别人提供了总线, 它自己也被认为是接在platform总线上的一个客户。 Linux的总线、 设备和驱动模型实际上是一个树形结构, 每个节点虽然可能成为别人的总线控制器, 但是自己也被认为是从上一级总线枚举出来的。
通常我们会在与I2C适配器所对应的platform_driver的probe() 函数中完成两个工作。
- 初始化I2C适配器所使用的硬件资源, 如申请I/O地址、 中断号、 时钟等。
- 通过i2c_add_adapter() 添加i2c_adapter的数据结构, 当然这个i2c_adapter数据结构的成员已经被xxx适配器的相应函数指针所初始化。
通常我们会在platform_driver的remove() 函数中完成与加载函数相反的工作。
- 释放I2C适配器所使用的硬件资源, 如释放I/O地址、 中断号、 时钟等。
- 通过i2c_del_adapter() 删除i2c_adapter的数据结构。
这里我们看2.6内核,三星是怎么做的。
平台设备这边,就是一个i2c适配器的寄存器和中断资源的指定
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",
.id = 0,
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource = s3c_i2c_resource,
};
当然这里也带了一些平台相关的参数过去,包括IIC的时钟频率,从机地址等。
static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {
.flags = 0,
.slave_addr = 0x10,
.frequency = 400*1000,
.sda_delay = S3C2410_IICLC_SDA_DELAY15 | 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;
}
平台设备注册以后会匹配到平台驱动。
/* device driver for platform bus bits */
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);
}
subsys_initcall(i2c_adap_s3c_init);
static void __exit i2c_adap_s3c_exit(void)
{
platform_driver_unregister(&s3c24xx_i2c_driver);
}
module_exit(i2c_adap_s3c_exit);
我们看到,这个和id_table中的"s3c2410-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;
/* 拿到平台设备中,iic控制器的时钟之类 */
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "no platform data\n");
return -EINVAL;
}
/* 申请一个三星自定义的i2c的管理结构体,见下面1分析 */
i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
if (!i2c) {
dev_err(&pdev->dev, "no memory for state\n");
return -ENOMEM;
}
/* 初始化adapor中的信息 */
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设备就是平台设备那个设备 */
i2c->dev = &pdev->dev;
i2c->clk = clk_get(&pdev->dev, "i2c");
if (IS_ERR(i2c->clk)) {
dev_err(&pdev->dev, "cannot get clock\n");
ret = -ENOENT;
goto err_noclk;
}
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);
if (res == NULL) {
dev_err(&pdev->dev, "cannot find IO resource\n");
ret = -ENOENT;
goto err_clk;
}
/* 申请资源 */
i2c->ioarea = request_mem_region(res->start, resource_size(res),
pdev->name);
if (i2c->ioarea == NULL) {
dev_err(&pdev->dev, "cannot request IO\n");
ret = -ENXIO;
goto err_clk;
}
/* 映射资源 */
i2c->regs = ioremap(res->start, resource_size(res));
if (i2c->regs == NULL) {
dev_err(&pdev->dev, "cannot map IO\n");
ret = -ENXIO;
goto err_ioarea;
}
dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
i2c->regs, i2c->ioarea, res);
/* setup info block for the i2c core */
/* 这个i2c绑定到适配器的私有数据,方便访问 */
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;
/* initialise the i2c controller */
/* 初始化i2c控制器,件下面分析2 */
ret = s3c24xx_i2c_init(i2c);
if (ret != 0)
goto err_iomap;
/* 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);
if (ret <= 0) {
dev_err(&pdev->dev, "cannot find IRQ\n");
goto err_iomap;
}
ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
dev_name(&pdev->dev), i2c);
if (ret != 0) {
dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
goto err_iomap;
}
/* 注册i2c的发送和接收频率 */
ret = s3c24xx_i2c_register_cpufreq(i2c);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
goto err_irq;
}
/* 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控制器 */
i2c->adap.nr = pdata->bus_num;
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret < 0) {
dev_err(&pdev->dev, "failed to add bus to i2c core\n");
goto err_cpufreq;
}
platform_set_drvdata(pdev, i2c);
clk_disable(i2c->clk);
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
return 0;
err_cpufreq:
s3c24xx_i2c_deregister_cpufreq(i2c);
err_irq:
free_irq(i2c->irq, i2c);
err_iomap:
iounmap(i2c->regs);
err_ioarea:
release_resource(i2c->ioarea);
kfree(i2c->ioarea);
err_clk:
clk_disable(i2c->clk);
clk_put(i2c->clk);
err_noclk:
kfree(i2c);
return ret;
}
分析1
struct s3c24xx_i2c {
spinlock_t lock;
wait_queue_head_t wait;
unsigned int suspended:1; /* 表示IIC控制器是否已经挂起,挂起的话就不能操作IIC控制器了 */
struct i2c_msg *msg; /* 存放消息 */
unsigned int msg_num; /* 消息个数 */
unsigned int msg_idx;
unsigned int msg_ptr;
unsigned int tx_setup;
unsigned int irq;
enum s3c24xx_i2c_state state;
unsigned long clkrate;
void __iomem *regs; /* 寄存器虚拟地址 */
struct clk *clk; /* i2c控制器的时钟 */
struct device *dev;
struct resource *ioarea;
struct i2c_adapter adap; /* 一个i2c控制器就是一个adaptor */
#ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
#endif
};
分析2
/* s3c24xx_i2c_init
*
* initialise the controller, set the IO lines and frequency
*/
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 */
//初始化用到的两个gpio
if (pdata->cfg_gpio)
pdata->cfg_gpio(to_platform_device(i2c->dev));
/* write slave address */
/* 设置i2c设备地址 */
writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);
dev_dbg(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... */
/* 计算时钟分频系数,并设置SCL时钟频率 */
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_dbg(i2c->dev, "bus frequency set to %d KHz\n", freq);
dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);
dev_dbg(i2c->dev, "S3C2440_IICLC=%08x\n", pdata->sda_delay);
writel(pdata->sda_delay, i2c->regs + S3C2440_IICLC); /* 设置数据线等时间 */
return 0;
}
上面的都很简单,i2c的核心在发生数据的算法和中断函数上面。