PCIe使用INTA报错的示例及解决

问题分析

某soc的pcie设备节点更改如下;以使用inta这个传统中断

@@ -1400,6 +1400,9 @@
                                num-vectors = <256>;
                                num-viewport = <8>;
                                status = "disabled";
+                               #interrupt-cells = <1>;
+                               interrupt-map-mask = <0 0 0 0x7>;
+                               interrupt-map = <0 0 0 1 &gic GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>; /* int_a */
                                dummy@0 {
                                        label = "msi_int";
                                        interrupt-parent = <&gic>;
@@ -1407,13 +1410,6 @@
                                        interrupts = <GIC_SPI 22
                                                      IRQ_TYPE_LEVEL_HIGH>;
                                };

但是获取中断的时候会报错,打印如下

pcieport 0000:00:00.0: of_irq_parse_pci: failed with rc=-14

分析of_irq_parse_pci的调用栈,那个函数会返回这个-14的错误值,错误值的定义在include/uapi/asm-generic/errno-base.h,

#define	EPERM		 1	/* Operation not permitted */
#define	ENOENT		 2	/* No such file or directory */
#define	ESRCH		 3	/* No such process */
#define	EINTR		 4	/* Interrupted system call */
#define	EIO		 5	/* I/O error */
#define	ENXIO		 6	/* No such device or address */
#define	E2BIG		 7	/* Argument list too long */
#define	ENOEXEC		 8	/* Exec format error */
#define	EBADF		 9	/* Bad file number */
#define	ECHILD		10	/* No child processes */
#define	EAGAIN		11	/* Try again */
#define	ENOMEM		12	/* Out of memory */
#define	EACCES		13	/* Permission denied */
#define	EFAULT		14	/* Bad address */
#define	ENOTBLK		15	/* Block device required */
#define	EBUSY		16	/* Device or resource busy */
#define	EEXIST		17	/* File exists */
#define	EXDEV		18	/* Cross-device link */
#define	ENODEV		19	/* No such device */
#define	ENOTDIR		20	/* Not a directory */
#define	EISDIR		21	/* Is a directory */
#define	EINVAL		22	/* Invalid argument */
#define	ENFILE		23	/* File table overflow */
#define	EMFILE		24	/* Too many open files */
#define	ENOTTY		25	/* Not a typewriter */
#define	ETXTBSY		26	/* Text file busy */
#define	EFBIG		27	/* File too large */
#define	ENOSPC		28	/* No space left on device */
#define	ESPIPE		29	/* Illegal seek */
#define	EROFS		30	/* Read-only file system */
#define	EMLINK		31	/* Too many links */
#define	EPIPE		32	/* Broken pipe */
#define	EDOM		33	/* Math argument out of domain of func */
#define	ERANGE		34	/* Math result not representable */

实际我们就能找到是 of_irq_parse_raw 里面对中断相关属性的读取后,进行的判断报错了源码如下,加打印,打印如下:OF:  -> newintsize=3, newaddrsize=2 imaplen=3

int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
{
	struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL;
	__be32 initial_match_array[MAX_PHANDLE_ARGS];
	const __be32 *match_array = initial_match_array;
	const __be32 *tmp, *imap, *imask, dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = cpu_to_be32(~0) };
	u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0;
	int imaplen, match, i, rc = -EINVAL;

#ifdef DEBUG
	of_print_phandle_args("of_irq_parse_raw: ", out_irq);
#endif

	ipar = of_node_get(out_irq->np);

	/* First get the #interrupt-cells property of the current cursor
	 * that tells us how to interpret the passed-in intspec. If there
	 * is none, we are nice and just walk up the tree
	 */
	do {
		if (!of_property_read_u32(ipar, "#interrupt-cells", &intsize))
			break;
		tnode = ipar;
		ipar = of_irq_find_parent(ipar);
		of_node_put(tnode);
	} while (ipar);
	if (ipar == NULL) {
		pr_debug(" -> no parent found !\n");
		goto fail;
	}

	pr_debug("of_irq_parse_raw: ipar=%pOF, size=%d\n", ipar, intsize);

	if (out_irq->args_count != intsize)
		goto fail;

	/* Look for this #address-cells. We have to implement the old linux
	 * trick of looking for the parent here as some device-trees rely on it
	 */
	old = of_node_get(ipar);
	do {
		tmp = of_get_property(old, "#address-cells", NULL);
		tnode = of_get_parent(old);
		of_node_put(old);
		old = tnode;
	} while (old && tmp == NULL);
	of_node_put(old);
	old = NULL;
	addrsize = (tmp == NULL) ? 2 : be32_to_cpu(*tmp);

	pr_debug(" -> addrsize=%d\n", addrsize);

	/* Range check so that the temporary buffer doesn't overflow */
	if (WARN_ON(addrsize + intsize > MAX_PHANDLE_ARGS)) {
		rc = -EFAULT;
		goto fail;
	}

	/* Precalculate the match array - this simplifies match loop */
	for (i = 0; i < addrsize; i++)
		initial_match_array[i] = addr ? addr[i] : 0;
	for (i = 0; i < intsize; i++)
		initial_match_array[addrsize + i] = cpu_to_be32(out_irq->args[i]);

	/* Now start the actual "proper" walk of the interrupt tree */
	while (ipar != NULL) {
		/* Now check if cursor is an interrupt-controller and if it is
		 * then we are done
		 */
		if (of_property_read_bool(ipar, "interrupt-controller")) {
			pr_debug(" -> got it !\n");
			return 0;
		}

		/*
		 * interrupt-map parsing does not work without a reg
		 * property when #address-cells != 0
		 */
		if (addrsize && !addr) {
			pr_debug(" -> no reg passed in when needed !\n");
			goto fail;
		}

		/* Now look for an interrupt-map */
		imap = of_get_property(ipar, "interrupt-map", &imaplen);
		/* No interrupt map, check for an interrupt parent */
		if (imap == NULL) {
			pr_debug(" -> no map, getting parent\n");
			newpar = of_irq_find_parent(ipar);
			goto skiplevel;
		}
		imaplen /= sizeof(u32);

		/* Look for a mask */
		imask = of_get_property(ipar, "interrupt-map-mask", NULL);
		if (!imask)
			imask = dummy_imask;

		/* Parse interrupt-map */
		match = 0;
		while (imaplen > (addrsize + intsize + 1) && !match) {
			/* Compare specifiers */
			match = 1;
			for (i = 0; i < (addrsize + intsize); i++, imaplen--)
				match &= !((match_array[i] ^ *imap++) & imask[i]);

			pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen);

			/* Get the interrupt parent */
			if (of_irq_workarounds & OF_IMAP_NO_PHANDLE)
				newpar = of_node_get(of_irq_dflt_pic);
			else
				newpar = of_find_node_by_phandle(be32_to_cpup(imap));
			imap++;
			--imaplen;

			/* Check if not found */
			if (newpar == NULL) {
				pr_debug(" -> imap parent not found !\n");
				goto fail;
			}

			if (!of_device_is_available(newpar))
				match = 0;

			/* Get #interrupt-cells and #address-cells of new
			 * parent
			 */
			if (of_property_read_u32(newpar, "#interrupt-cells",
						 &newintsize)) {
				pr_debug(" -> parent lacks #interrupt-cells!\n");
				goto fail;
			}
			if (of_property_read_u32(newpar, "#address-cells",
						 &newaddrsize))
				newaddrsize = 0;

+			pr_info(" -> newintsize=%d, newaddrsize=%d, imaplen=%d\n",
			    newintsize, newaddrsize, imaplen);

			/* Check for malformed properties */
			if (WARN_ON(newaddrsize + newintsize > MAX_PHANDLE_ARGS)
			    || (imaplen < (newaddrsize + newintsize))) {
				rc = -EFAULT;
				goto fail;
			}

			imap += newaddrsize + newintsize;
			imaplen -= newaddrsize + newintsize;

			pr_debug(" -> imaplen=%d\n", imaplen);
		}
		if (!match)
			goto fail;

		/*
		 * Successfully parsed an interrrupt-map translation; copy new
		 * interrupt specifier into the out_irq structure
		 */
		match_array = imap - newaddrsize - newintsize;
		for (i = 0; i < newintsize; i++)
			out_irq->args[i] = be32_to_cpup(imap - newintsize + i);
		out_irq->args_count = intsize = newintsize;
		addrsize = newaddrsize;

	skiplevel:
		/* Iterate again with new parent */
		out_irq->np = newpar;
		pr_debug(" -> new parent: %pOF\n", newpar);
		of_node_put(ipar);
		ipar = newpar;
		newpar = NULL;
	}
	rc = -ENOENT; /* No interrupt-map found */

 fail:
	of_node_put(ipar);
	of_node_put(newpar);

	return rc;
}
EXPORT_SYMBOL_GPL(of_irq_parse_raw);

由于是从中断控制器节点获取的 interrupt-cells确实是3个单元 ,address-cells是2个单元;所以大于加入的中断属性 interrupt-map = <0 0 0 1 &gic GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>; 中gic后的三个单元

gic: interrupt-controller@12000000 {
                compatible = "arm,gic-v3";
                #interrupt-cells = <3>;
                #address-cells = <2>;
                #size-cells = <2>;
                ranges;
                redistributor-stride = <0x0 0x20000>;   // 128KB stride
                #redistributor-regions = <1>;
                interrupt-controller;
                reg = <0x0 0x12000000 0 0x20000>,       // GICD
                        <0x0 0x12040000 0 0x100000>;    // GICR
                interrupts = <1 9 4>;
        };

更改

直接作如下更改即可

-                               interrupt-map = <0 0 0 1 &gic GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>; /* int_a */
+                               interrupt-map = <0 0 0 1 &gic 0 0 GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>; /* int_a */

或者暴力点,将address-cells写成0;但不建议

--- a/source/kernel/kernel4.14_orca/drivers/of/irq.c
+++ b/source/kernel/kernel4.14_orca/drivers/of/irq.c
@@ -232,7 +232,7 @@ int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
                        if (of_property_read_u32(newpar, "#address-cells",
                                                 &newaddrsize))
                                newaddrsize = 0;
-
+                       newaddrsize = 0;
                        pr_debug(" -> newintsize=%d, newaddrsize=%d\n",
                            newintsize, newaddrsize);

cat /proc/interrupt 查看wifi驱动对这个中断的注册情况;上述中断号为5,加上偏移32,正好为37 ;表明对这个INTA的获取和注册都是正常的了

 92:        106          0     GICv3  37 Level     mtlk

其他示例

示例1:这是pcie控制器自带的中断控制器;interrupt-map = <0 0 0 1 &pcie0_intc 0>中中断单元就一个;正好等于pcie0_intc这个中断控制器的#address-cells + #interrupt-cell

pcie0: pcie@f8000000 {
                compatible = "rockchip,rk3399-pcie";
                #address-cells = <3>;
                #size-cells = <2>;
                aspm-no-l0s;
                clocks = <&cru ACLK_PCIE>, <&cru ACLK_PERF_PCIE>,
                         <&cru PCLK_PCIE>, <&cru SCLK_PCIE_PM>;
                clock-names = "aclk", "aclk-perf",
                              "hclk", "pm";
                bus-range = <0x0 0x1f>;
                max-link-speed = <1>;
                linux,pci-domain = <0>;
                msi-map = <0x0 &its 0x0 0x1000>;
                interrupts = <GIC_SPI 49 IRQ_TYPE_LEVEL_HIGH 0>,
                             <GIC_SPI 50 IRQ_TYPE_LEVEL_HIGH 0>,
                             <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH 0>;
                interrupt-names = "sys", "legacy", "client";
                #interrupt-cells = <1>;
                interrupt-map-mask = <0 0 0 7>;
                interrupt-map = <0 0 0 1 &pcie0_intc 0>,
                                <0 0 0 2 &pcie0_intc 1>,
                                <0 0 0 3 &pcie0_intc 2>,
                                <0 0 0 4 &pcie0_intc 3>;
                phys = <&pcie_phy>;
                phy-names = "pcie-phy";
                ranges = <0x83000000 0x0 0xfa000000 0x0 0xfa000000 0x0 0x1e00000
                          0x81000000 0x0 0xfbe00000 0x0 0xfbe00000 0x0 0x100000>;
                reg = <0x0 0xf8000000 0x0 0x2000000>,
                      <0x0 0xfd000000 0x0 0x1000000>;
                reg-names = "axi-base", "apb-base";
                resets = <&cru SRST_PCIE_CORE>, <&cru SRST_PCIE_MGMT>,
                         <&cru SRST_PCIE_MGMT_STICKY>, <&cru SRST_PCIE_PIPE>,
                         <&cru SRST_PCIE_PM>, <&cru SRST_P_PCIE>,
                         <&cru SRST_A_PCIE>;
                reset-names = "core", "mgmt", "mgmt-sticky", "pipe",
                              "pm", "pclk", "aclk";
                status = "disabled";
                pcie0_intc: interrupt-controller {
                        interrupt-controller;
                        #address-cells = <0>;
                        #interrupt-cells = <1>;
                };
        };

示例2:interrupt-map = <0000 0 0 1 &gic 0 0 0 109 4>, 中断控制器gic后面是5个单元,对应gic控制器的#interrupt-cells = <3>;加上 #address-cells = <2>;

pcie@3400000 {
        compatible = "fsl,ls2080a-pcie", "snps,dw-pcie";
        reg = <0x00 0x03400000 0x0 0x00100000   /* controller registers */
                0x10 0x00000000 0x0 0x00002000>; /* configuration space */
        reg-names = "regs", "config";
        interrupts = <0 108 0x4>; /* Level high type */
        interrupt-names = "intr";
        #address-cells = <3>;
        #size-cells = <2>;
        device_type = "pci";
        num-lanes = <4>;
        bus-range = <0x0 0xff>;
        ranges = <0x81000000 0x0 0x00000000 0x10 0x00010000 0x0 0x00010000   /* downstream I/O */
                    0x82000000 0x0 0x40000000 0x10 0x40000000 0x0 0x40000000>; /* non-prefetchable memory */
        msi-parent = <&its>;
        #interrupt-cells = <1>;
        interrupt-map-mask = <0 0 0 7>;
        interrupt-map = <0000 0 0 1 &gic 0 0 0 109 4>,
                        <0000 0 0 2 &gic 0 0 0 110 4>,
                        <0000 0 0 3 &gic 0 0 0 111 4>,
                        <0000 0 0 4 &gic 0 0 0 112 4>;
};

gic: interrupt-controller@6000000 {
        compatible = "arm,gic-v3";
        reg = <0x0 0x06000000 0 0x10000>, /* GIC Dist */
                <0x0 0x06100000 0 0x100000>, /* GICR (RD_base + SGI_base) */
                <0x0 0x0c0c0000 0 0x2000>, /* GICC */
                <0x0 0x0c0d0000 0 0x1000>, /* GICH */
                <0x0 0x0c0e0000 0 0x20000>; /* GICV */
        #interrupt-cells = <3>;
        #address-cells = <2>;
        #size-cells = <2>;
        ranges;
        interrupt-controller;
        interrupts = <1 9 0x4>;

        its: gic-its@6020000 {
                compatible = "arm,gic-v3-its";
                msi-controller;
                reg = <0x0 0x6020000 0 0x20000>;
        };
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值