[OpenWrt-Devel] [PATCH] ath79: ar71xx create a separate driver for ar71xx pci interrupt controller.

Dmitry Tunin hanipouspilot at gmail.com
Tue Aug 21 10:59:15 EDT 2018


It is based on Chuanhong Guo work.

PCI interrupt controller is not part of PCI. It is a part of reset controller
with 0x18060018, 0x1806001c control registers.

This should fix a bug with one IRQ for all PCI devices and also handle the PCI_CORE interrupt.

I am not sure that this kind of cascading is good for performance, but this way we are more flexible when cofiguring IRQs.

Run tested on DIR-825 B1.
---
 target/linux/ath79/dts/ar7100.dtsi                 |  22 ++-
 ...turn-pci-ar71xx-driver-into-a-pure-OF-dri.patch | 168 ++++++++---------
 .../0036-MIPS-ath79-add-pci-intc.patch             | 205 +++++++++++++++++++++
 3 files changed, 306 insertions(+), 89 deletions(-)
 create mode 100644 target/linux/ath79/patches-4.14/0036-MIPS-ath79-add-pci-intc.patch

diff --git a/target/linux/ath79/dts/ar7100.dtsi b/target/linux/ath79/dts/ar7100.dtsi
index 8994a7d..6dc1751 100644
--- a/target/linux/ath79/dts/ar7100.dtsi
+++ b/target/linux/ath79/dts/ar7100.dtsi
@@ -96,6 +96,16 @@
 				#reset-cells = <1>;
 			};
 
+			pci_intc: interrupt-controller at 18060018 {
+				compatible = "qca,ar7100-pci-intc";
+				reg = <0x18060018 0x4>;
+				interrupt-parent = <&cpuintc>;
+				interrupts = <2>;
+				interrupt-controller;
+				#interrupt-cells = <1>;
+			};
+
+
 			pcie0: pcie-controller at 180c0000 {
 				compatible = "qca,ar7100-pci";
 				#address-cells = <3>;
@@ -105,14 +115,16 @@
 				reg-names = "cfg_base";
 				ranges = <0x2000000 0 0x10000000 0x10000000 0 0x07000000	/* pci memory */
 					  0x1000000 0 0x00000000 0x0000000 0 0x000001>;		/* io space */
-				interrupt-parent = <&cpuintc>;
-				interrupts = <2>;
+				interrupt-parent = <&pci_intc>;
+				interrupts = <4>;
 
-				interrupt-controller;
 				#interrupt-cells = <1>;
 
-				interrupt-map-mask = <0 0 0 1>;
-				interrupt-map = <0 0 0 0 &pcie0 0>;
+				interrupt-map-mask = <0xf800 0 0 0>;
+				interrupt-map = <0x8800 0 0 0 &pci_intc 1
+						 0x9000 0 0 0 &pci_intc 2
+						 0x9800 0 0 0 &pci_intc 3>;
+
 				status = "disabled";
 			};
 		};
diff --git a/target/linux/ath79/patches-4.14/0020-MIPS-ath79-turn-pci-ar71xx-driver-into-a-pure-OF-dri.patch b/target/linux/ath79/patches-4.14/0020-MIPS-ath79-turn-pci-ar71xx-driver-into-a-pure-OF-dri.patch
index ea3514a..95ca6d1 100644
--- a/target/linux/ath79/patches-4.14/0020-MIPS-ath79-turn-pci-ar71xx-driver-into-a-pure-OF-dri.patch
+++ b/target/linux/ath79/patches-4.14/0020-MIPS-ath79-turn-pci-ar71xx-driver-into-a-pure-OF-dri.patch
@@ -9,8 +9,10 @@ Signed-off-by: John Crispin <john at phrozen.org>
  arch/mips/pci/pci-ar71xx.c | 81 +++++++++++++++++++++++-----------------------
  1 file changed, 40 insertions(+), 41 deletions(-)
 
---- a/arch/mips/pci/pci-ar71xx.c
-+++ b/arch/mips/pci/pci-ar71xx.c
+Index: linux-4.14.65/arch/mips/pci/pci-ar71xx.c
+===================================================================
+--- linux-4.14.65.orig/arch/mips/pci/pci-ar71xx.c
++++ linux-4.14.65/arch/mips/pci/pci-ar71xx.c
 @@ -18,8 +18,11 @@
  #include <linux/pci.h>
  #include <linux/pci_regs.h>
@@ -38,89 +40,86 @@ Signed-off-by: John Crispin <john at phrozen.org>
  };
  
  /* Byte lane enable bits */
-@@ -228,29 +232,30 @@ static struct pci_ops ar71xx_pci_ops = {
+@@ -226,96 +230,6 @@ static struct pci_ops ar71xx_pci_ops = {
+ 	.write	= ar71xx_pci_write_config,
+ };
  
- static void ar71xx_pci_irq_handler(struct irq_desc *desc)
- {
+-static void ar71xx_pci_irq_handler(struct irq_desc *desc)
+-{
 -	struct ar71xx_pci_controller *apc;
- 	void __iomem *base = ath79_reset_base;
-+	struct irq_chip *chip = irq_desc_get_chip(desc);
-+	struct ar71xx_pci_controller *apc = irq_desc_get_handler_data(desc);
- 	u32 pending;
- 
+-	void __iomem *base = ath79_reset_base;
+-	u32 pending;
+-
 -	apc = irq_desc_get_handler_data(desc);
 -
-+	chained_irq_enter(chip, desc);
- 	pending = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_STATUS) &
- 		  __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
- 
- 	if (pending & AR71XX_PCI_INT_DEV0)
+-	pending = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_STATUS) &
+-		  __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
+-
+-	if (pending & AR71XX_PCI_INT_DEV0)
 -		generic_handle_irq(apc->irq_base + 0);
-+		generic_handle_irq(irq_linear_revmap(apc->domain, 1));
- 
- 	else if (pending & AR71XX_PCI_INT_DEV1)
+-
+-	else if (pending & AR71XX_PCI_INT_DEV1)
 -		generic_handle_irq(apc->irq_base + 1);
-+		generic_handle_irq(irq_linear_revmap(apc->domain, 2));
- 
- 	else if (pending & AR71XX_PCI_INT_DEV2)
+-
+-	else if (pending & AR71XX_PCI_INT_DEV2)
 -		generic_handle_irq(apc->irq_base + 2);
-+		generic_handle_irq(irq_linear_revmap(apc->domain, 3));
- 
- 	else if (pending & AR71XX_PCI_INT_CORE)
+-
+-	else if (pending & AR71XX_PCI_INT_CORE)
 -		generic_handle_irq(apc->irq_base + 4);
-+		generic_handle_irq(irq_linear_revmap(apc->domain, 4));
- 
- 	else
- 		spurious_interrupt();
-+	chained_irq_exit(chip, desc);
- }
- 
- static void ar71xx_pci_irq_unmask(struct irq_data *d)
-@@ -261,7 +266,7 @@ static void ar71xx_pci_irq_unmask(struct
- 	u32 t;
- 
- 	apc = irq_data_get_irq_chip_data(d);
+-
+-	else
+-		spurious_interrupt();
+-}
+-
+-static void ar71xx_pci_irq_unmask(struct irq_data *d)
+-{
+-	struct ar71xx_pci_controller *apc;
+-	unsigned int irq;
+-	void __iomem *base = ath79_reset_base;
+-	u32 t;
+-
+-	apc = irq_data_get_irq_chip_data(d);
 -	irq = d->irq - apc->irq_base;
-+	irq = irq_linear_revmap(apc->domain, d->irq);
- 
- 	t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
- 	__raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE);
-@@ -278,7 +283,7 @@ static void ar71xx_pci_irq_mask(struct i
- 	u32 t;
- 
- 	apc = irq_data_get_irq_chip_data(d);
+-
+-	t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
+-	__raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE);
+-
+-	/* flush write */
+-	__raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
+-}
+-
+-static void ar71xx_pci_irq_mask(struct irq_data *d)
+-{
+-	struct ar71xx_pci_controller *apc;
+-	unsigned int irq;
+-	void __iomem *base = ath79_reset_base;
+-	u32 t;
+-
+-	apc = irq_data_get_irq_chip_data(d);
 -	irq = d->irq - apc->irq_base;
-+	irq = irq_linear_revmap(apc->domain, d->irq);
- 
- 	t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
- 	__raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE);
-@@ -294,24 +299,30 @@ static struct irq_chip ar71xx_pci_irq_ch
- 	.irq_mask_ack	= ar71xx_pci_irq_mask,
- };
- 
-+static int ar71xx_pci_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
-+{
-+	struct ar71xx_pci_controller *apc = d->host_data;
-+
-+	irq_set_chip_and_handler(irq, &ar71xx_pci_irq_chip, handle_level_irq);
-+	irq_set_chip_data(irq, apc);
-+
-+	return 0;
-+}
-+
-+static const struct irq_domain_ops ar71xx_pci_domain_ops = {
-+	.xlate = irq_domain_xlate_onecell,
-+	.map = ar71xx_pci_irq_map,
-+};
-+
- static void ar71xx_pci_irq_init(struct ar71xx_pci_controller *apc)
- {
- 	void __iomem *base = ath79_reset_base;
+-
+-	t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
+-	__raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_PCI_INT_ENABLE);
+-
+-	/* flush write */
+-	__raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
+-}
+-
+-static struct irq_chip ar71xx_pci_irq_chip = {
+-	.name		= "AR71XX PCI",
+-	.irq_mask	= ar71xx_pci_irq_mask,
+-	.irq_unmask	= ar71xx_pci_irq_unmask,
+-	.irq_mask_ack	= ar71xx_pci_irq_mask,
+-};
+-
+-static void ar71xx_pci_irq_init(struct ar71xx_pci_controller *apc)
+-{
+-	void __iomem *base = ath79_reset_base;
 -	int i;
- 
- 	__raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_ENABLE);
- 	__raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_STATUS);
- 
+-
+-	__raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_ENABLE);
+-	__raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_STATUS);
+-
 -	BUILD_BUG_ON(ATH79_PCI_IRQ_COUNT < AR71XX_PCI_IRQ_COUNT);
 -
 -	apc->irq_base = ATH79_PCI_IRQ_BASE;
@@ -131,12 +130,14 @@ Signed-off-by: John Crispin <john at phrozen.org>
 -		irq_set_chip_data(i, apc);
 -	}
 -
-+	apc->domain = irq_domain_add_linear(apc->np, AR71XX_PCI_IRQ_COUNT,
-+					    &ar71xx_pci_domain_ops, apc);
- 	irq_set_chained_handler_and_data(apc->irq, ar71xx_pci_irq_handler,
- 					 apc);
- }
-@@ -328,6 +339,11 @@ static void ar71xx_pci_reset(void)
+-	irq_set_chained_handler_and_data(apc->irq, ar71xx_pci_irq_handler,
+-					 apc);
+-}
+-
+ static void ar71xx_pci_reset(void)
+ {
+ 	ath79_device_reset_set(AR71XX_RESET_PCI_BUS | AR71XX_RESET_PCI_CORE);
+@@ -328,6 +242,11 @@ static void ar71xx_pci_reset(void)
  	mdelay(100);
  }
  
@@ -148,7 +149,7 @@ Signed-off-by: John Crispin <john at phrozen.org>
  static int ar71xx_pci_probe(struct platform_device *pdev)
  {
  	struct ar71xx_pci_controller *apc;
-@@ -348,26 +364,6 @@ static int ar71xx_pci_probe(struct platf
+@@ -348,26 +267,6 @@ static int ar71xx_pci_probe(struct platf
  	if (apc->irq < 0)
  		return -EINVAL;
  
@@ -175,7 +176,7 @@ Signed-off-by: John Crispin <john at phrozen.org>
  	ar71xx_pci_reset();
  
  	/* setup COMMAND register */
-@@ -378,11 +374,13 @@ static int ar71xx_pci_probe(struct platf
+@@ -378,11 +277,12 @@ static int ar71xx_pci_probe(struct platf
  	/* clear bus errors */
  	ar71xx_pci_check_error(apc, 1);
  
@@ -187,11 +188,10 @@ Signed-off-by: John Crispin <john at phrozen.org>
  	apc->pci_ctrl.io_resource = &apc->io_res;
 +	pci_load_of_ranges(&apc->pci_ctrl, pdev->dev.of_node);
 +
-+	ar71xx_pci_irq_init(apc);
  
  	register_pci_controller(&apc->pci_ctrl);
  
-@@ -393,6 +391,7 @@ static struct platform_driver ar71xx_pci
+@@ -393,6 +293,7 @@ static struct platform_driver ar71xx_pci
  	.probe = ar71xx_pci_probe,
  	.driver = {
  		.name = "ar71xx-pci",
diff --git a/target/linux/ath79/patches-4.14/0036-MIPS-ath79-add-pci-intc.patch b/target/linux/ath79/patches-4.14/0036-MIPS-ath79-add-pci-intc.patch
new file mode 100644
index 0000000..23d6a55
--- /dev/null
+++ b/target/linux/ath79/patches-4.14/0036-MIPS-ath79-add-pci-intc.patch
@@ -0,0 +1,205 @@
+Index: linux-4.14.65/drivers/irqchip/Makefile
+===================================================================
+--- linux-4.14.65.orig/drivers/irqchip/Makefile
++++ linux-4.14.65/drivers/irqchip/Makefile
+@@ -5,6 +5,7 @@ obj-$(CONFIG_ALPINE_MSI)		+= irq-alpine-
+ obj-$(CONFIG_ATH79)			+= irq-ath79-cpu.o
+ obj-$(CONFIG_ATH79)			+= irq-ath79-intc.o
+ obj-$(CONFIG_ATH79)			+= irq-ath79-misc.o
++obj-$(CONFIG_ATH79)                     += irq-ath79-pci.o
+ obj-$(CONFIG_ARCH_BCM2835)		+= irq-bcm2835.o
+ obj-$(CONFIG_ARCH_BCM2835)		+= irq-bcm2836.o
+ obj-$(CONFIG_ARCH_EXYNOS)		+= exynos-combiner.o
+Index: linux-4.14.65/drivers/irqchip/irq-ath79-pci.c
+===================================================================
+--- /dev/null
++++ linux-4.14.65/drivers/irqchip/irq-ath79-pci.c
+@@ -0,0 +1,188 @@
++/*
++ *  Atheros AR71xx PCI interrupt controller
++ *
++ *  Copyright (C) 2015 Alban Bedel <albeu at free.fr>
++ *  Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan at atheros.com>
++ *  Copyright (C) 2008-2011 Gabor Juhos <juhosg at openwrt.org>
++ *  Copyright (C) 2008 Imre Kaloz <kaloz at openwrt.org>
++ *
++ *  Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP
++ *
++ *  This program is free software; you can redistribute it and/or modify it
++ *  under the terms of the GNU General Public License version 2 as published
++ *  by the Free Software Foundation.
++ */
++
++#include <linux/irqchip.h>
++#include <linux/irqchip/chained_irq.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++
++#define AR71XX_RESET_REG_PCI_INT_STATUS	0
++#define AR71XX_RESET_REG_PCI_INT_ENABLE	4
++
++#define AR71XX_PCI_INT_CORE			BIT(4)
++#define AR71XX_PCI_INT_DEV2			BIT(2)
++#define AR71XX_PCI_INT_DEV1			BIT(1)
++#define AR71XX_PCI_INT_DEV0			BIT(0)
++
++#define ATH79_PCI_IRQ_COUNT			5
++
++static void ath79_pci_irq_handler(struct irq_desc *desc)
++{
++	struct irq_domain *domain = irq_desc_get_handler_data(desc);
++	struct irq_chip *chip = irq_desc_get_chip(desc);
++	void __iomem *base = domain->host_data;
++	u32 pending;
++
++	chained_irq_enter(chip, desc);
++
++	pending = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_STATUS) &
++		  __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
++
++	if (pending & AR71XX_PCI_INT_DEV0)
++		generic_handle_irq(irq_linear_revmap(domain, 1));
++
++	else if (pending & AR71XX_PCI_INT_DEV1)
++		generic_handle_irq(irq_linear_revmap(domain, 2));
++
++	else if (pending & AR71XX_PCI_INT_DEV2)
++		generic_handle_irq(irq_linear_revmap(domain, 3));
++
++	else if (pending & AR71XX_PCI_INT_CORE)
++		generic_handle_irq(irq_linear_revmap(domain, 4));
++
++	else
++		spurious_interrupt();
++
++	chained_irq_exit(chip, desc);
++}
++
++static void ar71xx_pci_irq_unmask(struct irq_data *d)
++{
++	void __iomem *base = irq_data_get_irq_chip_data(d);
++	u32 t;
++
++	t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
++
++	switch (d->hwirq) {
++	case 1:
++		t |= AR71XX_PCI_INT_DEV0;
++		break;
++	case 2:
++		t |= AR71XX_PCI_INT_DEV1;
++		break;
++	case 3:
++		t |= AR71XX_PCI_INT_DEV2;
++		break;
++	case 4:
++		t |= AR71XX_PCI_INT_CORE;
++		break;
++	default:
++		WARN(1, "Incorrect IRQ used for PCI device.");
++		return;
++	}
++
++	__raw_writel(t, base + AR71XX_RESET_REG_PCI_INT_ENABLE);
++
++	/* flush write */
++	__raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
++}
++
++static void ar71xx_pci_irq_mask(struct irq_data *d)
++{
++	void __iomem *base = irq_data_get_irq_chip_data(d);
++	u32 t;
++
++	t = __raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
++
++	switch (d->hwirq) {
++	case 1:
++		t &= (~AR71XX_PCI_INT_DEV0);
++		break;
++	case 2:
++		t &= (~AR71XX_PCI_INT_DEV1);
++		break;
++	case 3:
++		t &= (~AR71XX_PCI_INT_DEV2);
++		break;
++	case 4:
++		t &= (~AR71XX_PCI_INT_CORE);
++		break;
++	default:
++		WARN(1, "Incorrect IRQ used for PCI device.");
++		return;
++	}
++
++	__raw_writel(t, base + AR71XX_RESET_REG_PCI_INT_ENABLE);
++
++	/* flush write */
++	__raw_readl(base + AR71XX_RESET_REG_PCI_INT_ENABLE);
++}
++
++static struct irq_chip ar71xx_pci_irq_chip = {
++	.name		= "AR71XX PCI",
++	.irq_unmask	= ar71xx_pci_irq_unmask,
++	.irq_mask	= ar71xx_pci_irq_mask,
++	.irq_mask_ack	= ar71xx_pci_irq_mask,
++};
++
++static int ar71xx_pci_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
++{
++	struct ar71xx_pci_controller *apc = d->host_data;
++
++	irq_set_chip_and_handler(irq, &ar71xx_pci_irq_chip, handle_level_irq);
++	irq_set_chip_data(irq, apc);
++
++	return 0;
++}
++
++static const struct irq_domain_ops pci_irq_domain_ops = {
++	.xlate = irq_domain_xlate_onecell,
++	.map = ar71xx_pci_irq_map,
++};
++
++static void __init ath79_pci_intc_domain_init(
++	struct irq_domain *domain, int irq)
++{
++	void __iomem *base = domain->host_data;
++
++	/* Disable and clear all interrupts */
++	__raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_ENABLE);
++	__raw_writel(0, base + AR71XX_RESET_REG_PCI_INT_STATUS);
++
++	irq_set_chained_handler_and_data(irq, ath79_pci_irq_handler, domain);
++}
++
++static int __init ar7100_pci_intc_of_init(
++	struct device_node *node, struct device_node *parent)
++{
++	struct irq_domain *domain;
++	void __iomem *base;
++	int irq;
++
++	irq = irq_of_parse_and_map(node, 0);
++	if (!irq) {
++		pr_err("Failed to get PCI IRQ\n");
++		return -EINVAL;
++	}
++
++	base = of_iomap(node, 0);
++	if (!base) {
++		pr_err("Failed to get PCI IRQ registers\n");
++		return -ENOMEM;
++	}
++
++	domain = irq_domain_add_linear(node, ATH79_PCI_IRQ_COUNT,
++				&pci_irq_domain_ops, base);
++	if (!domain) {
++		pr_err("Failed to add PCI irqdomain\n");
++		return -EINVAL;
++	}
++
++	ath79_pci_intc_domain_init(domain, irq);
++	return 0;
++}
++
++IRQCHIP_DECLARE(ar7100_pci_intc, "qca,ar7100-pci-intc",
++		ar7100_pci_intc_of_init);
-- 
2.7.4


_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel



More information about the openwrt-devel mailing list