Search code examples
linux-kernellinux-device-driveri2cdevice-treeiio

freescale imx6 with mpu9250


I am trying to interface freescale imx6 SoC with mpu92/65 sensor device. I have taken mpu92/65 device driver from android (https://github.com/NoelMacwan/Kernel-10.4.1.B.0.101/tree/master/drivers/staging/iio/imu ) and have done necessary modifications to the driver and device tree.

Device tree modifications:

&i2c3{
...
    extaccelerometer: mpu9250@68{
        compatible = "mpu9250";
        reg = <0x68>;
        interrupt-parent = <&gpio2>;
        interrupts = <9>;
        int_config  = /bits/ 8 <0x00>;
        level_shifter = /bits/ 8 <0>;
        orientation = [ 01 00 00 00 01 00 00 00 01 ];
        sec_slave_type = <2>;
        sec_slave_id = <0x12>;
        secondary_i2c_addr = /bits/ 16 <0x0C>;
        secondary_orientation = [ 00 01 00 ff 00 00 00 00 01 ];
    };
}

inv_mpu_iio driver modifications:

static void get_platdata(struct device *dev, struct inv_mpu_iio_s *st){
    struct device_node *np = dev->of_node;
    int i=0;
     of_property_read_u8(np, "int_config", &st->plat_data.int_config);
     of_property_read_u8(np, "level_shifter", &st->plat_data.level_shifter);
     of_property_read_u8_array(np, "orientation", &st->plat_data.orientation,9);
     of_property_read_u32(np, "sec_slave_type", &st->plat_data.sec_slave_type);
     of_property_read_u32(np, "sec_slave_id", &st->plat_data.sec_slave_id);
     of_property_read_u16(np, "secondary_i2c_addr", &st->plat_data.secondary_i2c_addr);
     of_property_read_u8_array(np, "secondary_orientation", &st->plat_data.secondary_orientation,9);
}

static int inv_mpu_probe(struct i2c_client *client,
    const struct i2c_device_id *id)
{
.....
    if (client->dev.of_node) {
        get_platdata(&client->dev, st);
    } else {
        st->plat_data =    *(struct mpu_platform_data *)dev_get_platdata(&client->dev);   
     }
.....
}

I have retrieved the platform data from device tree in the above manner. In probe function I am getting client->irq=0. But I have mentioned about the IRQ in the device tree. Please can someone tell me what else I need to do to mention gpio2-9 (linux pad) as an interrupt line for this i2c device.

0x68 is the slave address of the i2c device. Driver probe functionality is trying to write on to the device for verifying the chip type initially. So the data and the address of the slave is sent to the adapter driver where in the adapter driver start function writes onto and reads from control and status registers is successfully executed.

static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
{
    unsigned int temp = 0;
    int result;

    dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);

    i2c_imx_set_clk(i2c_imx);

    result = clk_prepare_enable(i2c_imx->clk);
    if (result)
        return result;
    imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR,__func__);
    /* Enable I2C controller */
    imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR,__func__);
    imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode, i2c_imx, IMX_I2C_I2CR,__func__);

    /* Wait controller to be stable */
    udelay(50);

    /* Start I2C transaction */
    temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
    temp |= I2CR_MSTA;
    imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR,__func__);
    result = i2c_imx_bus_busy(i2c_imx, 1);
    if (result)
        return result;
    i2c_imx->stopped = 0;

    temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK;
    temp &= ~I2CR_DMAEN;
    imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR,__func__);
    return result;
}

Then the adapter driver writes on to the data register

imx_i2c_write_reg(msgs->addr << 1, i2c_imx, IMX_I2C_I2DR,__func__);

After this the adapter interrupt is generated ( bus interrupt got i2c3: 291).

static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
{
    struct imx_i2c_struct *i2c_imx = dev_id;
    unsigned int temp;
    printk("irq:%d\n",irq);
    temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
    if (temp & I2SR_IIF) {
        /* save status register */
        i2c_imx->i2csr = temp;
        temp &= ~I2SR_IIF;
        printk("temp=%d\n",temp);
        temp |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IIF);
        imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR,__func__);
        wake_up(&i2c_imx->queue);
        return IRQ_HANDLED;
    }

    return IRQ_NONE;
}

In ISR after reading status register the value should be 162 (last bit should be 0 to indicate acknowledged) but for my device I am getting this value as 163 (last bit is 1 so it is not acknowledged). Then in acknowledge success function -EIO error is thrown. For all the other device connected to this bus the status register after writing onto the data register is 162.

I don't know why I am getting the above behavior. And one more thing is that even if I don't connect the device the start function is able to write into and read from the status and control registers. I am not sure which status register is being read and writing into. If I assume that this writes and reads the adapter registers, then can I also assume that the adapter h/w automatically reads and writes onto the device connected. If so then how am I getting the same behavior if I don't connect the device?

Please help me out.


Solution

  • In probe function I am getting client->irq=0. But I have mentioned about the IRQ in the device tree. Please can someone tell me what else I need to do to mention gpio2-9 (linux pad) as an interrupt line for this i2c device.

    Wrong definition of interrupts property

    Your interrupts definition seems incorrect:

    interrupts = <9>;
    

    It should be in "two cells" format (see Documentation/devicetree/bindings/interrupt-controller/interrupts.txt for details).

    I ran next command:

    $ find arch/arm/boot/dts/ -name '*imx6*' -exec grep -Hn interrupt {} \; | grep cell
    

    and I see that most of imx6 SoCs have two-cell format for GPIO interrupts. So your definition of interrupts should look like that:

    interrupts = <9 IRQ_TYPE_EDGE_FALLING>;
    

    or if your kernel version still doesn't have named constants for IRQ types:

    interrupts = <9 2>;
    

    Refer to the datasheet or driver code for MPU9250 to figure out the type of IRQ (falling/rising).

    Missingof_match_table

    I'm not 100% sure that what explained next is the cause of your issue, but at least that's worth to be checked.

    As I see it, the problem is that OF (device tree) matching is not happening. To fix this, in addition to .id_table you need to define and assign .of_match_table in your driver struct. So for now you have next driver definition in your driver:

    static const struct i2c_device_id inv_mpu_id[] = {
        ...
        {"mpu9250", INV_MPU9250},
        ...
        {}
    };
    
    static struct i2c_driver inv_mpu_driver = {
        ...
        .id_table   =   inv_mpu_id,
        ...
    };
    

    And you need to add something like this:

    #include <linux/of.h>
    
    #ifdef CONFIG_OF
    static const struct of_device_id inv_mpu_of_table[] = {
        ...
        { .compatible = "invensense,mpu9250" },
        ...
        { }
    };
    MODULE_DEVICE_TABLE(of, inv_mpu_of_table);
    #endif
    
    static struct i2c_driver inv_mpu_driver = {
        .driver = {
            .of_match_table = of_match_ptr(inv_mpu_of_table),
            ...
        },
        ...
    };
    

    Be sure that your compatible strings have exactly "vendor,product" format (which is "invensense,mpu9250" in your case).

    Now in your device tree you can describe your device using "invensense,mpu9250" as a value for compatible property:

    &i2c3 {
    ...
        extaccelerometer: mpu9250@68 {
            compatible = "invensense,mpu9250";
            ...
        }
    

    After these steps OF matching should happen correctly and you should see your client->irq assigned appropriately (so it's not 0).

    Run next command to list all I2C/IIO drivers that has device tree support, and you'll see that they all have both tables in driver struct:

    $ git grep --all-match -e of_match_table -e '\i2c_driver' -e '\.id_table\b' drivers/iio/* | sed 's/:.*//g' | sort -u
    

    Under the hood

    Look into drivers/i2c/i2c-core.c, i2c_device_probe() function to see how IRQ number is being read from device tree for I2C device:

    static int i2c_device_probe(struct device *dev)
    {
        ...
        if (dev->of_node) {
            ...
            irq = of_irq_get(dev->of_node, 0);
        }
        ...
        client->irq = irq;
        ...
        status = driver->probe(client, i2c_match_id(driver->id_table, client));
    }
    

    This function is being executed when device/driver match happens. Devices information is read from device tree on your I2C adapter probe. So on i2c_add_driver() call for your driver there can be match (by compatible string) with device from device tree, and i2c_device_probe() called, populating client->irq and calling your driver probe function next.

    of_irq_get() function obtains IRQ number from device tree interrupts property

    Also, there was an attempt to get rid of .id_table and use .of_match_table exclusively for device matching: commit. But then it was reverted further in this commit, due to some side effects. So for now we must define both .id_table AND .of_match_table for I2C driver to work correctly.