您的位置:首页 > 其它

dm6467中I2C总线驱动分析

2012-04-23 16:06 381 查看
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 adapter driver层即I2C适配器驱动层,每种处理器平台都有自己的适配器驱动,属于平台移植相关层。它的职责是为系统中每条I2C总线实现相应的读写方法。但是适配器驱动本身并不会进行任何的通讯,而是等待设备驱动调用其函数。 在系统开机时,I2C适配器驱动被首先装载。一个适配器驱动用于支持一条特定的I2C总线的读写。一个适配器驱动通常需要两个模块,一个struct
i2c_adapter和一个struct i2c_algorithm来描述。

i2c adapter 构造一个对I2C core层接口的数据结构,并通过相应的接口函数向I2C core注册一个适配器。i2c_algorithm主要实现对I2C总线访问的算法,master_xfer和smbus_xfer即I2C adapter底层对I2C总线读写方法的实现,相关的数据结构如下:

/*
	 * The following structs are for those who like to implement new bus drivers:
	 * i2c_algorithm is the interface to a class of hardware solutions which can
	 * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
	 * to name two of the most common.
	 */
	struct i2c_algorithm {
		/* If an adapter algorithm can't do I2C-level access, set master_xfer
		   to NULL. If an adapter algorithm can do SMBus access, set
		   smbus_xfer. If set to NULL, the SMBus protocol is simulated
		   using common I2C messages */
		/* master_xfer should return the number of messages successfully
		   processed, or a negative value on error */
		int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
				   int num);
		int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
				   unsigned short flags, char read_write,
				   u8 command, int size, union i2c_smbus_data *data);
		/* To determine what the adapter supports */
		u32 (*functionality) (struct i2c_adapter *);
	};


主要就是master_xfer方法,其和具体的总线控制器相关,不同的CPU在实现上会有差异。
/*
	 * 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;
		/* data fields that are valid for all devices	*/
		struct rt_mutex bus_lock;
		int timeout;			/* in jiffies */
		int retries;
		struct device dev;		/* the adapter device */
		int nr;
		char name[48];
		struct completion dev_released;
		struct list_head userspace_clients;
	};


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设备驱动。相关的数据结构如下:
/**
	 * struct i2c_driver - represent an I2C device driver
	 * @class: What kind of i2c device we instantiate (for detect)
	 * @attach_adapter: Callback for bus addition (for legacy drivers)
	 * @detach_adapter: Callback for bus removal (for legacy drivers)
	 * @probe: Callback for device binding
	 * @remove: Callback for device unbinding
	 * @shutdown: Callback for device shutdown
	 * @suspend: Callback for device suspend
	 * @resume: Callback for device resume
	 * @command: Callback for bus-wide signaling (optional)
	 * @driver: Device driver model driver
	 * @id_table: List of I2C devices supported by this driver
	 * @detect: Callback for device detection
	 * @address_list: The I2C addresses to probe (for detect)
	 * @clients: List of detected clients we created (for i2c-core use only)
	 *
	 * The driver.owner field should be set to the module owner of this driver.
	 * The driver.name field should be set to the name of this driver.
	 *
	 * For automatic device detection, both @detect and @address_data must
	 * be defined. @class should also be set, otherwise only devices forced
	 * with module parameters will be created. The detect function must
	 * fill at least the name field of the i2c_board_info structure it is
	 * handed upon successful detection, and possibly also the flags field.
	 *
	 * If @detect is missing, the driver will still work fine for enumerated
	 * devices. Detected devices simply won't be supported. This is expected
	 * for the many I2C/SMBus devices which can't be detected reliably, and
	 * the ones which can always be enumerated in practice.
	 *
	 * The i2c_client structure which is handed to the @detect callback is
	 * not a real i2c_client. It is initialized just enough so that you can
	 * call i2c_smbus_read_byte_data and friends on it. Don't do anything
	 * else with it. In particular, calling dev_dbg and friends on it is
	 * not allowed.
	 */
	struct i2c_driver {
		unsigned int class;
		/* Notifies the driver that a new bus has appeared or is about to be
		 * removed. You should avoid using this if you can, it will probably
		 * be removed in a near future.
		 */
		int (*attach_adapter)(struct i2c_adapter *);
		int (*detach_adapter)(struct i2c_adapter *);
		/* Standard driver model interfaces */
		int (*probe)(struct i2c_client *, const struct i2c_device_id *);
		int (*remove)(struct i2c_client *);
		/* driver model interfaces that don't relate to enumeration  */
		void (*shutdown)(struct i2c_client *);
		int (*suspend)(struct i2c_client *, pm_message_t mesg);
		int (*resume)(struct i2c_client *);
		/* Alert callback, for example for the SMBus alert protocol.
		 * The format and meaning of the data value depends on the protocol.
		 * For the SMBus alert protocol, there is a single bit of data passed
		 * as the alert response's low bit ("event flag").
		 */
		void (*alert)(struct i2c_client *, unsigned int data);
		/* a ioctl like command that can be used to perform specific functions
		 * with the device.
		 */
		int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
		struct device_driver driver;
		const struct i2c_device_id *id_table;
		/* Device detection callback for automatic device creation */
		int (*detect)(struct i2c_client *, struct i2c_board_info *);
		const unsigned short *address_list;
		struct list_head clients;
	};


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

Linux中,I2C device driver层的代码位于drivers/i2c/chips目录。

dm6467 I2C 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

在arch/arm/mach-davinci/devices.c中。Platmform device 定义如下:
	32 static struct resource i2c_resources[] = {
	 33         {
	 34                 .start          = D***INCI_I2C_BASE,
	 35                 .end            = D***INCI_I2C_BASE + 0x40,
	 36                 .flags          = IORESOURCE_MEM,
	 37         },
	 38         {
	 39                 .start          = IRQ_I2C,
	 40                 .flags          = IORESOURCE_IRQ,
	 41         },
	 42 };
	 43 
	 44 static struct davinci_i2c_platform_data dm644x_i2c_data = {
	 45         .bus_freq       = 20,
	 46         .bus_delay      = 100,
	 47 };
	 48 
	 49 static struct davinci_i2c_platform_data dm355_i2c_data = {
	 50         .bus_freq       = 20,
	 51         .bus_delay      = 100,
	 52 };
	 53 
	 54 static struct davinci_i2c_platform_data dm646x_i2c_data = {
	 55         .bus_freq       = 100,
	 56         .bus_delay      = 0,
	 57 };
	 58 
	 59 static struct platform_device i2c_device = {
	 60         .name           = "i2c_davinci",
	 61         .id             = 1,
	 62         .dev            = {
	 63                 .platform_data = &dm355_i2c_data,
	  64         },
	 65         .num_resources  = ARRAY_SIZE(i2c_resources),
	 66         .resource       = i2c_resources,
	 67 };
	
	i2c_resources[] 定义了I2C的地址范围、中断号等相关信息,
	platform_device i2c_device 初始化的时候初始化了dm355的数据,在davinci_init_cpu_i2c中再给i2c_device.dev.platform_data赋值为dm6467的数据。
	
	132 static struct platform_device *devices[] __initdata = {
	133         &i2c_device,
	134         &watchdog_device,
	135         &usb_device,
	136 };
	
	
	138 static void __init davinci_init_cpu_i2c(void)
	139 {
	140         if (cpu_is_davinci_dm644x())
	141                 i2c_device.dev.platform_data = &dm644x_i2c_data;
	142         else if (cpu_is_davinci_dm6467())
	143                 i2c_device.dev.platform_data = &dm646x_i2c_data;
	144 
	145         /* all others default to use dm355 because dm355 uses the max speed */
	146 }
	147 
	
	最后在davinci_init_devices中调用drivers/base/platform.c 提供的导出函数platform_add_devices添加设备。
	160 static int __init davinci_init_devices(void)
	161 {
	162         davinci_init_cpu_i2c();
	163         davinci_init_cpu_usb();
	164         platform_add_devices(devices, ARRAY_SIZE(devices));
	165         return 0;
	166 }
	167 arch_initcall(davinci_init_devices);
	
	
	
	
	
platform_add_devices函数具体如下:
	110 int platform_add_devices(struct platform_device **devs, int num)
	111 {
	112         int i, ret = 0;
	113                 
	114         for (i = 0; i < num; i++) {
	115                 ret = platform_device_register(devs[i]);
	116                 if (ret) {
	117                         while (--i >= 0)
	118                                 platform_device_unregister(devs[i]);
	119                         break;
	120                 }
	121         }
	122         
	123         return ret;
	124 }
	125 EXPORT_SYMBOL_GPL(platform_add_devices);


注册完成后,中断号及寄存器的基地址等信息会在设备树中描述了,此后只需利用 platform_get_resource等标准接口自动获取即可,实现了驱动和资源的分离。

platform_get_resource如下:
 30 /**     
 31  *      platform_get_resource - get a resource for a device
 32  *      @dev: platform device
 33  *      @type: resource type
 34  *      @num: resource index
 35  */                     
 36 struct resource *               
 37 platform_get_resource(struct platform_device *dev, unsigned int type,
 38                       unsigned int num)
 39 {       
 40         int i;
 41         
 42         for (i = 0; i < dev->num_resources; i++) {
 43                 struct resource *r = &dev->resource[i];
 44 
 45                 if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM|
 46                                  IORESOURCE_IRQ|IORESOURCE_DMA))
 47                     == type)
 48                         if (num-- == 0)
 49                                 return r;
 50         }
 51         return NULL;
 52 }


I2C adapter的Platform driver

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的定义 如下:

599 static struct platform_driver davinci_i2c_driver = {
	600         .probe          = davinci_i2c_probe,
	601         .remove         = davinci_i2c_remove,
	602         .driver         = {
	603                 .name   = "i2c_davinci",
	604                 .owner  = THIS_MODULE,
	605         },
	606 };
	其注册方法如下:
	608 /* I2C may be needed to bring up other drivers */
	609 static int __init davinci_i2c_init_driver(void)
	610 {
	611         return platform_driver_register(&davinci_i2c_driver);
	612 }
	613 subsys_initcall(davinci_i2c_init_driver);


通过platform_driver_register()函数注册Platform driver 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。具体如下:

487 static int davinci_i2c_probe(struct platform_device *pdev)
	488 {
	489         struct davinci_i2c_dev *dev;
	490         struct i2c_adapter *adap;
	491         struct resource *mem, *irq, *ioarea;
	492         int r;
	493 
	494         /* NOTE: driver uses the static register mapping */
	495         mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	496         if (!mem) {
	497                 dev_err(&pdev->dev, "no mem resource?\n");
	498                 return -ENODEV;
	499         }
	500 
	501         irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	502         if (!irq) {
	503                 dev_err(&pdev->dev, "no irq resource?\n");
	504                 return -ENODEV;
	505         }
	506 
	507         ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1,
	508                                     pdev->name);
	509         if (!ioarea) {
	510                 dev_err(&pdev->dev, "I2C region already claimed\n");
	511                 return -EBUSY;
	512         }
	513 
	514         dev = kzalloc(sizeof(struct davinci_i2c_dev), GFP_KERNEL);
	515         if (!dev) {
	516                 r = -ENOMEM;
	517                 goto err_release_region;
	518         }
	519 
	520         dev->dev = get_device(&pdev->dev);
	521         dev->irq = irq->start;
	522         platform_set_drvdata(pdev, dev);
	523 
	524         dev->clk = clk_get(&pdev->dev, "I2CCLK");
	525         if (IS_ERR(dev->clk)) {
	526                 r = -ENODEV;
	527                 goto err_free_mem;
	528         }
	529         clk_enable(dev->clk);
	530 
	531         dev->base = (void __iomem *)IO_ADDRESS(mem->start);
	532         i2c_davinci_init(dev);
	533 
	534         r = request_irq(dev->irq, i2c_davinci_isr, 0, pdev->name, dev);
	535         if (r) {
	536                 dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
	537                 goto err_unuse_clocks;
	538         }
	539 
	540         adap = &dev->adapter;
	541         i2c_set_adapdata(adap, dev);
	542         adap->owner = THIS_MODULE;
	543         adap->class = I2C_CLASS_HWMON;
	544         strlcpy(adap->name, "DaVinci I2C adapter", sizeof(adap->name));
	545         adap->algo = &i2c_davinci_algo;
	546         adap->dev.parent = &pdev->dev;
	547 
	548         /* FIXME */
	549         adap->timeout = 1;
	550         adap->retries = 1;
	551 
	552         adap->nr = pdev->id;
	553         r = i2c_add_adapter(adap);
	554         if (r) {
	555                 dev_err(&pdev->dev, "failure adding adapter\n");
	556                 goto err_free_irq;
	557         }
	558 
	559         return 0;
	560 
	561 err_free_irq:
	562         free_irq(dev->irq, dev);
	563 err_unuse_clocks:
	564         clk_disable(dev->clk);
	565         clk_put(dev->clk);
	566         dev->clk = NULL;
	567 err_free_mem:
	568         platform_set_drvdata(pdev, NULL);
	569         put_device(&pdev->dev);
	570         kfree(dev);
	571 err_release_region:
	572         release_mem_region(mem->start, (mem->end - mem->start) + 1);
	573 
	574         return r;
	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);

原文主要参考了:http://blog.csdn.net/kellycan/article/details/6394737
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: