linux iic分析
2013-01-30 14:24 superlcc 阅读(1112) 评论(0) 编辑 收藏 举报/* arch/arm/plat-samsung/include/plat/iic.h */ #ifndef __ASM_ARCH_IIC_H #define __ASM_ARCH_IIC_H __FILE__ #define S3C_IICFLG_FILTER (1<<0) /* enable s3c2440 filter */ /** * struct s3c2410_platform_i2c - Platform data for s3c I2C. * @bus_num: The bus number to use (if possible). * @flags: Any flags for the I2C bus (E.g. S3C_IICFLK_FILTER). * @slave_addr: The I2C address for the slave device (if enabled). * @frequency: The desired frequency in Hz of the bus. This is * guaranteed to not be exceeded. If the caller does * not care, use zero and the driver will select a * useful default. * @sda_delay: The delay (in ns) applied to SDA edges. * @cfg_gpio: A callback to configure the pins for I2C operation. */ struct s3c2410_platform_i2c { int bus_num; unsigned int flags; unsigned int slave_addr; unsigned long frequency; unsigned int sda_delay; void (*cfg_gpio)(struct platform_device *dev); }; /** * s3c_i2c0_set_platdata - set platform data for i2c0 device * @i2c: The platform data to set, or NULL for default data. * * Register the given platform data for use with the i2c0 device. This * call copies the platform data, so the caller can use __initdata for * their copy. * * This call will set cfg_gpio if is null to the default platform * implementation. * * Any user of s3c_device_i2c0 should call this, even if it is with * NULL to ensure that the device is given the default platform data * as the driver will no longer carry defaults. */ extern void s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *i2c); extern void s3c_i2c1_set_platdata(struct s3c2410_platform_i2c *i2c); extern void s5p_i2c_hdmiphy_set_platdata(struct s3c2410_platform_i2c *i2c); /* defined by architecture to configure gpio */ extern void s3c_i2c0_cfg_gpio(struct platform_device *dev); extern void s3c_i2c1_cfg_gpio(struct platform_device *dev); extern struct s3c2410_platform_i2c default_i2c_data; #endif /* __ASM_ARCH_IIC_H */
这个文件定义了i2c的platform_data的结构,struct s3c2410_platform_i2c, 以及声明了platform_device设置platform_data的函数和配置gpio的函数
在下面的文件定义了 上面声明的函数 s3c_i2c0_set_platdata(struct s3c2410_platfrom_i2c *i2c);并且调用了config gpio的函数
/* linux/arch/arm/plat-samsung/dev-i2c0.c */ #include <linux/gfp.h> #include <linux/kernel.h> #include <linux/string.h> #include <linux/platform_device.h> #include <mach/irqs.h> #include <mach/map.h> #include <plat/regs-iic.h> #include <plat/iic.h> #include <plat/devs.h> #include <plat/cpu.h> 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_i2c default_i2c_data __initdata = { .flags = 0, .slave_addr = 0x10, .frequency = 400*1000, .sda_delay = 100, }; void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd) { struct s3c2410_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; }
这里I2C0控制器,被实现为一个platform_device,struct platform_device s3c_device_i2c0,而platform_device的几大要素:resource,platform_data.其中platform_data由s3c_i2c0_set_platdata()函数提供
s3c_i2c0_set_platdata()->s3c_set_platdata()这个函数经常被调用,其实现如下:
/* linux/arch/arm/plat-samsung/platformdata.c */ #include <linux/kernel.h> #include <linux/string.h> #include <linux/platform_device.h> #include <plat/devs.h> void __init *s3c_set_platdata(void *pd, size_t pdsize, struct platform_device *pdev) { void *npd; if (!pd) { /* too early to use dev_name(), may not be registered */ printk(KERN_ERR "%s: no platform data supplied\n", pdev->name); return NULL; } npd = kmemdup(pd, pdsize, GFP_KERNEL); if (!npd) { printk(KERN_ERR "%s: cannot clone platform data\n", pdev->name); return NULL; } pdev->dev.platform_data = npd; return npd; }
除了为I2C0控制器设置resource,platform_data之外,还调用
if (!npd->cfg_gpio)
npd->cfg_gpio = s3c_i2c0_cfg_gpio;
来设置gpio,因为I2C0控制器的两个引脚线也可以通过配置,设置为普通的GPIO脚,也可以设置为I2C的SDA,和SCL脚。
/* * linux/arch/arm/mach-exynos/setup-i2c0.c * * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * I2C0 GPIO configuration. * * Based on plat-s3c64xx/setup-i2c0.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ struct platform_device; /* don't need the contents */ #include <linux/gpio.h> #include <plat/iic.h> #include <plat/gpio-cfg.h> #include <plat/cpu.h> void s3c_i2c0_cfg_gpio(struct platform_device *dev) { if (soc_is_exynos5210() || soc_is_exynos5250()) s3c_gpio_cfgall_range(EXYNOS5_GPB3(0), 2, S3C_GPIO_SFN(2), S3C_GPIO_PULL_UP); else s3c_gpio_cfgall_range(EXYNOS4_GPD1(0), 2, S3C_GPIO_SFN(2), S3C_GPIO_PULL_NONE); }
这样的话,就可以把思路整理以下:
1.首先是I2C控制器,这个设备arch/arm/plat-samsung/dev-i2c0.c这个文件把,I2C控制器实现为一个平台设备platform_device:s3c_device_i2c0.这个文件中实现了一个设置平台数据platform_data的函数。(这个函数在arch/arm/mach-exynos/mach-m040:m040_machine_init()中被调用)而这其中设置了默认的platform_data:default_i2c_data,以及调用了默认的cfg_gpio(arch/arm/mach-exynos/setup-i2c0.c)函数,调用了同样的的设置platform_data的函数s3c_set_platdata(这个函数在arch/arm/plat-samsung/platformdata.c).
涉及到如下文件:
arch/arm/plat-samsung/dev-i2c0.c (for i2c controller device implement as a platform_device)
arch/arm/plat-samsung/include/plat/iic.h(for i2c controller device platform_data definition : struct s3c2410_platform_i2c)
arch/arm/plat-samsung/include/plat/devs.h(声明了所有的platform_device,以及一个通用的 设置platform_data的函数)
arch/arm/plat-samsung/platformdata.c(for the common function to set platform data)
arch/arm/mach-exynos/setup-i2c0.c(for cfg - gpio)
arch/arm/mach-exynos/mach-m040.c(m040_machine_init()中调用了设置platform_data的函数,同时在m040_machine_init中注册了platform_devices(platform_add_devices()))