带 mpu9250 的飞思卡尔 imx6
freescale imx6 with mpu9250
我正在尝试将飞思卡尔 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设备的slave地址。驱动程序探测功能正在尝试写入设备以最初验证芯片类型。因此从站的数据和地址被发送到适配器驱动程序,适配器驱动程序启动函数写入和读取控制和状态寄存器成功执行。
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 自动读取和写入连接的设备。如果是这样,那么如果我不连接设备,我怎么会得到相同的行为?
请帮帮我。
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.
interrupts
属性
定义错误
您的 interrupts
定义似乎不正确:
interrupts = <9>;
应该是"two cells"格式(详见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>;
参考datasheet或MPU9250的驱动代码来判断IRQ的类型(falling/rising)。
失踪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));
}
这个函数在device/driver匹配发生时被执行。设备信息从 I2C 适配器探针上的设备树中读取。因此,在 i2c_add_driver()
调用您的驱动程序时,可以(通过 compatible
字符串)与设备树中的设备进行匹配,并调用 i2c_device_probe()
,填充 client->irq
并调用您的驱动程序探测函数接下来。
of_irq_get()
函数从设备树中获取IRQ号interrupts
属性
此外,由于一些副作用,曾尝试摆脱 .id_table
并专门使用 .of_match_table
进行设备匹配:commit. But then it was reverted further in this 提交。 所以现在我们必须同时定义 .id_table
和 .of_match_table
才能使 I2C 驱动程序正常工作。
我正在尝试将飞思卡尔 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设备的slave地址。驱动程序探测功能正在尝试写入设备以最初验证芯片类型。因此从站的数据和地址被发送到适配器驱动程序,适配器驱动程序启动函数写入和读取控制和状态寄存器成功执行。
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 自动读取和写入连接的设备。如果是这样,那么如果我不连接设备,我怎么会得到相同的行为?
请帮帮我。
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.
interrupts
属性
定义错误
您的 interrupts
定义似乎不正确:
interrupts = <9>;
应该是"two cells"格式(详见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>;
参考datasheet或MPU9250的驱动代码来判断IRQ的类型(falling/rising)。
失踪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));
}
这个函数在device/driver匹配发生时被执行。设备信息从 I2C 适配器探针上的设备树中读取。因此,在 i2c_add_driver()
调用您的驱动程序时,可以(通过 compatible
字符串)与设备树中的设备进行匹配,并调用 i2c_device_probe()
,填充 client->irq
并调用您的驱动程序探测函数接下来。
of_irq_get()
函数从设备树中获取IRQ号interrupts
属性
此外,由于一些副作用,曾尝试摆脱 .id_table
并专门使用 .of_match_table
进行设备匹配:commit. But then it was reverted further in this 提交。 所以现在我们必须同时定义 .id_table
和 .of_match_table
才能使 I2C 驱动程序正常工作。