I2C子系统分析

本文以s3c2440的I2C子系统为例, 分析其代码实现

本人学习驱动不久, 如有瑕疵纰漏, 欢迎指教, 谢谢

从硬件的角度看, I2C子系统由总线适配器和挂在总线上的设备组成
因此, 很容易想到, Linux的I2C子系统至少要提供:
    总线上设备的支持, 以及其驱动
    总线适配器的支持, 以及其驱动

1. S3C2440的I2C总线作为一个平台设备, 来看下添加平台设备的代码: /arch/arm/mach-s3c2440/mach-smdk2440.c

  1. static struct platform_device *smdk2440_devices[] __initdata = {  
  2.     &s3c_device_usb,  
  3.     &s3c_device_lcd,  
  4.     &s3c_device_wdt,  
  5.     &s3c_device_i2c0,  
  6.     &s3c_device_iis,  
  7. };  
  8.   
  9. static void __init smdk2440_machine_init(void)  
  10. {  
  11.     s3c24xx_fb_set_platdata(&smdk2440_fb_info);  
  12.     s3c_i2c0_set_platdata(NULL);  
  13.   
  14.     platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));  
  15.     smdk_machine_init();  
  16. }  
static struct platform_device *smdk2440_devices[] __initdata = {
	&s3c_device_usb,
	&s3c_device_lcd,
	&s3c_device_wdt,
	&s3c_device_i2c0,
	&s3c_device_iis,
};

static void __init smdk2440_machine_init(void)
{
	s3c24xx_fb_set_platdata(&smdk2440_fb_info);
	s3c_i2c0_set_platdata(NULL);

	platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
	smdk_machine_init();
}
其中, s3c_device_i2c0即为I2C的平台设备, 看下它是怎么定义的: /arch/arm/plat-s3c/dev-i2c0.c
  1. static struct resource s3c_i2c_resource[] = {  
  2.     [0] = {  
  3.         .start = S3C_PA_IIC,  
  4.         .end   = S3C_PA_IIC + SZ_4K - 1,  
  5.         .flags = IORESOURCE_MEM,  
  6.     },  
  7.     [1] = {  
  8.         .start = IRQ_IIC,  
  9.         .end   = IRQ_IIC,  
  10.         .flags = IORESOURCE_IRQ,  
  11.     },  
  12. };  
  13.   
  14. struct platform_device s3c_device_i2c0 = {  
  15.     .name         = "s3c2410-i2c",  
  16. #ifdef CONFIG_S3C_DEV_I2C1  
  17.     .id       = 0,  
  18. #else  
  19.     .id       = -1,  
  20. #endif  
  21.     .num_resources    = ARRAY_SIZE(s3c_i2c_resource),  
  22.     .resource     = s3c_i2c_resource,  
  23. };  
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,
};
设备名称为s3c2410-i2c, 同时定义了两个资源, 一段IO内存, 一个中断号
在smdk2440_machine_init中, 即初始化过程中, 调用了s3c_i2c0_set_platdata(NULL)
  1. static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {  
  2.     .flags      = 0,  
  3.     .slave_addr = 0x10,  
  4.     .bus_freq   = 100*1000,  
  5.     .max_freq   = 400*1000,  
  6.     .sda_delay  = S3C2410_IICLC_SDA_DELAY5 | S3C2410_IICLC_FILTER_ON,  
  7. };  
  8.   
  9. void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd)  
  10. {  
  11.     struct s3c2410_platform_i2c *npd;  
  12.   
  13.     if (!pd)  
  14.         pd = &default_i2c_data0;  
  15.   
  16.     npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL);  
  17.     if (!npd)  
  18.         printk(KERN_ERR "%s: no memory for platform data\n", __func__);  
  19.     else if (!npd->cfg_gpio)  
  20.         npd->cfg_gpio = s3c_i2c0_cfg_gpio;  
  21.   
  22.     s3c_device_i2c0.dev.platform_data = npd;  
  23. }  
static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {
	.flags		= 0,
	.slave_addr	= 0x10,
	.bus_freq	= 100*1000,
	.max_freq	= 400*1000,
	.sda_delay	= S3C2410_IICLC_SDA_DELAY5 | S3C2410_IICLC_FILTER_ON,
};

void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd)
{
	struct s3c2410_platform_i2c *npd;

	if (!pd)
		pd = &default_i2c_data0;

	npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL);
	if (!npd)
		printk(KERN_ERR "%s: no memory for platform data\n", __func__);
	else if (!npd->cfg_gpio)
		npd->cfg_gpio = s3c_i2c0_cfg_gpio;

	s3c_device_i2c0.dev.platform_data = npd;
}
阅读s3c_i2c0_set_platdata函数, 是对s3c_device_i2c0添加一些设备参数(default_i2c_data0)
简单的看下系统默认提供的这套参数, slave_addr应该是作为从机的地址, bus_freq应该是总线频率, 为100kHz, ...

所以, 设备结构体s3c_device_i2c0最后应该是这个样子:
  1. struct platform_device s3c_device_i2c0 = {  
  2.     .name         = "s3c2410-i2c",  
  3.     .id       = -1,  
  4.     .num_resources    = ARRAY_SIZE(s3c_i2c_resource),  
  5.     .resource     = s3c_i2c_resource,  
  6. };  
  7.   
  8. s3c_device_i2c0.dev.platform_data = {  
  9.     .flags      = 0,  
  10.     .slave_addr = 0x10,  
  11.     .bus_freq   = 100*1000,  
  12.     .max_freq   = 400*1000,  
  13.     .sda_delay  = S3C2410_IICLC_SDA_DELAY5 | S3C2410_IICLC_FILTER_ON,  
  14.     .cfg_gpio   = s3c_i2c0_cfg_gpio;  
  15. };  
struct platform_device s3c_device_i2c0 = {
	.name		  = "s3c2410-i2c",
	.id		  = -1,
	.num_resources	  = ARRAY_SIZE(s3c_i2c_resource),
	.resource	  = s3c_i2c_resource,
};

s3c_device_i2c0.dev.platform_data = {
	.flags		= 0,
	.slave_addr	= 0x10,
	.bus_freq	= 100*1000,
	.max_freq	= 400*1000,
	.sda_delay	= S3C2410_IICLC_SDA_DELAY5 | S3C2410_IICLC_FILTER_ON,
	.cfg_gpio	= s3c_i2c0_cfg_gpio;
};
其中有个函数指针cfg_gpio, 指向了s3c_i2c0_cfg_gpio, 作用是配置I2C相应IO接口:
  1. void s3c_i2c0_cfg_gpio(struct platform_device *dev)  
  2. {  
  3.     s3c2410_gpio_cfgpin(S3C2410_GPE15, S3C2410_GPE15_IICSDA);  
  4.     s3c2410_gpio_cfgpin(S3C2410_GPE14, S3C2410_GPE14_IICSCL);  
  5. }  
void s3c_i2c0_cfg_gpio(struct platform_device *dev)
{
	s3c2410_gpio_cfgpin(S3C2410_GPE15, S3C2410_GPE15_IICSDA);
	s3c2410_gpio_cfgpin(S3C2410_GPE14, S3C2410_GPE14_IICSCL);
}

自此, I2C作为平台设备, 被添加到了平台总线上.

2. 设备添加完了, 就该驱动了, 来看下I2C设备驱动定义: /drivers/i2c/buses/i2c-s3c2410.c
  1. static struct platform_driver s3c2410_i2c_driver = {  
  2.     .probe      = s3c24xx_i2c_probe,  
  3.     .remove     = s3c24xx_i2c_remove,  
  4.     .suspend_late   = s3c24xx_i2c_suspend_late,  
  5.     .resume     = s3c24xx_i2c_resume,  
  6.     .driver     = {  
  7.         .owner  = THIS_MODULE,  
  8.         .name   = "s3c2410-i2c",  
  9.     },  
  10. };  
static struct platform_driver s3c2410_i2c_driver = {
	.probe		= s3c24xx_i2c_probe,
	.remove		= s3c24xx_i2c_remove,
	.suspend_late	= s3c24xx_i2c_suspend_late,
	.resume		= s3c24xx_i2c_resume,
	.driver		= {
		.owner	= THIS_MODULE,
		.name	= "s3c2410-i2c",
	},
};
驱动名字为s3c2410-i2c, 和设备名字一样
然后将注册驱动, 将驱动添加到平台总线上:
  1. static int __init i2c_adap_s3c_init(void)  
  2. {  
  3.     int ret;  
  4.   
  5.     ret = platform_driver_register(&s3c2410_i2c_driver);  
  6.     if (ret == 0) {  
  7.         ret = platform_driver_register(&s3c2440_i2c_driver);  
  8.         if (ret)  
  9.             platform_driver_unregister(&s3c2410_i2c_driver);  
  10.     }  
  11.   
  12.     return ret;  
  13. }  
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;
}

回想一下, 当添加一个平台设备或者平台驱动时, 都会调用总线的match函数来将设备和驱动配对
而平台总线的match是根据设备和驱动的名字, 当相同时, 即匹配成功, 并调用驱动的probe函数

那么, 接着来看下s3c24xx_i2c_probe函数, 为了看着清晰些, 我把错误检测和错误恢复的代码删了
  1. /* s3c24xx_i2c_probe 
  2.  * 
  3.  * called by the bus driver when a suitable device is found 
  4. */  
  5. static int s3c24xx_i2c_probe(struct platform_device *pdev)  
  6. {  
  7.     struct s3c24xx_i2c *i2c;  
  8.     struct s3c2410_platform_i2c *pdata;  
  9.     struct resource *res;  
  10.     int ret;  
  11.   
  12.     pdata = pdev->dev.platform_data;  
  13.   
  14.     i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);  
  15.   
  16.     strlcpy(i2c->adap.name, "s3c2410-i2c"sizeof(i2c->adap.name));  
  17.     i2c->adap.owner   = THIS_MODULE;  
  18.     i2c->adap.algo    = &s3c24xx_i2c_algorithm;  
  19.     i2c->adap.retries = 2;  
  20.     i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;  
  21.     i2c->tx_setup     = 50;  
  22.   
  23.     spin_lock_init(&i2c->lock);  
  24.     init_waitqueue_head(&i2c->wait);  
  25.   
  26.     /* find the clock and enable it */  
  27.     i2c->dev = &pdev->dev;  
  28.     i2c->clk = clk_get(&pdev->dev, "i2c");  
  29.   
  30.     dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);  
  31.   
  32.     clk_enable(i2c->clk);  
  33.   
  34.     /* map the registers */  
  35.     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  
  36.   
  37.     i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,  
  38.                      pdev->name);  
  39.   
  40.     i2c->regs = ioremap(res->start, (res->end-res->start)+1);  
  41.   
  42.     dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",  
  43.         i2c->regs, i2c->ioarea, res);  
  44.   
  45.     /* setup info block for the i2c core */  
  46.     i2c->adap.algo_data = i2c;  
  47.     i2c->adap.dev.parent = &pdev->dev;  
  48.   
  49.     /* initialise the i2c controller */  
  50.     ret = s3c24xx_i2c_init(i2c);  
  51.   
  52.     /* find the IRQ for this unit (note, this relies on the init call to 
  53.      * ensure no current IRQs pending 
  54.      */  
  55.     i2c->irq = ret = platform_get_irq(pdev, 0);  
  56.   
  57.     ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,  
  58.               dev_name(&pdev->dev), i2c);  
  59.   
  60.     ret = s3c24xx_i2c_register_cpufreq(i2c);  
  61.   
  62.     /* Note, previous versions of the driver used i2c_add_adapter() 
  63.      * to add the bus at any number. We now pass the bus number via 
  64.      * the platform data, so if unset it will now default to always 
  65.      * being bus 0. 
  66.      */  
  67.     i2c->adap.nr = pdata->bus_num;  
  68.   
  69.     ret = i2c_add_numbered_adapter(&i2c->adap);  
  70.   
  71.     platform_set_drvdata(pdev, i2c);  
  72.   
  73.     dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));  
  74.     return 0;  
  75. }  
/* s3c24xx_i2c_probe
 *
 * called by the bus driver when a suitable device is found
*/
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
	struct s3c24xx_i2c *i2c;
	struct s3c2410_platform_i2c *pdata;
	struct resource *res;
	int ret;

	pdata = pdev->dev.platform_data;

	i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);

	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);

	/* find the clock and enable it */
	i2c->dev = &pdev->dev;
	i2c->clk = clk_get(&pdev->dev, "i2c");

	dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);

	clk_enable(i2c->clk);

	/* map the registers */
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

	i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,
					 pdev->name);

	i2c->regs = ioremap(res->start, (res->end-res->start)+1);

	dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
		i2c->regs, i2c->ioarea, res);

	/* setup info block for the i2c core */
	i2c->adap.algo_data = i2c;
	i2c->adap.dev.parent = &pdev->dev;

	/* initialise the i2c controller */
	ret = s3c24xx_i2c_init(i2c);

	/* find the IRQ for this unit (note, this relies on the init call to
	 * ensure no current IRQs pending
	 */
	i2c->irq = ret = platform_get_irq(pdev, 0);

	ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
			  dev_name(&pdev->dev), i2c);

	ret = s3c24xx_i2c_register_cpufreq(i2c);

	/* Note, previous versions of the driver used i2c_add_adapter()
	 * to add the bus at any number. We now pass the bus number via
	 * the platform data, so if unset it will now default to always
	 * being bus 0.
	 */
	i2c->adap.nr = pdata->bus_num;

	ret = i2c_add_numbered_adapter(&i2c->adap);

	platform_set_drvdata(pdev, i2c);

	dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
	return 0;
}
比较常规的probe, 使能时钟, 申请IO内存, 映射IO内存, 申请中断就不说了, 删去, 得到以下:
  1. static int s3c24xx_i2c_probe(struct platform_device *pdev)  
  2. {  
  3.     struct s3c24xx_i2c *i2c;  
  4.     struct s3c2410_platform_i2c *pdata;  
  5.     struct resource *res;  
  6.     int ret;  
  7.   
  8.     pdata = pdev->dev.platform_data;  
  9.   
  10.     i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);  
  11.   
  12.     strlcpy(i2c->adap.name, "s3c2410-i2c"sizeof(i2c->adap.name));  
  13.     i2c->adap.owner   = THIS_MODULE;  
  14.     i2c->adap.algo    = &s3c24xx_i2c_algorithm;  
  15.     i2c->adap.retries = 2;  
  16.     i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;  
  17.     i2c->tx_setup     = 50;  
  18.   
  19.     /* setup info block for the i2c core */  
  20.     i2c->adap.algo_data = i2c;  
  21.     i2c->adap.dev.parent = &pdev->dev;  
  22.   
  23.     /* initialise the i2c controller */  
  24.     ret = s3c24xx_i2c_init(i2c);  
  25.     ret = s3c24xx_i2c_register_cpufreq(i2c);  
  26.   
  27.     /* Note, previous versions of the driver used i2c_add_adapter() 
  28.      * to add the bus at any number. We now pass the bus number via 
  29.      * the platform data, so if unset it will now default to always 
  30.      * being bus 0. 
  31.      */  
  32.     i2c->adap.nr = pdata->bus_num;  
  33.     ret = i2c_add_numbered_adapter(&i2c->adap);  
  34.   
  35.     platform_set_drvdata(pdev, i2c);  
  36.   
  37.     return 0;  
  38. }  
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
	struct s3c24xx_i2c *i2c;
	struct s3c2410_platform_i2c *pdata;
	struct resource *res;
	int ret;

	pdata = pdev->dev.platform_data;

	i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);

	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;

	/* setup info block for the i2c core */
	i2c->adap.algo_data = i2c;
	i2c->adap.dev.parent = &pdev->dev;

	/* initialise the i2c controller */
	ret = s3c24xx_i2c_init(i2c);
	ret = s3c24xx_i2c_register_cpufreq(i2c);

	/* Note, previous versions of the driver used i2c_add_adapter()
	 * to add the bus at any number. We now pass the bus number via
	 * the platform data, so if unset it will now default to always
	 * being bus 0.
	 */
	i2c->adap.nr = pdata->bus_num;
	ret = i2c_add_numbered_adapter(&i2c->adap);

	platform_set_drvdata(pdev, i2c);

	return 0;
}
是不是清爽多了
函数定义了一个struct s3c24xx_i2c *i2c, 然后对其初始化
而且可以看出, 主要是对i2c->adap进行初始化
i2c->adap指向一个struct i2c_adapter结构体, 可以翻译为i2c适配器, 来看下
  1. /* 
  2.  * i2c_adapter is the structure used to identify a physical i2c bus along 
  3.  * with the access algorithms necessary to access it. 
  4.  */  
  5. struct i2c_adapter {  
  6.     struct module *owner;  
  7.     unsigned int id;  
  8.     unsigned int class;       /* classes to allow probing for */  
  9.     const struct i2c_algorithm *algo; /* the algorithm to access the bus */  
  10.     void *algo_data;  
  11.   
  12.     /* --- administration stuff. */  
  13.     int (*client_register)(struct i2c_client *);  
  14.     int (*client_unregister)(struct i2c_client *);  
  15.   
  16.     /* data fields that are valid for all devices   */  
  17.     u8 level;           /* nesting level for lockdep */  
  18.     struct mutex bus_lock;  
  19.     struct mutex clist_lock;  
  20.   
  21.     int timeout;            /* in jiffies */  
  22.     int retries;  
  23.     struct device dev;      /* the adapter device */  
  24.   
  25.     int nr;  
  26.     struct list_head clients;   /* DEPRECATED */  
  27.     char name[48];  
  28.     struct completion dev_released;  
  29. };  
/*
 * 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;

	/* --- administration stuff. */
	int (*client_register)(struct i2c_client *);
	int (*client_unregister)(struct i2c_client *);

	/* data fields that are valid for all devices	*/
	u8 level; 			/* nesting level for lockdep */
	struct mutex bus_lock;
	struct mutex clist_lock;

	int timeout;			/* in jiffies */
	int retries;
	struct device dev;		/* the adapter device */

	int nr;
	struct list_head clients;	/* DEPRECATED */
	char name[48];
	struct completion dev_released;
};

struct i2c_adapter中还有个const struct i2c_algorithm *algo
将i2c_adapter简称为adp, i2c_algorithm简称为algo
具体讲一下这两个数据结构有什么用:
I2C子系统主要有三部分组成: core, adap, algo
core是i2c子系统的核心层, 将与具体硬件相关的代码交给adap
而adap含有一个algo, algo定义了与具体硬件通讯的代码所以
如果有一个应用程序希望与i2c设备通讯, 将依次通过core, adap, algo, 最终将数据发到总线上

继续看probe初始化过程, ret = s3c24xx_i2c_init(i2c);
  1. static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)  
  2. {  
  3.     unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;  
  4.     struct s3c2410_platform_i2c *pdata;  
  5.     unsigned int freq;  
  6.   
  7.     /* get the plafrom data */  
  8.     pdata = i2c->dev->platform_data;  
  9.   
  10.     /* inititalise the gpio */  
  11.     if (pdata->cfg_gpio)  
  12.         pdata->cfg_gpio(to_platform_device(i2c->dev));  
  13.   
  14.     /* write slave address */  
  15.     writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);  
  16.   
  17.     dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);  
  18.   
  19.     writel(iicon, i2c->regs + S3C2410_IICCON);  
  20.   
  21.     /* we need to work out the divisors for the clock... */  
  22.     if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {  
  23.         writel(0, i2c->regs + S3C2410_IICCON);  
  24.         dev_err(i2c->dev, "cannot meet bus frequency required\n");  
  25.         return -EINVAL;  
  26.     }  
  27.   
  28.     /* todo - check that the i2c lines aren't being dragged anywhere */  
  29.     dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);  
  30.     dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);  
  31.   
  32.     /* check for s3c2440 i2c controller  */  
  33.     if (s3c24xx_i2c_is2440(i2c)) {  
  34.         dev_dbg(i2c->dev, "S3C2440_IICLC=%08x\n", pdata->sda_delay);  
  35.   
  36.         writel(pdata->sda_delay, i2c->regs + S3C2440_IICLC);  
  37.     }  
  38.   
  39.     return 0;  
  40. }  
static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
{
	unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN;
	struct s3c2410_platform_i2c *pdata;
	unsigned int freq;

	/* get the plafrom data */
	pdata = i2c->dev->platform_data;

	/* inititalise the gpio */
	if (pdata->cfg_gpio)
		pdata->cfg_gpio(to_platform_device(i2c->dev));

	/* write slave address */
	writeb(pdata->slave_addr, i2c->regs + S3C2410_IICADD);

	dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr);

	writel(iicon, i2c->regs + S3C2410_IICCON);

	/* we need to work out the divisors for the clock... */
	if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) {
		writel(0, i2c->regs + S3C2410_IICCON);
		dev_err(i2c->dev, "cannot meet bus frequency required\n");
		return -EINVAL;
	}

	/* todo - check that the i2c lines aren't being dragged anywhere */
	dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq);
	dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon);

	/* check for s3c2440 i2c controller  */
	if (s3c24xx_i2c_is2440(i2c)) {
		dev_dbg(i2c->dev, "S3C2440_IICLC=%08x\n", pdata->sda_delay);

		writel(pdata->sda_delay, i2c->regs + S3C2440_IICLC);
	}

	return 0;
}
主要是初始化硬件寄存器, 没什么好说的, 参考s3c2440手册

继续看probe初始化过程
i2c->adap.nr = pdata->bus_num;
ret = i2c_add_numbered_adapter(&i2c->adap);
  1. int i2c_add_numbered_adapter(struct i2c_adapter *adap)  
  2. {  
  3.     int id;  
  4.     int status;  
  5.   
  6.     if (adap->nr & ~MAX_ID_MASK)  
  7.         return -EINVAL;  
  8.   
  9. retry:  
  10.     if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)  
  11.         return -ENOMEM;  
  12.   
  13.     mutex_lock(&core_lock);  
  14.     /* "above" here means "above or equal to", sigh; 
  15.      * we need the "equal to" result to force the result 
  16.      */  
  17.     status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);  
  18.     if (status == 0 && id != adap->nr) {  
  19.         status = -EBUSY;  
  20.         idr_remove(&i2c_adapter_idr, id);  
  21.     }  
  22.     mutex_unlock(&core_lock);  
  23.     if (status == -EAGAIN)  
  24.         goto retry;  
  25.   
  26.     if (status == 0)  
  27.         status = i2c_register_adapter(adap);  
  28.     return status;  
  29. }  
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
	int	id;
	int	status;

	if (adap->nr & ~MAX_ID_MASK)
		return -EINVAL;

retry:
	if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
		return -ENOMEM;

	mutex_lock(&core_lock);
	/* "above" here means "above or equal to", sigh;
	 * we need the "equal to" result to force the result
	 */
	status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);
	if (status == 0 && id != adap->nr) {
		status = -EBUSY;
		idr_remove(&i2c_adapter_idr, id);
	}
	mutex_unlock(&core_lock);
	if (status == -EAGAIN)
		goto retry;

	if (status == 0)
		status = i2c_register_adapter(adap);
	return status;
}
调用了i2c_register_adapter(adap)
  1. static int i2c_register_adapter(struct i2c_adapter *adap)  
  2. {  
  3.     int res = 0, dummy;  
  4.   
  5.     /* Can't register until after driver model init */  
  6.     if (unlikely(WARN_ON(!i2c_bus_type.p)))  
  7.         return -EAGAIN;  
  8.   
  9.     mutex_init(&adap->bus_lock);  
  10.     mutex_init(&adap->clist_lock);  
  11.     INIT_LIST_HEAD(&adap->clients);  
  12.   
  13.     mutex_lock(&core_lock);  
  14.   
  15.     /* Add the adapter to the driver core. 
  16.      * If the parent pointer is not set up, 
  17.      * we add this adapter to the host bus. 
  18.      */  
  19.     if (adap->dev.parent == NULL) {  
  20.         adap->dev.parent = &platform_bus;  
  21.         pr_debug("I2C adapter driver [%s] forgot to specify "  
  22.              "physical device\n", adap->name);  
  23.     }  
  24.     dev_set_name(&adap->dev, "i2c-%d", adap->nr);  
  25.     adap->dev.release = &i2c_adapter_dev_release;  
  26.     adap->dev.class = &i2c_adapter_class;  
  27.     res = device_register(&adap->dev);  
  28.     if (res)  
  29.         goto out_list;  
  30.   
  31.     dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);  
  32.   
  33.     /* create pre-declared device nodes for new-style drivers */  
  34.     if (adap->nr < __i2c_first_dynamic_bus_num)  
  35.         i2c_scan_static_board_info(adap);  
  36.   
  37.     /* Notify drivers */  
  38.     dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,  
  39.                  i2c_do_add_adapter);  
  40.   
  41. out_unlock:  
  42.     mutex_unlock(&core_lock);  
  43.     return res;  
  44.   
  45. out_list:  
  46.     idr_remove(&i2c_adapter_idr, adap->nr);  
  47.     goto out_unlock;  
  48. }  
static int i2c_register_adapter(struct i2c_adapter *adap)
{
	int res = 0, dummy;

	/* Can't register until after driver model init */
	if (unlikely(WARN_ON(!i2c_bus_type.p)))
		return -EAGAIN;

	mutex_init(&adap->bus_lock);
	mutex_init(&adap->clist_lock);
	INIT_LIST_HEAD(&adap->clients);

	mutex_lock(&core_lock);

	/* Add the adapter to the driver core.
	 * If the parent pointer is not set up,
	 * we add this adapter to the host bus.
	 */
	if (adap->dev.parent == NULL) {
		adap->dev.parent = &platform_bus;
		pr_debug("I2C adapter driver [%s] forgot to specify "
			 "physical device\n", adap->name);
	}
	dev_set_name(&adap->dev, "i2c-%d", adap->nr);
	adap->dev.release = &i2c_adapter_dev_release;
	adap->dev.class = &i2c_adapter_class;
	res = device_register(&adap->dev);
	if (res)
		goto out_list;

	dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);

	/* create pre-declared device nodes for new-style drivers */
	if (adap->nr < __i2c_first_dynamic_bus_num)
		i2c_scan_static_board_info(adap);

	/* Notify drivers */
	dummy = bus_for_each_drv(&i2c_bus_type, NULL, adap,
				 i2c_do_add_adapter);

out_unlock:
	mutex_unlock(&core_lock);
	return res;

out_list:
	idr_remove(&i2c_adapter_idr, adap->nr);
	goto out_unlock;
}
其中, 最关键的是i2c_scan_static_board_info(adap);
  1. static void i2c_scan_static_board_info(struct i2c_adapter *adapter)  
  2. {  
  3.     struct i2c_devinfo  *devinfo;  
  4.   
  5.     mutex_lock(&__i2c_board_lock);  
  6.     list_for_each_entry(devinfo, &__i2c_board_list, list) {  
  7.         if (devinfo->busnum == adapter->nr  
  8.                 && !i2c_new_device(adapter,  
  9.                         &devinfo->board_info))  
  10.             printk(KERN_ERR "i2c-core: can't create i2c%d-%04x\n",  
  11.                 i2c_adapter_id(adapter),  
  12.                 devinfo->board_info.addr);  
  13.     }  
  14.     mutex_unlock(&__i2c_board_lock);  
  15. }  
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
	struct i2c_devinfo	*devinfo;

	mutex_lock(&__i2c_board_lock);
	list_for_each_entry(devinfo, &__i2c_board_list, list) {
		if (devinfo->busnum == adapter->nr
				&& !i2c_new_device(adapter,
						&devinfo->board_info))
			printk(KERN_ERR "i2c-core: can't create i2c%d-%04x\n",
				i2c_adapter_id(adapter),
				devinfo->board_info.addr);
	}
	mutex_unlock(&__i2c_board_lock);
}
此函数扫描所有预先定义好的设备(挂在总线上的设备), 然后添加设备(i2c_new_device)

来看下i2c的从机设备是如何添加的:
  1. struct i2c_client *  
  2. i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)  
  3. {  
  4.     struct i2c_client   *client;  
  5.     int         status;  
  6.   
  7.     client = kzalloc(sizeof *client, GFP_KERNEL);  
  8.     if (!client)  
  9.         return NULL;  
  10.   
  11.     client->adapter = adap;  
  12.   
  13.     client->dev.platform_data = info->platform_data;  
  14.   
  15.     if (info->archdata)  
  16.         client->dev.archdata = *info->archdata;  
  17.   
  18.     client->flags = info->flags;  
  19.     client->addr = info->addr;  
  20.     client->irq = info->irq;  
  21.   
  22.     strlcpy(client->name, info->type, sizeof(client->name));  
  23.   
  24.     /* a new style driver may be bound to this device when we 
  25.      * return from this function, or any later moment (e.g. maybe 
  26.      * hotplugging will load the driver module).  and the device 
  27.      * refcount model is the standard driver model one. 
  28.      */  
  29.     status = i2c_attach_client(client);  
  30.     if (status < 0) {  
  31.         kfree(client);  
  32.         client = NULL;  
  33.     }  
  34.     return client;  
  35. }  
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
	struct i2c_client	*client;
	int			status;

	client = kzalloc(sizeof *client, GFP_KERNEL);
	if (!client)
		return NULL;

	client->adapter = adap;

	client->dev.platform_data = info->platform_data;

	if (info->archdata)
		client->dev.archdata = *info->archdata;

	client->flags = info->flags;
	client->addr = info->addr;
	client->irq = info->irq;

	strlcpy(client->name, info->type, sizeof(client->name));

	/* a new style driver may be bound to this device when we
	 * return from this function, or any later moment (e.g. maybe
	 * hotplugging will load the driver module).  and the device
	 * refcount model is the standard driver model one.
	 */
	status = i2c_attach_client(client);
	if (status < 0) {
		kfree(client);
		client = NULL;
	}
	return client;
}
可以看出, i2c的从机设备被定义为一个i2c_client结构, addr为从机地址, irq为中断号
接着调用i2c_attach_client
  1. int i2c_attach_client(struct i2c_client *client)  
  2. {  
  3.     struct i2c_adapter *adapter = client->adapter;  
  4.     int res;  
  5.   
  6.     /* Check for address business */  
  7.     res = i2c_check_addr(adapter, client->addr);  
  8.     if (res)  
  9.         return res;  
  10.   
  11.     client->dev.parent = &client->adapter->dev;  
  12.     client->dev.bus = &i2c_bus_type;  
  13.   
  14.     if (client->driver)  
  15.         client->dev.driver = &client->driver->driver;  
  16.   
  17.     if (client->driver && !is_newstyle_driver(client->driver)) {  
  18.         client->dev.release = i2c_client_release;  
  19.         client->dev.uevent_suppress = 1;  
  20.     } else  
  21.         client->dev.release = i2c_client_dev_release;  
  22.   
  23.     dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adapter),  
  24.              client->addr);  
  25.     res = device_register(&client->dev);  
  26.     if (res)  
  27.         goto out_err;  
  28.   
  29.     mutex_lock(&adapter->clist_lock);  
  30.     list_add_tail(&client->list, &adapter->clients);  
  31.     mutex_unlock(&adapter->clist_lock);  
  32.   
  33.     dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",  
  34.         client->name, dev_name(&client->dev));  
  35.   
  36.     if (adapter->client_register)  {  
  37.         if (adapter->client_register(client)) {  
  38.             dev_dbg(&adapter->dev, "client_register "  
  39.                 "failed for client [%s] at 0x%02x\n",  
  40.                 client->name, client->addr);  
  41.         }  
  42.     }  
  43.   
  44.     return 0;  
  45.   
  46. out_err:  
  47.     dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "  
  48.         "(%d)\n", client->name, client->addr, res);  
  49.     return res;  
  50. }  
int i2c_attach_client(struct i2c_client *client)
{
	struct i2c_adapter *adapter = client->adapter;
	int res;

	/* Check for address business */
	res = i2c_check_addr(adapter, client->addr);
	if (res)
		return res;

	client->dev.parent = &client->adapter->dev;
	client->dev.bus = &i2c_bus_type;

	if (client->driver)
		client->dev.driver = &client->driver->driver;

	if (client->driver && !is_newstyle_driver(client->driver)) {
		client->dev.release = i2c_client_release;
		client->dev.uevent_suppress = 1;
	} else
		client->dev.release = i2c_client_dev_release;

	dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adapter),
		     client->addr);
	res = device_register(&client->dev);
	if (res)
		goto out_err;

	mutex_lock(&adapter->clist_lock);
	list_add_tail(&client->list, &adapter->clients);
	mutex_unlock(&adapter->clist_lock);

	dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
		client->name, dev_name(&client->dev));

	if (adapter->client_register)  {
		if (adapter->client_register(client)) {
			dev_dbg(&adapter->dev, "client_register "
				"failed for client [%s] at 0x%02x\n",
				client->name, client->addr);
		}
	}

	return 0;

out_err:
	dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "
		"(%d)\n", client->name, client->addr, res);
	return res;
}
i2c_attach_client做了添加设备(device_register)和将此client加入到其所在adp的clients链表中

自此, 总线驱动就添加完了, 总结一下, 具体经过以下步骤:
添加总线驱动 -> probe -> 创建适配器结构 -> 初始化适配器硬件 -> 扫描从机设备并添加

再来看下algo
在probe中, 有i2c->adap.algo = &s3c24xx_i2c_algorithm;
  1. static const struct i2c_algorithm s3c24xx_i2c_algorithm = {  
  2.     .master_xfer        = s3c24xx_i2c_xfer,  
  3.     .functionality      = s3c24xx_i2c_func,  
  4. };  
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
	.master_xfer		= s3c24xx_i2c_xfer,
	.functionality		= s3c24xx_i2c_func,
};
其中, master_xfer即为与具体硬件通讯的收发数据函数
  1. static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,  
  2.             struct i2c_msg *msgs, int num)  
  3. {  
  4.     struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;  
  5.     int retry;  
  6.     int ret;  
  7.   
  8.     for (retry = 0; retry < adap->retries; retry++) {  
  9.   
  10.         ret = s3c24xx_i2c_doxfer(i2c, msgs, num);  
  11.   
  12.         if (ret != -EAGAIN)  
  13.             return ret;  
  14.   
  15.         dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);  
  16.   
  17.         udelay(100);  
  18.     }  
  19.   
  20.     return -EREMOTEIO;  
  21. }  
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
			struct i2c_msg *msgs, int num)
{
	struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
	int retry;
	int ret;

	for (retry = 0; retry < adap->retries; retry++) {

		ret = s3c24xx_i2c_doxfer(i2c, msgs, num);

		if (ret != -EAGAIN)
			return ret;

		dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);

		udelay(100);
	}

	return -EREMOTEIO;
}
参数i2c_msg封装了buf以及flags, 用以指定读或者写或者其他操作:
  1. struct i2c_msg {  
  2.     __u16 addr; /* slave address            */  
  3.     __u16 flags;  
  4. #define I2C_M_TEN       0x0010  /* this is a ten bit chip address */  
  5. #define I2C_M_RD        0x0001  /* read data, from slave to master */  
  6. #define I2C_M_NOSTART       0x4000  /* if I2C_FUNC_PROTOCOL_MANGLING */  
  7. #define I2C_M_REV_DIR_ADDR  0x2000  /* if I2C_FUNC_PROTOCOL_MANGLING */  
  8. #define I2C_M_IGNORE_NAK    0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */  
  9. #define I2C_M_NO_RD_ACK     0x0800  /* if I2C_FUNC_PROTOCOL_MANGLING */  
  10. #define I2C_M_RECV_LEN      0x0400  /* length will be first received byte */  
  11.     __u16 len;      /* msg length               */  
  12.     __u8 *buf;      /* pointer to msg data          */  
  13. };  
struct i2c_msg {
	__u16 addr;	/* slave address			*/
	__u16 flags;
#define I2C_M_TEN		0x0010	/* this is a ten bit chip address */
#define I2C_M_RD		0x0001	/* read data, from slave to master */
#define I2C_M_NOSTART		0x4000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR	0x2000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK	0x1000	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NO_RD_ACK		0x0800	/* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_RECV_LEN		0x0400	/* length will be first received byte */
	__u16 len;		/* msg length				*/
	__u8 *buf;		/* pointer to msg data			*/
};

3. 从机设备
来看下如何静态添加一个设备
在arch/arm/mach-s3c2440/mach-anubis.c中, 可以看到一个例子:

  1. /* I2C devices. */  
  2. static struct i2c_board_info anubis_i2c_devs[] __initdata = {  
  3.     {  
  4.         I2C_BOARD_INFO("tps65011", 0x48),  
  5.         .irq    = IRQ_EINT20,  
  6.     }  
  7. };  
/* I2C devices. */
static struct i2c_board_info anubis_i2c_devs[] __initdata = {
	{
		I2C_BOARD_INFO("tps65011", 0x48),
		.irq	= IRQ_EINT20,
	}
};
tps65011是设备名称, 0x48为从机地址
还有个中断, 连接在2440的EINT20上
可以查一下这个芯片, 是个锂电的电源管理芯片
那个中断用于当电量少于10%时报警

4. 从机设备驱动

来看一个I2C EEPROM设备驱动: /drivers/i2c/chips/ ds1682.c

  1. static struct i2c_driver ds1682_driver = {  
  2.     .driver = {  
  3.         .name = "ds1682",  
  4.     },  
  5.     .probe = ds1682_probe,  
  6.     .remove = ds1682_remove,  
  7.     .id_table = ds1682_id,  
  8. };  
  9.   
  10. static int __init ds1682_init(void)  
  11. {  
  12.     return i2c_add_driver(&ds1682_driver);  
  13. }  
static struct i2c_driver ds1682_driver = {
	.driver = {
		.name = "ds1682",
	},
	.probe = ds1682_probe,
	.remove = ds1682_remove,
	.id_table = ds1682_id,
};

static int __init ds1682_init(void)
{
	return i2c_add_driver(&ds1682_driver);
}
  1. i2c_add_driver会遍历I2C总线上的设备, 匹配设备与驱动, 并调用probe  
  2. <pre name="code" class="cpp">/* 
  3.  * Called when a ds1682 device is matched with this driver 
  4.  */  
  5. static int ds1682_probe(struct i2c_client *client,  
  6.             const struct i2c_device_id *id)  
  7. {  
  8.     int rc;  
  9.   
  10.     if (!i2c_check_functionality(client->adapter,  
  11.                      I2C_FUNC_SMBUS_I2C_BLOCK)) {  
  12.         dev_err(&client->dev, "i2c bus does not support the ds1682\n");  
  13.         rc = -ENODEV;  
  14.         goto exit;  
  15.     }  
  16.   
  17.     rc = sysfs_create_group(&client->dev.kobj, &ds1682_group);  
  18.     if (rc)  
  19.         goto exit;  
  20.   
  21.     rc = sysfs_create_bin_file(&client->dev.kobj, &ds1682_eeprom_attr);  
  22.     if (rc)  
  23.         goto exit_bin_attr;  
  24.   
  25.     return 0;  
  26.   
  27.  exit_bin_attr:  
  28.     sysfs_remove_group(&client->dev.kobj, &ds1682_group);  
  29.  exit:  
  30.     return rc;  
  31. }</pre>由于EEPROM比较简单, 不需要什么硬件初始化所以我们看到, 这个probe没有做硬件初始化probe创建了在sysfs中的自己的设备目录下创建了一些属性: ds1682_group和ds1682_eeprom_attr<br>  
  32. <pre name="code" class="cpp">static SENSOR_DEVICE_ATTR_2(elapsed_time, S_IRUGO | S_IWUSR, ds1682_show,  
  33.                 ds1682_store, 4, DS1682_REG_ELAPSED);  
  34. static SENSOR_DEVICE_ATTR_2(alarm_time, S_IRUGO | S_IWUSR, ds1682_show,  
  35.                 ds1682_store, 4, DS1682_REG_ALARM);  
  36. static SENSOR_DEVICE_ATTR_2(event_count, S_IRUGO | S_IWUSR, ds1682_show,  
  37.                 ds1682_store, 2, DS1682_REG_EVT_CNTR);  
  38.   
  39. static const struct attribute_group ds1682_group = {  
  40.     .attrs = (struct attribute *[]) {  
  41.         &sensor_dev_attr_elapsed_time.dev_attr.attr,  
  42.         &sensor_dev_attr_alarm_time.dev_attr.attr,  
  43.         &sensor_dev_attr_event_count.dev_attr.attr,  
  44.         NULL,  
  45.     },  
  46. };  
  47.   
  48. static struct bin_attribute ds1682_eeprom_attr = {  
  49.     .attr = {  
  50.         .name = "eeprom",  
  51.         .mode = S_IRUGO | S_IWUSR,  
  52.     },  
  53.     .size = DS1682_EEPROM_SIZE,  
  54.     .read = ds1682_eeprom_read,  
  55.     .write = ds1682_eeprom_write,  
  56. };</pre>根据这些属性的定义, 程序在ds1682的设备目录下建立了以下文件:  
  57. <pre></pre>  
  58. <p></p>  
  59. <blockquote style="margin: 0pt 0pt 0pt 40px; border: medium none; padding: 0px;">  
  60. <p></p><pre name="code" class="cpp" style="background-color: rgb(255, 255, 255);"><pre name="code" class="cpp" style="background-color: rgb(255, 255, 255);">elapsed_time, alarm_time, event_count, eeprom  
  61. </pre>  
  62. <pre></pre>  
  63. <p></p>  
  64. </pre></blockquote>  
  65. 其中eeprom文件为一个二进制文件, 直接将整片存储空间映射到该文件, 方便用户空间对其读写<br>  
  66. <p><br>  
  67. 再来看下前三个属性的读写方法: ds1682_show, ds1682_store<br>  
  68. </p><pre name="code" class="cpp">/* 
  69.  * Generic counter attributes 
  70.  */  
  71. static ssize_t ds1682_show(struct device *dev, struct device_attribute *attr,  
  72.                char *buf)  
  73. {  
  74.     struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);  
  75.     struct i2c_client *client = to_i2c_client(dev);  
  76.     __le32 val = 0;  
  77.     int rc;  
  78.   
  79.     dev_dbg(dev, "ds1682_show() called on %s\n", attr->attr.name);  
  80.   
  81.     /* Read the register */  
  82.     rc = i2c_smbus_read_i2c_block_data(client, sattr->index, sattr->nr,  
  83.                        (u8 *) & val);  
  84.     if (rc < 0)  
  85.         return -EIO;  
  86.   
  87.     /* Special case: the 32 bit regs are time values with 1/4s 
  88.      * resolution, scale them up to milliseconds */  
  89.     if (sattr->nr == 4)  
  90.         return sprintf(buf, "%llu\n",  
  91.             ((unsigned long long)le32_to_cpu(val)) * 250);  
  92.   
  93.     /* Format the output string and return # of bytes */  
  94.     return sprintf(buf, "%li\n", (long)le32_to_cpu(val));  
  95. }  
  96.   
  97. static ssize_t ds1682_store(struct device *dev, struct device_attribute *attr,  
  98.                 const char *buf, size_t count)  
  99. {  
  100.     struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);  
  101.     struct i2c_client *client = to_i2c_client(dev);  
  102.     char *endp;  
  103.     u64 val;  
  104.     __le32 val_le;  
  105.     int rc;  
  106.   
  107.     dev_dbg(dev, "ds1682_store() called on %s\n", attr->attr.name);  
  108.   
  109.     /* Decode input */  
  110.     val = simple_strtoull(buf, &endp, 0);  
  111.     if (buf == endp) {  
  112.         dev_dbg(dev, "input string not a number\n");  
  113.         return -EINVAL;  
  114.     }  
  115.   
  116.     /* Special case: the 32 bit regs are time values with 1/4s 
  117.      * resolution, scale input down to quarter-seconds */  
  118.     if (sattr->nr == 4)  
  119.         do_div(val, 250);  
  120.   
  121.     /* write out the value */  
  122.     val_le = cpu_to_le32(val);  
  123.     rc = i2c_smbus_write_i2c_block_data(client, sattr->index, sattr->nr,  
  124.                         (u8 *) & val_le);  
  125.     if (rc < 0) {  
  126.         dev_err(dev, "register write failed; reg=0x%x, size=%i\n",  
  127.             sattr->index, sattr->nr);  
  128.         return -EIO;  
  129.     }  
  130.   
  131.     return count;  
  132. }</pre>show函数中根据属性的编号, 读取ds1682的寄存器, 获取其相应属性:<br>  
  133.   
  134. <blockquote style="margin: 0pt 0pt 0pt 40px; border: medium none; padding: 0px;">  
  135. <p>rc = i2c_smbus_read_i2c_block_data(client, sattr->index, sattr->nr, (u8 *) & val);</p>  
  136. </blockquote>  
  137. <p>读写是不很方便呢, 这里show只调用了i2c_smbus_read_i2c_block_data, 并告诉它地址和长度即可<br>  
  138. 而i2c_smbus_read_i2c_block_data是i2c_core提供的功能<br>  
  139. 这里可以看到这样抽象的好处了, 编写i2c设备驱动的人, 不需要关心适配器是什么或者怎么实现的<br>  
  140. <br>  
  141. store函数也类似<br>  
  142. <br>  
  143. 再来看下eeprom属性的读写方法:<br>  
  144. </p><pre name="code" class="cpp">/* 
  145.  * User data attribute 
  146.  */  
  147. static ssize_t ds1682_eeprom_read(struct kobject *kobj, struct bin_attribute *attr,  
  148.                   char *buf, loff_t off, size_t count)  
  149. {  
  150.     struct i2c_client *client = kobj_to_i2c_client(kobj);  
  151.     int rc;  
  152.   
  153.     dev_dbg(&client->dev, "ds1682_eeprom_read(p=%p, off=%lli, c=%zi)\n",  
  154.         buf, off, count);  
  155.   
  156.     if (off >= DS1682_EEPROM_SIZE)  
  157.         return 0;  
  158.   
  159.     if (off + count > DS1682_EEPROM_SIZE)  
  160.         count = DS1682_EEPROM_SIZE - off;  
  161.   
  162.     rc = i2c_smbus_read_i2c_block_data(client, DS1682_REG_EEPROM + off,  
  163.                        count, buf);  
  164.     if (rc < 0)  
  165.         return -EIO;  
  166.   
  167.     return count;  
  168. }  
  169.   
  170. static ssize_t ds1682_eeprom_write(struct kobject *kobj, struct bin_attribute *attr,  
  171.                    char *buf, loff_t off, size_t count)  
  172. {  
  173.     struct i2c_client *client = kobj_to_i2c_client(kobj);  
  174.   
  175.     dev_dbg(&client->dev, "ds1682_eeprom_write(p=%p, off=%lli, c=%zi)\n",  
  176.         buf, off, count);  
  177.   
  178.     if (off >= DS1682_EEPROM_SIZE)  
  179.         return -ENOSPC;  
  180.   
  181.     if (off + count > DS1682_EEPROM_SIZE)  
  182.         count = DS1682_EEPROM_SIZE - off;  
  183.   
  184.     /* Write out to the device */  
  185.     if (i2c_smbus_write_i2c_block_data(client, DS1682_REG_EEPROM + off,  
  186.                        count, buf) < 0)  
  187.         return -EIO;  
  188.   
  189.     return count;  
  190. }</pre>这个就更简单了, 像不像普通字符设备的read和write<br>  
  191. <br>  
  192. 自此, 除了I2C Core的代码没有分析, 其他的都分析完了<br> 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
dsp中使用i2c协议来存储eeprom是一种常见的应用场景,下面我将通过一个实例来详细介绍这个过程。 首先,i2c是一种串行通信协议,用于在主设备和从设备之间进行数据传输。而eeprom是一种电可擦写可编程只读存储器,可以在电源关闭的情况下持久保存数据。 在dsp中,使用i2c协议访问eeprom通常需要按照以下步骤进行: 1. 初始化i2c总线:在使用i2c之前,需要初始化i2c总线,设置控制寄存器和时钟等参数。这些参数通常根据eeprom的规格手册来配置。 2. 设置从设备地址:根据eeprom的规格手册,设置该eeprom的从设备地址。在i2c总线上,每个从设备都有一个唯一的地址,通过这个地址可以识别和访问该设备。 3. 写入数据:使用i2c协议,发送写入命令和数据到eeprom,将要保存的数据写入到eeprom的存储区域。 4. 读取数据:使用i2c协议,发送读取命令到eeprom,并从eeprom的存储区域读取数据。可以通过读取指定地址或者连续读取多个地址的方式获取需要的数据。 5. 关闭i2c总线:在完成数据读写操作后,关闭和释放i2c总线资源。 总结起来,dsp中使用i2c存储eeprom的实例分析主要涉及初始化i2c总线、设置从设备地址、写入数据、读取数据以及关闭i2c总线等过程。这些步骤可以根据具体的dsp和eeprom的规格手册进行具体的实现和调试。通过使用i2c协议来存储eeprom,可以实现dsp与外部存储器之间的数据交互,扩展dsp系统的存储容量和功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值