* [PATCH v3 01/10] dt-bindings: vendor-prefixes: Add EcoNet
2025-03-30 17:02 [PATCH v3 00/10] Add EcoNet EN751221 MIPS platform support Caleb James DeLisle
@ 2025-03-30 17:02 ` Caleb James DeLisle
2025-03-31 7:00 ` Krzysztof Kozlowski
2025-04-10 13:24 ` Rob Herring
2025-03-30 17:02 ` [PATCH v3 02/10] dt-bindings: interrupt-controller: Add EcoNet EN751221 INTC Caleb James DeLisle
` (8 subsequent siblings)
9 siblings, 2 replies; 18+ messages in thread
From: Caleb James DeLisle @ 2025-03-30 17:02 UTC (permalink / raw)
To: linux-mips
Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thomas Bogendoerfer, Daniel Lezcano, devicetree, linux-kernel,
benjamin.larsson, linux-mediatek, Caleb James DeLisle
Add the "econet" vendor prefix for SoC maker
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
---
Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 5079ca6ce1d1..371f6bb81f81 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -428,6 +428,8 @@ patternProperties:
description: EBV Elektronik
"^eckelmann,.*":
description: Eckelmann AG
+ "^econet,.*":
+ description: EcoNet (HK) Limited
"^edgeble,.*":
description: Edgeble AI Technologies Pvt. Ltd.
"^edimax,.*":
--
2.39.5
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v3 01/10] dt-bindings: vendor-prefixes: Add EcoNet
2025-03-30 17:02 ` [PATCH v3 01/10] dt-bindings: vendor-prefixes: Add EcoNet Caleb James DeLisle
@ 2025-03-31 7:00 ` Krzysztof Kozlowski
2025-04-10 13:24 ` Rob Herring
1 sibling, 0 replies; 18+ messages in thread
From: Krzysztof Kozlowski @ 2025-03-31 7:00 UTC (permalink / raw)
To: Caleb James DeLisle
Cc: linux-mips, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Thomas Bogendoerfer, Daniel Lezcano, devicetree,
linux-kernel, benjamin.larsson, linux-mediatek
On Sun, Mar 30, 2025 at 05:02:57PM +0000, Caleb James DeLisle wrote:
> Add the "econet" vendor prefix for SoC maker
>
> Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
> ---
> Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
> 1 file changed, 2 insertions(+)
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v3 01/10] dt-bindings: vendor-prefixes: Add EcoNet
2025-03-30 17:02 ` [PATCH v3 01/10] dt-bindings: vendor-prefixes: Add EcoNet Caleb James DeLisle
2025-03-31 7:00 ` Krzysztof Kozlowski
@ 2025-04-10 13:24 ` Rob Herring
1 sibling, 0 replies; 18+ messages in thread
From: Rob Herring @ 2025-04-10 13:24 UTC (permalink / raw)
To: Caleb James DeLisle
Cc: linux-mips, Thomas Gleixner, Krzysztof Kozlowski, Conor Dooley,
Thomas Bogendoerfer, Daniel Lezcano, devicetree, linux-kernel,
benjamin.larsson, linux-mediatek
On Sun, Mar 30, 2025 at 05:02:57PM +0000, Caleb James DeLisle wrote:
> Add the "econet" vendor prefix for SoC maker
>
> Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
> ---
> Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
> 1 file changed, 2 insertions(+)
Applied since there's a user already applied.
Rob
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v3 02/10] dt-bindings: interrupt-controller: Add EcoNet EN751221 INTC
2025-03-30 17:02 [PATCH v3 00/10] Add EcoNet EN751221 MIPS platform support Caleb James DeLisle
2025-03-30 17:02 ` [PATCH v3 01/10] dt-bindings: vendor-prefixes: Add EcoNet Caleb James DeLisle
@ 2025-03-30 17:02 ` Caleb James DeLisle
2025-03-31 21:01 ` Rob Herring (Arm)
2025-04-07 7:43 ` [tip: irq/drivers] " tip-bot2 for Caleb James DeLisle
2025-03-30 17:02 ` [PATCH v3 03/10] irqchip: " Caleb James DeLisle
` (7 subsequent siblings)
9 siblings, 2 replies; 18+ messages in thread
From: Caleb James DeLisle @ 2025-03-30 17:02 UTC (permalink / raw)
To: linux-mips
Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thomas Bogendoerfer, Daniel Lezcano, devicetree, linux-kernel,
benjamin.larsson, linux-mediatek, Caleb James DeLisle
Document the device tree binding for the interrupt controller in the
EcoNet EN751221 MIPS SoC.
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
---
.../econet,en751221-intc.yaml | 78 +++++++++++++++++++
1 file changed, 78 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml
diff --git a/Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml
new file mode 100644
index 000000000000..5536319c49c3
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml
@@ -0,0 +1,78 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/econet,en751221-intc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: EcoNet EN751221 Interrupt Controller
+
+maintainers:
+ - Caleb James DeLisle <cjd@cjdns.fr>
+
+description:
+ The EcoNet EN751221 Interrupt Controller is a simple interrupt controller
+ designed for the MIPS 34Kc MT SMP processor with 2 VPEs. Each interrupt can
+ be routed to either VPE but not both, so to support per-CPU interrupts, a
+ secondary IRQ number is allocated to control masking/unmasking on VPE#1. For
+ lack of a better term we call these "shadow interrupts". The assignment of
+ shadow interrupts is defined by the SoC integrator when wiring the interrupt
+ lines, so they are configurable in the device tree.
+
+allOf:
+ - $ref: /schemas/interrupt-controller.yaml#
+
+properties:
+ compatible:
+ const: econet,en751221-intc
+
+ reg:
+ maxItems: 1
+
+ "#interrupt-cells":
+ const: 1
+
+ interrupt-controller: true
+
+ interrupts:
+ maxItems: 1
+ description: Interrupt line connecting this controller to its parent.
+
+ econet,shadow-interrupts:
+ $ref: /schemas/types.yaml#/definitions/uint32-matrix
+ description:
+ An array of interrupt number pairs where each pair represents a shadow
+ interrupt relationship. The first number in each pair is the primary IRQ,
+ and the second is its shadow IRQ used for VPE#1 control. For example,
+ <8 3> means IRQ 8 is shadowed by IRQ 3, so IRQ 3 cannot be mapped, but
+ when VPE#1 requests IRQ 8, it will manipulate the IRQ 3 mask bit.
+ minItems: 1
+ maxItems: 20
+ items:
+ items:
+ - description: primary per-CPU IRQ
+ - description: shadow IRQ number
+
+required:
+ - compatible
+ - reg
+ - interrupt-controller
+ - "#interrupt-cells"
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |
+ interrupt-controller@1fb40000 {
+ compatible = "econet,en751221-intc";
+ reg = <0x1fb40000 0x100>;
+
+ interrupt-controller;
+ #interrupt-cells = <1>;
+
+ interrupt-parent = <&cpuintc>;
+ interrupts = <2>;
+
+ econet,shadow-interrupts = <7 2>, <8 3>, <13 12>, <30 29>;
+ };
+...
--
2.39.5
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v3 02/10] dt-bindings: interrupt-controller: Add EcoNet EN751221 INTC
2025-03-30 17:02 ` [PATCH v3 02/10] dt-bindings: interrupt-controller: Add EcoNet EN751221 INTC Caleb James DeLisle
@ 2025-03-31 21:01 ` Rob Herring (Arm)
2025-04-07 7:43 ` [tip: irq/drivers] " tip-bot2 for Caleb James DeLisle
1 sibling, 0 replies; 18+ messages in thread
From: Rob Herring (Arm) @ 2025-03-31 21:01 UTC (permalink / raw)
To: Caleb James DeLisle
Cc: Conor Dooley, Daniel Lezcano, Thomas Bogendoerfer,
Thomas Gleixner, Krzysztof Kozlowski, linux-kernel, linux-mips,
benjamin.larsson, devicetree, linux-mediatek
On Sun, 30 Mar 2025 17:02:58 +0000, Caleb James DeLisle wrote:
> Document the device tree binding for the interrupt controller in the
> EcoNet EN751221 MIPS SoC.
>
> Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
> ---
> .../econet,en751221-intc.yaml | 78 +++++++++++++++++++
> 1 file changed, 78 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml
>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [tip: irq/drivers] dt-bindings: interrupt-controller: Add EcoNet EN751221 INTC
2025-03-30 17:02 ` [PATCH v3 02/10] dt-bindings: interrupt-controller: Add EcoNet EN751221 INTC Caleb James DeLisle
2025-03-31 21:01 ` Rob Herring (Arm)
@ 2025-04-07 7:43 ` tip-bot2 for Caleb James DeLisle
1 sibling, 0 replies; 18+ messages in thread
From: tip-bot2 for Caleb James DeLisle @ 2025-04-07 7:43 UTC (permalink / raw)
To: linux-tip-commits
Cc: Caleb James DeLisle, Thomas Gleixner, Rob Herring (Arm), x86,
linux-kernel
The following commit has been merged into the irq/drivers branch of tip:
Commit-ID: 9773c540441c6ae15aefb49e67142e94369dbbc0
Gitweb: https://git.kernel.org/tip/9773c540441c6ae15aefb49e67142e94369dbbc0
Author: Caleb James DeLisle <cjd@cjdns.fr>
AuthorDate: Sun, 30 Mar 2025 17:02:58
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Mon, 07 Apr 2025 09:39:38 +02:00
dt-bindings: interrupt-controller: Add EcoNet EN751221 INTC
Document the device tree binding for the interrupt controller in the
EcoNet EN751221 MIPS SoC.
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Link: https://lore.kernel.org/all/20250330170306.2584136-3-cjd@cjdns.fr
---
Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 78 insertions(+)
create mode 100644 Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml
diff --git a/Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml
new file mode 100644
index 0000000..5536319
--- /dev/null
+++ b/Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml
@@ -0,0 +1,78 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/interrupt-controller/econet,en751221-intc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: EcoNet EN751221 Interrupt Controller
+
+maintainers:
+ - Caleb James DeLisle <cjd@cjdns.fr>
+
+description:
+ The EcoNet EN751221 Interrupt Controller is a simple interrupt controller
+ designed for the MIPS 34Kc MT SMP processor with 2 VPEs. Each interrupt can
+ be routed to either VPE but not both, so to support per-CPU interrupts, a
+ secondary IRQ number is allocated to control masking/unmasking on VPE#1. For
+ lack of a better term we call these "shadow interrupts". The assignment of
+ shadow interrupts is defined by the SoC integrator when wiring the interrupt
+ lines, so they are configurable in the device tree.
+
+allOf:
+ - $ref: /schemas/interrupt-controller.yaml#
+
+properties:
+ compatible:
+ const: econet,en751221-intc
+
+ reg:
+ maxItems: 1
+
+ "#interrupt-cells":
+ const: 1
+
+ interrupt-controller: true
+
+ interrupts:
+ maxItems: 1
+ description: Interrupt line connecting this controller to its parent.
+
+ econet,shadow-interrupts:
+ $ref: /schemas/types.yaml#/definitions/uint32-matrix
+ description:
+ An array of interrupt number pairs where each pair represents a shadow
+ interrupt relationship. The first number in each pair is the primary IRQ,
+ and the second is its shadow IRQ used for VPE#1 control. For example,
+ <8 3> means IRQ 8 is shadowed by IRQ 3, so IRQ 3 cannot be mapped, but
+ when VPE#1 requests IRQ 8, it will manipulate the IRQ 3 mask bit.
+ minItems: 1
+ maxItems: 20
+ items:
+ items:
+ - description: primary per-CPU IRQ
+ - description: shadow IRQ number
+
+required:
+ - compatible
+ - reg
+ - interrupt-controller
+ - "#interrupt-cells"
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |
+ interrupt-controller@1fb40000 {
+ compatible = "econet,en751221-intc";
+ reg = <0x1fb40000 0x100>;
+
+ interrupt-controller;
+ #interrupt-cells = <1>;
+
+ interrupt-parent = <&cpuintc>;
+ interrupts = <2>;
+
+ econet,shadow-interrupts = <7 2>, <8 3>, <13 12>, <30 29>;
+ };
+...
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 03/10] irqchip: Add EcoNet EN751221 INTC
2025-03-30 17:02 [PATCH v3 00/10] Add EcoNet EN751221 MIPS platform support Caleb James DeLisle
2025-03-30 17:02 ` [PATCH v3 01/10] dt-bindings: vendor-prefixes: Add EcoNet Caleb James DeLisle
2025-03-30 17:02 ` [PATCH v3 02/10] dt-bindings: interrupt-controller: Add EcoNet EN751221 INTC Caleb James DeLisle
@ 2025-03-30 17:02 ` Caleb James DeLisle
2025-04-07 7:43 ` [tip: irq/drivers] " tip-bot2 for Caleb James DeLisle
2025-03-30 17:03 ` [PATCH v3 04/10] dt-bindings: timer: Add EcoNet EN751221 "HPT" CPU Timer Caleb James DeLisle
` (6 subsequent siblings)
9 siblings, 1 reply; 18+ messages in thread
From: Caleb James DeLisle @ 2025-03-30 17:02 UTC (permalink / raw)
To: linux-mips
Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thomas Bogendoerfer, Daniel Lezcano, devicetree, linux-kernel,
benjamin.larsson, linux-mediatek, Caleb James DeLisle
Add a driver for the interrupt controller in the EcoNet EN751221 MIPS SoC.
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
---
drivers/irqchip/Kconfig | 5 +
drivers/irqchip/Makefile | 1 +
drivers/irqchip/irq-econet-en751221.c | 309 ++++++++++++++++++++++++++
3 files changed, 315 insertions(+)
create mode 100644 drivers/irqchip/irq-econet-en751221.c
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index c11b9965c4ad..a591ad3156dc 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -147,6 +147,11 @@ config DW_APB_ICTL
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN_HIERARCHY
+config ECONET_EN751221_INTC
+ bool
+ select GENERIC_IRQ_CHIP
+ select IRQ_DOMAIN
+
config FARADAY_FTINTC010
bool
select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 25e9ad29b8c4..1ee83823928d 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o
obj-$(CONFIG_ARCH_ACTIONS) += irq-owl-sirq.o
obj-$(CONFIG_DAVINCI_CP_INTC) += irq-davinci-cp-intc.o
obj-$(CONFIG_EXYNOS_IRQ_COMBINER) += exynos-combiner.o
+obj-$(CONFIG_ECONET_EN751221_INTC) += irq-econet-en751221.o
obj-$(CONFIG_FARADAY_FTINTC010) += irq-ftintc010.o
obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o
obj-$(CONFIG_ARCH_LPC32XX) += irq-lpc32xx.o
diff --git a/drivers/irqchip/irq-econet-en751221.c b/drivers/irqchip/irq-econet-en751221.c
new file mode 100644
index 000000000000..886d60c6f8a0
--- /dev/null
+++ b/drivers/irqchip/irq-econet-en751221.c
@@ -0,0 +1,309 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * EN751221 Interrupt Controller Driver.
+ *
+ * The EcoNet EN751221 Interrupt Controller is a simple interrupt controller
+ * designed for the MIPS 34Kc MT SMP processor with 2 VPEs. Each interrupt can
+ * be routed to either VPE but not both, so to support per-CPU interrupts, a
+ * secondary IRQ number is allocated to control masking/unmasking on VPE#1. In
+ * this driver, these are called "shadow interrupts". The assignment of shadow
+ * interrupts is defined by the SoC integrator when wiring the interrupt lines,
+ * so they are configurable in the device tree.
+ *
+ * If an interrupt (say 30) needs per-CPU capability, the SoC integrator
+ * allocates another IRQ number (say 29) to be its shadow. The device tree
+ * reflects this by adding the pair <30 29> to the "econet,shadow-interrupts"
+ * property.
+ *
+ * When VPE#1 requests IRQ 30, the driver manipulates the mask bit for IRQ 29,
+ * telling the hardware to mask VPE#1's view of IRQ 30.
+ *
+ * Copyright (C) 2025 Caleb James DeLisle <cjd@cjdns.fr>
+ */
+
+#include <linux/cleanup.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+
+#define IRQ_COUNT 40
+
+#define NOT_PERCPU 0xff
+#define IS_SHADOW 0xfe
+
+#define REG_MASK0 0x04
+#define REG_MASK1 0x50
+#define REG_PENDING0 0x08
+#define REG_PENDING1 0x54
+
+/**
+ * @membase: Base address of the interrupt controller registers
+ * @interrupt_shadows: Array of all interrupts, for each value,
+ * - NOT_PERCPU: This interrupt is not per-cpu, so it has no shadow
+ * - IS_SHADOW: This interrupt is a shadow of another per-cpu interrupt
+ * - else: This is a per-cpu interrupt whose shadow is the value
+ */
+static struct {
+ void __iomem *membase;
+ u8 interrupt_shadows[IRQ_COUNT];
+} econet_intc __ro_after_init;
+
+static DEFINE_RAW_SPINLOCK(irq_lock);
+
+/* IRQs must be disabled */
+static void econet_wreg(u32 reg, u32 val, u32 mask)
+{
+ u32 v;
+
+ guard(raw_spinlock)(&irq_lock);
+
+ v = ioread32(econet_intc.membase + reg);
+ v &= ~mask;
+ v |= val & mask;
+ iowrite32(v, econet_intc.membase + reg);
+}
+
+/* IRQs must be disabled */
+static void econet_chmask(u32 hwirq, bool unmask)
+{
+ u32 reg, mask;
+ u8 shadow;
+
+ /*
+ * If the IRQ is a shadow, it should never be manipulated directly.
+ * It should only be masked/unmasked as a result of the "real" per-cpu
+ * irq being manipulated by a thread running on VPE#1.
+ * If it is per-cpu (has a shadow), and we're on VPE#1, the shadow is what we mask.
+ * This is single processor only, so smp_processor_id() never exceeds 1.
+ */
+ shadow = econet_intc.interrupt_shadows[hwirq];
+ if (WARN_ON_ONCE(shadow == IS_SHADOW))
+ return;
+ else if (shadow != NOT_PERCPU && smp_processor_id() == 1)
+ hwirq = shadow;
+
+ if (hwirq >= 32) {
+ reg = REG_MASK1;
+ mask = BIT(hwirq - 32);
+ } else {
+ reg = REG_MASK0;
+ mask = BIT(hwirq);
+ }
+
+ econet_wreg(reg, unmask ? mask : 0, mask);
+}
+
+/* IRQs must be disabled */
+static void econet_intc_mask(struct irq_data *d)
+{
+ econet_chmask(d->hwirq, false);
+}
+
+/* IRQs must be disabled */
+static void econet_intc_unmask(struct irq_data *d)
+{
+ econet_chmask(d->hwirq, true);
+}
+
+static void econet_mask_all(void)
+{
+ /* IRQs are generally disabled during init, but guarding here makes it non-obligatory. */
+ guard(irqsave)();
+ econet_wreg(REG_MASK0, 0, ~0);
+ econet_wreg(REG_MASK1, 0, ~0);
+}
+
+static void econet_intc_handle_pending(struct irq_domain *d, u32 pending, u32 offset)
+{
+ int hwirq;
+
+ while (pending) {
+ hwirq = fls(pending) - 1;
+ generic_handle_domain_irq(d, hwirq + offset);
+ pending &= ~BIT(hwirq);
+ }
+}
+
+static void econet_intc_from_parent(struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct irq_domain *domain;
+ u32 pending0, pending1;
+
+ chained_irq_enter(chip, desc);
+
+ pending0 = ioread32(econet_intc.membase + REG_PENDING0);
+ pending1 = ioread32(econet_intc.membase + REG_PENDING1);
+
+ if (unlikely(!(pending0 | pending1))) {
+ spurious_interrupt();
+ } else {
+ domain = irq_desc_get_handler_data(desc);
+ econet_intc_handle_pending(domain, pending0, 0);
+ econet_intc_handle_pending(domain, pending1, 32);
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static const struct irq_chip econet_irq_chip;
+
+static int econet_intc_map(struct irq_domain *d, u32 irq, irq_hw_number_t hwirq)
+{
+ int ret;
+
+ if (hwirq >= IRQ_COUNT) {
+ pr_err("%s: hwirq %lu out of range\n", __func__, hwirq);
+ return -EINVAL;
+ } else if (econet_intc.interrupt_shadows[hwirq] == IS_SHADOW) {
+ pr_err("%s: can't map hwirq %lu, it is a shadow interrupt\n", __func__, hwirq);
+ return -EINVAL;
+ }
+
+ if (econet_intc.interrupt_shadows[hwirq] == NOT_PERCPU) {
+ irq_set_chip_and_handler(irq, &econet_irq_chip, handle_level_irq);
+ } else {
+ irq_set_chip_and_handler(irq, &econet_irq_chip, handle_percpu_devid_irq);
+ ret = irq_set_percpu_devid(irq);
+ if (ret)
+ pr_warn("%s: Failed irq_set_percpu_devid for %u: %d\n", d->name, irq, ret);
+ }
+
+ irq_set_chip_data(irq, NULL);
+ return 0;
+}
+
+static const struct irq_chip econet_irq_chip = {
+ .name = "en751221-intc",
+ .irq_unmask = econet_intc_unmask,
+ .irq_mask = econet_intc_mask,
+ .irq_mask_ack = econet_intc_mask,
+};
+
+static const struct irq_domain_ops econet_domain_ops = {
+ .xlate = irq_domain_xlate_onecell,
+ .map = econet_intc_map
+};
+
+static int __init get_shadow_interrupts(struct device_node *node)
+{
+ const char *field = "econet,shadow-interrupts";
+ int num_shadows;
+
+ num_shadows = of_property_count_u32_elems(node, field);
+
+ memset(econet_intc.interrupt_shadows, NOT_PERCPU,
+ sizeof(econet_intc.interrupt_shadows));
+
+ if (num_shadows <= 0) {
+ return 0;
+ } else if (num_shadows % 2) {
+ pr_err("%pOF: %s count is odd, ignoring\n", node, field);
+ return 0;
+ }
+
+ u32 *shadows __free(kfree) = kmalloc_array(num_shadows, sizeof(u32), GFP_KERNEL);
+ if (!shadows)
+ return -ENOMEM;
+
+ if (of_property_read_u32_array(node, field, shadows, num_shadows)) {
+ pr_err("%pOF: Failed to read %s\n", node, field);
+ return -EINVAL;
+ }
+
+ for (int i = 0; i < num_shadows; i += 2) {
+ u32 shadow = shadows[i + 1];
+ u32 target = shadows[i];
+
+ if (shadow > IRQ_COUNT) {
+ pr_err("%pOF: %s[%d] shadow(%d) out of range\n",
+ node, field, i + 1, shadow);
+ continue;
+ }
+
+ if (target >= IRQ_COUNT) {
+ pr_err("%pOF: %s[%d] target(%d) out of range\n", node, field, i, target);
+ continue;
+ }
+
+ if (econet_intc.interrupt_shadows[target] != NOT_PERCPU) {
+ pr_err("%pOF: %s[%d] target(%d) already has a shadow\n",
+ node, field, i, target);
+ continue;
+ }
+
+ if (econet_intc.interrupt_shadows[shadow] != NOT_PERCPU) {
+ pr_err("%pOF: %s[%d] shadow(%d) already has a target\n",
+ node, field, i + 1, shadow);
+ continue;
+ }
+
+ econet_intc.interrupt_shadows[target] = shadow;
+ econet_intc.interrupt_shadows[shadow] = IS_SHADOW;
+ }
+
+ return 0;
+}
+
+static int __init econet_intc_of_init(struct device_node *node, struct device_node *parent)
+{
+ struct irq_domain *domain;
+ struct resource res;
+ int ret, irq;
+
+ ret = get_shadow_interrupts(node);
+ if (ret)
+ return ret;
+
+ irq = irq_of_parse_and_map(node, 0);
+ if (!irq) {
+ pr_err("%pOF: DT: Failed to get IRQ from 'interrupts'\n", node);
+ return -EINVAL;
+ }
+
+ if (of_address_to_resource(node, 0, &res)) {
+ pr_err("%pOF: DT: Failed to get 'reg'\n", node);
+ ret = -EINVAL;
+ goto err_dispose_mapping;
+ }
+
+ if (!request_mem_region(res.start, resource_size(&res), res.name)) {
+ pr_err("%pOF: Failed to request memory\n", node);
+ ret = -EBUSY;
+ goto err_dispose_mapping;
+ }
+
+ econet_intc.membase = ioremap(res.start, resource_size(&res));
+ if (!econet_intc.membase) {
+ pr_err("%pOF: Failed to remap membase\n", node);
+ ret = -ENOMEM;
+ goto err_release;
+ }
+
+ econet_mask_all();
+
+ domain = irq_domain_add_linear(node, IRQ_COUNT, &econet_domain_ops, NULL);
+ if (!domain) {
+ pr_err("%pOF: Failed to add irqdomain\n", node);
+ ret = -ENOMEM;
+ goto err_unmap;
+ }
+
+ irq_set_chained_handler_and_data(irq, econet_intc_from_parent, domain);
+
+ return 0;
+
+err_unmap:
+ iounmap(econet_intc.membase);
+err_release:
+ release_mem_region(res.start, resource_size(&res));
+err_dispose_mapping:
+ irq_dispose_mapping(irq);
+ return ret;
+}
+
+IRQCHIP_DECLARE(econet_en751221_intc, "econet,en751221-intc", econet_intc_of_init);
--
2.39.5
^ permalink raw reply related [flat|nested] 18+ messages in thread* [tip: irq/drivers] irqchip: Add EcoNet EN751221 INTC
2025-03-30 17:02 ` [PATCH v3 03/10] irqchip: " Caleb James DeLisle
@ 2025-04-07 7:43 ` tip-bot2 for Caleb James DeLisle
0 siblings, 0 replies; 18+ messages in thread
From: tip-bot2 for Caleb James DeLisle @ 2025-04-07 7:43 UTC (permalink / raw)
To: linux-tip-commits; +Cc: Caleb James DeLisle, Thomas Gleixner, x86, linux-kernel
The following commit has been merged into the irq/drivers branch of tip:
Commit-ID: 1902a59cf5f9d8b99ecf0cb8f122cb00ef7a3f13
Gitweb: https://git.kernel.org/tip/1902a59cf5f9d8b99ecf0cb8f122cb00ef7a3f13
Author: Caleb James DeLisle <cjd@cjdns.fr>
AuthorDate: Sun, 30 Mar 2025 17:02:59
Committer: Thomas Gleixner <tglx@linutronix.de>
CommitterDate: Mon, 07 Apr 2025 09:39:39 +02:00
irqchip: Add EcoNet EN751221 INTC
Add a driver for the interrupt controller in the EcoNet EN751221 MIPS SoC.
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20250330170306.2584136-4-cjd@cjdns.fr
---
drivers/irqchip/Kconfig | 5 +-
drivers/irqchip/Makefile | 1 +-
drivers/irqchip/irq-econet-en751221.c | 309 +++++++++++++++++++++++++-
3 files changed, 315 insertions(+)
create mode 100644 drivers/irqchip/irq-econet-en751221.c
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index cec05e4..4b43870 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -166,6 +166,11 @@ config DW_APB_ICTL
select GENERIC_IRQ_CHIP
select IRQ_DOMAIN_HIERARCHY
+config ECONET_EN751221_INTC
+ bool
+ select GENERIC_IRQ_CHIP
+ select IRQ_DOMAIN
+
config FARADAY_FTINTC010
bool
select IRQ_DOMAIN
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index 365bcea..23ca495 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o
obj-$(CONFIG_ARCH_ACTIONS) += irq-owl-sirq.o
obj-$(CONFIG_DAVINCI_CP_INTC) += irq-davinci-cp-intc.o
obj-$(CONFIG_EXYNOS_IRQ_COMBINER) += exynos-combiner.o
+obj-$(CONFIG_ECONET_EN751221_INTC) += irq-econet-en751221.o
obj-$(CONFIG_FARADAY_FTINTC010) += irq-ftintc010.o
obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o
obj-$(CONFIG_ARCH_LPC32XX) += irq-lpc32xx.o
diff --git a/drivers/irqchip/irq-econet-en751221.c b/drivers/irqchip/irq-econet-en751221.c
new file mode 100644
index 0000000..886d60c
--- /dev/null
+++ b/drivers/irqchip/irq-econet-en751221.c
@@ -0,0 +1,309 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * EN751221 Interrupt Controller Driver.
+ *
+ * The EcoNet EN751221 Interrupt Controller is a simple interrupt controller
+ * designed for the MIPS 34Kc MT SMP processor with 2 VPEs. Each interrupt can
+ * be routed to either VPE but not both, so to support per-CPU interrupts, a
+ * secondary IRQ number is allocated to control masking/unmasking on VPE#1. In
+ * this driver, these are called "shadow interrupts". The assignment of shadow
+ * interrupts is defined by the SoC integrator when wiring the interrupt lines,
+ * so they are configurable in the device tree.
+ *
+ * If an interrupt (say 30) needs per-CPU capability, the SoC integrator
+ * allocates another IRQ number (say 29) to be its shadow. The device tree
+ * reflects this by adding the pair <30 29> to the "econet,shadow-interrupts"
+ * property.
+ *
+ * When VPE#1 requests IRQ 30, the driver manipulates the mask bit for IRQ 29,
+ * telling the hardware to mask VPE#1's view of IRQ 30.
+ *
+ * Copyright (C) 2025 Caleb James DeLisle <cjd@cjdns.fr>
+ */
+
+#include <linux/cleanup.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+
+#define IRQ_COUNT 40
+
+#define NOT_PERCPU 0xff
+#define IS_SHADOW 0xfe
+
+#define REG_MASK0 0x04
+#define REG_MASK1 0x50
+#define REG_PENDING0 0x08
+#define REG_PENDING1 0x54
+
+/**
+ * @membase: Base address of the interrupt controller registers
+ * @interrupt_shadows: Array of all interrupts, for each value,
+ * - NOT_PERCPU: This interrupt is not per-cpu, so it has no shadow
+ * - IS_SHADOW: This interrupt is a shadow of another per-cpu interrupt
+ * - else: This is a per-cpu interrupt whose shadow is the value
+ */
+static struct {
+ void __iomem *membase;
+ u8 interrupt_shadows[IRQ_COUNT];
+} econet_intc __ro_after_init;
+
+static DEFINE_RAW_SPINLOCK(irq_lock);
+
+/* IRQs must be disabled */
+static void econet_wreg(u32 reg, u32 val, u32 mask)
+{
+ u32 v;
+
+ guard(raw_spinlock)(&irq_lock);
+
+ v = ioread32(econet_intc.membase + reg);
+ v &= ~mask;
+ v |= val & mask;
+ iowrite32(v, econet_intc.membase + reg);
+}
+
+/* IRQs must be disabled */
+static void econet_chmask(u32 hwirq, bool unmask)
+{
+ u32 reg, mask;
+ u8 shadow;
+
+ /*
+ * If the IRQ is a shadow, it should never be manipulated directly.
+ * It should only be masked/unmasked as a result of the "real" per-cpu
+ * irq being manipulated by a thread running on VPE#1.
+ * If it is per-cpu (has a shadow), and we're on VPE#1, the shadow is what we mask.
+ * This is single processor only, so smp_processor_id() never exceeds 1.
+ */
+ shadow = econet_intc.interrupt_shadows[hwirq];
+ if (WARN_ON_ONCE(shadow == IS_SHADOW))
+ return;
+ else if (shadow != NOT_PERCPU && smp_processor_id() == 1)
+ hwirq = shadow;
+
+ if (hwirq >= 32) {
+ reg = REG_MASK1;
+ mask = BIT(hwirq - 32);
+ } else {
+ reg = REG_MASK0;
+ mask = BIT(hwirq);
+ }
+
+ econet_wreg(reg, unmask ? mask : 0, mask);
+}
+
+/* IRQs must be disabled */
+static void econet_intc_mask(struct irq_data *d)
+{
+ econet_chmask(d->hwirq, false);
+}
+
+/* IRQs must be disabled */
+static void econet_intc_unmask(struct irq_data *d)
+{
+ econet_chmask(d->hwirq, true);
+}
+
+static void econet_mask_all(void)
+{
+ /* IRQs are generally disabled during init, but guarding here makes it non-obligatory. */
+ guard(irqsave)();
+ econet_wreg(REG_MASK0, 0, ~0);
+ econet_wreg(REG_MASK1, 0, ~0);
+}
+
+static void econet_intc_handle_pending(struct irq_domain *d, u32 pending, u32 offset)
+{
+ int hwirq;
+
+ while (pending) {
+ hwirq = fls(pending) - 1;
+ generic_handle_domain_irq(d, hwirq + offset);
+ pending &= ~BIT(hwirq);
+ }
+}
+
+static void econet_intc_from_parent(struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct irq_domain *domain;
+ u32 pending0, pending1;
+
+ chained_irq_enter(chip, desc);
+
+ pending0 = ioread32(econet_intc.membase + REG_PENDING0);
+ pending1 = ioread32(econet_intc.membase + REG_PENDING1);
+
+ if (unlikely(!(pending0 | pending1))) {
+ spurious_interrupt();
+ } else {
+ domain = irq_desc_get_handler_data(desc);
+ econet_intc_handle_pending(domain, pending0, 0);
+ econet_intc_handle_pending(domain, pending1, 32);
+ }
+
+ chained_irq_exit(chip, desc);
+}
+
+static const struct irq_chip econet_irq_chip;
+
+static int econet_intc_map(struct irq_domain *d, u32 irq, irq_hw_number_t hwirq)
+{
+ int ret;
+
+ if (hwirq >= IRQ_COUNT) {
+ pr_err("%s: hwirq %lu out of range\n", __func__, hwirq);
+ return -EINVAL;
+ } else if (econet_intc.interrupt_shadows[hwirq] == IS_SHADOW) {
+ pr_err("%s: can't map hwirq %lu, it is a shadow interrupt\n", __func__, hwirq);
+ return -EINVAL;
+ }
+
+ if (econet_intc.interrupt_shadows[hwirq] == NOT_PERCPU) {
+ irq_set_chip_and_handler(irq, &econet_irq_chip, handle_level_irq);
+ } else {
+ irq_set_chip_and_handler(irq, &econet_irq_chip, handle_percpu_devid_irq);
+ ret = irq_set_percpu_devid(irq);
+ if (ret)
+ pr_warn("%s: Failed irq_set_percpu_devid for %u: %d\n", d->name, irq, ret);
+ }
+
+ irq_set_chip_data(irq, NULL);
+ return 0;
+}
+
+static const struct irq_chip econet_irq_chip = {
+ .name = "en751221-intc",
+ .irq_unmask = econet_intc_unmask,
+ .irq_mask = econet_intc_mask,
+ .irq_mask_ack = econet_intc_mask,
+};
+
+static const struct irq_domain_ops econet_domain_ops = {
+ .xlate = irq_domain_xlate_onecell,
+ .map = econet_intc_map
+};
+
+static int __init get_shadow_interrupts(struct device_node *node)
+{
+ const char *field = "econet,shadow-interrupts";
+ int num_shadows;
+
+ num_shadows = of_property_count_u32_elems(node, field);
+
+ memset(econet_intc.interrupt_shadows, NOT_PERCPU,
+ sizeof(econet_intc.interrupt_shadows));
+
+ if (num_shadows <= 0) {
+ return 0;
+ } else if (num_shadows % 2) {
+ pr_err("%pOF: %s count is odd, ignoring\n", node, field);
+ return 0;
+ }
+
+ u32 *shadows __free(kfree) = kmalloc_array(num_shadows, sizeof(u32), GFP_KERNEL);
+ if (!shadows)
+ return -ENOMEM;
+
+ if (of_property_read_u32_array(node, field, shadows, num_shadows)) {
+ pr_err("%pOF: Failed to read %s\n", node, field);
+ return -EINVAL;
+ }
+
+ for (int i = 0; i < num_shadows; i += 2) {
+ u32 shadow = shadows[i + 1];
+ u32 target = shadows[i];
+
+ if (shadow > IRQ_COUNT) {
+ pr_err("%pOF: %s[%d] shadow(%d) out of range\n",
+ node, field, i + 1, shadow);
+ continue;
+ }
+
+ if (target >= IRQ_COUNT) {
+ pr_err("%pOF: %s[%d] target(%d) out of range\n", node, field, i, target);
+ continue;
+ }
+
+ if (econet_intc.interrupt_shadows[target] != NOT_PERCPU) {
+ pr_err("%pOF: %s[%d] target(%d) already has a shadow\n",
+ node, field, i, target);
+ continue;
+ }
+
+ if (econet_intc.interrupt_shadows[shadow] != NOT_PERCPU) {
+ pr_err("%pOF: %s[%d] shadow(%d) already has a target\n",
+ node, field, i + 1, shadow);
+ continue;
+ }
+
+ econet_intc.interrupt_shadows[target] = shadow;
+ econet_intc.interrupt_shadows[shadow] = IS_SHADOW;
+ }
+
+ return 0;
+}
+
+static int __init econet_intc_of_init(struct device_node *node, struct device_node *parent)
+{
+ struct irq_domain *domain;
+ struct resource res;
+ int ret, irq;
+
+ ret = get_shadow_interrupts(node);
+ if (ret)
+ return ret;
+
+ irq = irq_of_parse_and_map(node, 0);
+ if (!irq) {
+ pr_err("%pOF: DT: Failed to get IRQ from 'interrupts'\n", node);
+ return -EINVAL;
+ }
+
+ if (of_address_to_resource(node, 0, &res)) {
+ pr_err("%pOF: DT: Failed to get 'reg'\n", node);
+ ret = -EINVAL;
+ goto err_dispose_mapping;
+ }
+
+ if (!request_mem_region(res.start, resource_size(&res), res.name)) {
+ pr_err("%pOF: Failed to request memory\n", node);
+ ret = -EBUSY;
+ goto err_dispose_mapping;
+ }
+
+ econet_intc.membase = ioremap(res.start, resource_size(&res));
+ if (!econet_intc.membase) {
+ pr_err("%pOF: Failed to remap membase\n", node);
+ ret = -ENOMEM;
+ goto err_release;
+ }
+
+ econet_mask_all();
+
+ domain = irq_domain_add_linear(node, IRQ_COUNT, &econet_domain_ops, NULL);
+ if (!domain) {
+ pr_err("%pOF: Failed to add irqdomain\n", node);
+ ret = -ENOMEM;
+ goto err_unmap;
+ }
+
+ irq_set_chained_handler_and_data(irq, econet_intc_from_parent, domain);
+
+ return 0;
+
+err_unmap:
+ iounmap(econet_intc.membase);
+err_release:
+ release_mem_region(res.start, resource_size(&res));
+err_dispose_mapping:
+ irq_dispose_mapping(irq);
+ return ret;
+}
+
+IRQCHIP_DECLARE(econet_en751221_intc, "econet,en751221-intc", econet_intc_of_init);
^ permalink raw reply related [flat|nested] 18+ messages in thread
* [PATCH v3 04/10] dt-bindings: timer: Add EcoNet EN751221 "HPT" CPU Timer
2025-03-30 17:02 [PATCH v3 00/10] Add EcoNet EN751221 MIPS platform support Caleb James DeLisle
` (2 preceding siblings ...)
2025-03-30 17:02 ` [PATCH v3 03/10] irqchip: " Caleb James DeLisle
@ 2025-03-30 17:03 ` Caleb James DeLisle
2025-03-31 7:01 ` Krzysztof Kozlowski
2025-03-30 17:03 ` [PATCH v3 05/10] clocksource/drivers: Add EcoNet Timer HPT driver Caleb James DeLisle
` (5 subsequent siblings)
9 siblings, 1 reply; 18+ messages in thread
From: Caleb James DeLisle @ 2025-03-30 17:03 UTC (permalink / raw)
To: linux-mips
Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thomas Bogendoerfer, Daniel Lezcano, devicetree, linux-kernel,
benjamin.larsson, linux-mediatek, Caleb James DeLisle
Add device tree bindings for the so-called high-precision timer (HPT)
in the EcoNet EN751221 SoC.
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
---
HPT is a name commonly used in vendor and 3rd party out-of-tree sources.
---
.../bindings/timer/econet,en751221-timer.yaml | 80 +++++++++++++++++++
1 file changed, 80 insertions(+)
create mode 100644 Documentation/devicetree/bindings/timer/econet,en751221-timer.yaml
diff --git a/Documentation/devicetree/bindings/timer/econet,en751221-timer.yaml b/Documentation/devicetree/bindings/timer/econet,en751221-timer.yaml
new file mode 100644
index 000000000000..c1e7c2b6afde
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/econet,en751221-timer.yaml
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/timer/econet,en751221-timer.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: EcoNet EN751221 High Precision Timer (HPT)
+
+maintainers:
+ - Caleb James DeLisle <cjd@cjdns.fr>
+
+description:
+ The EcoNet High Precision Timer (HPT) is a timer peripheral found in various
+ EcoNet SoCs, including the EN751221 and EN751627 families. It provides per-VPE
+ count/compare registers and a per-CPU control register, with a single interrupt
+ line using a percpu-devid interrupt mechanism.
+
+properties:
+ compatible:
+ oneOf:
+ - const: econet,en751221-timer
+ - items:
+ - const: econet,en751627-timer
+ - const: econet,en751221-timer
+
+ reg:
+ minItems: 1
+ maxItems: 2
+
+ interrupts:
+ maxItems: 1
+ description: A percpu-devid timer interrupt shared across CPUs.
+
+ clocks:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: econet,en751627-timer
+ then:
+ properties:
+ reg:
+ items:
+ - description: VPE timers 0 and 1
+ - description: VPE timers 2 and 3
+ else:
+ properties:
+ reg:
+ items:
+ - description: VPE timers 0 and 1
+
+additionalProperties: false
+
+examples:
+ - |
+ timer@1fbf0400 {
+ compatible = "econet,en751627-timer", "econet,en751221-timer";
+ reg = <0x1fbf0400 0x100>, <0x1fbe0000 0x100>;
+ interrupt-parent = <&intc>;
+ interrupts = <30>;
+ clocks = <&hpt_clock>;
+ };
+ - |
+ timer@1fbf0400 {
+ compatible = "econet,en751221-timer";
+ reg = <0x1fbe0400 0x100>;
+ interrupt-parent = <&intc>;
+ interrupts = <30>;
+ clocks = <&hpt_clock>;
+ };
+...
--
2.39.5
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v3 04/10] dt-bindings: timer: Add EcoNet EN751221 "HPT" CPU Timer
2025-03-30 17:03 ` [PATCH v3 04/10] dt-bindings: timer: Add EcoNet EN751221 "HPT" CPU Timer Caleb James DeLisle
@ 2025-03-31 7:01 ` Krzysztof Kozlowski
0 siblings, 0 replies; 18+ messages in thread
From: Krzysztof Kozlowski @ 2025-03-31 7:01 UTC (permalink / raw)
To: Caleb James DeLisle
Cc: linux-mips, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Thomas Bogendoerfer, Daniel Lezcano, devicetree,
linux-kernel, benjamin.larsson, linux-mediatek
On Sun, Mar 30, 2025 at 05:03:00PM +0000, Caleb James DeLisle wrote:
> Add device tree bindings for the so-called high-precision timer (HPT)
> in the EcoNet EN751221 SoC.
>
> Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
> ---
> HPT is a name commonly used in vendor and 3rd party out-of-tree sources.
> ---
> .../bindings/timer/econet,en751221-timer.yaml | 80 +++++++++++++++++++
> 1 file changed, 80 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/timer/econet,en751221-timer.yaml
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v3 05/10] clocksource/drivers: Add EcoNet Timer HPT driver
2025-03-30 17:02 [PATCH v3 00/10] Add EcoNet EN751221 MIPS platform support Caleb James DeLisle
` (3 preceding siblings ...)
2025-03-30 17:03 ` [PATCH v3 04/10] dt-bindings: timer: Add EcoNet EN751221 "HPT" CPU Timer Caleb James DeLisle
@ 2025-03-30 17:03 ` Caleb James DeLisle
2025-04-21 17:10 ` Caleb James DeLisle
2025-03-30 17:03 ` [PATCH v3 06/10] dt-bindings: mips: Add EcoNet platform binding Caleb James DeLisle
` (4 subsequent siblings)
9 siblings, 1 reply; 18+ messages in thread
From: Caleb James DeLisle @ 2025-03-30 17:03 UTC (permalink / raw)
To: linux-mips
Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thomas Bogendoerfer, Daniel Lezcano, devicetree, linux-kernel,
benjamin.larsson, linux-mediatek, Caleb James DeLisle
Introduce a clocksource driver for the so-called high-precision timer (HPT)
in the EcoNet EN751221 MIPS SoC.
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
---
drivers/clocksource/Kconfig | 8 +
drivers/clocksource/Makefile | 1 +
drivers/clocksource/timer-econet-en751221.c | 216 ++++++++++++++++++++
3 files changed, 225 insertions(+)
create mode 100644 drivers/clocksource/timer-econet-en751221.c
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 487c85259967..976afb0b2312 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -73,6 +73,14 @@ config DW_APB_TIMER_OF
select DW_APB_TIMER
select TIMER_OF
+config ECONET_EN751221_TIMER
+ bool "EcoNet EN751221 High Precision Timer" if COMPILE_TEST
+ depends on HAS_IOMEM
+ select CLKSRC_MMIO
+ select TIMER_OF
+ help
+ Support for CPU timer found on EcoNet MIPS based SoCs.
+
config FTTMR010_TIMER
bool "Faraday Technology timer driver" if COMPILE_TEST
depends on HAS_IOMEM
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 43ef16a4efa6..d2998601eda5 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_CLKBLD_I8253) += i8253.o
obj-$(CONFIG_CLKSRC_MMIO) += mmio.o
obj-$(CONFIG_DAVINCI_TIMER) += timer-davinci.o
obj-$(CONFIG_DIGICOLOR_TIMER) += timer-digicolor.o
+obj-$(CONFIG_ECONET_EN751221_TIMER) += timer-econet-en751221.o
obj-$(CONFIG_OMAP_DM_TIMER) += timer-ti-dm.o
obj-$(CONFIG_OMAP_DM_SYSTIMER) += timer-ti-dm-systimer.o
obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
diff --git a/drivers/clocksource/timer-econet-en751221.c b/drivers/clocksource/timer-econet-en751221.c
new file mode 100644
index 000000000000..9cfeead09377
--- /dev/null
+++ b/drivers/clocksource/timer-econet-en751221.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Timer present on EcoNet EN75xx MIPS based SoCs.
+ *
+ * Copyright (C) 2025 by Caleb James DeLisle <cjd@cjdns.fr>
+ */
+
+#include <linux/io.h>
+#include <linux/cpumask.h>
+#include <linux/interrupt.h>
+#include <linux/clockchips.h>
+#include <linux/sched_clock.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/cpuhotplug.h>
+#include <linux/clk.h>
+
+#define ECONET_BITS 32
+#define ECONET_MIN_DELTA 0x00001000
+#define ECONET_MAX_DELTA GENMASK(ECONET_BITS - 2, 0)
+/* 34Kc hardware has 1 block and 1004Kc has 2. */
+#define ECONET_NUM_BLOCKS DIV_ROUND_UP(NR_CPUS, 2)
+
+static struct {
+ void __iomem *membase[ECONET_NUM_BLOCKS];
+ u32 freq_hz;
+} econet_timer __ro_after_init;
+
+static DEFINE_PER_CPU(struct clock_event_device, econet_timer_pcpu);
+
+/* Each memory block has 2 timers, the order of registers is:
+ * CTL, CMR0, CNT0, CMR1, CNT1
+ */
+static inline void __iomem *reg_ctl(u32 timer_n)
+{
+ return econet_timer.membase[timer_n >> 1];
+}
+
+static inline void __iomem *reg_compare(u32 timer_n)
+{
+ return econet_timer.membase[timer_n >> 1] + (timer_n & 1) * 0x08 + 0x04;
+}
+
+static inline void __iomem *reg_count(u32 timer_n)
+{
+ return econet_timer.membase[timer_n >> 1] + (timer_n & 1) * 0x08 + 0x08;
+}
+
+static inline u32 ctl_bit_enabled(u32 timer_n)
+{
+ return 1U << (timer_n & 1);
+}
+
+static inline u32 ctl_bit_pending(u32 timer_n)
+{
+ return 1U << ((timer_n & 1) + 16);
+}
+
+static bool cevt_is_pending(int cpu_id)
+{
+ return ioread32(reg_ctl(cpu_id)) & ctl_bit_pending(cpu_id);
+}
+
+static irqreturn_t cevt_interrupt(int irq, void *dev_id)
+{
+ struct clock_event_device *dev = this_cpu_ptr(&econet_timer_pcpu);
+ int cpu = cpumask_first(dev->cpumask);
+
+ if (!cevt_is_pending(cpu)) {
+ pr_debug("%s IRQ %d on CPU %d is not pending\n", __func__, irq, cpu);
+ return IRQ_NONE;
+ }
+
+ iowrite32(ioread32(reg_count(cpu)), reg_compare(cpu));
+ dev->event_handler(dev);
+ return IRQ_HANDLED;
+}
+
+static int cevt_set_next_event(ulong delta, struct clock_event_device *dev)
+{
+ u32 next;
+ int cpu;
+
+ cpu = cpumask_first(dev->cpumask);
+ next = ioread32(reg_count(cpu)) + delta;
+ iowrite32(next, reg_compare(cpu));
+
+ if ((s32)(next - ioread32(reg_count(cpu))) < ECONET_MIN_DELTA / 2)
+ return -ETIME;
+
+ return 0;
+}
+
+static int cevt_init_cpu(uint cpu)
+{
+ struct clock_event_device *cd = &per_cpu(econet_timer_pcpu, cpu);
+ u32 reg;
+
+ pr_info("%s: Setting up clockevent for CPU %d\n", cd->name, cpu);
+
+ reg = ioread32(reg_ctl(cpu)) | ctl_bit_enabled(cpu);
+ iowrite32(reg, reg_ctl(cpu));
+
+ enable_percpu_irq(cd->irq, IRQ_TYPE_NONE);
+
+ /* Do this last because it synchronously configures the timer */
+ clockevents_config_and_register(
+ cd, econet_timer.freq_hz,
+ ECONET_MIN_DELTA, ECONET_MAX_DELTA);
+
+ return 0;
+}
+
+static u64 notrace sched_clock_read(void)
+{
+ /* Always read from clock zero no matter the CPU */
+ return (u64)ioread32(reg_count(0));
+}
+
+/* Init */
+
+static void __init cevt_dev_init(uint cpu)
+{
+ iowrite32(0, reg_count(cpu));
+ iowrite32(U32_MAX, reg_compare(cpu));
+}
+
+static int __init cevt_init(struct device_node *np)
+{
+ int i, irq, ret;
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (irq <= 0) {
+ pr_err("%pOFn: irq_of_parse_and_map failed", np);
+ return -EINVAL;
+ }
+
+ ret = request_percpu_irq(irq, cevt_interrupt, np->name, &econet_timer_pcpu);
+
+ if (ret < 0) {
+ pr_err("%pOFn: IRQ %d setup failed (%d)\n", np, irq, ret);
+ goto err_unmap_irq;
+ }
+
+ for_each_possible_cpu(i) {
+ struct clock_event_device *cd = &per_cpu(econet_timer_pcpu, i);
+
+ cd->rating = 310,
+ cd->features = CLOCK_EVT_FEAT_ONESHOT |
+ CLOCK_EVT_FEAT_C3STOP |
+ CLOCK_EVT_FEAT_PERCPU;
+ cd->set_next_event = cevt_set_next_event;
+ cd->irq = irq;
+ cd->cpumask = cpumask_of(i);
+ cd->name = np->name;
+
+ cevt_dev_init(i);
+ }
+
+ cpuhp_setup_state(CPUHP_AP_MIPS_GIC_TIMER_STARTING,
+ "clockevents/en75/timer:starting",
+ cevt_init_cpu, NULL);
+ return 0;
+
+err_unmap_irq:
+ irq_dispose_mapping(irq);
+ return ret;
+}
+
+static int __init timer_init(struct device_node *np)
+{
+ int num_blocks = DIV_ROUND_UP(num_possible_cpus(), 2);
+ struct clk *clk;
+ int ret;
+
+ clk = of_clk_get(np, 0);
+ if (IS_ERR(clk)) {
+ pr_err("%pOFn: Failed to get CPU clock from DT %ld\n", np, PTR_ERR(clk));
+ return PTR_ERR(clk);
+ }
+
+ econet_timer.freq_hz = clk_get_rate(clk);
+
+ for (int i = 0; i < num_blocks; i++) {
+ econet_timer.membase[i] = of_iomap(np, i);
+ if (!econet_timer.membase[i]) {
+ pr_err("%pOFn: failed to map register [%d]\n", np, i);
+ return -ENXIO;
+ }
+ }
+
+ /* For clocksource purposes always read clock zero, whatever the CPU */
+ ret = clocksource_mmio_init(reg_count(0), np->name,
+ econet_timer.freq_hz, 301, ECONET_BITS,
+ clocksource_mmio_readl_up);
+ if (ret) {
+ pr_err("%pOFn: clocksource_mmio_init failed: %d", np, ret);
+ return ret;
+ }
+
+ ret = cevt_init(np);
+ if (ret < 0)
+ return ret;
+
+ sched_clock_register(sched_clock_read, ECONET_BITS,
+ econet_timer.freq_hz);
+
+ pr_info("%pOFn: using %u.%03u MHz high precision timer\n", np,
+ econet_timer.freq_hz / 1000000,
+ (econet_timer.freq_hz / 1000) % 1000);
+
+ return 0;
+}
+
+TIMER_OF_DECLARE(econet_timer_hpt, "econet,en751221-timer", timer_init);
--
2.39.5
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v3 05/10] clocksource/drivers: Add EcoNet Timer HPT driver
2025-03-30 17:03 ` [PATCH v3 05/10] clocksource/drivers: Add EcoNet Timer HPT driver Caleb James DeLisle
@ 2025-04-21 17:10 ` Caleb James DeLisle
0 siblings, 0 replies; 18+ messages in thread
From: Caleb James DeLisle @ 2025-04-21 17:10 UTC (permalink / raw)
To: linux-mips
Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thomas Bogendoerfer, Daniel Lezcano, devicetree, linux-kernel,
benjamin.larsson, linux-mediatek
On 30/03/2025 19:03, Caleb James DeLisle wrote:
> Introduce a clocksource driver for the so-called high-precision timer (HPT)
> in the EcoNet EN751221 MIPS SoC.
>
> Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
Hello all,
It's been a couple of weeks and I wanted to check if this is good and/or
if there's anything I can do to help make the process easier.
Thanks to Tglx, Rob, Krzysztof, and Sergey for your time reviewing my code.
In v3 I updated this patch in accordance with feedback I received on the
interrupt controller. Unless there's advice to do otherwise, I can resend
the remaining patches later on this week.
Thanks,
Caleb
> ---
> drivers/clocksource/Kconfig | 8 +
> drivers/clocksource/Makefile | 1 +
> drivers/clocksource/timer-econet-en751221.c | 216 ++++++++++++++++++++
> 3 files changed, 225 insertions(+)
> create mode 100644 drivers/clocksource/timer-econet-en751221.c
>
> diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
> index 487c85259967..976afb0b2312 100644
> --- a/drivers/clocksource/Kconfig
> +++ b/drivers/clocksource/Kconfig
> @@ -73,6 +73,14 @@ config DW_APB_TIMER_OF
> select DW_APB_TIMER
> select TIMER_OF
>
> +config ECONET_EN751221_TIMER
> + bool "EcoNet EN751221 High Precision Timer" if COMPILE_TEST
> + depends on HAS_IOMEM
> + select CLKSRC_MMIO
> + select TIMER_OF
> + help
> + Support for CPU timer found on EcoNet MIPS based SoCs.
> +
> config FTTMR010_TIMER
> bool "Faraday Technology timer driver" if COMPILE_TEST
> depends on HAS_IOMEM
> diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
> index 43ef16a4efa6..d2998601eda5 100644
> --- a/drivers/clocksource/Makefile
> +++ b/drivers/clocksource/Makefile
> @@ -17,6 +17,7 @@ obj-$(CONFIG_CLKBLD_I8253) += i8253.o
> obj-$(CONFIG_CLKSRC_MMIO) += mmio.o
> obj-$(CONFIG_DAVINCI_TIMER) += timer-davinci.o
> obj-$(CONFIG_DIGICOLOR_TIMER) += timer-digicolor.o
> +obj-$(CONFIG_ECONET_EN751221_TIMER) += timer-econet-en751221.o
> obj-$(CONFIG_OMAP_DM_TIMER) += timer-ti-dm.o
> obj-$(CONFIG_OMAP_DM_SYSTIMER) += timer-ti-dm-systimer.o
> obj-$(CONFIG_DW_APB_TIMER) += dw_apb_timer.o
> diff --git a/drivers/clocksource/timer-econet-en751221.c b/drivers/clocksource/timer-econet-en751221.c
> new file mode 100644
> index 000000000000..9cfeead09377
> --- /dev/null
> +++ b/drivers/clocksource/timer-econet-en751221.c
> @@ -0,0 +1,216 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Timer present on EcoNet EN75xx MIPS based SoCs.
> + *
> + * Copyright (C) 2025 by Caleb James DeLisle <cjd@cjdns.fr>
> + */
> +
> +#include <linux/io.h>
> +#include <linux/cpumask.h>
> +#include <linux/interrupt.h>
> +#include <linux/clockchips.h>
> +#include <linux/sched_clock.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_address.h>
> +#include <linux/cpuhotplug.h>
> +#include <linux/clk.h>
> +
> +#define ECONET_BITS 32
> +#define ECONET_MIN_DELTA 0x00001000
> +#define ECONET_MAX_DELTA GENMASK(ECONET_BITS - 2, 0)
> +/* 34Kc hardware has 1 block and 1004Kc has 2. */
> +#define ECONET_NUM_BLOCKS DIV_ROUND_UP(NR_CPUS, 2)
> +
> +static struct {
> + void __iomem *membase[ECONET_NUM_BLOCKS];
> + u32 freq_hz;
> +} econet_timer __ro_after_init;
> +
> +static DEFINE_PER_CPU(struct clock_event_device, econet_timer_pcpu);
> +
> +/* Each memory block has 2 timers, the order of registers is:
> + * CTL, CMR0, CNT0, CMR1, CNT1
> + */
> +static inline void __iomem *reg_ctl(u32 timer_n)
> +{
> + return econet_timer.membase[timer_n >> 1];
> +}
> +
> +static inline void __iomem *reg_compare(u32 timer_n)
> +{
> + return econet_timer.membase[timer_n >> 1] + (timer_n & 1) * 0x08 + 0x04;
> +}
> +
> +static inline void __iomem *reg_count(u32 timer_n)
> +{
> + return econet_timer.membase[timer_n >> 1] + (timer_n & 1) * 0x08 + 0x08;
> +}
> +
> +static inline u32 ctl_bit_enabled(u32 timer_n)
> +{
> + return 1U << (timer_n & 1);
> +}
> +
> +static inline u32 ctl_bit_pending(u32 timer_n)
> +{
> + return 1U << ((timer_n & 1) + 16);
> +}
> +
> +static bool cevt_is_pending(int cpu_id)
> +{
> + return ioread32(reg_ctl(cpu_id)) & ctl_bit_pending(cpu_id);
> +}
> +
> +static irqreturn_t cevt_interrupt(int irq, void *dev_id)
> +{
> + struct clock_event_device *dev = this_cpu_ptr(&econet_timer_pcpu);
> + int cpu = cpumask_first(dev->cpumask);
> +
> + if (!cevt_is_pending(cpu)) {
> + pr_debug("%s IRQ %d on CPU %d is not pending\n", __func__, irq, cpu);
> + return IRQ_NONE;
> + }
> +
> + iowrite32(ioread32(reg_count(cpu)), reg_compare(cpu));
> + dev->event_handler(dev);
> + return IRQ_HANDLED;
> +}
> +
> +static int cevt_set_next_event(ulong delta, struct clock_event_device *dev)
> +{
> + u32 next;
> + int cpu;
> +
> + cpu = cpumask_first(dev->cpumask);
> + next = ioread32(reg_count(cpu)) + delta;
> + iowrite32(next, reg_compare(cpu));
> +
> + if ((s32)(next - ioread32(reg_count(cpu))) < ECONET_MIN_DELTA / 2)
> + return -ETIME;
> +
> + return 0;
> +}
> +
> +static int cevt_init_cpu(uint cpu)
> +{
> + struct clock_event_device *cd = &per_cpu(econet_timer_pcpu, cpu);
> + u32 reg;
> +
> + pr_info("%s: Setting up clockevent for CPU %d\n", cd->name, cpu);
> +
> + reg = ioread32(reg_ctl(cpu)) | ctl_bit_enabled(cpu);
> + iowrite32(reg, reg_ctl(cpu));
> +
> + enable_percpu_irq(cd->irq, IRQ_TYPE_NONE);
> +
> + /* Do this last because it synchronously configures the timer */
> + clockevents_config_and_register(
> + cd, econet_timer.freq_hz,
> + ECONET_MIN_DELTA, ECONET_MAX_DELTA);
> +
> + return 0;
> +}
> +
> +static u64 notrace sched_clock_read(void)
> +{
> + /* Always read from clock zero no matter the CPU */
> + return (u64)ioread32(reg_count(0));
> +}
> +
> +/* Init */
> +
> +static void __init cevt_dev_init(uint cpu)
> +{
> + iowrite32(0, reg_count(cpu));
> + iowrite32(U32_MAX, reg_compare(cpu));
> +}
> +
> +static int __init cevt_init(struct device_node *np)
> +{
> + int i, irq, ret;
> +
> + irq = irq_of_parse_and_map(np, 0);
> + if (irq <= 0) {
> + pr_err("%pOFn: irq_of_parse_and_map failed", np);
> + return -EINVAL;
> + }
> +
> + ret = request_percpu_irq(irq, cevt_interrupt, np->name, &econet_timer_pcpu);
> +
> + if (ret < 0) {
> + pr_err("%pOFn: IRQ %d setup failed (%d)\n", np, irq, ret);
> + goto err_unmap_irq;
> + }
> +
> + for_each_possible_cpu(i) {
> + struct clock_event_device *cd = &per_cpu(econet_timer_pcpu, i);
> +
> + cd->rating = 310,
> + cd->features = CLOCK_EVT_FEAT_ONESHOT |
> + CLOCK_EVT_FEAT_C3STOP |
> + CLOCK_EVT_FEAT_PERCPU;
> + cd->set_next_event = cevt_set_next_event;
> + cd->irq = irq;
> + cd->cpumask = cpumask_of(i);
> + cd->name = np->name;
> +
> + cevt_dev_init(i);
> + }
> +
> + cpuhp_setup_state(CPUHP_AP_MIPS_GIC_TIMER_STARTING,
> + "clockevents/en75/timer:starting",
> + cevt_init_cpu, NULL);
> + return 0;
> +
> +err_unmap_irq:
> + irq_dispose_mapping(irq);
> + return ret;
> +}
> +
> +static int __init timer_init(struct device_node *np)
> +{
> + int num_blocks = DIV_ROUND_UP(num_possible_cpus(), 2);
> + struct clk *clk;
> + int ret;
> +
> + clk = of_clk_get(np, 0);
> + if (IS_ERR(clk)) {
> + pr_err("%pOFn: Failed to get CPU clock from DT %ld\n", np, PTR_ERR(clk));
> + return PTR_ERR(clk);
> + }
> +
> + econet_timer.freq_hz = clk_get_rate(clk);
> +
> + for (int i = 0; i < num_blocks; i++) {
> + econet_timer.membase[i] = of_iomap(np, i);
> + if (!econet_timer.membase[i]) {
> + pr_err("%pOFn: failed to map register [%d]\n", np, i);
> + return -ENXIO;
> + }
> + }
> +
> + /* For clocksource purposes always read clock zero, whatever the CPU */
> + ret = clocksource_mmio_init(reg_count(0), np->name,
> + econet_timer.freq_hz, 301, ECONET_BITS,
> + clocksource_mmio_readl_up);
> + if (ret) {
> + pr_err("%pOFn: clocksource_mmio_init failed: %d", np, ret);
> + return ret;
> + }
> +
> + ret = cevt_init(np);
> + if (ret < 0)
> + return ret;
> +
> + sched_clock_register(sched_clock_read, ECONET_BITS,
> + econet_timer.freq_hz);
> +
> + pr_info("%pOFn: using %u.%03u MHz high precision timer\n", np,
> + econet_timer.freq_hz / 1000000,
> + (econet_timer.freq_hz / 1000) % 1000);
> +
> + return 0;
> +}
> +
> +TIMER_OF_DECLARE(econet_timer_hpt, "econet,en751221-timer", timer_init);
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v3 06/10] dt-bindings: mips: Add EcoNet platform binding
2025-03-30 17:02 [PATCH v3 00/10] Add EcoNet EN751221 MIPS platform support Caleb James DeLisle
` (4 preceding siblings ...)
2025-03-30 17:03 ` [PATCH v3 05/10] clocksource/drivers: Add EcoNet Timer HPT driver Caleb James DeLisle
@ 2025-03-30 17:03 ` Caleb James DeLisle
2025-03-30 17:03 ` [PATCH v3 07/10] mips: Add EcoNet MIPS platform support Caleb James DeLisle
` (3 subsequent siblings)
9 siblings, 0 replies; 18+ messages in thread
From: Caleb James DeLisle @ 2025-03-30 17:03 UTC (permalink / raw)
To: linux-mips
Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thomas Bogendoerfer, Daniel Lezcano, devicetree, linux-kernel,
benjamin.larsson, linux-mediatek, Caleb James DeLisle,
Krzysztof Kozlowski
Document the top-level device tree binding for EcoNet MIPS-based SoCs.
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
.../devicetree/bindings/mips/econet.yaml | 26 +++++++++++++++++++
1 file changed, 26 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mips/econet.yaml
diff --git a/Documentation/devicetree/bindings/mips/econet.yaml b/Documentation/devicetree/bindings/mips/econet.yaml
new file mode 100644
index 000000000000..d8181b58c781
--- /dev/null
+++ b/Documentation/devicetree/bindings/mips/econet.yaml
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mips/econet.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: EcoNet MIPS SoCs
+
+maintainers:
+ - Caleb James DeLisle <cjd@cjdns.fr>
+
+properties:
+ $nodename:
+ const: '/'
+
+ compatible:
+ oneOf:
+ - description: Boards with EcoNet EN751221 family SoC
+ items:
+ - enum:
+ - smartfiber,xp8421-b
+ - const: econet,en751221
+
+additionalProperties: true
+
+...
--
2.39.5
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v3 07/10] mips: Add EcoNet MIPS platform support
2025-03-30 17:02 [PATCH v3 00/10] Add EcoNet EN751221 MIPS platform support Caleb James DeLisle
` (5 preceding siblings ...)
2025-03-30 17:03 ` [PATCH v3 06/10] dt-bindings: mips: Add EcoNet platform binding Caleb James DeLisle
@ 2025-03-30 17:03 ` Caleb James DeLisle
2025-03-30 17:03 ` [PATCH v3 08/10] dt-bindings: vendor-prefixes: Add SmartFiber Caleb James DeLisle
` (2 subsequent siblings)
9 siblings, 0 replies; 18+ messages in thread
From: Caleb James DeLisle @ 2025-03-30 17:03 UTC (permalink / raw)
To: linux-mips
Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thomas Bogendoerfer, Daniel Lezcano, devicetree, linux-kernel,
benjamin.larsson, linux-mediatek, Caleb James DeLisle
Add platform support for EcoNet MIPS SoCs.
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
---
arch/mips/Kbuild.platforms | 1 +
arch/mips/Kconfig | 25 +++++++++
arch/mips/boot/compressed/uart-16550.c | 5 ++
arch/mips/econet/Kconfig | 37 ++++++++++++
arch/mips/econet/Makefile | 2 +
arch/mips/econet/Platform | 5 ++
arch/mips/econet/init.c | 78 ++++++++++++++++++++++++++
7 files changed, 153 insertions(+)
create mode 100644 arch/mips/econet/Kconfig
create mode 100644 arch/mips/econet/Makefile
create mode 100644 arch/mips/econet/Platform
create mode 100644 arch/mips/econet/init.c
diff --git a/arch/mips/Kbuild.platforms b/arch/mips/Kbuild.platforms
index bca37ddf974b..41a00fa860c1 100644
--- a/arch/mips/Kbuild.platforms
+++ b/arch/mips/Kbuild.platforms
@@ -11,6 +11,7 @@ platform-$(CONFIG_CAVIUM_OCTEON_SOC) += cavium-octeon/
platform-$(CONFIG_EYEQ) += mobileye/
platform-$(CONFIG_MIPS_COBALT) += cobalt/
platform-$(CONFIG_MACH_DECSTATION) += dec/
+platform-$(CONFIG_ECONET) += econet/
platform-$(CONFIG_MIPS_GENERIC) += generic/
platform-$(CONFIG_MACH_JAZZ) += jazz/
platform-$(CONFIG_LANTIQ) += lantiq/
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 1924f2d83932..909bf0847af0 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -390,6 +390,30 @@ config MACH_DECSTATION
otherwise choose R3000.
+config ECONET
+ bool "EcoNet MIPS family"
+ select BOOT_RAW
+ select CPU_BIG_ENDIAN
+ select DEBUG_ZBOOT
+ select EARLY_PRINTK_8250
+ select ECONET_EN751221_TIMER
+ select SERIAL_OF_PLATFORM
+ select SYS_SUPPORTS_BIG_ENDIAN
+ select SYS_HAS_CPU_MIPS32_R1
+ select SYS_HAS_CPU_MIPS32_R2
+ select SYS_HAS_EARLY_PRINTK
+ select SYS_SUPPORTS_32BIT_KERNEL
+ select SYS_SUPPORTS_MIPS16
+ select SYS_SUPPORTS_ZBOOT_UART16550
+ select USE_GENERIC_EARLY_PRINTK_8250
+ select USE_OF
+ help
+ EcoNet EN75xx MIPS devices are big endian MIPS machines used
+ in XPON (fiber) and DSL applications. They have SPI, PCI, USB,
+ GPIO, and Ethernet, with optional XPON, DSL, and VoIP DSP cores.
+ Don't confuse these with the Airoha ARM devices sometimes referred
+ to as "EcoNet", this family is for MIPS based devices only.
+
config MACH_JAZZ
bool "Jazz family of machines"
select ARC_MEMORY
@@ -1019,6 +1043,7 @@ source "arch/mips/ath79/Kconfig"
source "arch/mips/bcm47xx/Kconfig"
source "arch/mips/bcm63xx/Kconfig"
source "arch/mips/bmips/Kconfig"
+source "arch/mips/econet/Kconfig"
source "arch/mips/generic/Kconfig"
source "arch/mips/ingenic/Kconfig"
source "arch/mips/jazz/Kconfig"
diff --git a/arch/mips/boot/compressed/uart-16550.c b/arch/mips/boot/compressed/uart-16550.c
index db618e72a0c4..529e77a6487c 100644
--- a/arch/mips/boot/compressed/uart-16550.c
+++ b/arch/mips/boot/compressed/uart-16550.c
@@ -20,6 +20,11 @@
#define PORT(offset) (CKSEG1ADDR(INGENIC_UART_BASE_ADDR) + (4 * offset))
#endif
+#ifdef CONFIG_ECONET
+#define EN75_UART_BASE 0x1fbf0003
+#define PORT(offset) (CKSEG1ADDR(EN75_UART_BASE) + (4 * (offset)))
+#endif
+
#ifndef IOTYPE
#define IOTYPE char
#endif
diff --git a/arch/mips/econet/Kconfig b/arch/mips/econet/Kconfig
new file mode 100644
index 000000000000..d03f90f3daa4
--- /dev/null
+++ b/arch/mips/econet/Kconfig
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: GPL-2.0
+if ECONET
+
+choice
+ prompt "EcoNet SoC selection"
+ default SOC_ECONET_EN751221
+ help
+ Select EcoNet MIPS SoC type. Individual SoCs within a family are
+ very similar, so is it enough to select the right family, and
+ then customize to the specific SoC using the device tree only.
+
+ config SOC_ECONET_EN751221
+ bool "EN751221 family"
+ select COMMON_CLK
+ select ECONET_EN751221_INTC
+ select IRQ_MIPS_CPU
+ select SMP
+ select SMP_UP
+ select SYS_SUPPORTS_SMP
+ help
+ The EN751221 family includes EN7512, RN7513, EN7521, EN7526.
+ They are based on single core MIPS 34Kc processors. To boot
+ this kernel, you will need a device tree such as
+ MIPS_RAW_APPENDED_DTB=y, and a root filesystem.
+endchoice
+
+choice
+ prompt "Devicetree selection"
+ default DTB_ECONET_NONE
+ help
+ Select the devicetree.
+
+ config DTB_ECONET_NONE
+ bool "None"
+endchoice
+
+endif
diff --git a/arch/mips/econet/Makefile b/arch/mips/econet/Makefile
new file mode 100644
index 000000000000..7e4529e7d3d7
--- /dev/null
+++ b/arch/mips/econet/Makefile
@@ -0,0 +1,2 @@
+
+obj-y := init.o
diff --git a/arch/mips/econet/Platform b/arch/mips/econet/Platform
new file mode 100644
index 000000000000..ea5616447bcd
--- /dev/null
+++ b/arch/mips/econet/Platform
@@ -0,0 +1,5 @@
+# To address a 7.2MB kernel size limit in the EcoNet SDK bootloader,
+# we put the load address well above where the bootloader loads and then use
+# zboot. So please set CONFIG_ZBOOT_LOAD_ADDRESS to the address where your
+# bootloader actually places the kernel.
+load-$(CONFIG_ECONET) += 0xffffffff81000000
diff --git a/arch/mips/econet/init.c b/arch/mips/econet/init.c
new file mode 100644
index 000000000000..6f43ffb209cb
--- /dev/null
+++ b/arch/mips/econet/init.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * EcoNet setup code
+ *
+ * Copyright (C) 2025 Caleb James DeLisle <cjd@cjdns.fr>
+ */
+
+#include <linux/init.h>
+#include <linux/of_clk.h>
+#include <linux/irqchip.h>
+
+#include <asm/addrspace.h>
+#include <asm/io.h>
+#include <asm/bootinfo.h>
+#include <asm/time.h>
+#include <asm/prom.h>
+#include <asm/smp-ops.h>
+#include <asm/reboot.h>
+
+#define CR_AHB_RSTCR ((void __iomem *)CKSEG1ADDR(0x1fb00040))
+#define RESET BIT(31)
+
+#define UART_BASE CKSEG1ADDR(0x1fbf0003)
+#define UART_REG_SHIFT 2
+
+static void hw_reset(char *command)
+{
+ iowrite32(RESET, CR_AHB_RSTCR);
+}
+
+/* 1. Bring up early printk. */
+void __init prom_init(void)
+{
+ setup_8250_early_printk_port(UART_BASE, UART_REG_SHIFT, 0);
+ _machine_restart = hw_reset;
+}
+
+/* 2. Parse the DT and find memory */
+void __init plat_mem_setup(void)
+{
+ void *dtb;
+
+ set_io_port_base(KSEG1);
+
+ dtb = get_fdt();
+ if (!dtb)
+ panic("no dtb found");
+
+ __dt_setup_arch(dtb);
+
+ early_init_dt_scan_memory();
+}
+
+/* 3. Overload __weak device_tree_init(), add SMP_UP ops */
+void __init device_tree_init(void)
+{
+ unflatten_and_copy_device_tree();
+
+ register_up_smp_ops();
+}
+
+const char *get_system_type(void)
+{
+ return "EcoNet-EN75xx";
+}
+
+/* 4. Initialize the IRQ subsystem */
+void __init arch_init_irq(void)
+{
+ irqchip_init();
+}
+
+/* 5. Timers */
+void __init plat_time_init(void)
+{
+ of_clk_init(NULL);
+ timer_probe();
+}
--
2.39.5
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v3 08/10] dt-bindings: vendor-prefixes: Add SmartFiber
2025-03-30 17:02 [PATCH v3 00/10] Add EcoNet EN751221 MIPS platform support Caleb James DeLisle
` (6 preceding siblings ...)
2025-03-30 17:03 ` [PATCH v3 07/10] mips: Add EcoNet MIPS platform support Caleb James DeLisle
@ 2025-03-30 17:03 ` Caleb James DeLisle
2025-03-30 17:03 ` [PATCH v3 09/10] mips: dts: Add EcoNet DTS with EN751221 and SmartFiber XP8421-B board Caleb James DeLisle
2025-03-30 17:03 ` [PATCH v3 10/10] MAINTAINERS: Add entry for newly added EcoNet platform Caleb James DeLisle
9 siblings, 0 replies; 18+ messages in thread
From: Caleb James DeLisle @ 2025-03-30 17:03 UTC (permalink / raw)
To: linux-mips
Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thomas Bogendoerfer, Daniel Lezcano, devicetree, linux-kernel,
benjamin.larsson, linux-mediatek, Caleb James DeLisle,
Krzysztof Kozlowski
Add "smartfiber" vendor prefix for manufactorer of EcoNet based boards.
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
---
Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 371f6bb81f81..dc064e6acf4b 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -1388,6 +1388,8 @@ patternProperties:
description: SKOV A/S
"^skyworks,.*":
description: Skyworks Solutions, Inc.
+ "^smartfiber,.*":
+ description: ShenZhen Smartfiber Technology Co, Ltd.
"^smartlabs,.*":
description: SmartLabs LLC
"^smartrg,.*":
--
2.39.5
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v3 09/10] mips: dts: Add EcoNet DTS with EN751221 and SmartFiber XP8421-B board
2025-03-30 17:02 [PATCH v3 00/10] Add EcoNet EN751221 MIPS platform support Caleb James DeLisle
` (7 preceding siblings ...)
2025-03-30 17:03 ` [PATCH v3 08/10] dt-bindings: vendor-prefixes: Add SmartFiber Caleb James DeLisle
@ 2025-03-30 17:03 ` Caleb James DeLisle
2025-03-30 17:03 ` [PATCH v3 10/10] MAINTAINERS: Add entry for newly added EcoNet platform Caleb James DeLisle
9 siblings, 0 replies; 18+ messages in thread
From: Caleb James DeLisle @ 2025-03-30 17:03 UTC (permalink / raw)
To: linux-mips
Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thomas Bogendoerfer, Daniel Lezcano, devicetree, linux-kernel,
benjamin.larsson, linux-mediatek, Caleb James DeLisle
Add DTS files in support of EcoNet platform, including SmartFiber XP8421-B,
a low cost commercially available board based on EN751221.
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
---
To my knowledge, evaluation boards from EcoNet are not currently available.
SmartFiber XP8421-B is currently manufactured, low cost, and can be
purchased as single units by the general public.
---
arch/mips/boot/dts/Makefile | 1 +
arch/mips/boot/dts/econet/Makefile | 2 +
arch/mips/boot/dts/econet/en751221.dtsi | 67 +++++++++++++++++++
.../econet/en751221_smartfiber_xp8421-b.dts | 19 ++++++
arch/mips/econet/Kconfig | 11 +++
5 files changed, 100 insertions(+)
create mode 100644 arch/mips/boot/dts/econet/Makefile
create mode 100644 arch/mips/boot/dts/econet/en751221.dtsi
create mode 100644 arch/mips/boot/dts/econet/en751221_smartfiber_xp8421-b.dts
diff --git a/arch/mips/boot/dts/Makefile b/arch/mips/boot/dts/Makefile
index ff468439a8c4..7375c6ced82b 100644
--- a/arch/mips/boot/dts/Makefile
+++ b/arch/mips/boot/dts/Makefile
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
subdir-$(CONFIG_BMIPS_GENERIC) += brcm
subdir-$(CONFIG_CAVIUM_OCTEON_SOC) += cavium-octeon
+subdir-$(CONFIG_ECONET) += econet
subdir-$(CONFIG_EYEQ) += mobileye
subdir-$(CONFIG_FIT_IMAGE_FDT_MARDUK) += img
subdir-$(CONFIG_FIT_IMAGE_FDT_BOSTON) += img
diff --git a/arch/mips/boot/dts/econet/Makefile b/arch/mips/boot/dts/econet/Makefile
new file mode 100644
index 000000000000..b467d5624e39
--- /dev/null
+++ b/arch/mips/boot/dts/econet/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+dtb-$(CONFIG_DTB_ECONET_SMARTFIBER_XP8421_B) += en751221_smartfiber_xp8421-b.dtb
diff --git a/arch/mips/boot/dts/econet/en751221.dtsi b/arch/mips/boot/dts/econet/en751221.dtsi
new file mode 100644
index 000000000000..66197e73d4f0
--- /dev/null
+++ b/arch/mips/boot/dts/econet/en751221.dtsi
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/dts-v1/;
+
+/ {
+ compatible = "econet,en751221";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ hpt_clock: clock {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <200000000>; /* 200 MHz */
+ };
+
+ cpus: cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ device_type = "cpu";
+ compatible = "mips,mips24KEc";
+ reg = <0>;
+ };
+ };
+
+ cpuintc: interrupt-controller {
+ compatible = "mti,cpu-interrupt-controller";
+ interrupt-controller;
+ #address-cells = <0>;
+ #interrupt-cells = <1>;
+ };
+
+ intc: interrupt-controller@1fb40000 {
+ compatible = "econet,en751221-intc";
+ reg = <0x1fb40000 0x100>;
+ interrupt-parent = <&cpuintc>;
+ interrupts = <2>;
+
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ econet,shadow-interrupts = <7 2>, <8 3>, <13 12>, <30 29>;
+ };
+
+ uart: serial@1fbf0000 {
+ compatible = "ns16550";
+ reg = <0x1fbf0000 0x30>;
+ reg-io-width = <4>;
+ reg-shift = <2>;
+ interrupt-parent = <&intc>;
+ interrupts = <0>;
+ /*
+ * Conversion of baud rate to clock frequency requires a
+ * computation that is not in the ns16550 driver, so this
+ * uart is fixed at 115200 baud.
+ */
+ clock-frequency = <1843200>;
+ };
+
+ timer_hpt: timer@1fbf0400 {
+ compatible = "econet,en751221-timer";
+ reg = <0x1fbf0400 0x100>;
+
+ interrupt-parent = <&intc>;
+ interrupts = <30>;
+ clocks = <&hpt_clock>;
+ };
+};
diff --git a/arch/mips/boot/dts/econet/en751221_smartfiber_xp8421-b.dts b/arch/mips/boot/dts/econet/en751221_smartfiber_xp8421-b.dts
new file mode 100644
index 000000000000..8223c5bce67f
--- /dev/null
+++ b/arch/mips/boot/dts/econet/en751221_smartfiber_xp8421-b.dts
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+/dts-v1/;
+
+#include "en751221.dtsi"
+
+/ {
+ model = "SmartFiber XP8421-B";
+ compatible = "smartfiber,xp8421-b", "econet,en751221";
+
+ memory@0 {
+ device_type = "memory";
+ reg = <0x00000000 0x1c000000>;
+ };
+
+ chosen {
+ stdout-path = "/serial@1fbf0000:115200";
+ linux,usable-memory-range = <0x00020000 0x1bfe0000>;
+ };
+};
diff --git a/arch/mips/econet/Kconfig b/arch/mips/econet/Kconfig
index d03f90f3daa4..fd69884cc9a8 100644
--- a/arch/mips/econet/Kconfig
+++ b/arch/mips/econet/Kconfig
@@ -32,6 +32,17 @@ choice
config DTB_ECONET_NONE
bool "None"
+
+ config DTB_ECONET_SMARTFIBER_XP8421_B
+ bool "EN751221 SmartFiber XP8421-B"
+ depends on SOC_ECONET_EN751221
+ select BUILTIN_DTB
+ help
+ The SmartFiber XP8421-B is a device based on the EN751221 SoC.
+ It has 512MB of memory and 256MB of NAND flash. This kernel
+ needs only an appended initramfs to boot. It can be loaded
+ through XMODEM and booted from memory in the bootloader, or
+ it can be packed in tclinux.trx format and written to flash.
endchoice
endif
--
2.39.5
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v3 10/10] MAINTAINERS: Add entry for newly added EcoNet platform.
2025-03-30 17:02 [PATCH v3 00/10] Add EcoNet EN751221 MIPS platform support Caleb James DeLisle
` (8 preceding siblings ...)
2025-03-30 17:03 ` [PATCH v3 09/10] mips: dts: Add EcoNet DTS with EN751221 and SmartFiber XP8421-B board Caleb James DeLisle
@ 2025-03-30 17:03 ` Caleb James DeLisle
9 siblings, 0 replies; 18+ messages in thread
From: Caleb James DeLisle @ 2025-03-30 17:03 UTC (permalink / raw)
To: linux-mips
Cc: Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Thomas Bogendoerfer, Daniel Lezcano, devicetree, linux-kernel,
benjamin.larsson, linux-mediatek, Caleb James DeLisle
Add a MAINTAINERS entry as part of integration of the EcoNet MIPS platform.
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
---
MAINTAINERS | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index efee40ea589f..ed5329762584 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8182,6 +8182,18 @@ W: https://linuxtv.org
Q: http://patchwork.linuxtv.org/project/linux-media/list/
F: drivers/media/dvb-frontends/ec100*
+ECONET MIPS PLATFORM
+M: Caleb James DeLisle <cjd@cjdns.fr>
+L: linux-mips@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/interrupt-controller/econet,en751221-intc.yaml
+F: Documentation/devicetree/bindings/mips/econet.yaml
+F: Documentation/devicetree/bindings/timer/econet,en751221-timer.yaml
+F: arch/mips/boot/dts/econet/
+F: arch/mips/econet/
+F: drivers/clocksource/timer-econet-en751221.c
+F: drivers/irqchip/irq-econet-en751221.c
+
ECRYPT FILE SYSTEM
M: Tyler Hicks <code@tyhicks.com>
L: ecryptfs@vger.kernel.org
--
2.39.5
^ permalink raw reply related [flat|nested] 18+ messages in thread