* [PATCH v9 1/5] clocksource/timer-econet-en751221: fix refcount leak
2026-09-18 15:36 [PATCH v9 0/5] clocksource/timer-econet-en751221: Support irq number per timer Caleb James DeLisle
@ 2026-09-18 15:36 ` Caleb James DeLisle
2026-09-18 15:36 ` [PATCH v9 2/5] dt-bindings: timer: econet: Update EN751627 for multi-IRQ Caleb James DeLisle
` (3 subsequent siblings)
4 siblings, 0 replies; 10+ messages in thread
From: Caleb James DeLisle @ 2026-09-18 15:36 UTC (permalink / raw)
To: linux-mips
Cc: conor+dt, daniel.lezcano, devicetree, krzk+dt, linux-kernel,
naseefkm, robh, tglx, grandmaster, Caleb James DeLisle
From: "Alexander A. Klimov" <grandmaster@al2klimov.de>
Every value returned from of_clk_get() is supposed to be cleaned up
via clk_put() once not needed anymore.
Fixes: 3b4c33ac87d0 ("clocksource/drivers: Add EcoNet Timer HPT driver")
Signed-off-by: Alexander A. Klimov <grandmaster@al2klimov.de>
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
---
drivers/clocksource/timer-econet-en751221.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/clocksource/timer-econet-en751221.c b/drivers/clocksource/timer-econet-en751221.c
index 4008076b1a21..1859335345b5 100644
--- a/drivers/clocksource/timer-econet-en751221.c
+++ b/drivers/clocksource/timer-econet-en751221.c
@@ -181,6 +181,7 @@ static int __init timer_init(struct device_node *np)
}
econet_timer.freq_hz = clk_get_rate(clk);
+ clk_put(clk);
for (int i = 0; i < num_blocks; i++) {
econet_timer.membase[i] = of_iomap(np, i);
--
2.39.5
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH v9 2/5] dt-bindings: timer: econet: Update EN751627 for multi-IRQ
2026-09-18 15:36 [PATCH v9 0/5] clocksource/timer-econet-en751221: Support irq number per timer Caleb James DeLisle
2026-09-18 15:36 ` [PATCH v9 1/5] clocksource/timer-econet-en751221: fix refcount leak Caleb James DeLisle
@ 2026-09-18 15:36 ` Caleb James DeLisle
2026-09-18 15:36 ` [PATCH v9 3/5] clocksource/timer-econet-en751221: Init teardown on error if possible Caleb James DeLisle
` (2 subsequent siblings)
4 siblings, 0 replies; 10+ messages in thread
From: Caleb James DeLisle @ 2026-09-18 15:36 UTC (permalink / raw)
To: linux-mips
Cc: conor+dt, daniel.lezcano, devicetree, krzk+dt, linux-kernel,
naseefkm, robh, tglx, grandmaster, Caleb James DeLisle,
Conor Dooley
This hardware is found in the EN751221 SoC family as well as the
EN751627. The former uses a percpu IRQ for all timers while the
latter uses an individual IRQ number per timer.
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
---
.../bindings/timer/econet,en751221-timer.yaml | 31 ++++++++++++-------
1 file changed, 20 insertions(+), 11 deletions(-)
diff --git a/Documentation/devicetree/bindings/timer/econet,en751221-timer.yaml b/Documentation/devicetree/bindings/timer/econet,en751221-timer.yaml
index c1e7c2b6afde..ac25785ecfdf 100644
--- a/Documentation/devicetree/bindings/timer/econet,en751221-timer.yaml
+++ b/Documentation/devicetree/bindings/timer/econet,en751221-timer.yaml
@@ -12,24 +12,23 @@ maintainers:
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.
+ count/compare registers and a per-CPU control register. On EN751221 it uses a
+ single interrupt line using a percpu-devid interrupt mechanism, and on
+ EN751627 it uses an interrupt per VPE.
properties:
compatible:
- oneOf:
- - const: econet,en751221-timer
- - items:
- - const: econet,en751627-timer
- - const: econet,en751221-timer
+ enum:
+ - econet,en751221-timer
+ - econet,en751627-timer
reg:
minItems: 1
maxItems: 2
interrupts:
- maxItems: 1
- description: A percpu-devid timer interrupt shared across CPUs.
+ minItems: 1
+ maxItems: 4
clocks:
maxItems: 1
@@ -52,21 +51,31 @@ allOf:
items:
- description: VPE timers 0 and 1
- description: VPE timers 2 and 3
+ interrupts:
+ description: An interrupt for each timer (one per VPE)
+ minItems: 4
else:
properties:
reg:
items:
- description: VPE timers 0 and 1
+ interrupts:
+ description: A percpu-devid timer interrupt shared across timers
+ maxItems: 1
additionalProperties: false
examples:
- |
+ #include <dt-bindings/interrupt-controller/mips-gic.h>
timer@1fbf0400 {
- compatible = "econet,en751627-timer", "econet,en751221-timer";
+ compatible = "econet,en751627-timer";
reg = <0x1fbf0400 0x100>, <0x1fbe0000 0x100>;
interrupt-parent = <&intc>;
- interrupts = <30>;
+ interrupts = <GIC_SHARED 30 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 29 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 37 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SHARED 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&hpt_clock>;
};
- |
--
2.39.5
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH v9 3/5] clocksource/timer-econet-en751221: Init teardown on error if possible
2026-09-18 15:36 [PATCH v9 0/5] clocksource/timer-econet-en751221: Support irq number per timer Caleb James DeLisle
2026-09-18 15:36 ` [PATCH v9 1/5] clocksource/timer-econet-en751221: fix refcount leak Caleb James DeLisle
2026-09-18 15:36 ` [PATCH v9 2/5] dt-bindings: timer: econet: Update EN751627 for multi-IRQ Caleb James DeLisle
@ 2026-09-18 15:36 ` Caleb James DeLisle
2026-09-18 15:36 ` [PATCH v9 4/5] clocksource/timer-econet-en751221: Disable IRQ until cevt registered Caleb James DeLisle
2026-09-18 15:36 ` [PATCH v9 5/5] clocksource/timer-econet-en751221: Support EN751627 without percpu IRQ Caleb James DeLisle
4 siblings, 0 replies; 10+ messages in thread
From: Caleb James DeLisle @ 2026-09-18 15:36 UTC (permalink / raw)
To: linux-mips
Cc: conor+dt, daniel.lezcano, devicetree, krzk+dt, linux-kernel,
naseefkm, robh, tglx, grandmaster, Caleb James DeLisle
As a clocksource, much of the initialization process is irreversible
and the impact of a failure to initialize is a failure to boot.
That said, good practice is to attempt a clean exit if probing fails,
and supporting this pattern will reduce the likelihood that future
contributions introduce a bug by trying to teardown after it is no
longer possible to do so.
Convert the init process into two clearly delineated phases, one
which is reverted in case of error, and the other which can't be.
Move all IRQ and address resource mapping before that point, and add
teardown logic in case of error before the point of no return.
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
---
drivers/clocksource/timer-econet-en751221.c | 84 ++++++++++++---------
1 file changed, 50 insertions(+), 34 deletions(-)
diff --git a/drivers/clocksource/timer-econet-en751221.c b/drivers/clocksource/timer-econet-en751221.c
index 1859335345b5..714702b9ef12 100644
--- a/drivers/clocksource/timer-econet-en751221.c
+++ b/drivers/clocksource/timer-econet-en751221.c
@@ -24,6 +24,7 @@
static struct {
void __iomem *membase[ECONET_NUM_BLOCKS];
+ int irq;
u32 freq_hz;
} econet_timer __ro_after_init;
@@ -126,22 +127,9 @@ static void __init cevt_dev_init(uint cpu)
iowrite32(U32_MAX, reg_compare(cpu));
}
-static int __init cevt_init(struct device_node *np)
+static void __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;
- }
+ int i;
for_each_possible_cpu(i) {
struct clock_event_device *cd = &per_cpu(econet_timer_pcpu, i);
@@ -151,21 +139,12 @@ static int __init cevt_init(struct device_node *np)
CLOCK_EVT_FEAT_C3STOP |
CLOCK_EVT_FEAT_PERCPU;
cd->set_next_event = cevt_set_next_event;
- cd->irq = irq;
+ cd->irq = econet_timer.irq;
cd->cpumask = cpumask_of(i);
cd->name = np->name;
cevt_dev_init(i);
}
-
- cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
- "clockevents/econet/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)
@@ -187,22 +166,45 @@ static int __init timer_init(struct device_node *np)
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;
+ ret = -ENXIO;
+ goto out_membase;
}
}
+ econet_timer.irq = irq_of_parse_and_map(np, 0);
+ if (econet_timer.irq <= 0) {
+ pr_err("%pOFn: irq_of_parse_and_map failed\n", np);
+ ret = -EINVAL;
+ goto out_membase;
+ }
+
+ ret = request_percpu_irq(econet_timer.irq, cevt_interrupt, np->name,
+ &econet_timer_pcpu);
+
+ if (ret < 0) {
+ pr_err("%pOFn: IRQ %d setup failed (%d)\n", np,
+ econet_timer.irq, ret);
+ goto out_irq_mapping;
+ }
+
+ cevt_init(np);
+
+ ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+ "clockevents/econet/timer:starting",
+ cevt_init_cpu, NULL);
+ if (ret < 0) {
+ pr_err("%pOFn: cpuhp setup failed (%d)\n", np, ret);
+ goto out_irq_free;
+ }
+
+ /* Point of no return, do not attempt to tear down after this. */
+
/* 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;
+ if (ret)
+ pr_err("%pOFn: clocksource_mmio_init failed: %d\n", np, ret);
sched_clock_register(sched_clock_read, ECONET_BITS,
econet_timer.freq_hz);
@@ -212,6 +214,20 @@ static int __init timer_init(struct device_node *np)
(econet_timer.freq_hz / 1000) % 1000);
return 0;
+
+out_irq_free:
+ free_percpu_irq(econet_timer.irq, &econet_timer_pcpu);
+out_irq_mapping:
+ irq_dispose_mapping(econet_timer.irq);
+out_membase:
+ for (int i = 0; i < ARRAY_SIZE(econet_timer.membase); i++) {
+ if (econet_timer.membase[i]) {
+ iounmap(econet_timer.membase[i]);
+ econet_timer.membase[i] = NULL;
+ }
+ }
+
+ return ret;
}
TIMER_OF_DECLARE(econet_timer_hpt, "econet,en751221-timer", timer_init);
--
2.39.5
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH v9 4/5] clocksource/timer-econet-en751221: Disable IRQ until cevt registered
2026-09-18 15:36 [PATCH v9 0/5] clocksource/timer-econet-en751221: Support irq number per timer Caleb James DeLisle
` (2 preceding siblings ...)
2026-09-18 15:36 ` [PATCH v9 3/5] clocksource/timer-econet-en751221: Init teardown on error if possible Caleb James DeLisle
@ 2026-09-18 15:36 ` Caleb James DeLisle
2026-09-18 15:36 ` [PATCH v9 5/5] clocksource/timer-econet-en751221: Support EN751627 without percpu IRQ Caleb James DeLisle
4 siblings, 0 replies; 10+ messages in thread
From: Caleb James DeLisle @ 2026-09-18 15:36 UTC (permalink / raw)
To: linux-mips
Cc: conor+dt, daniel.lezcano, devicetree, krzk+dt, linux-kernel,
naseefkm, robh, tglx, grandmaster, Caleb James DeLisle
Eliminate a race condition where cevt_interrupt may trigger before
clockevents_config_and_register has been called, and dev->event_handler
is at that point NULL. Additionally, NULL check dev->event_handler in
interrupt callback just in case clockevents_config_and_register failed.
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
---
drivers/clocksource/timer-econet-en751221.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/drivers/clocksource/timer-econet-en751221.c b/drivers/clocksource/timer-econet-en751221.c
index 714702b9ef12..f2c4c1ee0a56 100644
--- a/drivers/clocksource/timer-econet-en751221.c
+++ b/drivers/clocksource/timer-econet-en751221.c
@@ -75,7 +75,10 @@ static irqreturn_t cevt_interrupt(int irq, void *dev_id)
return IRQ_NONE;
iowrite32(ioread32(reg_count(cpu)), reg_compare(cpu));
- dev->event_handler(dev);
+
+ if (dev->event_handler)
+ dev->event_handler(dev);
+
return IRQ_HANDLED;
}
@@ -104,12 +107,11 @@ static int cevt_init_cpu(uint 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);
+ enable_percpu_irq(cd->irq, IRQ_TYPE_NONE);
+
return 0;
}
@@ -178,6 +180,8 @@ static int __init timer_init(struct device_node *np)
goto out_membase;
}
+ irq_set_status_flags(econet_timer.irq, IRQ_NOAUTOEN);
+
ret = request_percpu_irq(econet_timer.irq, cevt_interrupt, np->name,
&econet_timer_pcpu);
--
2.39.5
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH v9 5/5] clocksource/timer-econet-en751221: Support EN751627 without percpu IRQ
2026-09-18 15:36 [PATCH v9 0/5] clocksource/timer-econet-en751221: Support irq number per timer Caleb James DeLisle
` (3 preceding siblings ...)
2026-09-18 15:36 ` [PATCH v9 4/5] clocksource/timer-econet-en751221: Disable IRQ until cevt registered Caleb James DeLisle
@ 2026-09-18 15:36 ` Caleb James DeLisle
2026-10-14 18:43 ` Daniel Lezcano
4 siblings, 1 reply; 10+ messages in thread
From: Caleb James DeLisle @ 2026-09-18 15:36 UTC (permalink / raw)
To: linux-mips
Cc: conor+dt, daniel.lezcano, devicetree, krzk+dt, linux-kernel,
naseefkm, robh, tglx, grandmaster, Caleb James DeLisle
EN751627 is based on the 1004Kc which uses a different interrupt number
for each CPU timer. Support both this and the EN751221 which uses a
single percpu interrupt.
Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
---
drivers/clocksource/timer-econet-en751221.c | 122 ++++++++++++++++----
1 file changed, 99 insertions(+), 23 deletions(-)
diff --git a/drivers/clocksource/timer-econet-en751221.c b/drivers/clocksource/timer-econet-en751221.c
index f2c4c1ee0a56..ff86a7edd31d 100644
--- a/drivers/clocksource/timer-econet-en751221.c
+++ b/drivers/clocksource/timer-econet-en751221.c
@@ -21,10 +21,12 @@
#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)
+#define ECONET_NUM_IRQS NR_CPUS
static struct {
void __iomem *membase[ECONET_NUM_BLOCKS];
- int irq;
+ int irqs[ECONET_NUM_IRQS];
+ bool is_percpu;
u32 freq_hz;
} econet_timer __ro_after_init;
@@ -102,6 +104,25 @@ static int cevt_init_cpu(uint cpu)
struct clock_event_device *cd = &per_cpu(econet_timer_pcpu, cpu);
u32 reg;
+ if (!reg_ctl(cpu)) {
+ pr_err("%s: missing address resource for CPU %d\n", cd->name,
+ cpu);
+ return -EINVAL;
+ }
+ if (cd->irq <= 0) {
+ pr_err("%s: missing IRQ for CPU %d\n", cd->name, cpu);
+ return -EINVAL;
+ }
+ if (!econet_timer.is_percpu) {
+ int ret = irq_force_affinity(cd->irq, cpumask_of(cpu));
+
+ if (ret) {
+ pr_err("%s: failed to set IRQ affinity to CPU %d: %pe\n",
+ cd->name, cpu, ERR_PTR(ret));
+ return ret;
+ }
+ }
+
pr_debug("%s: Setting up clockevent for CPU %d\n", cd->name, cpu);
reg = ioread32(reg_ctl(cpu)) | ctl_bit_enabled(cpu);
@@ -110,7 +131,10 @@ static int cevt_init_cpu(uint cpu)
clockevents_config_and_register(cd, econet_timer.freq_hz,
ECONET_MIN_DELTA, ECONET_MAX_DELTA);
- enable_percpu_irq(cd->irq, IRQ_TYPE_NONE);
+ if (econet_timer.is_percpu)
+ enable_percpu_irq(cd->irq, IRQ_TYPE_NONE);
+ else
+ enable_irq(cd->irq);
return 0;
}
@@ -141,19 +165,52 @@ static void __init cevt_init(struct device_node *np)
CLOCK_EVT_FEAT_C3STOP |
CLOCK_EVT_FEAT_PERCPU;
cd->set_next_event = cevt_set_next_event;
- cd->irq = econet_timer.irq;
+
+ if (econet_timer.is_percpu)
+ cd->irq = econet_timer.irqs[0];
+ else
+ cd->irq = econet_timer.irqs[i];
+
cd->cpumask = cpumask_of(i);
cd->name = np->name;
- cevt_dev_init(i);
+ /*
+ * Tolerate CPUs that could exist but don't.
+ * Fail in cevt_init_cpu when they try to start.
+ */
+ if (reg_ctl(i))
+ cevt_dev_init(i);
}
}
static int __init timer_init(struct device_node *np)
{
- int num_blocks = DIV_ROUND_UP(num_possible_cpus(), 2);
+ int num_blocks = of_address_count(np);
+ int num_irqs = of_irq_count(np);
struct clk *clk;
- int ret;
+ int ret, i;
+
+ econet_timer.is_percpu = of_device_is_compatible(np, "econet,en751221-timer");
+
+ if (econet_timer.is_percpu && num_irqs != 1) {
+ pr_err("%pOFn: EN751221 clock must have 1 IRQ not %d\n", np,
+ num_irqs);
+ return -EINVAL;
+ }
+ if (num_irqs > ARRAY_SIZE(econet_timer.irqs)) {
+ pr_err("%pOFn: Too many IRQs max %zd got %d\n", np,
+ ARRAY_SIZE(econet_timer.irqs), num_irqs);
+ return -EINVAL;
+ }
+ if (num_blocks > ARRAY_SIZE(econet_timer.membase)) {
+ pr_err("%pOFn: Too many regs: max %zd got %d\n", np,
+ ARRAY_SIZE(econet_timer.membase), num_blocks);
+ return -EINVAL;
+ }
+ if (num_blocks == 0) {
+ pr_err("%pOFn: At least one reg block must be provided\n", np);
+ return -EINVAL;
+ }
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
@@ -164,7 +221,7 @@ static int __init timer_init(struct device_node *np)
econet_timer.freq_hz = clk_get_rate(clk);
clk_put(clk);
- for (int i = 0; i < num_blocks; i++) {
+ for (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);
@@ -173,22 +230,32 @@ static int __init timer_init(struct device_node *np)
}
}
- econet_timer.irq = irq_of_parse_and_map(np, 0);
- if (econet_timer.irq <= 0) {
- pr_err("%pOFn: irq_of_parse_and_map failed\n", np);
- ret = -EINVAL;
- goto out_membase;
+ for (i = 0; i < num_irqs; i++) {
+ econet_timer.irqs[i] = irq_of_parse_and_map(np, i);
+ if (econet_timer.irqs[i] <= 0) {
+ pr_err("%pOFn: failed mapping irq %d\n", np, i);
+ ret = -EINVAL;
+ goto out_irq_mapping;
+ }
}
- irq_set_status_flags(econet_timer.irq, IRQ_NOAUTOEN);
-
- ret = request_percpu_irq(econet_timer.irq, cevt_interrupt, np->name,
- &econet_timer_pcpu);
-
- if (ret < 0) {
- pr_err("%pOFn: IRQ %d setup failed (%d)\n", np,
- econet_timer.irq, ret);
- goto out_irq_mapping;
+ for (i = 0; i < num_irqs; i++) {
+ irq_set_status_flags(econet_timer.irqs[i], IRQ_NOAUTOEN);
+
+ if (econet_timer.is_percpu)
+ ret = request_percpu_irq(econet_timer.irqs[i],
+ cevt_interrupt, np->name,
+ &econet_timer_pcpu);
+ else
+ ret = request_irq(econet_timer.irqs[i], cevt_interrupt,
+ IRQF_TIMER | IRQF_NOBALANCING,
+ np->name, NULL);
+
+ if (ret < 0) {
+ pr_err("%pOFn: IRQ %d setup failed: %pe\n", np,
+ i, ERR_PTR(ret));
+ goto out_irq_free;
+ }
}
cevt_init(np);
@@ -220,9 +287,17 @@ static int __init timer_init(struct device_node *np)
return 0;
out_irq_free:
- free_percpu_irq(econet_timer.irq, &econet_timer_pcpu);
+ while (--i >= 0) {
+ if (econet_timer.is_percpu)
+ free_percpu_irq(econet_timer.irqs[i], &econet_timer_pcpu);
+ else
+ free_irq(econet_timer.irqs[i], NULL);
+ }
out_irq_mapping:
- irq_dispose_mapping(econet_timer.irq);
+ for (i = 0; i < num_irqs; i++) {
+ if (econet_timer.irqs[i] > 0)
+ irq_dispose_mapping(econet_timer.irqs[i]);
+ }
out_membase:
for (int i = 0; i < ARRAY_SIZE(econet_timer.membase); i++) {
if (econet_timer.membase[i]) {
@@ -235,3 +310,4 @@ static int __init timer_init(struct device_node *np)
}
TIMER_OF_DECLARE(econet_timer_hpt, "econet,en751221-timer", timer_init);
+TIMER_OF_DECLARE(econet_timer_en751627, "econet,en751627-timer", timer_init);
--
2.39.5
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH v9 5/5] clocksource/timer-econet-en751221: Support EN751627 without percpu IRQ
2026-09-18 15:36 ` [PATCH v9 5/5] clocksource/timer-econet-en751221: Support EN751627 without percpu IRQ Caleb James DeLisle
@ 2026-10-14 18:43 ` Daniel Lezcano
2026-10-14 20:36 ` Caleb James DeLisle
0 siblings, 1 reply; 10+ messages in thread
From: Daniel Lezcano @ 2026-10-14 18:43 UTC (permalink / raw)
To: Caleb James DeLisle, linux-mips
Cc: conor+dt, daniel.lezcano, devicetree, krzk+dt, linux-kernel,
naseefkm, robh, tglx, grandmaster
Hi Caleb,
with the help of a LLM, I got the following two issues with the changes
On 9/18/26 17:36, Caleb James DeLisle wrote:
> EN751627 is based on the 1004Kc which uses a different interrupt number
> for each CPU timer. Support both this and the EN751221 which uses a
> single percpu interrupt.
>
> Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
> ---
> drivers/clocksource/timer-econet-en751221.c | 122 ++++++++++++++++----
> 1 file changed, 99 insertions(+), 23 deletions(-)
>
> diff --git a/drivers/clocksource/timer-econet-en751221.c b/drivers/clocksource/timer-econet-en751221.c
> index f2c4c1ee0a56..ff86a7edd31d 100644
> --- a/drivers/clocksource/timer-econet-en751221.c
> +++ b/drivers/clocksource/timer-econet-en751221.c
> @@ -21,10 +21,12 @@
> #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)
> +#define ECONET_NUM_IRQS NR_CPUS
>
> static struct {
> void __iomem *membase[ECONET_NUM_BLOCKS];
> - int irq;
> + int irqs[ECONET_NUM_IRQS];
> + bool is_percpu;
> u32 freq_hz;
> } econet_timer __ro_after_init;
>
> @@ -102,6 +104,25 @@ static int cevt_init_cpu(uint cpu)
> struct clock_event_device *cd = &per_cpu(econet_timer_pcpu, cpu);
> u32 reg;
>
> + if (!reg_ctl(cpu)) {
> + pr_err("%s: missing address resource for CPU %d\n", cd->name,
> + cpu);
> + return -EINVAL;
> + }
> + if (cd->irq <= 0) {
> + pr_err("%s: missing IRQ for CPU %d\n", cd->name, cpu);
> + return -EINVAL;
> + }
> + if (!econet_timer.is_percpu) {
> + int ret = irq_force_affinity(cd->irq, cpumask_of(cpu));
> +
> + if (ret) {
> + pr_err("%s: failed to set IRQ affinity to CPU %d: %pe\n",
> + cd->name, cpu, ERR_PTR(ret));
> + return ret;
> + }
> + }
> +
> pr_debug("%s: Setting up clockevent for CPU %d\n", cd->name, cpu);
>
> reg = ioread32(reg_ctl(cpu)) | ctl_bit_enabled(cpu);
> @@ -110,7 +131,10 @@ static int cevt_init_cpu(uint cpu)
> clockevents_config_and_register(cd, econet_timer.freq_hz,
> ECONET_MIN_DELTA, ECONET_MAX_DELTA);
>
> - enable_percpu_irq(cd->irq, IRQ_TYPE_NONE);
> + if (econet_timer.is_percpu)
> + enable_percpu_irq(cd->irq, IRQ_TYPE_NONE);
> + else
> + enable_irq(cd->irq);
The new path calls enable_irq() each time a CPU starts, but the hotplug
state has no teardown callback to disable it. After an offline/online
cycle, the next enable_irq() encounters depth zero and warns about an
unbalanced enable.
The IRQ also remains eligible for migration when its CPU goes offline.
Since cevt_interrupt() selects the timer using this_cpu_ptr(), an
interrupt migrated to another CPU checks and acknowledges that CPU’s
timer rather than the source timer, potentially leaving the source
interrupt asserted.
Add a CPU-offline callback that masks the CPU’s timer interrupt and
balances enable_irq() with disable_irq_nosync() before migration.
> return 0;
> }
> @@ -141,19 +165,52 @@ static void __init cevt_init(struct device_node *np)
> CLOCK_EVT_FEAT_C3STOP |
> CLOCK_EVT_FEAT_PERCPU;
> cd->set_next_event = cevt_set_next_event;
> - cd->irq = econet_timer.irq;
> +
> + if (econet_timer.is_percpu)
> + cd->irq = econet_timer.irqs[0];
> + else
> + cd->irq = econet_timer.irqs[i];
> +
> cd->cpumask = cpumask_of(i);
> cd->name = np->name;
>
> - cevt_dev_init(i);
> + /*
> + * Tolerate CPUs that could exist but don't.
> + * Fail in cevt_init_cpu when they try to start.
> + */
> + if (reg_ctl(i))
> + cevt_dev_init(i);
> }
> }
>
> static int __init timer_init(struct device_node *np)
> {
> - int num_blocks = DIV_ROUND_UP(num_possible_cpus(), 2);
> + int num_blocks = of_address_count(np);
> + int num_irqs = of_irq_count(np);
> struct clk *clk;
> - int ret;
> + int ret, i;
> +
> + econet_timer.is_percpu = of_device_is_compatible(np, "econet,en751221-timer");
> +
> + if (econet_timer.is_percpu && num_irqs != 1) {
> + pr_err("%pOFn: EN751221 clock must have 1 IRQ not %d\n", np,
> + num_irqs);
> + return -EINVAL;
> + }
> + if (num_irqs > ARRAY_SIZE(econet_timer.irqs)) {
> + pr_err("%pOFn: Too many IRQs max %zd got %d\n", np,
> + ARRAY_SIZE(econet_timer.irqs), num_irqs);
> + return -EINVAL;
> + }
> + if (num_blocks > ARRAY_SIZE(econet_timer.membase)) {
> + pr_err("%pOFn: Too many regs: max %zd got %d\n", np,
> + ARRAY_SIZE(econet_timer.membase), num_blocks);
> + return -EINVAL;
> + }
> + if (num_blocks == 0) {
> + pr_err("%pOFn: At least one reg block must be provided\n", np);
> + return -EINVAL;
> + }
The binding requires four interrupts and two register blocks for
EN751627, but the driver sizes its resource arrays from NR_CPUS and
rejects DTs exceeding those sizes. Consequently, a UP kernel (NR_CPUS=1)
or an SMP kernel configured for two CPUs rejects a valid EN751627 DT
before registering any timer.
> clk = of_clk_get(np, 0);
> if (IS_ERR(clk)) {
> @@ -164,7 +221,7 @@ static int __init timer_init(struct device_node *np)
> econet_timer.freq_hz = clk_get_rate(clk);
> clk_put(clk);
>
> - for (int i = 0; i < num_blocks; i++) {
> + for (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);
> @@ -173,22 +230,32 @@ static int __init timer_init(struct device_node *np)
> }
> }
>
> - econet_timer.irq = irq_of_parse_and_map(np, 0);
> - if (econet_timer.irq <= 0) {
> - pr_err("%pOFn: irq_of_parse_and_map failed\n", np);
> - ret = -EINVAL;
> - goto out_membase;
> + for (i = 0; i < num_irqs; i++) {
> + econet_timer.irqs[i] = irq_of_parse_and_map(np, i);
> + if (econet_timer.irqs[i] <= 0) {
> + pr_err("%pOFn: failed mapping irq %d\n", np, i);
> + ret = -EINVAL;
> + goto out_irq_mapping;
> + }
> }
>
> - irq_set_status_flags(econet_timer.irq, IRQ_NOAUTOEN);
> -
> - ret = request_percpu_irq(econet_timer.irq, cevt_interrupt, np->name,
> - &econet_timer_pcpu);
> -
> - if (ret < 0) {
> - pr_err("%pOFn: IRQ %d setup failed (%d)\n", np,
> - econet_timer.irq, ret);
> - goto out_irq_mapping;
> + for (i = 0; i < num_irqs; i++) {
> + irq_set_status_flags(econet_timer.irqs[i], IRQ_NOAUTOEN);
> +
> + if (econet_timer.is_percpu)
> + ret = request_percpu_irq(econet_timer.irqs[i],
> + cevt_interrupt, np->name,
> + &econet_timer_pcpu);
> + else
> + ret = request_irq(econet_timer.irqs[i], cevt_interrupt,
> + IRQF_TIMER | IRQF_NOBALANCING,
> + np->name, NULL);
> +
> + if (ret < 0) {
> + pr_err("%pOFn: IRQ %d setup failed: %pe\n", np,
> + i, ERR_PTR(ret));
> + goto out_irq_free;
> + }
> }
>
> cevt_init(np);
> @@ -220,9 +287,17 @@ static int __init timer_init(struct device_node *np)
> return 0;
>
> out_irq_free:
> - free_percpu_irq(econet_timer.irq, &econet_timer_pcpu);
> + while (--i >= 0) {
> + if (econet_timer.is_percpu)
> + free_percpu_irq(econet_timer.irqs[i], &econet_timer_pcpu);
> + else
> + free_irq(econet_timer.irqs[i], NULL);
> + }
> out_irq_mapping:
> - irq_dispose_mapping(econet_timer.irq);
> + for (i = 0; i < num_irqs; i++) {
> + if (econet_timer.irqs[i] > 0)
> + irq_dispose_mapping(econet_timer.irqs[i]);
> + }
> out_membase:
> for (int i = 0; i < ARRAY_SIZE(econet_timer.membase); i++) {
> if (econet_timer.membase[i]) {
> @@ -235,3 +310,4 @@ static int __init timer_init(struct device_node *np)
> }
>
> TIMER_OF_DECLARE(econet_timer_hpt, "econet,en751221-timer", timer_init);
> +TIMER_OF_DECLARE(econet_timer_en751627, "econet,en751627-timer", timer_init);
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH v9 5/5] clocksource/timer-econet-en751221: Support EN751627 without percpu IRQ
2026-10-14 18:43 ` Daniel Lezcano
@ 2026-10-14 20:36 ` Caleb James DeLisle
2026-10-15 7:21 ` Daniel Lezcano
0 siblings, 1 reply; 10+ messages in thread
From: Caleb James DeLisle @ 2026-10-14 20:36 UTC (permalink / raw)
To: Daniel Lezcano, linux-mips
Cc: conor+dt, daniel.lezcano, devicetree, krzk+dt, linux-kernel,
naseefkm, robh, tglx, grandmaster
Hello Daniel,
Thank you for the review.
On 14/10/2026 20:43, Daniel Lezcano wrote:
>
> Hi Caleb,
>
> with the help of a LLM, I got the following two issues with the changes
>
> On 9/18/26 17:36, Caleb James DeLisle wrote:
>> EN751627 is based on the 1004Kc which uses a different interrupt number
>> for each CPU timer. Support both this and the EN751221 which uses a
>> single percpu interrupt.
>>
>> Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
>> ---
>> drivers/clocksource/timer-econet-en751221.c | 122 ++++++++++++++++----
>> 1 file changed, 99 insertions(+), 23 deletions(-)
>>
>> diff --git a/drivers/clocksource/timer-econet-en751221.c
>> b/drivers/clocksource/timer-econet-en751221.c
>> index f2c4c1ee0a56..ff86a7edd31d 100644
>> --- a/drivers/clocksource/timer-econet-en751221.c
>> +++ b/drivers/clocksource/timer-econet-en751221.c
>> @@ -21,10 +21,12 @@
>> #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)
>> +#define ECONET_NUM_IRQS NR_CPUS
>> static struct {
>> void __iomem *membase[ECONET_NUM_BLOCKS];
>> - int irq;
>> + int irqs[ECONET_NUM_IRQS];
>> + bool is_percpu;
>> u32 freq_hz;
>> } econet_timer __ro_after_init;
>> @@ -102,6 +104,25 @@ static int cevt_init_cpu(uint cpu)
>> struct clock_event_device *cd = &per_cpu(econet_timer_pcpu, cpu);
>> u32 reg;
>> + if (!reg_ctl(cpu)) {
>> + pr_err("%s: missing address resource for CPU %d\n", cd->name,
>> + cpu);
>> + return -EINVAL;
>> + }
>> + if (cd->irq <= 0) {
>> + pr_err("%s: missing IRQ for CPU %d\n", cd->name, cpu);
>> + return -EINVAL;
>> + }
>> + if (!econet_timer.is_percpu) {
>> + int ret = irq_force_affinity(cd->irq, cpumask_of(cpu));
>> +
>> + if (ret) {
>> + pr_err("%s: failed to set IRQ affinity to CPU %d: %pe\n",
>> + cd->name, cpu, ERR_PTR(ret));
>> + return ret;
>> + }
>> + }
>> +
>> pr_debug("%s: Setting up clockevent for CPU %d\n", cd->name, cpu);
>> reg = ioread32(reg_ctl(cpu)) | ctl_bit_enabled(cpu);
>> @@ -110,7 +131,10 @@ static int cevt_init_cpu(uint cpu)
>> clockevents_config_and_register(cd, econet_timer.freq_hz,
>> ECONET_MIN_DELTA, ECONET_MAX_DELTA);
>> - enable_percpu_irq(cd->irq, IRQ_TYPE_NONE);
>> + if (econet_timer.is_percpu)
>> + enable_percpu_irq(cd->irq, IRQ_TYPE_NONE);
>> + else
>> + enable_irq(cd->irq);
>
> The new path calls enable_irq() each time a CPU starts, but the
> hotplug state has no teardown callback to disable it. After an
> offline/online cycle, the next enable_irq() encounters depth zero and
> warns about an unbalanced enable.
> The IRQ also remains eligible for migration when its CPU goes offline.
> Since cevt_interrupt() selects the timer using this_cpu_ptr(), an
> interrupt migrated to another CPU checks and acknowledges that CPU’s
> timer rather than the source timer, potentially leaving the source
> interrupt asserted.
> Add a CPU-offline callback that masks the CPU’s timer interrupt and
> balances enable_irq() with disable_irq_nosync() before migration.
>
I got this note from sashiko, and my thought process was that this CPU
doesn't have hotplug, the vendor implementation didn't have it, the
vendor doesn't make any MIPS based products anymore so no new products
are coming out with this timer. So supporting hotplug here is adding
code that will never run and adds confusion.
That said, I'm definitely open to alternative opinions on that.
>> return 0;
>> }
>> @@ -141,19 +165,52 @@ static void __init cevt_init(struct device_node
>> *np)
>> CLOCK_EVT_FEAT_C3STOP |
>> CLOCK_EVT_FEAT_PERCPU;
>> cd->set_next_event = cevt_set_next_event;
>> - cd->irq = econet_timer.irq;
>> +
>> + if (econet_timer.is_percpu)
>> + cd->irq = econet_timer.irqs[0];
>> + else
>> + cd->irq = econet_timer.irqs[i];
>> +
>> cd->cpumask = cpumask_of(i);
>> cd->name = np->name;
>> - cevt_dev_init(i);
>> + /*
>> + * Tolerate CPUs that could exist but don't.
>> + * Fail in cevt_init_cpu when they try to start.
>> + */
>> + if (reg_ctl(i))
>> + cevt_dev_init(i);
>> }
>> }
>> static int __init timer_init(struct device_node *np)
>> {
>> - int num_blocks = DIV_ROUND_UP(num_possible_cpus(), 2);
>> + int num_blocks = of_address_count(np);
>> + int num_irqs = of_irq_count(np);
>> struct clk *clk;
>> - int ret;
>> + int ret, i;
>> +
>> + econet_timer.is_percpu = of_device_is_compatible(np,
>> "econet,en751221-timer");
>> +
>> + if (econet_timer.is_percpu && num_irqs != 1) {
>> + pr_err("%pOFn: EN751221 clock must have 1 IRQ not %d\n", np,
>> + num_irqs);
>> + return -EINVAL;
>> + }
>> + if (num_irqs > ARRAY_SIZE(econet_timer.irqs)) {
>> + pr_err("%pOFn: Too many IRQs max %zd got %d\n", np,
>> + ARRAY_SIZE(econet_timer.irqs), num_irqs);
>> + return -EINVAL;
>> + }
>> + if (num_blocks > ARRAY_SIZE(econet_timer.membase)) {
>> + pr_err("%pOFn: Too many regs: max %zd got %d\n", np,
>> + ARRAY_SIZE(econet_timer.membase), num_blocks);
>> + return -EINVAL;
>> + }
>> + if (num_blocks == 0) {
>> + pr_err("%pOFn: At least one reg block must be provided\n", np);
>> + return -EINVAL;
>> + }
>
> The binding requires four interrupts and two register blocks for
> EN751627, but the driver sizes its resource arrays from NR_CPUS and
> rejects DTs exceeding those sizes. Consequently, a UP kernel
> (NR_CPUS=1) or an SMP kernel configured for two CPUs rejects a valid
> EN751627 DT before registering any timer.
>
This is a fair point, and I suppose a reasonable thing to do here is
instead set num_blocks = ARRAY_SIZE(econet_timer.membase) so we just
don't use the other blocks. I can send an updated patchset with that.
Does this sound good to you?
Thanks,
Caleb
>
>> clk = of_clk_get(np, 0);
>> if (IS_ERR(clk)) {
>> @@ -164,7 +221,7 @@ static int __init timer_init(struct device_node *np)
>> econet_timer.freq_hz = clk_get_rate(clk);
>> clk_put(clk);
>> - for (int i = 0; i < num_blocks; i++) {
>> + for (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);
>> @@ -173,22 +230,32 @@ static int __init timer_init(struct device_node
>> *np)
>> }
>> }
>> - econet_timer.irq = irq_of_parse_and_map(np, 0);
>> - if (econet_timer.irq <= 0) {
>> - pr_err("%pOFn: irq_of_parse_and_map failed\n", np);
>> - ret = -EINVAL;
>> - goto out_membase;
>> + for (i = 0; i < num_irqs; i++) {
>> + econet_timer.irqs[i] = irq_of_parse_and_map(np, i);
>> + if (econet_timer.irqs[i] <= 0) {
>> + pr_err("%pOFn: failed mapping irq %d\n", np, i);
>> + ret = -EINVAL;
>> + goto out_irq_mapping;
>> + }
>> }
>> - irq_set_status_flags(econet_timer.irq, IRQ_NOAUTOEN);
>> -
>> - ret = request_percpu_irq(econet_timer.irq, cevt_interrupt,
>> np->name,
>> - &econet_timer_pcpu);
>> -
>> - if (ret < 0) {
>> - pr_err("%pOFn: IRQ %d setup failed (%d)\n", np,
>> - econet_timer.irq, ret);
>> - goto out_irq_mapping;
>> + for (i = 0; i < num_irqs; i++) {
>> + irq_set_status_flags(econet_timer.irqs[i], IRQ_NOAUTOEN);
>> +
>> + if (econet_timer.is_percpu)
>> + ret = request_percpu_irq(econet_timer.irqs[i],
>> + cevt_interrupt, np->name,
>> + &econet_timer_pcpu);
>> + else
>> + ret = request_irq(econet_timer.irqs[i], cevt_interrupt,
>> + IRQF_TIMER | IRQF_NOBALANCING,
>> + np->name, NULL);
>> +
>> + if (ret < 0) {
>> + pr_err("%pOFn: IRQ %d setup failed: %pe\n", np,
>> + i, ERR_PTR(ret));
>> + goto out_irq_free;
>> + }
>> }
>> cevt_init(np);
>> @@ -220,9 +287,17 @@ static int __init timer_init(struct device_node
>> *np)
>> return 0;
>> out_irq_free:
>> - free_percpu_irq(econet_timer.irq, &econet_timer_pcpu);
>> + while (--i >= 0) {
>> + if (econet_timer.is_percpu)
>> + free_percpu_irq(econet_timer.irqs[i], &econet_timer_pcpu);
>> + else
>> + free_irq(econet_timer.irqs[i], NULL);
>> + }
>> out_irq_mapping:
>> - irq_dispose_mapping(econet_timer.irq);
>> + for (i = 0; i < num_irqs; i++) {
>> + if (econet_timer.irqs[i] > 0)
>> + irq_dispose_mapping(econet_timer.irqs[i]);
>> + }
>> out_membase:
>> for (int i = 0; i < ARRAY_SIZE(econet_timer.membase); i++) {
>> if (econet_timer.membase[i]) {
>> @@ -235,3 +310,4 @@ static int __init timer_init(struct device_node *np)
>> }
>> TIMER_OF_DECLARE(econet_timer_hpt, "econet,en751221-timer",
>> timer_init);
>> +TIMER_OF_DECLARE(econet_timer_en751627, "econet,en751627-timer",
>> timer_init);
>
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH v9 5/5] clocksource/timer-econet-en751221: Support EN751627 without percpu IRQ
2026-10-14 20:36 ` Caleb James DeLisle
@ 2026-10-15 7:21 ` Daniel Lezcano
2026-10-15 8:30 ` Caleb James DeLisle
0 siblings, 1 reply; 10+ messages in thread
From: Daniel Lezcano @ 2026-10-15 7:21 UTC (permalink / raw)
To: Caleb James DeLisle, linux-mips
Cc: conor+dt, daniel.lezcano, devicetree, krzk+dt, linux-kernel,
naseefkm, robh, tglx, grandmaster
On 10/14/26 22:36, Caleb James DeLisle wrote:
> Hello Daniel,
>
> Thank you for the review.
>
>
> On 14/10/2026 20:43, Daniel Lezcano wrote:
>>
>> Hi Caleb,
>>
>> with the help of a LLM, I got the following two issues with the changes
>>
>> On 9/18/26 17:36, Caleb James DeLisle wrote:
>>> EN751627 is based on the 1004Kc which uses a different interrupt number
>>> for each CPU timer. Support both this and the EN751221 which uses a
>>> single percpu interrupt.
>>>
>>> Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
>>> ---
[ ... ]
>> The binding requires four interrupts and two register blocks for
>> EN751627, but the driver sizes its resource arrays from NR_CPUS and
>> rejects DTs exceeding those sizes. Consequently, a UP kernel
>> (NR_CPUS=1) or an SMP kernel configured for two CPUs rejects a valid
>> EN751627 DT before registering any timer.
>>
>
> This is a fair point, and I suppose a reasonable thing to do here is
> instead set num_blocks = ARRAY_SIZE(econet_timer.membase) so we just
> don't use the other blocks. I can send an updated patchset with that.
IMO, econet_timer.irqs[] and econet_timer.membase[] should just be big
enough to store all nodes without using NR_CPUS.
Given you have num_irqs and num_blocks, why not just kzalloc them ?
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v9 5/5] clocksource/timer-econet-en751221: Support EN751627 without percpu IRQ
2026-10-15 7:21 ` Daniel Lezcano
@ 2026-10-15 8:30 ` Caleb James DeLisle
0 siblings, 0 replies; 10+ messages in thread
From: Caleb James DeLisle @ 2026-10-15 8:30 UTC (permalink / raw)
To: Daniel Lezcano, linux-mips
Cc: conor+dt, daniel.lezcano, devicetree, krzk+dt, linux-kernel,
naseefkm, robh, tglx, grandmaster
On 15/10/2026 09:21, Daniel Lezcano wrote:
> On 10/14/26 22:36, Caleb James DeLisle wrote:
>> Hello Daniel,
>>
>> Thank you for the review.
>>
>>
>> On 14/10/2026 20:43, Daniel Lezcano wrote:
>>>
>>> Hi Caleb,
>>>
>>> with the help of a LLM, I got the following two issues with the changes
>>>
>>> On 9/18/26 17:36, Caleb James DeLisle wrote:
>>>> EN751627 is based on the 1004Kc which uses a different interrupt
>>>> number
>>>> for each CPU timer. Support both this and the EN751221 which uses a
>>>> single percpu interrupt.
>>>>
>>>> Signed-off-by: Caleb James DeLisle <cjd@cjdns.fr>
>>>> ---
>
> [ ... ]
>
>>> The binding requires four interrupts and two register blocks for
>>> EN751627, but the driver sizes its resource arrays from NR_CPUS and
>>> rejects DTs exceeding those sizes. Consequently, a UP kernel
>>> (NR_CPUS=1) or an SMP kernel configured for two CPUs rejects a valid
>>> EN751627 DT before registering any timer.
>>>
>>
>> This is a fair point, and I suppose a reasonable thing to do here is
>> instead set num_blocks = ARRAY_SIZE(econet_timer.membase) so we just
>> don't use the other blocks. I can send an updated patchset with that.
>
> IMO, econet_timer.irqs[] and econet_timer.membase[] should just be big
> enough to store all nodes without using NR_CPUS.
>
> Given you have num_irqs and num_blocks, why not just kzalloc them ?
That works for me, I'll re-send it like that then.
Thanks,
Caleb
^ permalink raw reply [flat|nested] 10+ messages in thread