前一篇簡單說明了如果寫I2C device的driver。這篇主要要介紹I2C adapter(controller的Driver)。在這裡我們直接以Tegra3的I2C Controller為例來說明。要注意的是,一個板子可能會有很多controller,而一個controller可能會有多條bus。每一個bus會對應一個adapter。
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);
}
看完了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);
- Introduction to I2C
- Linux I2C driver: what's new-style !?
- I2C总线使用方法继续研究
- nVidia Tegra Kernel Source