freescale imx6 с mpu9250

Я пытаюсь связать freescale imx6 SoC с сенсорным устройством mpu92/65. Я взял драйвер устройства mpu92/65 от Android ( https://github.com/NoelMacwan/Kernel-10.4.1.B.0.101/tree/master/drivers/staging/iio/imu) и внес необходимые изменения в дерево драйверов и устройств.

Модификации дерева устройств:

&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) в качестве линии прерывания для этого устройства 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,

Я не знаю, почему у меня такое поведение. И еще одна вещь: даже если я не подключу устройство, функция запуска сможет записывать и считывать данные из регистров состояния и управления. Я не уверен, какой регистр статуса читается и записывается. Если я предполагаю, что это пишет и читает регистры адаптера, то я также могу предположить, что адаптер h/w автоматически читает и пишет на подключенное устройство. Если так, то как я получу такое же поведение, если я не подключу устройство?

Пожалуйста, помогите мне.

1 ответ

Решение

В функции зонда я получаю client->irq=0, Но я упомянул про IRQ в дереве устройств. Может кто-нибудь сказать мне, что еще мне нужно сделать, чтобы упомянуть gpio2-9 (панель linux) в качестве линии прерывания для этого устройства i2c.

Неправильное определение interrupts имущество

Ваш interrupts определение кажется неверным:

interrupts = <9>;

Он должен быть в формате "две ячейки" (подробности см. В http://lxr.free-electrons.com/source/Documentation/devicetree/bindings/interrupt-controller/interrupts.txt).

Я запустил следующую команду:

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

и я вижу, что большинство SoC imx6 имеют формат двух ячеек для прерываний 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() Функция, чтобы увидеть, как номер IRQ читается из дерева устройств для устройства I2C:

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() функция получает номер IRQ из дерева устройств interrupts имущество

Также была попытка избавиться от .id_table и использовать .of_match_table исключительно для сопоставления устройств: коммит. Но затем он был возвращен в этом коммите из-за некоторых побочных эффектов. Так что сейчас мы должны определить оба .id_table А ТАКЖЕ .of_match_table для корректной работы драйвера I2C.

Другие вопросы по тегам