1.S3C2410I2C
控制器硬件描述
S3C2410处理器内部集成了一个I2C控制器,通过4个寄存器就可方便地对其进行控制,这4个寄存器如下。
1 IICCON:I2C
控制寄存器。
2 IICSTAT:I2C
状态寄存器。
3 IICDS:I2C
收发数据移位寄存器。
4 IICADD:I2C
地址寄存器。
S3C2410处理器内部集成的I2C控制器可支持主、从两种模式,我们主要使用其主模式。通过对IICCON、IICDS和IICADD寄存器的操作,可在I2C总线上产生开始位、停止位、数据和地址,而传输的状态则通过IICSTAT寄存器获取。
2.S3C2410I2C
总线驱动总体分析
S3C2410的I2C总线驱动设计主要要完成以下工作。
1 设计对应于i2c_adapter_xxx_init()模板的S3C2410的模块加载函数和对应于i2c_adapter_xxx_exit()函数模板的模块卸载函数。
2设计对应于i2c_adapter_xxx_xfer()模板的S3C2410适配器的通信方法函数。针对
S3C2410, functionality()
函数 只需 简单 地返 回I2C_FUNC_I2C|I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING
表明其支持的功能。内核 源代 码中 的/drivers/i2c/busses/i2c-s3c2410.c
图1
给出了S3C2410驱动中的主要函数与模板函数的对应关系,由于实现通信方法的方式不一样,模板的一个函数可能对应与S3C2410I2C
总线驱动的多个函数。
图1 I2C
总线驱动模板与S3C2410I2C
总线驱动的映射
3.S3C2410I2C
适配器驱动的模块加载与卸载
I2C适配器驱动被作为一个单独的模块加载进内核,在模块的加载和卸载函数中,只需注册和注销一个platform_driver结构体,如代码清单1.1所示。
代码清单1.1 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 void _ _exit i2c_adap_s3c_exit(void)
{
platform_driver_unregister(&s3c2410_i2c_driver);
platform_driver_unregister(&s3c2440_i2c_driver);
}
module_init(i2c_adap_s3c_init);
module_exit(i2c_adap_s3c_exit);
platform_driver结构体包含了具体适配器的probe()函数、remove()函数、resume()
函数指针等信息,它需要被定义和赋值,如代码清单1.2所示。
代码清单1.2platform_driver
结构体
staticstruct platform_driver s3c2410_i2c_driver = {
.probe=s3c24xx_i2c_probe,
.remove=s3c24xx_i2c_remove,
.resume=s3c24xx_i2c_resume,
.driver={
.owner=THIS_MODULE,
.name="s3c2410-i2c",
},
};
当通 过Linux内核
源代 码/drivers/base/platform.c文件 中定义platform_driver_unregister()函数注册platform_driver结构体时,
其中probe指针指向的
s3c24xx_i2c_probe()函数将被调用,以初始化适配器硬件,如代码清单1.3所示。
代码清单1.3 S3C2410 I2C
总线驱动中的s3c24xx_i2c_probe
函数
staticint s3c24xx_i2c_probe(struct platform_device *pdev)
{
structs3c24xx_i2c *i2c = &s3c24xx_i2c;
structresource *res;
intret;
/*使能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;
gotoout;
}
clk_enable(i2c->clk);
/*映射寄存器*/
res= platform_get_resource(pdev, IORESOURCE_MEM, 0);
if(res == NULL)
{
dev_err(&pdev->dev,"cannot find IO resource\n");
ret= -ENOENT;
gotoout;
}
i2c->ioarea
=
request_mem_region(res->start,
(res->end-res->start)+1,
pdev->name);
if(i2c->ioarea == NULL)
{
dev_err(&pdev->dev,"cannot request IO\n");
ret= -ENXIO;
gotoout;
}
i2c->regs= ioremap(res->start, (res->end-res->start)+1);
if(i2c->regs == NULL)
{
dev_err(&pdev->dev,"cannot map IO\n");
ret= -ENXIO;
gotoout;
}
/*设置I2C的信息块*/
i2c->adap.algo_data= i2c;
i2c->adap.dev.parent= &pdev->dev;
/*初始化I2C控制器*/
ret= s3c24xx_i2c_init(i2c);
if(ret != 0)
goto out;
/*申请中断*/
res= platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if(res == NULL)
{
ret= -ENOENT;
gotoout;
}
ret= request_irq(res->start, s3c24xx_i2c_irq, SA_INTERRUPT,
pdev->name,i2c);
if(ret != 0)
{
gotoout;
}
i2c->irq= res;
ret= i2c_add_adapter(&i2c->adap); /*添加i2c_adapter*/
if(ret < 0)
{
gotoout;
}
platform_set_drvdata(pdev,i2c);
out:
if(ret < 0)
s3c24xx_i2c_free(i2c);
returnret;
}
上述代码中的主体工作是使能硬件并申请I2C适配器使用的I/O地址、
在这些工作都完成无误后,通过IC
核心提供的i2c_add_adapter()函数添加这个适配
器。 因为
S3C2410内部 集成
I2C控制 器,可以
确定 I2C适配 器一 定存 在,
s3c24xx_i2c_probe()函数虽然命名“探测”
,但实际没有也不必进行任何探测工作,之
所以这样命名完全是一种设计习惯。
与s3c24xx_i2c_probe()函数完成相反功能的函数是s3c24xx_i2c_remove()函数,
它
在适 配器 模块 卸载 函数 调用platform_driver_unregister()函数
时所 示通 过
platform_driver的remove指针方式被调用。xxx_i2c_remove()的设计模板如代码清单
1.4所示。
代码清单1.4S3C2410 I2C
总线驱动中的s3c24xx_i2c_remove函数
staticint s3c24xx_i2c_remove(struct platform_device *pdev)
{
structs3c24xx_i2c *i2c = platform_get_drvdata(pdev);
if(i2c != NULL)
{
s3c24xx_i2c_free(i2c);
platform_set_drvdata(pdev,NULL);
}
return 0;
}
代码清单1.3和代码清单1.4中用到的s3c24xx_i2c结构体进行适配器所有信
息的封装,类似于私有信息结构体,它与xxx_i2c结构体模板
对应。代码清单1.5所示s3c24xx_i2c结构体的定义,以及驱动模块定义的一个
s3c24xx_i2c结构体全局实例。
xxx_i2c结构体模板
structxxx_i2c
{
spinlock_t
lock;
wait_queue_head_twait;
structi2c_msg *msg;
unsignedint
msg_num;
unsignedint
msg_idx;
unsignedint
msg_ptr;
...
structi2c_adapter adap;
};
代码清单1.5s3c24xx_i2c
结构体
structs3c24xxx_i2c
{
spinlock_t
lock;
wait_queue_head_twait;
structi2c_msg *msg;
unsignedint
msg_num;
unsignedint
msg_idx;
unsignedint
msg_ptr;
enums3c24xx_i2c_state state;
void_ _iomem *regs;
structclk *clk;
structdevice *dev;
structresource *irq;
structresource *ioarea;
structi2c_adapter adap;
};
staticstruct s3c24xx_i2c s3c24xx_i2c = {
.lock= SPIN_LOCK_UNLOCKED, /*自旋锁未锁定