Aditya Xavier
Aditya Xavier

Reputation: 31

Linux I2C Kernel Driver

I have a requirement to create a Linux Kernel Driver to interface with a bunch of MCU's over I2C, through an Interface Board. Because they would be many in number, i.e around 1-18, we are utilising a NXP / TI I2C Multiplexer.

I have imported the NXP / TI I2C Multiplexer kernel driver and have incorporated the same in the DTS file. I have been successfully able to list all the I2C Multiplexers as different i2c-x nodes.

The MCU's all are using the same Address 0x08. And I create a kernel driver which creates a hwmon + sysfs interface for it. However, the driver upon insmod only triggers __init function. And does not bother with probe().

Upon referring to many documentations I was able to trigger probe function only when am inserting

static struct i2c_board_info xxxx_i2c_devices[] = {
  {
     I2C_BOARD_INFO("xxxx", 0x08),
  },
};

And its corresponding

i2c_register_board_info(0, xxxx_i2c_devices, ARRAY_SIZE(xxxx_i2c_devices));

However, this creates only one instance in HWMON and not several as I imagined it to be. I have referred https://www.kernel.org/doc/Documentation/i2c/instantiating-devices

Without the changes in board init, I tried to utilize the Method 3, however kernel doesn't even call the probe() and detect function(), only the __init function of the driver.

Am following the driver - https://github.com/1119553797/sprd-kernel-common/blob/sprdb2g_gonk4.0/drivers/hwmon/w83l785ts.c

Would post the entire kernel driver if needed, for reference am using the Kernel 3.0.8, a custom board using board level changes to the same mentioned kernel.

Upvotes: 1

Views: 946

Answers (1)

Aditya Xavier
Aditya Xavier

Reputation: 31

Ok, after extensive googling and patching different methods heres a workaround.

First and foremost in the DTS, we can assign the I2C address in the Mux addressing directly. For .e.g.

i2c@0 {
    #address-cells = <1>;
    #size-cells = <0>;
    reg = <0>;

    adc0: nau7802@2a {
        compatible = "nuvoton,nau7802";
        reg = <0x2a>;
        nuvoton,vldo = <3000>;
    };
};

i2c@1 {
    #address-cells = <1>;
    #size-cells = <0>;
    reg = <1>;

    adc1: nau7802@2a {
        compatible = "nuvoton,nau7802";
        reg = <0x2a>;
        nuvoton,vldo = <3000>;
    };
};

i2c@2 {
    #address-cells = <1>;
    #size-cells = <0>;
    reg = <2>;

    adc2: nau7802@2a {
        compatible = "nuvoton,nau7802";
        reg = <0x2a>;
        nuvoton,vldo = <3000>;
    };
};

Then in the driver, in order to identify with the VendorID,ProductID structure as in the above example "nuvoton,nau7802", we have to make the following changes :-

static const struct i2c_device_id nau7802_i2c_id[] = {
    { "nau7802", 0 },
    { }
};
MODULE_DEVICE_TABLE(i2c, nau7802_i2c_id);

static const struct of_device_id nau7802_dt_ids[] = {
    { .compatible = "nuvoton,nau7802" },
    {},
};
MODULE_DEVICE_TABLE(of, nau7802_dt_ids);

static struct i2c_driver nau7802_driver = {
    .probe = nau7802_probe,
    .remove = nau7802_remove,
    .id_table = nau7802_i2c_id,
    .driver = {
       .name = "nau7802",
       .of_match_table = nau7802_dt_ids,
    },
 };

The part "adc1" etc can be simply skipped. "MODULE_DEVICE_TABLE(of," is meant to be device_tree information, from what I could gather.

Please do let me know if you find a better method which does not require a DTS & board _init changes.

Upvotes: 2

Related Questions