![关于 PCA9555 扩展器上的 #interrupt-cells 配置的混乱](https://linux22.com/image/113371/%E5%85%B3%E4%BA%8E%20PCA9555%20%E6%89%A9%E5%B1%95%E5%99%A8%E4%B8%8A%E7%9A%84%20%23interrupt-cells%20%E9%85%8D%E7%BD%AE%E7%9A%84%E6%B7%B7%E4%B9%B1.png)
我第一次尝试在我的自定义平台上设置设备树源文件。板上有一个 NXP PCA9555 gpio 扩展器。我正在尝试为设备设置节点,但有点困惑。
这是我在 dts 文件中的节点所在的位置:
ioexp0: gpio-exp@21 {
compatible = "nxp,pca9555";
reg = <21>;
interrupt-parent = <&gpio>;
interrupts = <8 0>;
gpio-controller;
#gpio-cells = <2>;
/*I don't understand the following two lines*/
interrupt-controller;
#interrupt-cells = <2>;
};
我通过使用达到这一点舰队-388-gp.dts来源作为指导。
我的困惑在于哪些代码处理该#interrupt-cells
属性。这绑定文档对于该芯片来说根本没有多大帮助,因为它没有说明任何有关中断单元解释的信息。
查看pca953x_irq_setup
函数中的源代码对于 pca9555 驱动程序 - 我没有看到任何#interrupt-cells
处理该属性的地方。这是在linux中断处理代码中处理的吗?我只是对如何知道两个中断单元的含义感到困惑。
pca953x_irq_setup
为了您的方便:
static int pca953x_irq_setup(struct pca953x_chip *chip,
int irq_base)
{
struct i2c_client *client = chip->client;
int ret, i;
if (client->irq && irq_base != -1
&& (chip->driver_data & PCA_INT)) {
ret = pca953x_read_regs(chip,
chip->regs->input, chip->irq_stat);
if (ret)
return ret;
/*
* There is no way to know which GPIO line generated the
* interrupt. We have to rely on the previous read for
* this purpose.
*/
for (i = 0; i < NBANK(chip); i++)
chip->irq_stat[i] &= chip->reg_direction[i];
mutex_init(&chip->irq_lock);
ret = devm_request_threaded_irq(&client->dev,
client->irq,
NULL,
pca953x_irq_handler,
IRQF_TRIGGER_LOW | IRQF_ONESHOT |
IRQF_SHARED,
dev_name(&client->dev), chip);
if (ret) {
dev_err(&client->dev, "failed to request irq %d\n",
client->irq);
return ret;
}
ret = gpiochip_irqchip_add_nested(&chip->gpio_chip,
&pca953x_irq_chip,
irq_base,
handle_simple_irq,
IRQ_TYPE_NONE);
if (ret) {
dev_err(&client->dev,
"could not connect irqchip to gpiochip\n");
return ret;
}
gpiochip_set_nested_irqchip(&chip->gpio_chip,
&pca953x_irq_chip,
client->irq);
}
return 0;
}
这是我第一次使用设备树,所以我希望这是我所缺少的明显的东西。
更新:
作为澄清 - 我4.12-rc4
目前正在使用内核版本。
我现在明白我误解了设备树的一些属性。我之前的印象是驱动程序必须指定如何处理所有属性。我现在看到 linux 实际上会处理许多通用属性,例如gpios
or interrupts
(这很有意义)。
以下是关于如何从 intspec 转换为发生的更详细解释IRQ_TYPE*
:
该函数of_irq_parse_one
将中断说明符整数复制到struct of_phandle_args
这里。然后这个参数irq_create_of_mapping
通过消费者函数传递给(例如of_irq_get
)。然后,该函数将这些参数映射到struct irq_fwspec
viaof_phandle_args_to_fwspec
并将其 fwspec 数据传递到irq_create_fwspec_mapping
.这些功能都可以在irqdomain.c。此时该irq将属于irq_domain
或使用irq_default_domain
.据我所知 -pca853x
驱动程序使用默认域。该域通常由平台特定代码设置。我通过搜索找到了我的irq_domain_ops
交叉引用。其中很多似乎都intspec[1] & IRQ_TYPE_SENSE_MASK
对viatype
中的变量进行了简单的复制。从这里开始,类型被设置为 irq 的via 。irq_create_fwspec_mapping
irq_domain_translate
irq_data
irqd_set_trigger_type
答案1
请阅读本文的第 2 节:指定设备的中断信息...
2) 中断控制器节点
设备被标记为具有“中断控制器”属性的中断控制器。这是一个空的布尔属性。附加的“#interrupt-cells”属性定义指定单个中断所需的单元数量。
中断控制器的绑定负责定义中断说明符的长度和格式。常用的有以下两种变体:
a) 一个细胞
#interrupt-cells 属性设置为 1,单个单元定义控制器内中断的索引。
例子:
vic: intc@10140000 { compatible = "arm,versatile-vic"; interrupt-controller; #interrupt-cells = <1>; reg = <0x10140000 0x1000>; }; sic: intc@10003000 { compatible = "arm,versatile-sic"; interrupt-controller; #interrupt-cells = <1>; reg = <0x10003000 0x1000>; interrupt-parent = <&vic>; interrupts = <31>; /* Cascaded to vic */ };
b) 两个细胞
#interrupt-cells 属性设置为 2,第一个单元定义控制器内中断的索引,而第二个单元用于指定以下任何标志:
位[3:0]触发类型和级别标志
1 = 从低到高边沿触发
2 = 从高到低边沿触发
4 = 高电平有效,敏感
8 = 低电平有效,敏感例子:
i2c@7000c000 { gpioext: gpio-adnp@41 { compatible = "ad,gpio-adnp"; reg = <0x41>; interrupt-parent = <&gpio>; interrupts = <160 1>; gpio-controller; #gpio-cells = <1>; interrupt-controller; #interrupt-cells = <2>; nr-gpios = <64>; }; sx8634@2b { compatible = "smtc,sx8634"; reg = <0x2b>; interrupt-parent = <&gpioext>; interrupts = <3 0x8>; #address-cells = <1>; #size-cells = <0>; threshold = <0x40>; sensitivity = <7>; }; };
因此,对于双单元变体,第一个数字是索引,第二个数字是定义中断输入类型的位掩码。
drivers/of/irq.c
设备树的这一部分由(例如)中的代码处理of_irq_parse_one()
。
您在引用的示例中引用的两行将设备 ( gpio-exp@21
) 声明为中断控制器,并且任何其他想要使用它的设备必须为每个中断提供两个单元。
就在这些行的上方是一个设备在另一个中断控制器(不是这个,而是具有别名的设备)中指定中断的示例gpio
,通过两个属性interrupt-parent
和interrupts
(或者您可以使用新的属性interrupts-extended
,它允许每个中断使用不同的中断控制器将父级指定为属性的第一个单元格)。