編寫i2c設備驅動(從設備)一般有兩種方式: 1.用戶自己編寫獨立的從設備驅動,應用程式直接使用即可。 2.linux內核內部已經實現了一個通用的設備驅動,利用通用設備驅動編寫一個應用程式(用戶態驅動),在應用程式中用到大量設備驅動提供的介面,通過應用程式來控制從設備。 匯流排驅動 4.1 概述 I2
編寫i2c設備驅動(從設備)一般有兩種方式:
1.用戶自己編寫獨立的從設備驅動,應用程式直接使用即可。
2.linux內核內部已經實現了一個通用的設備驅動,利用通用設備驅動編寫一個應用程式(用戶態驅動),在應用程式中用到大量設備驅動提供的介面,通過應用程式來控制從設備。
匯流排驅動
4.1 概述
I2C匯流排驅動是I2C適配器的軟體實現,提供I2C適配器與從設備間完成數據通信的能力,比如起始,停止,應答信號和master_xfer的實現函數。
I2C匯流排驅動由i2c_adapter和i2c_algorithm來描述
4.2 S3c2440I2C控制器的硬體描述
S3c2440處理器內部集成了一個I2C控制器,通過四個寄存器來進行控制:
IICCON I2C控制寄存器
IICSTAT I2C狀態寄存器
IICDS I2C收發數據移位寄存器
IICADD I2C地址寄存器
通過IICCON,IICDS,IICADD寄存器操作,可在I2C匯流排上產生開始位、停止位、數據和地址,而傳輸的狀態則通過IICSTAT寄存器來獲取。
4.3 i2c-s3c2410匯流排驅動分析(platform_driver)
I2C匯流排驅動代碼在drivers/i2c/busses/i2c-s3c2410.c,這個代碼同樣支持s3c2410,s3c6410,s5pc110等Samsung 系列的晶元。
初始化模塊和卸載模塊
[cpp] view plaincopy- static int __init i2c_adap_s3c_init(void)
- {
- returnplatform_driver_register(&s3c24xx_i2c_driver);
- }
- static void __exit i2c_adap_s3c_exit(void)
- {
- platform_driver_unregister(&s3c24xx_i2c_driver);
- }
匯流排驅動是基於platform來實現的,很符合設備驅動模型的思想。
[cpp] view plaincopy- static struct platform_drivers3c24xx_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,
- .of_match_table= s3c24xx_i2c_match,
- },
- };
s3c24xx_i2c_probe函數
當調用platform_driver_register函數註冊platform_driver結構體時,如果platformdevice 和 platform driver匹配成功後,會調用probe函數,來初始化適配器硬體。
[cpp] view plaincopy- static int s3c24xx_i2c_probe(structplatform_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;
- /*初始化自旋鎖和等待隊列頭 */
- spin_lock_init(&i2c->lock);
- init_waitqueue_head(&i2c->wait);
- /*映射寄存器 */
- res= platform_get_resource(pdev, IORESOURCE_MEM, 0);
- i2c->ioarea= request_mem_region(res->start, resource_size(res),
- pdev->name);
- i2c->regs= ioremap(res->start, resource_size(res));
- /*設置I2C核心需要的信息 */
- i2c->adap.algo_data= i2c;
- i2c->adap.dev.parent= &pdev->dev;
- /*初始化I2C控制器 */
- ret= s3c24xx_i2c_init(i2c);
- /*申請中斷 */
- i2c->irq= ret = platform_get_irq(pdev, 0);
- ret= request_irq(i2c->irq, s3c24xx_i2c_irq, 0,
- dev_name(&pdev->dev), i2c);
- /* 註冊I2C適配器 */
- ret= i2c_add_numbered_adapter(&i2c->adap);
- ……
- }
Probe主要工作是時能硬體並申請I2C適配器使用的IO地址,中斷號等,然後向I2C核心添加這個適配器。I2c_adapter註冊過程i2c_add_numbered_adapter->i2c_register_adapter
I2C匯流排通信方法
[cpp] view plaincopy- static const struct i2c_algorithms3c24xx_i2c_algorithm = {
- .master_xfer = s3c24xx_i2c_xfer,
- .functionality = s3c24xx_i2c_func,
- };
s3c24xx_i2c_xfer函數是匯流排通信方式的具體實現,依賴於s3c24xx_i2c_doxfer和s3c24xx_i2c_message_start兩個函數;
[cpp] view plaincopy- static int s3c24xx_i2c_doxfer(structs3c24xx_i2c *i2c,
- struct i2c_msg *msgs, int num)
- {
- ret =s3c24xx_i2c_set_master(i2c);
- i2c->msg = msgs;
- i2c->msg_num= num;
- i2c->msg_ptr= 0;
- i2c->msg_idx= 0;
- i2c->state = STATE_START;
- s3c24xx_i2c_message_start(i2c,msgs);
- }
首先設置s3c I2C設備器為主設備,然後調用s3c24xx_i2c_message_start函數啟動I2C消息傳輸。
s3c24xx_i2c_func函數返回適配器所支持的通信功能。
4.4 適配器的設備資源(platform_device)
S3c2440的I2C匯流排驅動是基於platform來實現,前面我們分析了platformdriver部分,再來看下platform device部分。
在arch/arm/plat-samsung/dev-i2c0.c文件中定義了platform_device結構體以及I2C控制器的資源信息:
[cpp] view plaincopy- 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,
- };
- struct s3c2410_platform_i2cdefault_i2c_data __initdata = {
- .flags = 0,
- .slave_addr = 0x10, /* I2C適配器的地址 */
- .frequency = 100*1000, /* 匯流排頻率 */
- .sda_delay = 100, /* SDA邊沿延遲時間ns */
- };
- void __init s3c_i2c0_set_platdata(structs3c2410_platform_i2c *pd)
- {
- structs3c2410_platform_i2c *npd;
- if(!pd)
- pd= &default_i2c_data;
- npd= s3c_set_platdata(pd, sizeof(struct s3c2410_platform_i2c),
- &s3c_device_i2c0);
- if(!npd->cfg_gpio)
- npd->cfg_gpio= s3c_i2c0_cfg_gpio;
- }
在板文件中把platform_device註冊進內核:
[cpp] view plaincopy- static struct platform_device*mini2440_devices[] __initdata = {
- ……
- &s3c_device_i2c0,
- ……
- };
調用s3c_i2c0_set_platdata 函數把適配器具體的數據賦值給dev.platform_data:
[cpp] view plaincopy- static void __init mini2440_init(void)
- {
- ……
- s3c_i2c0_set_platdata(NULL);
- }
I2C匯流排驅動就分析到這裡。