mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net] net: ethernet: ti: cpts: drop stale events before the misc IRQ can fire
@ 2026-10-26  7:23 Hung-Chun Tseng
  2026-10-26  8:18 ` Siddharth Vadapalli
  0 siblings, 1 reply; 2+ messages in thread
From: Hung-Chun Tseng @ 2026-10-26  7:23 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: Siddharth Vadapalli, Roger Quadros, Richard Cochran,
	Grygorii Strashko, linux-omap, netdev, linux-kernel,
	Hung-Chun Tseng

cpsw requests its misc IRQ at probe, but the CPTS event pool is only set
up later, in cpts_register() from ndo_open. That is fine from a cold boot,
where the CPTS comes out of reset stopped with its interrupt masked. After
a kexec it is not: the previous kernel leaves the CPTS running with
TS_PEND_EN set and events in its FIFO, so the interrupt fires as soon as
it is requested and cpts_fifo_read() takes an entry from the still empty
pool list:

  Unable to handle kernel NULL pointer dereference at virtual address 00000010 when write
  PC is at cpts_fifo_read+0xa4/0x3c8
  Call trace:
   cpts_fifo_read from cpsw_misc_interrupt+0x30/0x4c
   cpsw_misc_interrupt from __handle_irq_event_percpu+0x74/0x378
   ...
   __setup_irq from request_threaded_irq+0xbc/0x170
   request_threaded_irq from devm_request_threaded_irq+0x70/0x104

On a BeagleBone Black the CPTS registers read control=0x1, int_enable=0x1
and intstat_raw=0x1 in cpts_create() after the kexec, with one event
queued.

Mask the CPTS interrupt and drain the FIFO in cpts_create(), before
either cpsw driver requests the IRQ. cpts_register() unmasks it again
once the pool exists. The CPTS is left enabled: clearing CPTS_EN at this
point made TS_PEND_RAW read back as set, which would hand the handler a
bogus event.

Masking alone is not enough, as the interrupt may already be latched by
then, and clearing the wrapper's misc_en before requesting the IRQ does
not help either, since cpsw_misc_interrupt() sets it again on the way
out.

Fixes: 84ea9c0a95d7 ("net: ethernet: ti: cpsw: enable cpts irq")
Signed-off-by: Hung-Chun Tseng <alan.tseng.cs@gmail.com>
---
Tested on a BeagleBone Black (AM335x, cpsw_new) on top of net 7076f684d5:

 - Cold boot from U-Boot: probe and link come up and /dev/ptp0 is
   registered, as before.
 - kexec_load from a kernel without this patch into one with it, and from
   a patched kernel into itself: no oops, link comes up, /dev/ptp0 is
   registered and its time advances.

Without the patch the kexec'd kernel oopses as quoted above on every boot.

Not tested: the legacy cpsw driver, and Keystone netcp_ethss, which also
calls cpts_create(). The CPTS_EN behaviour described above was only
observed on AM335x.

How this was made: the investigation, the fix and this description were
done with an AI coding assistant (Claude Code) driving my board. Three
earlier attempts (clearing misc_en at probe, clearing CPTS_EN, draining and
then clearing CPTS_EN) were tried on the hardware and dropped; the register
values quoted above come from a debug print added for that. I have
reviewed the patch and can answer for it.

 drivers/net/ethernet/ti/cpts.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c
index 2ba4c8795d..3cc908ac90 100644
--- a/drivers/net/ethernet/ti/cpts.c
+++ b/drivers/net/ethernet/ti/cpts.c
@@ -747,7 +747,8 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs,
 			 struct device_node *node, u32 n_ext_ts)
 {
 	struct cpts *cpts;
-	int ret;
+	u32 high, low;
+	int ret, i;
 
 	cpts = devm_kzalloc(dev, sizeof(*cpts), GFP_KERNEL);
 	if (!cpts)
@@ -779,6 +780,17 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs,
 	if (ret)
 		return ERR_PTR(ret);
 
+	/* After a kexec the CPTS may still be running from the previous
+	 * kernel, with its interrupt enabled and events in the FIFO. The misc
+	 * IRQ is requested before cpts_register() sets up the event pool, so
+	 * mask the interrupt and drop the stale events now.
+	 */
+	cpts_write32(cpts, 0, int_enable);
+	for (i = 0; i < CPTS_FIFO_DEPTH; i++) {
+		if (cpts_fifo_pop(cpts, &high, &low))
+			break;
+	}
+
 	cpts->cc.read = cpts_systim_read;
 	cpts->cc.mask = CLOCKSOURCE_MASK(32);
 	cpts->info = cpts_info;
-- 
2.43.0


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH net] net: ethernet: ti: cpts: drop stale events before the misc IRQ can fire
  2026-10-26  7:23 [PATCH net] net: ethernet: ti: cpts: drop stale events before the misc IRQ can fire Hung-Chun Tseng
@ 2026-10-26  8:18 ` Siddharth Vadapalli
  0 siblings, 0 replies; 2+ messages in thread
From: Siddharth Vadapalli @ 2026-10-26  8:18 UTC (permalink / raw)
  To: Hung-Chun Tseng
  Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Roger Quadros, Richard Cochran, Grygorii Strashko,
	linux-omap, netdev, linux-kernel, s-vadapalli

On 10/26/26 12:53 PM, Hung-Chun Tseng wrote:
> cpsw requests its misc IRQ at probe, but the CPTS event pool is only set
> up later, in cpts_register() from ndo_open. That is fine from a cold boot,
> where the CPTS comes out of reset stopped with its interrupt masked. After
> a kexec it is not: the previous kernel leaves the CPTS running with
> TS_PEND_EN set and events in its FIFO, so the interrupt fires as soon as
> it is requested and cpts_fifo_read() takes an entry from the still empty
> pool list:
> 
>    Unable to handle kernel NULL pointer dereference at virtual address 00000010 when write
>    PC is at cpts_fifo_read+0xa4/0x3c8
>    Call trace:
>     cpts_fifo_read from cpsw_misc_interrupt+0x30/0x4c
>     cpsw_misc_interrupt from __handle_irq_event_percpu+0x74/0x378
>     ...
>     __setup_irq from request_threaded_irq+0xbc/0x170
>     request_threaded_irq from devm_request_threaded_irq+0x70/0x104
> 
> On a BeagleBone Black the CPTS registers read control=0x1, int_enable=0x1
> and intstat_raw=0x1 in cpts_create() after the kexec, with one event
> queued.
> 
> Mask the CPTS interrupt and drain the FIFO in cpts_create(), before
> either cpsw driver requests the IRQ. cpts_register() unmasks it again
> once the pool exists. The CPTS is left enabled: clearing CPTS_EN at this
> point made TS_PEND_RAW read back as set, which would hand the handler a
> bogus event.
> 
> Masking alone is not enough, as the interrupt may already be latched by
> then, and clearing the wrapper's misc_en before requesting the IRQ does
> not help either, since cpsw_misc_interrupt() sets it again on the way
> out.
> 
> Fixes: 84ea9c0a95d7 ("net: ethernet: ti: cpsw: enable cpts irq")
> Signed-off-by: Hung-Chun Tseng <alan.tseng.cs@gmail.com>
> ---
> Tested on a BeagleBone Black (AM335x, cpsw_new) on top of net 7076f684d5:
> 
>   - Cold boot from U-Boot: probe and link come up and /dev/ptp0 is
>     registered, as before.
>   - kexec_load from a kernel without this patch into one with it, and from
>     a patched kernel into itself: no oops, link comes up, /dev/ptp0 is
>     registered and its time advances.
> 
> Without the patch the kexec'd kernel oopses as quoted above on every boot.
> 
> Not tested: the legacy cpsw driver, and Keystone netcp_ethss, which also
> calls cpts_create(). The CPTS_EN behaviour described above was only
> observed on AM335x.
> 
> How this was made: the investigation, the fix and this description were
> done with an AI coding assistant (Claude Code) driving my board. Three
> earlier attempts (clearing misc_en at probe, clearing CPTS_EN, draining and
> then clearing CPTS_EN) were tried on the hardware and dropped; the register
> values quoted above come from a debug print added for that. I have
> reviewed the patch and can answer for it.
> 
>   drivers/net/ethernet/ti/cpts.c | 14 +++++++++++++-
>   1 file changed, 13 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c
> index 2ba4c8795d..3cc908ac90 100644
> --- a/drivers/net/ethernet/ti/cpts.c
> +++ b/drivers/net/ethernet/ti/cpts.c
> @@ -747,7 +747,8 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs,
>   			 struct device_node *node, u32 n_ext_ts)
>   {
>   	struct cpts *cpts;
> -	int ret;
> +	u32 high, low;
> +	int ret, i;
>   
>   	cpts = devm_kzalloc(dev, sizeof(*cpts), GFP_KERNEL);
>   	if (!cpts)
> @@ -779,6 +780,17 @@ struct cpts *cpts_create(struct device *dev, void __iomem *regs,
>   	if (ret)
>   		return ERR_PTR(ret);
>   
> +	/* After a kexec the CPTS may still be running from the previous
> +	 * kernel, with its interrupt enabled and events in the FIFO. The misc
> +	 * IRQ is requested before cpts_register() sets up the event pool, so
> +	 * mask the interrupt and drop the stale events now.
> +	 */
> +	cpts_write32(cpts, 0, int_enable);
> +	for (i = 0; i < CPTS_FIFO_DEPTH; i++) {
> +		if (cpts_fifo_pop(cpts, &high, &low))
> +			break;
> +	}

Thank you for posting this patch to fix the issue. While it will solve this 
particular issue, I believe that the underlying problem is that CPTS 
remains powered-on during the kexec sequence. Therefore the proper fix will 
be to power-off and power-on the CPTS module (to perform a complete reset) 
rather than fixing this particular symptom (please note that the remaining 
registers of CPTS retain their state as well, and a subset of the 
underlying cause is addressed by this patch).

Regards,
Siddharth.

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-10-26  8:19 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-26  7:23 [PATCH net] net: ethernet: ti: cpts: drop stale events before the misc IRQ can fire Hung-Chun Tseng
2026-10-26  8:18 ` Siddharth Vadapalli

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®