代码改变世界

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