2

我正在尝试将飞思卡尔 imx6 SoC 与 mpu92/65 传感器设备连接。我从 android ( https://github.com/NoelMacwan/Kernel-10.4.1.B.0.101/tree/master/drivers/staging/iio/imu ) 获取了 mpu92/65 设备驱动程序,并对驱动程序和设备树。

设备树修改:

&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 驱动修改:

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);   
     }
.....
}

我已经以上述方式从设备树中检索了平台数据。在探测功能中,我得到了client->irq=0. 但是我已经提到了设备树中的 IRQ。请有人告诉我我还需要做什么才能提到 gpio2-9 (linux pad) 作为这个 i2c 设备的中断线。

0x68 是 i2c 设备的从地址。驱动程序探测功能正在尝试写入设备以验证最初的芯片类型。因此,从站的数据和地址被发送到适配器驱动程序,在适配器驱动程序启动函数中,对控制和状态寄存器的写入和读取成功执行。

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;
}

然后适配器驱动程序写入数据寄存器

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

在此之后生成适配器中断(总线中断得到 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;
}

在读取状态寄存器后的 ISR 中,该值应为162(最后一位应0表示已确认),但对于我的设备,我得到此值163(最后一位为 1,因此未确认)。然后在确认成功函数-EIO中抛出错误。对于连接到该总线的所有其他设备,写入数据寄存器后的状态寄存器为162

我不知道为什么我会得到上述行为。还有一件事是,即使我没有连接设备,启动功能也能够写入和读取状态和控制寄存器。我不确定正在读取和写入哪个状态寄存器。如果我假设这会写入和读取适配器寄存器,那么我是否也可以假设适配器硬件自动读取和写入连接的设备。如果是这样,那么如果我不连接设备,我将如何获得相同的行为?

请帮帮我。

4

1 回答 1

2

在探测功能中,我得到了client->irq=0. 但是我已经提到了设备树中的 IRQ。请有人告诉我我还需要做什么才能提到 gpio2-9 (linux pad) 作为这个 i2c 设备的中断线。

错误的interrupts财产定义

您的interrupts定义似乎不正确:

interrupts = <9>;

它应该是“两个单元格”格式(有关详细信息,请参见Documentation/devicetree/bindings/interrupt-controller/interrupts.txt)。

我运行了下一个命令:

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

我看到大多数 imx6 SoC 都有用于 GPIO 中断的两单元格式。所以你的定义interrupts应该是这样的

interrupts = <9 IRQ_TYPE_EDGE_FALLING>;

或者如果您的内核版本仍然没有 IRQ 类型的命名常量:

interrupts = <9 2>;

请参阅MPU9250的数据表或驱动程序代码来确定 IRQ 的类型(下降/上升)。

失踪of_match_table

我不能 100% 确定接下来要解释的是你的问题的原因,但至少这值得检查。

正如我所看到的,问题是没有发生OF(设备树)匹配。要解决此问题,.id_table您还需要.of_match_table在驱动程序结构中定义和分配。所以现在你的驱动程序中有下一个驱动程序定义:

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

static struct i2c_driver inv_mpu_driver = {
    ...
    .id_table   =   inv_mpu_id,
    ...
};

你需要添加这样的东西:

#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),
        ...
    },
    ...
};

确保您的兼容字符串具有完全相同的"vendor,product"格式("invensense,mpu9250"在您的情况下)。

现在在您的设备树中,您可以使用属性"invensense,mpu9250"值来描述您的设备compatible

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

在这些步骤之后,匹配应该正确发生,并且您应该正确地看到您的client->irq分配(所以它不是 0)。

运行下一条命令列出所有支持设备树的 I2C/IIO 驱动程序,您会看到它们在驱动程序结构中都有两个表:

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

在引擎盖下

查看drivers/i2c/i2c-core.c,i2c_device_probe()函数以查看如何从 I2C 设备的设备树中读取 IRQ 号:

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));
}

当设备/驱动程序匹配发生时,该函数正在执行。设备信息是从 I2C 适配器探针上的设备树中读取的。因此,在i2c_add_driver()调用您的驱动程序时,可以(通过compatible字符串)与设备树中的设备匹配,然后i2c_device_probe()调用、填充client->irq和调用您的驱动程序探测函数。

of_irq_get()函数从设备树interrupts属性中获取 IRQ 号

此外,还试图摆脱.id_table.of_match_table专门用于设备匹配:commit。但是由于一些副作用,它在此提交中被进一步恢复。所以现在我们必须定义两个.id_tableAND.of_match_table才能使 I2C 驱动程序正常工作

于 2016-04-07T13:29:15.657 回答