2012年7月18日 星期三

I2C簡介(二)

前一篇簡單說明了如果寫I2C device的driver。這篇主要要介紹I2C adapter(controller的Driver)。在這裡我們直接以Tegra3的I2C Controller為例來說明。要注意的是,一個板子可能會有很多controller,而一個controller可能會有多條bus。每一個bus會對應一個adapter。

  • Board-Specific Code
Tegra3的board-specific code在arch/arm/mach-tegra/device.c 及arch/arm/mach-tegra/board-cardhu.c中。從source code我們可以看出Tegra 3有五組I2C controller。我們先看device.c的部份,在這裡主要是填寫platform_device的資料如下:
//arch/arm/mach-tegra/device.c
static struct resource i2c_resource1[] = {
 [0] = {
  .start = INT_I2C,
  .end = INT_I2C,
  .flags = IORESOURCE_IRQ,
 },
 [1] = {
  .start = TEGRA_I2C_BASE,
  .end = TEGRA_I2C_BASE + TEGRA_I2C_SIZE-1,
  .flags = IORESOURCE_MEM,
 },
};
                               .
                               .
                               .

static struct resource i2c_resource5[] = {
 [0] = {
  .start  = INT_I2C5,
  .end    = INT_I2C5,
  .flags  = IORESOURCE_IRQ,
 },
 [1] = {
  .start = TEGRA_I2C5_BASE,
  .end = TEGRA_I2C5_BASE + TEGRA_I2C5_SIZE-1,
  .flags = IORESOURCE_MEM,
 },
};
#endif

static struct tegra_i2c_platform_data tegra_i2c1_platform_data = {
 .bus_clk_rate   = { 400000 },
};
                               .
                               .
                               .

static struct tegra_i2c_platform_data tegra_dvc_platform_data = {
 .bus_clk_rate   = { 400000 },
};

struct platform_device tegra_i2c_device1 = {
 .name  = "tegra-i2c",
 .id  = 0,
 .resource = i2c_resource1,
 .num_resources = ARRAY_SIZE(i2c_resource1),
 .dev = {
  .platform_data = &tegra_i2c1_platform_data,
 },
};
                               .
                               .
                               .

struct platform_device tegra_i2c_device5 = {
 .name  = "tegra-i2c",
 .id  = 4,
 .resource = i2c_resource5,
 .num_resources = ARRAY_SIZE(i2c_resource5),
 .dev = {
  .platform_data = 0,
 },
};
接著我們繼續看board-cardhu.c。不知道為什麼,在這邊又把platform_data蓋掉@@。
static struct tegra_i2c_platform_data cardhu_i2c1_platform_data = {
 .adapter_nr = 0,
        //設定這個controller有幾個bus
 .bus_count = 1,
 .bus_clk_rate = { 100000, 0 },
 .scl_gpio  = {TEGRA_GPIO_PC4, 0},
 .sda_gpio  = {TEGRA_GPIO_PC5, 0},
 .arb_recovery = arb_lost_recovery,
};

                               .
                               .
                               .

static struct tegra_i2c_platform_data cardhu_i2c5_platform_data = {
 .adapter_nr = 4,
 .bus_count = 1,
 .bus_clk_rate = { 400000, 0 },
 .scl_gpio  = {TEGRA_GPIO_PZ6, 0},
 .sda_gpio  = {TEGRA_GPIO_PZ7, 0},
 .arb_recovery = arb_lost_recovery,
};

static struct i2c_board_info __initdata cardhu_codec_wm8903_info = {
 I2C_BOARD_INFO("wm8903", 0x1a),
 .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_CDC_IRQ),
 .platform_data = &cardhu_wm8903_pdata,
};

static struct i2c_board_info __initdata cardhu_codec_aic326x_info = {
 I2C_BOARD_INFO("aic3262-codec", 0x18),
 .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_CDC_IRQ),
};

static struct i2c_board_info __initdata cardhu_codec_max98095_info = {
 I2C_BOARD_INFO("max98095", 0x10),
 .irq = TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_CDC_IRQ),
 .platform_data = &cardhu_max98095_pdata,
};

static void cardhu_i2c_init(void)
{
 tegra_i2c_device1.dev.platform_data = &cardhu_i2c1_platform_data;
                        .
                               .
                               .

 tegra_i2c_device5.dev.platform_data = &cardhu_i2c5_platform_data;

 platform_device_register(&tegra_i2c_device5);
                        .
                               .
                               .

 platform_device_register(&tegra_i2c_device1);

        
}

  •  I2C Controller Driver

看完了platform_device的宣告後,我們理所當然的去看platform_driver的地方。實作的位置在driver/i2c/busses/i2c-tegra.c。這裡我們從tegra_i2c_init_driver()看起。
static int tegra_i2c_probe(struct platform_device *pdev)
{
 struct tegra_i2c_dev *i2c_dev;
 struct tegra_i2c_platform_data *plat = pdev->dev.platform_data;
 struct resource *res;
 struct resource *iomem;
 struct clk *div_clk;
 struct clk *fast_clk = NULL;
 const unsigned int *prop;
 void *base;
 int irq;
 int nbus;
 int i = 0;
 int ret = 0;

        //取得定義在device.c的platform_device 的resource且flag為IORESOURCE_MEM 的資料
 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
        //檢查從res->start~res->start+ resource_size(res)是否可用
        iomem = request_mem_region(res->start, resource_size(res), pdev->name);
 
        //mapping到kernel虛擬空間
 base = ioremap(iomem->start, resource_size(iomem));
 
 div_clk = clk_get(&pdev->dev, "i2c-div");
 
 fast_clk = clk_get(&pdev->dev, "i2c-fast");
 
        i2c_dev = kzalloc(sizeof(struct tegra_i2c_dev) +
     (nbus-1) * sizeof(struct tegra_i2c_bus), GFP_KERNEL);
 

 i2c_dev->base = base;
 i2c_dev->div_clk = div_clk;
 i2c_dev->fast_clk = fast_clk;
 i2c_dev->iomem = iomem;
 i2c_dev->irq = irq;
 i2c_dev->cont_id = pdev->id;
 i2c_dev->dev = &pdev->dev;
 i2c_dev->is_clkon_always = plat->is_clkon_always;

 i2c_dev->last_bus_clk_rate = 100000; /* default clock rate */
 i2c_dev->last_bus_clk_rate = plat->bus_clk_rate[0];
        i2c_dev->is_high_speed_enable = plat->is_high_speed_enable;
 i2c_dev->last_bus_clk_rate = plat->bus_clk_rate[0] ?: 100000;
 i2c_dev->msgs = NULL;
 i2c_dev->msgs_num = 0;
 rt_mutex_init(&i2c_dev->dev_lock);
 spin_lock_init(&i2c_dev->fifo_lock);

 i2c_dev->slave_addr = plat->slave_addr;
 i2c_dev->hs_master_code = plat->hs_master_code;
 i2c_dev->is_dvc = plat->is_dvc;
 i2c_dev->arb_recovery = plat->arb_recovery;
 init_completion(&i2c_dev->msg_complete);

 if (irq == INT_I2C || irq == INT_I2C2 || irq == INT_I2C3)
  i2c_dev->is_slave = true;

 platform_set_drvdata(pdev, i2c_dev);

 if (i2c_dev->is_clkon_always)
  tegra_i2c_clock_enable(i2c_dev);

 ret = tegra_i2c_init(i2c_dev);
 if (ret) {
  dev_err(&pdev->dev, "Failed to initialize i2c controller");
  goto err_free;
 }

 

 for (i = 0; i < nbus; i++) {
  struct tegra_i2c_bus *i2c_bus = &i2c_dev->busses[i];

  i2c_bus->dev = i2c_dev;
  i2c_bus->mux = plat->bus_mux[i];
  i2c_bus->mux_len = plat->bus_mux_len[i];
  i2c_bus->bus_clk_rate = plat->bus_clk_rate[i] ?: 100000;

  i2c_bus->scl_gpio = plat->scl_gpio[i];
  i2c_bus->sda_gpio = plat->sda_gpio[i];

  i2c_bus->adapter.dev.of_node = pdev->dev.of_node;
  i2c_bus->adapter.algo = &tegra_i2c_algo;
  i2c_set_adapdata(&i2c_bus->adapter, i2c_bus);
  i2c_bus->adapter.owner = THIS_MODULE;
  i2c_bus->adapter.class = I2C_CLASS_HWMON;
  strlcpy(i2c_bus->adapter.name, "Tegra I2C adapter",
   sizeof(i2c_bus->adapter.name));
  i2c_bus->adapter.dev.parent = &pdev->dev;
  i2c_bus->adapter.nr = plat->adapter_nr + i;

                //!!將此bus註冊進kernel中
  ret = i2c_add_numbered_adapter(&i2c_bus->adapter);
                //註冊此bus的adapter
  of_i2c_register_devices(&i2c_bus->adapter);

  i2c_dev->bus_count++;
 }

 return 0;
        ...
}

static struct platform_driver tegra_i2c_driver = {
 .probe   = tegra_i2c_probe,
 .remove  = tegra_i2c_remove,
 .driver  = {
  .name  = "tegra-i2c",
  .owner = THIS_MODULE,
  .of_match_table = tegra_i2c_of_match,
  .pm    = TEGRA_I2C_DEV_PM_OPS,
 },
};

static int __init tegra_i2c_init_driver(void)
{
        //註冊tegra_i2c_driver,跳到tegra_i2c_probe繼續看
 return platform_driver_register(&tegra_i2c_driver);
}
subsys_initcall(tegra_i2c_init_driver);
  • Reference
  1. Introduction to I2C
  2. Linux I2C driver: what's new-style !?
  3. I2C总线使用方法继续研究
  4. nVidia Tegra Kernel Source

沒有留言: