I2Cドライバの登録 (i2c_add_driver)

I2Cドライバの登録とは

 I2Cドライバの登録は、照度センサドライバ等のI2CクライアントドライバをLinuxカーネルのI2Cコアドライバに登録し、コールバック関数を呼び出してもらう為の処理です。4章の「ドライバの初期化処理について」の「driverとしての初期化」で最後に出てきたi2c_add_driverが、I2Cを登録する為の関数になります。

図のI2CホストドライバはSoCのレジスタを制御し、実際に波形を出す処理を実行するドライバです。SoCの制御方法はそれぞれでまったく異なりますが、SoCベンダーがLinux標準のインターフェースに沿ってI2Cコアドライバに登録することで、共通のI2Cクライアントドライバを複数の異なるSoC上で動作させることが可能となっています。これは、I2Cに限らず汎用的なバスであれば同じ構造となっています。

使い方

 i2c_add_driver関数については「kernel/mediatek/4.4/Documentation/i2c/writing-clients」 に簡単な説明があります。

Initializing the driver
=======================

When the kernel is booted, or when your foo driver module is inserted,
you have to do some initializing. Fortunately, just registering the
driver module is usually enough.

static int __init foo_init(void)
{
	return i2c_add_driver(&foo_driver);
}
module_init(foo_init);

I2Cクライアントドライバの初期化時に呼びましょうという程度なので、具体例からもう少し説明します。照度センサデバイスドライバでは、以下に使用箇所があります。

kernel/mediatek/4.4/drivers/misc/mediatek/sensors-1.0/alsps/ltr578/ltr578.c

if (i2c_add_driver(&ltr578_i2c_driver))	{

引数に取るstruct i2c_driver構造体、今回であればltr578_i2c_driverに、コールバック関数は登録します。

static struct i2c_driver ltr578_i2c_driver = {
	.probe      = ltr578_i2c_probe,
	.remove     = ltr578_i2c_remove,
	.detect     = ltr578_i2c_detect,

probeがi2c_add_driver完了後に呼び出される関数となり、これは、I2Cコアドライバのi2c_device_probe関数により呼び出されますので、ここからI2Cクライアントドライバの初期化を行う事ができます。

kernel/mediatek/4.4/drivers/i2c/i2c-core.c

static int i2c_device_probe(struct device *dev)
{
	struct i2c_client	*client = i2c_verify_client(dev);
	struct i2c_driver	*driver;
	int status;

	if (!client)
		return 0;

	if (!client->irq) {
		int irq = -ENOENT;

		if (dev->of_node) {
			irq = of_irq_get_byname(dev->of_node, "irq");
			if (irq == -EINVAL || irq == -ENODATA)
				irq = of_irq_get(dev->of_node, 0);
		} else if (ACPI_COMPANION(dev)) {
			irq = acpi_dev_gpio_irq_get(ACPI_COMPANION(dev), 0);
		}
		if (irq == -EPROBE_DEFER)
			return irq;
		if (irq < 0)
			irq = 0;

		client->irq = irq;
	}

	driver = to_i2c_driver(dev->driver);
	if (!driver->probe || !driver->id_table)
		return -ENODEV;

	if (client->flags & I2C_CLIENT_WAKE) {
		int wakeirq = -ENOENT;

		if (dev->of_node) {
			wakeirq = of_irq_get_byname(dev->of_node, "wakeup");
			if (wakeirq == -EPROBE_DEFER)
				return wakeirq;
		}

		device_init_wakeup(&client->dev, true);

		if (wakeirq > 0 && wakeirq != client->irq)
			status = dev_pm_set_dedicated_wake_irq(dev, wakeirq);
		else if (client->irq > 0)
			status = dev_pm_set_wake_irq(dev, client->irq);
		else
			status = 0;

		if (status)
			dev_warn(&client->dev, "failed to set up wakeup irq");
	}

	dev_dbg(dev, "probe\n");

	status = of_clk_set_defaults(dev->of_node, false);
	if (status < 0)
		goto err_clear_wakeup_irq;

	status = dev_pm_domain_attach(&client->dev, true);
	if (status == -EPROBE_DEFER)
		goto err_clear_wakeup_irq;

	status = driver->probe(client, i2c_match_id(driver->id_table, client));
	if (status)
		goto err_detach_pm_domain;

最後に

 probeは、I2Cホストドライバが既に読み込まれていれば即時に、読み込まれていなければI2Cホストドライバが登録されたタイミングで、実行されます。その為、ドライバの初期化処理 の「moduleとしての初期化」で説明した方法等を用いて、必ずしもI2Cホストドライバの登録の後に、I2Cクライアントドライバからi2c_add_driverを呼び出すように設計する必要はありません。

コメント

タイトルとURLをコピーしました