DM6467中I2C总线驱动分析

Linux下I2C驱动架构 


如上图所示,每条I2C总线会对应一个adapter,而每条I2C总线上则可以有多个client,在linux kernel中,通过I2C core层将I2C client与I2C adapter关联起来,Linux 中I2C驱动代码位于drivers/i2c目录。    Linux中I2C可以分为三个层次,分别为I2C core层、I2C adapter driver层、I2C device driver层。
 
   I2C core层
   I2C core是用于维护Linux的I2C核心部分,提供了核心的数据结构,I2C适配器驱动和设备驱动的注册、注销管理等API,同时还提供了I2C总线读写访问的一般接口(具体的实现在与I2C控制器相关的I2C adapter中实现)。    该层为硬件平台无关层,向下屏蔽了物理总线适配器的差异,定义了统一的访问策略和接口;向上则提供了统一的接口,以便I2C设备驱动可以通过总线适配器进行数据收发。   Linux中,I2C core层的代码位于driver/i2c/i2c-core.c。由于该层是平台无关层,本文将不再叙述,有兴趣可以查阅相关资料。
 
I2C adapter driver层    I2C adapterdriver层即I2C适配器驱动层,每种处理器平台都有自己的适配器驱动,属于平台移植相关层。它的职责是为系统中每条I2C总线实现相应的读写方法。但是适配器驱动本身并不会进行任何的通讯,而是等待设备驱动调用其函数。    在系统开机时,I2C适配器驱动被首先装载。一个适配器驱动用于支持一条特定的I2C总线的读写。一个适配器驱动通常需要两个模块,一个structi2c_adapter和一个struct i2c_algorithm来描述。 
 
i2c adapter 构造一个对I2C core层接口的数据结构,并通过相应的接口函数向I2C core注册一个适配器。i2c_algorithm主要实现对I2C总线访问的算法,master_xfer和smbus_xfer即I2C adapter底层对I2C总线读写方法的实现,相关的数据结构如下:


01. /*
02. * The following structs are for those who like to implement new busdrivers:
03. * i2c_algorithm is the interface to a class of hardware solutions which can
04. * be addressed using the same bus algorithms - i.e. bit-banging or thePCF8584
05. * to name two of the most common.
06. */ 
07.struct i2c_algorithm { 
08.    /* If an adapter algorithm can't do I2C-level access, setmaster_xfer
09.       to NULL. If an adapter algorithm can doSMBus access, set
10.       smbus_xfer. If set to NULL, the SMBusprotocol is simulated
11.       using common I2C messages */ 
12.    /* master_xfer should return the number of messagessuccessfully
13.       processed, or a negative value on error*/ 
14.    int (*master_xfer)(struct i2c_adapter *adap, structi2c_msg *msgs, 
15.              int num); 
16.    int (*smbus_xfer) (struct i2c_adapter *adap, u16addr, 
17.              unsigned short flags, char read_write, 
18.              u8 command, int size, union i2c_smbus_data *data); 
19.    /* To determine what the adapter supports */ 
20.    u32 (*functionality) (struct i2c_adapter *); 
21.}; 

主要就是master_xfer方法,其和具体的总线控制器相关,不同的CPU在实现上会有差异。
 
01./*
02. * i2c_adapter is the structure used to identify a physical i2c bus along
03. * with the access algorithms necessary to access it.
04. */ 
05.struct i2c_adapter { 
06.    struct module *owner; 
07.    unsigned int id; 
08.    unsigned int class;      /* classes to allow probing for */ 
09.    const struct i2c_algorithm *algo; /* the algorithm toaccess the bus */ 
10.    void *algo_data; 
11.    /* data fields that are valid for all devices  */ 
12.    struct rt_mutex bus_lock; 
13.    inttimeout;            /*in jiffies */ 
14.    int retries; 
15.    struct device dev;      /* theadapter device */ 
16.    int nr; 
17.    char name[48]; 
18.    struct completion dev_released; 
19.    struct list_head userspace_clients; 
20.}; 

 
 Algo是和底层硬件的接口,标识了具体的物理总线传输的实现。
     Userspace_clients为使用该总线的client链表。
     Nr为该适配器也就是某条I2C总线占据的全局编号。
     bus_lock总线的互斥锁,防止总线冲突。
     Linux中,I2C adapter driver层的代码位于drivers/i2c/busses目录。
 


 I2C device driver层
 

   I2C device driver层为用户接口层,其为用户提供了通过I2C总线访问具体设备的接口。
     I2C的device driver层可以用两个模块来描述,struct i2c_driver和struct i2c_client。
     i2c_client和i2c_driver分别构造对I2C core层接口的数据结构,并且通过相关的接口函数向 I2C Core注册I2C设备驱动。相关的数据结构如下:
 
01./**
02. * struct i2c_driver - represent an I2C device driver
03. * @class: What kind of i2c device we instantiate (for detect)
04. * @attach_adapter: Callback for bus addition (for legacy drivers)
05. * @detach_adapter: Callback for bus removal (for legacy drivers)
06. * @probe: Callback for device binding
07. * @remove: Callback for device unbinding
08. * @shutdown: Callback for device shutdown
09. * @suspend: Callback for device suspend
10. * @resume: Callback for device resume
11. * @command: Callback for bus-wide signaling (optional)
12. * @driver: Device driver model driver
13. * @id_table: List of I2C devices supported by this driver
14. * @detect: Callback for device detection
15. * @address_list: The I2C addresses to probe (for detect)
16. * @clients: List of detected clients we created (for i2c-core use only)
17. *
18. * The driver.owner field should be set to the module owner of this driver.
19. * The driver.name field should be set to the name of this driver.
20. *
21. * For automatic device detection, both @detect and @address_data must
22. * be defined. @class should also be set, otherwise only devices forced
23. * with module parameters will be created. The detect function must
24. * fill at least the name field of the i2c_board_info structure it is
25. * handed upon successful detection, and possibly also the flags field.
26. *
27. * If @detect is missing, the driver will still work fine for enumerated
28. * devices. Detected devices simply won't be supported. This is expected
29. * for the many I2C/SMBus devices which can't be detected reliably, and
30. * the ones which can always be enumerated in practice.
31. *
32. * The i2c_client structure which is handed to the @detect callback is
33. * not a real i2c_client. It is initialized just enough so that you can
34. * call i2c_smbus_read_byte_data and friends on it. Don't do anything
35. * else with it. In particular, calling dev_dbg and friends on it is
36. * not allowed.
37. */ 
38.struct i2c_driver { 
39.    unsigned int class; 
40.    /* Notifies the driver that a new bus has appeared or isabout to be
41.     * removed. You should avoid using this if you can,it will probably
42.     * be removed in a near future.
43.     */ 
44.    int (*attach_adapter)(struct i2c_adapter *); 
45.    int (*detach_adapter)(struct i2c_adapter *); 
46.    /* Standard driver model interfaces */ 
47.    int (*probe)(struct i2c_client *, const structi2c_device_id *); 
48.    int (*remove)(struct i2c_client *); 
49.    /* driver model interfaces that don't relate toenumeration  */ 
50.    void (*shutdown)(struct i2c_client *); 
51.    int (*suspend)(struct i2c_client *, pm_message_tmesg); 
52.    int (*resume)(struct i2c_client *); 
53.    /* Alert callback, for example for the SMBus alertprotocol.
54.     * The format and meaning of the data value dependson the protocol.
55.     * For the SMBus alert protocol, there is a singlebit of data passed
56.     * as the alert response's low bit ("eventflag").
57.     */ 
58.    void (*alert)(struct i2c_client *, unsigned intdata); 
59.    /* a ioctl like command that can be used to performspecific functions
60.     * with the device.
61.     */ 
62.    int (*command)(struct i2c_client *client, unsigned intcmd, void *arg); 
63.    struct device_driver driver; 
64.    const struct i2c_device_id *id_table; 
65.    /* Device detection callback for automatic devicecreation */ 
66.    int (*detect)(struct i2c_client *, struct i2c_board_info*); 
67.    const unsigned short *address_list; 
68.    struct list_head clients; 
69.}; 

 
Driver是为device服务的,i2c_driver注册时会扫描i2c bus上的设备,进行驱动和设备的绑定。主要有两种接口attach_adapter和probe,二者分别针对旧的和新式的驱动。
 
通常来说i2c_client对应着I2C总线上某个特定的slave或者是user space的某个用户对应,而此时的slave可以动态变化。
     Linux中,I2C device driver层的代码位于drivers/i2c/chips目录。
 

dm6467I2C adapter驱动
     在Linux内核中,I2C adapter驱动位于drivers/i2c/busses目录下,DM6467 的I2C adapter驱动程序为drivers/i2c/busses/i2c-davinci.c
 
 I2C adapter驱动,本质上就是实现了具体的总线传输算法并向核心层注册适配器。该驱动的注册采用Platform驱动和设备机制。
 
I2C adapter的Platform device
    DM6467中Platform device的注册的代码位于内核的arch/arm/mach-davinci/devices.c
 
01.在arch/arm/mach-davinci/devices.c中。Platmform device 定义如下: 
02.32 static struct resource i2c_resources[] = { 
03. 33         { 
04.34                .start          =DAVINCI_I2C_BASE, 
05.35                .end            =DAVINCI_I2C_BASE + 0x40, 
06.36                .flags          = IORESOURCE_MEM, 
07. 37         }, 
08. 38         { 
09.39                .start          = IRQ_I2C, 
10.40                .flags          =IORESOURCE_IRQ, 
11. 41         }, 
12. 42 }; 
13. 43  
14. 44 static struct davinci_i2c_platform_data dm644x_i2c_data = { 
15. 45        .bus_freq       = 20, 
16. 46        .bus_delay      = 100, 
17. 47 }; 
18. 48  
19. 49 static struct davinci_i2c_platform_data dm355_i2c_data = { 
20. 50        .bus_freq       = 20, 
21. 51        .bus_delay      = 100, 
22. 52 }; 
23. 53  
24. 54 static struct davinci_i2c_platform_data dm646x_i2c_data = { 
25. 55        .bus_freq       = 100, 
26. 56        .bus_delay      = 0, 
27. 57 }; 
28. 58  
29. 59 static struct platform_device i2c_device = { 
30. 60        .name           ="i2c_davinci", 
31. 61        .id             =1, 
32. 62        .dev            ={ 
33.63                .platform_data = &dm355_i2c_data, 
34.  64         }, 
35. 65         .num_resources  =ARRAY_SIZE(i2c_resources), 
36. 66        .resource       = i2c_resources, 
37. 67 }; 
38. 
39.i2c_resources[] 定义了I2C的地址范围、中断号等相关信息, 
40.platform_device i2c_device 初始化的时候初始化了dm355的数据,在davinci_init_cpu_i2c中再给i2c_device.dev.platform_data赋值为dm6467的数据。 
41. 
42.132 static struct platform_device *devices[] __initdata = { 
43.133         &i2c_device, 
44.134        &watchdog_device, 
45.135         &usb_device, 
46.136 }; 
47. 
48. 
49.138 static void __init davinci_init_cpu_i2c(void) 
50.139 { 
51.140         if (cpu_is_davinci_dm644x()) 
52.141                i2c_device.dev.platform_data = &dm644x_i2c_data; 
53.142         else if(cpu_is_davinci_dm6467()) 
54.143                i2c_device.dev.platform_data = &dm646x_i2c_data; 
55.144  
56.145         /* all others default touse dm355 because dm355 uses the max speed */ 
57.146 } 
58.147  
59. 
60.最后在davinci_init_devices中调用drivers/base/platform.c提供的导出函数platform_add_devices添加设备。 
61.160 static int __init davinci_init_devices(void) 
62.161 { 
63.162        davinci_init_cpu_i2c(); 
64.163        davinci_init_cpu_usb(); 
65.164        platform_add_devices(devices, ARRAY_SIZE(devices)); 
66.165         return 0; 
67.166 } 
68.167 arch_initcall(davinci_init_devices); 
69. 
70. 
71. 
72. 
73. 
74.latform_add_devices函数具体如下: 
75.110 int platform_add_devices(struct platform_device **devs, int num) 
76.111 { 
77.112         int i, ret = 0; 
78.113                  
79.114         for (i = 0; i < num;i++) { 
80.115                ret = platform_device_register(devs[i]); 
81.116                if (ret) { 
82.117                        while (--i >= 0) 
83.118                                platform_device_unregister(devs[i]); 
84.119                        break; 
85.120                } 
86.121         } 
87.122          
88.123         return ret; 
89.124 } 
90.125 EXPORT_SYMBOL_GPL(platform_add_devices); 

 
     注册完成后,中断号及寄存器的基地址等信息会在设备树中描述了,此后只需利用 platform_get_resource等标准接口自动获取即可,实现了驱动和资源的分离。
 
01.platform_get_resource如下: 
02.30 /**     
03.31  *      platform_get_resource - get aresource for a device
04.32  *      @dev: platform device
05.33  *      @type: resource type
06.34  *      @num: resource index
07.35 */                      
08.36 struct resource*                
09.37 platform_get_resource(struct platform_device *dev, unsigned inttype, 
10.38                      unsigned int num) 
11.39 {        
12.40         int i; 
13.41          
14.42         for (i = 0; i <dev->num_resources; i++) { 
15.43                struct resource *r = &dev->resource[i]; 
16.44  
17.45                if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM| 
18.46                                 IORESOURCE_IRQ|IORESOURCE_DMA)) 
19.47                    == type) 
20.48                        if (num-- == 0) 
21.49                                return r; 
22.50         } 
23.51         return NULL; 
24.52 } 

 
I2C adapter的Platformdriver
 dm6467中Platform driver的注册的代码位于内核的ddrivers/i2c/busses/i2c-davinci.c中,该驱动的注册目的是初始化dm6467的I2C adapter,提供I2C总线传输的具体实现,并且向I2C core注册I2C adapter。
 
Platform driver的定义
 
 在drivers/i2c/busses/i2c-davinci.c中Platform driver的定义 如下:

01.599static struct platform_driver davinci_i2c_driver = { 
02.600        .probe          =davinci_i2c_probe, 
03.601        .remove         =davinci_i2c_remove, 
04.602        .driver         = { 
05.603                .name   = "i2c_davinci", 
06.604                .owner  = THIS_MODULE, 
07.605         }, 
08.606 }; 
09.其注册方法如下: 
10.608 /* I2C may be needed to bring up other drivers */ 
11.609 static int __init davinci_i2c_init_driver(void) 
12.610 { 
13.611         returnplatform_driver_register(&davinci_i2c_driver); 
14.612 } 
15.613 subsys_initcall(davinci_i2c_init_driver); 

 

通过platform_driver_register()函数注册Platformdriver davinci_i2c_driver时,会扫描platform bus上的所有设备,由于匹配因子是name即"i2c_davinci",而之前已经将name为"i2c_davinci"的Platform device注册到platform bus上,因此匹配成功,调用函数davinci_i2c_probe将设备和驱动绑定起来。
 
davinci_i2c_probe的过程如下:
 
通过platform_get_resource获*mem,*irq, 资源,分配内存,初始化struct davinci_i2c_dev,申请i2c_davinci_isr中断,初始化 struct i2c_adapter,并通过i2c-core提供的i2c_add_adapter注册i2c_adapter。具体如下:
 
01.487 static int davinci_i2c_probe(struct platform_device *pdev) 
02.488 { 
03.489         struct davinci_i2c_dev*dev; 
04.490         struct i2c_adapter*adap; 
05.491         struct resource *mem,*irq, *ioarea; 
06.492         int r; 
07.493  
08.494         /* NOTE: driver uses thestatic register mapping */ 
09.495         mem =platform_get_resource(pdev, IORESOURCE_MEM, 0); 
10.496         if (!mem) { 
11.497                dev_err(&pdev->dev, "no mem resource?\n"); 
12.498                return -ENODEV; 
13.499         } 
14.500  
15.501         irq =platform_get_resource(pdev, IORESOURCE_IRQ, 0); 
16.502         if (!irq) { 
17.503                dev_err(&pdev->dev, "no irq resource?\n"); 
18.504                return -ENODEV; 
19.505         } 
20.506  
21.507         ioarea =request_mem_region(mem->start, (mem->end - mem->start) + 1, 
22.508                                    pdev->name); 
23.509         if (!ioarea) { 
24.510                dev_err(&pdev->dev, "I2C region already claimed\n"); 
25.511                return -EBUSY; 
26.512         } 
27.513  
28.514         dev =kzalloc(sizeof(struct davinci_i2c_dev), GFP_KERNEL); 
29.515         if (!dev) { 
30.516                r = -ENOMEM; 
31.517                goto err_release_region; 
32.518         } 
33.519  
34.520         dev->dev =get_device(&pdev->dev); 
35.521         dev->irq =irq->start; 
36.522        platform_set_drvdata(pdev, dev); 
37.523  
38.524         dev->clk =clk_get(&pdev->dev, "I2CCLK"); 
39.525         if (IS_ERR(dev->clk)){ 
40.526                r = -ENODEV; 
41.527                goto err_free_mem; 
42.528         } 
43.529        clk_enable(dev->clk); 
44.530  
45.531         dev->base = (void__iomem *)IO_ADDRESS(mem->start); 
46.532        i2c_davinci_init(dev); 
47.533  
48.534         r =request_irq(dev->irq, i2c_davinci_isr, 0, pdev->name, dev); 
49.535         if (r) { 
50.536                dev_err(&pdev->dev, "failure requesting irq %i\n",dev->irq); 
51.537                goto err_unuse_clocks; 
52.538         } 
53.539  
54.540         adap = &dev->adapter; 
55.541         i2c_set_adapdata(adap,dev); 
56.542         adap->owner =THIS_MODULE; 
57.543         adap->class =I2C_CLASS_HWMON; 
58.544         strlcpy(adap->name,"DaVinci I2C adapter", sizeof(adap->name)); 
59.545         adap->algo =&i2c_davinci_algo; 
60.546         adap->dev.parent =&pdev->dev; 
61.547  
62.548         /* FIXME */ 
63.549         adap->timeout =1; 
64.550         adap->retries =1; 
65.551  
66.552         adap->nr =pdev->id; 
67.553         r = i2c_add_adapter(adap); 
68.554         if (r) { 
69.555                dev_err(&pdev->dev, "failure adding adapter\n"); 
70.556                goto err_free_irq; 
71.557         } 
72.558  
73.559         return 0; 
74.560  
75.561 err_free_irq: 
76.562         free_irq(dev->irq,dev); 
77.563 err_unuse_clocks: 
78.564        clk_disable(dev->clk); 
79.565        clk_put(dev->clk); 
80.566         dev->clk =NULL; 
81.567 err_free_mem: 
82.568        platform_set_drvdata(pdev, NULL); 
83.569        put_device(&pdev->dev); 
84.570         kfree(dev); 
85.571 err_release_region: 
86.572        release_mem_region(mem->start, (mem->end - mem->start) + 1); 
87.573  
88.574         return r; 
89.575 } 

 这里定义了I2C adapter的中断处理函数i2c_davinci_isr(),该函数对I2C控制器的中断事件进行响应,主要实现了对I2C数据收发中断事件的处理。
 
结构i2c_davinci_algo 是adapter的I2C算法, i2c_davinci_xfer提供了I2C数据传输的实现。
 
482 static struct i2c_algorithm i2c_davinci_algo = {
 
483        .master_xfer    = i2c_davinci_xfer,
 
484         .functionality  =i2c_davinci_func,
 
485 }; 
 

在arch/arm/mach-davinci/i2c-client.c注册了davinci_i2c_client_driver,并提供3个导出函数,提供给外面的模块调用。
 
64 EXPORT_SYMBOL(davinci_i2c_read);
 
89 EXPORT_SYMBOL(davinci_i2c_write);
 
161 EXPORT_SYMBOL(davinci_i2c_expander_op);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值