* [PATCH v2 1/2] can: m_can: set init flag earlier in probe
@ 2024-09-19 11:27 Matthias Schiffer
2024-09-19 11:27 ` [PATCH v2 2/2] can: m_can: fix missed interrupts with m_can_pci Matthias Schiffer
2024-09-23 8:12 ` [PATCH v2 1/2] can: m_can: set init flag earlier in probe Markus Schneider-Pargmann
0 siblings, 2 replies; 11+ messages in thread
From: Matthias Schiffer @ 2024-09-19 11:27 UTC (permalink / raw)
To: Chandrasekar Ramakrishnan, Marc Kleine-Budde, Vincent Mailhol
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Martin Hundebøll, Markus Schneider-Pargmann,
Felipe Balbi (Intel),
Raymond Tan, Jarkko Nikula, linux-can, netdev, linux-kernel,
linux, Matthias Schiffer
While an m_can controller usually already has the init flag from a
hardware reset, no such reset happens on the integrated m_can_pci of the
Intel Elkhart Lake. If the CAN controller is found in an active state,
m_can_dev_setup() would fail because m_can_niso_supported() calls
m_can_cccr_update_bits(), which refuses to modify any other configuration
bits when CCCR_INIT is not set.
To avoid this issue, set CCCR_INIT before attempting to modify any other
configuration flags.
Fixes: cd5a46ce6fa6 ("can: m_can: don't enable transceiver when probing")
Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
---
v2: no changes
drivers/net/can/m_can/m_can.c | 14 +++++++++-----
1 file changed, 9 insertions(+), 5 deletions(-)
diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
index 012c3d22b01dd..47481afb9add3 100644
--- a/drivers/net/can/m_can/m_can.c
+++ b/drivers/net/can/m_can/m_can.c
@@ -1681,6 +1681,14 @@ static int m_can_dev_setup(struct m_can_classdev *cdev)
return -EINVAL;
}
+ /* Forcing standby mode should be redundant, as the chip should be in
+ * standby after a reset. Write the INIT bit anyways, should the chip
+ * be configured by previous stage.
+ */
+ err = m_can_cccr_update_bits(cdev, CCCR_INIT, CCCR_INIT);
+ if (err)
+ return err;
+
if (!cdev->is_peripheral)
netif_napi_add(dev, &cdev->napi, m_can_poll);
@@ -1732,11 +1740,7 @@ static int m_can_dev_setup(struct m_can_classdev *cdev)
return -EINVAL;
}
- /* Forcing standby mode should be redundant, as the chip should be in
- * standby after a reset. Write the INIT bit anyways, should the chip
- * be configured by previous stage.
- */
- return m_can_cccr_update_bits(cdev, CCCR_INIT, CCCR_INIT);
+ return 0;
}
static void m_can_stop(struct net_device *dev)
--
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
https://www.tq-group.com/
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 2/2] can: m_can: fix missed interrupts with m_can_pci
2024-09-19 11:27 [PATCH v2 1/2] can: m_can: set init flag earlier in probe Matthias Schiffer
@ 2024-09-19 11:27 ` Matthias Schiffer
2024-09-23 8:03 ` Markus Schneider-Pargmann
2024-09-23 10:17 ` [PATCH v2 2/2] can: m_can: fix missed interrupts with m_can_pci Marc Kleine-Budde
2024-09-23 8:12 ` [PATCH v2 1/2] can: m_can: set init flag earlier in probe Markus Schneider-Pargmann
1 sibling, 2 replies; 11+ messages in thread
From: Matthias Schiffer @ 2024-09-19 11:27 UTC (permalink / raw)
To: Chandrasekar Ramakrishnan, Marc Kleine-Budde, Vincent Mailhol
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Martin Hundebøll, Markus Schneider-Pargmann,
Felipe Balbi (Intel),
Raymond Tan, Jarkko Nikula, linux-can, netdev, linux-kernel,
linux, Matthias Schiffer
The interrupt line of PCI devices is interpreted as edge-triggered,
however the interrupt signal of the m_can controller integrated in Intel
Elkhart Lake CPUs appears to be generated level-triggered.
Consider the following sequence of events:
- IR register is read, interrupt X is set
- A new interrupt Y is triggered in the m_can controller
- IR register is written to acknowledge interrupt X. Y remains set in IR
As at no point in this sequence no interrupt flag is set in IR, the
m_can interrupt line will never become deasserted, and no edge will ever
be observed to trigger another run of the ISR. This was observed to
result in the TX queue of the EHL m_can to get stuck under high load,
because frames were queued to the hardware in m_can_start_xmit(), but
m_can_finish_tx() was never run to account for their successful
transmission.
To fix the issue, repeatedly read and acknowledge interrupts at the
start of the ISR until no interrupt flags are set, so the next incoming
interrupt will also result in an edge on the interrupt line.
Fixes: cab7ffc0324f ("can: m_can: add PCI glue driver for Intel Elkhart Lake")
Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
---
v2: introduce flag is_edge_triggered, so we can avoid the loop on !m_can_pci
drivers/net/can/m_can/m_can.c | 21 ++++++++++++++++-----
drivers/net/can/m_can/m_can.h | 1 +
drivers/net/can/m_can/m_can_pci.c | 1 +
3 files changed, 18 insertions(+), 5 deletions(-)
diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
index 47481afb9add3..2e182c3c98fed 100644
--- a/drivers/net/can/m_can/m_can.c
+++ b/drivers/net/can/m_can/m_can.c
@@ -1207,20 +1207,31 @@ static void m_can_coalescing_update(struct m_can_classdev *cdev, u32 ir)
static int m_can_interrupt_handler(struct m_can_classdev *cdev)
{
struct net_device *dev = cdev->net;
- u32 ir;
+ u32 ir = 0, ir_read;
int ret;
if (pm_runtime_suspended(cdev->dev))
return IRQ_NONE;
- ir = m_can_read(cdev, M_CAN_IR);
+ /* For m_can_pci, the interrupt line is interpreted as edge-triggered,
+ * but the m_can controller generates them as level-triggered. We must
+ * observe that IR is 0 at least once to be sure that the next
+ * interrupt will generate an edge.
+ */
+ while ((ir_read = m_can_read(cdev, M_CAN_IR)) != 0) {
+ ir |= ir_read;
+
+ /* ACK all irqs */
+ m_can_write(cdev, M_CAN_IR, ir);
+
+ if (!cdev->is_edge_triggered)
+ break;
+ }
+
m_can_coalescing_update(cdev, ir);
if (!ir)
return IRQ_NONE;
- /* ACK all irqs */
- m_can_write(cdev, M_CAN_IR, ir);
-
if (cdev->ops->clear_interrupts)
cdev->ops->clear_interrupts(cdev);
diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
index 92b2bd8628e6b..8c17eb94d2f98 100644
--- a/drivers/net/can/m_can/m_can.h
+++ b/drivers/net/can/m_can/m_can.h
@@ -99,6 +99,7 @@ struct m_can_classdev {
int pm_clock_support;
int pm_wake_source;
int is_peripheral;
+ bool is_edge_triggered;
// Cached M_CAN_IE register content
u32 active_interrupts;
diff --git a/drivers/net/can/m_can/m_can_pci.c b/drivers/net/can/m_can/m_can_pci.c
index d72fe771dfc7a..f98527981402a 100644
--- a/drivers/net/can/m_can/m_can_pci.c
+++ b/drivers/net/can/m_can/m_can_pci.c
@@ -127,6 +127,7 @@ static int m_can_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
mcan_class->pm_clock_support = 1;
mcan_class->pm_wake_source = 0;
mcan_class->can.clock.freq = id->driver_data;
+ mcan_class->is_edge_triggered = true;
mcan_class->ops = &m_can_pci_ops;
pci_set_drvdata(pci, mcan_class);
--
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
https://www.tq-group.com/
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 2/2] can: m_can: fix missed interrupts with m_can_pci
2024-09-19 11:27 ` [PATCH v2 2/2] can: m_can: fix missed interrupts with m_can_pci Matthias Schiffer
@ 2024-09-23 8:03 ` Markus Schneider-Pargmann
2024-09-23 9:32 ` Matthias Schiffer
2024-09-23 10:30 ` Marc Kleine-Budde
2024-09-23 10:17 ` [PATCH v2 2/2] can: m_can: fix missed interrupts with m_can_pci Marc Kleine-Budde
1 sibling, 2 replies; 11+ messages in thread
From: Markus Schneider-Pargmann @ 2024-09-23 8:03 UTC (permalink / raw)
To: Matthias Schiffer
Cc: Chandrasekar Ramakrishnan, Marc Kleine-Budde, Vincent Mailhol,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Martin Hundebøll, Felipe Balbi (Intel),
Raymond Tan, Jarkko Nikula, linux-can, netdev, linux-kernel,
linux
Hi Matthias,
On Thu, Sep 19, 2024 at 01:27:28PM GMT, Matthias Schiffer wrote:
> The interrupt line of PCI devices is interpreted as edge-triggered,
> however the interrupt signal of the m_can controller integrated in Intel
I have a similar patch though for a different setup (I didn't send it
yet). I have a tcan chip wired to a pin that is only capable of edge
interrupts.
> Elkhart Lake CPUs appears to be generated level-triggered.
>
> Consider the following sequence of events:
>
> - IR register is read, interrupt X is set
> - A new interrupt Y is triggered in the m_can controller
> - IR register is written to acknowledge interrupt X. Y remains set in IR
>
> As at no point in this sequence no interrupt flag is set in IR, the
> m_can interrupt line will never become deasserted, and no edge will ever
> be observed to trigger another run of the ISR. This was observed to
> result in the TX queue of the EHL m_can to get stuck under high load,
> because frames were queued to the hardware in m_can_start_xmit(), but
> m_can_finish_tx() was never run to account for their successful
> transmission.
>
> To fix the issue, repeatedly read and acknowledge interrupts at the
> start of the ISR until no interrupt flags are set, so the next incoming
> interrupt will also result in an edge on the interrupt line.
>
> Fixes: cab7ffc0324f ("can: m_can: add PCI glue driver for Intel Elkhart Lake")
> Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
> ---
>
> v2: introduce flag is_edge_triggered, so we can avoid the loop on !m_can_pci
>
> drivers/net/can/m_can/m_can.c | 21 ++++++++++++++++-----
> drivers/net/can/m_can/m_can.h | 1 +
> drivers/net/can/m_can/m_can_pci.c | 1 +
> 3 files changed, 18 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
> index 47481afb9add3..2e182c3c98fed 100644
> --- a/drivers/net/can/m_can/m_can.c
> +++ b/drivers/net/can/m_can/m_can.c
> @@ -1207,20 +1207,31 @@ static void m_can_coalescing_update(struct m_can_classdev *cdev, u32 ir)
> static int m_can_interrupt_handler(struct m_can_classdev *cdev)
> {
> struct net_device *dev = cdev->net;
> - u32 ir;
> + u32 ir = 0, ir_read;
> int ret;
>
> if (pm_runtime_suspended(cdev->dev))
> return IRQ_NONE;
>
> - ir = m_can_read(cdev, M_CAN_IR);
> + /* For m_can_pci, the interrupt line is interpreted as edge-triggered,
> + * but the m_can controller generates them as level-triggered. We must
> + * observe that IR is 0 at least once to be sure that the next
> + * interrupt will generate an edge.
> + */
Could you please remove this hardware specific comment? As mentioned
above this will be independent of any specific hardware.
> + while ((ir_read = m_can_read(cdev, M_CAN_IR)) != 0) {
> + ir |= ir_read;
> +
> + /* ACK all irqs */
> + m_can_write(cdev, M_CAN_IR, ir);
> +
> + if (!cdev->is_edge_triggered)
> + break;
> + }
> +
> m_can_coalescing_update(cdev, ir);
> if (!ir)
> return IRQ_NONE;
>
> - /* ACK all irqs */
> - m_can_write(cdev, M_CAN_IR, ir);
> -
> if (cdev->ops->clear_interrupts)
> cdev->ops->clear_interrupts(cdev);
>
> diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
> index 92b2bd8628e6b..8c17eb94d2f98 100644
> --- a/drivers/net/can/m_can/m_can.h
> +++ b/drivers/net/can/m_can/m_can.h
> @@ -99,6 +99,7 @@ struct m_can_classdev {
> int pm_clock_support;
> int pm_wake_source;
> int is_peripheral;
> + bool is_edge_triggered;
To avoid confusion could you rename it to irq_edge_triggered or
something similar, to make clear that it is not about the chip but the
way the interrupt line is connected?
Also I am not sure it is possible, but could you use
irq_get_trigger_type() to see if it is a level or edge based interrupt?
Then we wouldn't need this additional parameter at all and could just
detect it in m_can.c.
Best
Markus
>
> // Cached M_CAN_IE register content
> u32 active_interrupts;
> diff --git a/drivers/net/can/m_can/m_can_pci.c b/drivers/net/can/m_can/m_can_pci.c
> index d72fe771dfc7a..f98527981402a 100644
> --- a/drivers/net/can/m_can/m_can_pci.c
> +++ b/drivers/net/can/m_can/m_can_pci.c
> @@ -127,6 +127,7 @@ static int m_can_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
> mcan_class->pm_clock_support = 1;
> mcan_class->pm_wake_source = 0;
> mcan_class->can.clock.freq = id->driver_data;
> + mcan_class->is_edge_triggered = true;
> mcan_class->ops = &m_can_pci_ops;
>
> pci_set_drvdata(pci, mcan_class);
> --
> TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
> Amtsgericht München, HRB 105018
> Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
> https://www.tq-group.com/
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 1/2] can: m_can: set init flag earlier in probe
2024-09-19 11:27 [PATCH v2 1/2] can: m_can: set init flag earlier in probe Matthias Schiffer
2024-09-19 11:27 ` [PATCH v2 2/2] can: m_can: fix missed interrupts with m_can_pci Matthias Schiffer
@ 2024-09-23 8:12 ` Markus Schneider-Pargmann
1 sibling, 0 replies; 11+ messages in thread
From: Markus Schneider-Pargmann @ 2024-09-23 8:12 UTC (permalink / raw)
To: Matthias Schiffer
Cc: Chandrasekar Ramakrishnan, Marc Kleine-Budde, Vincent Mailhol,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Martin Hundebøll, Felipe Balbi (Intel),
Raymond Tan, Jarkko Nikula, linux-can, netdev, linux-kernel,
linux
On Thu, Sep 19, 2024 at 01:27:27PM GMT, Matthias Schiffer wrote:
> While an m_can controller usually already has the init flag from a
> hardware reset, no such reset happens on the integrated m_can_pci of the
> Intel Elkhart Lake. If the CAN controller is found in an active state,
> m_can_dev_setup() would fail because m_can_niso_supported() calls
> m_can_cccr_update_bits(), which refuses to modify any other configuration
> bits when CCCR_INIT is not set.
>
> To avoid this issue, set CCCR_INIT before attempting to modify any other
> configuration flags.
>
> Fixes: cd5a46ce6fa6 ("can: m_can: don't enable transceiver when probing")
> Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
> ---
>
> v2: no changes
>
> drivers/net/can/m_can/m_can.c | 14 +++++++++-----
> 1 file changed, 9 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
> index 012c3d22b01dd..47481afb9add3 100644
> --- a/drivers/net/can/m_can/m_can.c
> +++ b/drivers/net/can/m_can/m_can.c
> @@ -1681,6 +1681,14 @@ static int m_can_dev_setup(struct m_can_classdev *cdev)
> return -EINVAL;
> }
>
> + /* Forcing standby mode should be redundant, as the chip should be in
> + * standby after a reset. Write the INIT bit anyways, should the chip
> + * be configured by previous stage.
> + */
Could you please update the comment to reflect your findings?
Best
Markus
> + err = m_can_cccr_update_bits(cdev, CCCR_INIT, CCCR_INIT);
> + if (err)
> + return err;
> +
> if (!cdev->is_peripheral)
> netif_napi_add(dev, &cdev->napi, m_can_poll);
>
> @@ -1732,11 +1740,7 @@ static int m_can_dev_setup(struct m_can_classdev *cdev)
> return -EINVAL;
> }
>
> - /* Forcing standby mode should be redundant, as the chip should be in
> - * standby after a reset. Write the INIT bit anyways, should the chip
> - * be configured by previous stage.
> - */
> - return m_can_cccr_update_bits(cdev, CCCR_INIT, CCCR_INIT);
> + return 0;
> }
>
> static void m_can_stop(struct net_device *dev)
> --
> TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
> Amtsgericht München, HRB 105018
> Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
> https://www.tq-group.com/
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 2/2] can: m_can: fix missed interrupts with m_can_pci
2024-09-23 8:03 ` Markus Schneider-Pargmann
@ 2024-09-23 9:32 ` Matthias Schiffer
2024-09-23 10:07 ` Markus Schneider-Pargmann
2024-09-23 10:30 ` Marc Kleine-Budde
1 sibling, 1 reply; 11+ messages in thread
From: Matthias Schiffer @ 2024-09-23 9:32 UTC (permalink / raw)
To: Markus Schneider-Pargmann
Cc: Chandrasekar Ramakrishnan, Marc Kleine-Budde, Vincent Mailhol,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Martin Hundebøll, Felipe Balbi (Intel),
Raymond Tan, Jarkko Nikula, linux-can, netdev, linux-kernel,
linux
On Mon, 2024-09-23 at 10:03 +0200, Markus Schneider-Pargmann wrote:
> Hi Matthias,
>
> On Thu, Sep 19, 2024 at 01:27:28PM GMT, Matthias Schiffer wrote:
> > The interrupt line of PCI devices is interpreted as edge-triggered,
> > however the interrupt signal of the m_can controller integrated in Intel
>
> I have a similar patch though for a different setup (I didn't send it
> yet). I have a tcan chip wired to a pin that is only capable of edge
> interrupts.
Should I also change the Fixes tag to something else then?
>
> > Elkhart Lake CPUs appears to be generated level-triggered.
> >
> > Consider the following sequence of events:
> >
> > - IR register is read, interrupt X is set
> > - A new interrupt Y is triggered in the m_can controller
> > - IR register is written to acknowledge interrupt X. Y remains set in IR
> >
> > As at no point in this sequence no interrupt flag is set in IR, the
> > m_can interrupt line will never become deasserted, and no edge will ever
> > be observed to trigger another run of the ISR. This was observed to
> > result in the TX queue of the EHL m_can to get stuck under high load,
> > because frames were queued to the hardware in m_can_start_xmit(), but
> > m_can_finish_tx() was never run to account for their successful
> > transmission.
> >
> > To fix the issue, repeatedly read and acknowledge interrupts at the
> > start of the ISR until no interrupt flags are set, so the next incoming
> > interrupt will also result in an edge on the interrupt line.
> >
> > Fixes: cab7ffc0324f ("can: m_can: add PCI glue driver for Intel Elkhart Lake")
> > Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
> > ---
> >
> > v2: introduce flag is_edge_triggered, so we can avoid the loop on !m_can_pci
> >
> > drivers/net/can/m_can/m_can.c | 21 ++++++++++++++++-----
> > drivers/net/can/m_can/m_can.h | 1 +
> > drivers/net/can/m_can/m_can_pci.c | 1 +
> > 3 files changed, 18 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
> > index 47481afb9add3..2e182c3c98fed 100644
> > --- a/drivers/net/can/m_can/m_can.c
> > +++ b/drivers/net/can/m_can/m_can.c
> > @@ -1207,20 +1207,31 @@ static void m_can_coalescing_update(struct m_can_classdev *cdev, u32 ir)
> > static int m_can_interrupt_handler(struct m_can_classdev *cdev)
> > {
> > struct net_device *dev = cdev->net;
> > - u32 ir;
> > + u32 ir = 0, ir_read;
> > int ret;
> >
> > if (pm_runtime_suspended(cdev->dev))
> > return IRQ_NONE;
> >
> > - ir = m_can_read(cdev, M_CAN_IR);
> > + /* For m_can_pci, the interrupt line is interpreted as edge-triggered,
> > + * but the m_can controller generates them as level-triggered. We must
> > + * observe that IR is 0 at least once to be sure that the next
> > + * interrupt will generate an edge.
> > + */
>
> Could you please remove this hardware specific comment? As mentioned
> above this will be independent of any specific hardware.
Ok.
>
> > + while ((ir_read = m_can_read(cdev, M_CAN_IR)) != 0) {
> > + ir |= ir_read;
> > +
> > + /* ACK all irqs */
> > + m_can_write(cdev, M_CAN_IR, ir);
> > +
> > + if (!cdev->is_edge_triggered)
> > + break;
> > + }
> > +
> > m_can_coalescing_update(cdev, ir);
> > if (!ir)
> > return IRQ_NONE;
> >
> > - /* ACK all irqs */
> > - m_can_write(cdev, M_CAN_IR, ir);
> > -
> > if (cdev->ops->clear_interrupts)
> > cdev->ops->clear_interrupts(cdev);
> >
> > diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
> > index 92b2bd8628e6b..8c17eb94d2f98 100644
> > --- a/drivers/net/can/m_can/m_can.h
> > +++ b/drivers/net/can/m_can/m_can.h
> > @@ -99,6 +99,7 @@ struct m_can_classdev {
> > int pm_clock_support;
> > int pm_wake_source;
> > int is_peripheral;
> > + bool is_edge_triggered;
>
> To avoid confusion could you rename it to irq_edge_triggered or
> something similar, to make clear that it is not about the chip but the
> way the interrupt line is connected?
Will do.
>
> Also I am not sure it is possible, but could you use
> irq_get_trigger_type() to see if it is a level or edge based interrupt?
> Then we wouldn't need this additional parameter at all and could just
> detect it in m_can.c.
Unfortunately that doesn't seem to work. irq_get_trigger_type() only returns a meaningful value
after the IRQ has been requested. I thought about requesting the IRQ with IRQF_NO_AUTOEN and then
filling in the irq_edge_triggered field before enabling the IRQ, but IRQF_NO_AUTOEN is incompatible
with IRQF_SHARED.
Of course there are ways around this - checking irq_get_trigger_type() from the ISR itself, or
adding more locking, but neither seems quite worthwhile to me. Would you agree with this?
Maybe there is some other way to find out the trigger type that would be set when the IRQ is
requested? I don't know what that would be however - so I'd just keep setting the flag statically
for m_can_pci and leave a dynamic solution for future improvement.
Matthias
>
> Best
> Markus
>
> >
> > // Cached M_CAN_IE register content
> > u32 active_interrupts;
> > diff --git a/drivers/net/can/m_can/m_can_pci.c b/drivers/net/can/m_can/m_can_pci.c
> > index d72fe771dfc7a..f98527981402a 100644
> > --- a/drivers/net/can/m_can/m_can_pci.c
> > +++ b/drivers/net/can/m_can/m_can_pci.c
> > @@ -127,6 +127,7 @@ static int m_can_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
> > mcan_class->pm_clock_support = 1;
> > mcan_class->pm_wake_source = 0;
> > mcan_class->can.clock.freq = id->driver_data;
> > + mcan_class->is_edge_triggered = true;
> > mcan_class->ops = &m_can_pci_ops;
> >
> > pci_set_drvdata(pci, mcan_class);
> > --
> > TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
> > Amtsgericht München, HRB 105018
> > Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
> > https://www.tq-group.com/
> >
--
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
https://www.tq-group.com/
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 2/2] can: m_can: fix missed interrupts with m_can_pci
2024-09-23 9:32 ` Matthias Schiffer
@ 2024-09-23 10:07 ` Markus Schneider-Pargmann
2024-09-23 10:53 ` Marc Kleine-Budde
0 siblings, 1 reply; 11+ messages in thread
From: Markus Schneider-Pargmann @ 2024-09-23 10:07 UTC (permalink / raw)
To: Matthias Schiffer
Cc: Chandrasekar Ramakrishnan, Marc Kleine-Budde, Vincent Mailhol,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Martin Hundebøll, Felipe Balbi (Intel),
Raymond Tan, Jarkko Nikula, linux-can, netdev, linux-kernel,
linux
On Mon, Sep 23, 2024 at 11:32:49AM GMT, Matthias Schiffer wrote:
> On Mon, 2024-09-23 at 10:03 +0200, Markus Schneider-Pargmann wrote:
> > Hi Matthias,
> >
> > On Thu, Sep 19, 2024 at 01:27:28PM GMT, Matthias Schiffer wrote:
> > > The interrupt line of PCI devices is interpreted as edge-triggered,
> > > however the interrupt signal of the m_can controller integrated in Intel
> >
> > I have a similar patch though for a different setup (I didn't send it
> > yet). I have a tcan chip wired to a pin that is only capable of edge
> > interrupts.
>
> Should I also change the Fixes tag to something else then?
No, my use-case wasn't explicitly supported before, so I don't consider
it to be a fix but a new feature supporting edge triggered tcan.
>
> >
> > > Elkhart Lake CPUs appears to be generated level-triggered.
> > >
> > > Consider the following sequence of events:
> > >
> > > - IR register is read, interrupt X is set
> > > - A new interrupt Y is triggered in the m_can controller
> > > - IR register is written to acknowledge interrupt X. Y remains set in IR
> > >
> > > As at no point in this sequence no interrupt flag is set in IR, the
> > > m_can interrupt line will never become deasserted, and no edge will ever
> > > be observed to trigger another run of the ISR. This was observed to
> > > result in the TX queue of the EHL m_can to get stuck under high load,
> > > because frames were queued to the hardware in m_can_start_xmit(), but
> > > m_can_finish_tx() was never run to account for their successful
> > > transmission.
> > >
> > > To fix the issue, repeatedly read and acknowledge interrupts at the
> > > start of the ISR until no interrupt flags are set, so the next incoming
> > > interrupt will also result in an edge on the interrupt line.
> > >
> > > Fixes: cab7ffc0324f ("can: m_can: add PCI glue driver for Intel Elkhart Lake")
> > > Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
> > > ---
> > >
> > > v2: introduce flag is_edge_triggered, so we can avoid the loop on !m_can_pci
> > >
> > > drivers/net/can/m_can/m_can.c | 21 ++++++++++++++++-----
> > > drivers/net/can/m_can/m_can.h | 1 +
> > > drivers/net/can/m_can/m_can_pci.c | 1 +
> > > 3 files changed, 18 insertions(+), 5 deletions(-)
> > >
> > > diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
> > > index 47481afb9add3..2e182c3c98fed 100644
> > > --- a/drivers/net/can/m_can/m_can.c
> > > +++ b/drivers/net/can/m_can/m_can.c
> > > @@ -1207,20 +1207,31 @@ static void m_can_coalescing_update(struct m_can_classdev *cdev, u32 ir)
> > > static int m_can_interrupt_handler(struct m_can_classdev *cdev)
> > > {
> > > struct net_device *dev = cdev->net;
> > > - u32 ir;
> > > + u32 ir = 0, ir_read;
> > > int ret;
> > >
> > > if (pm_runtime_suspended(cdev->dev))
> > > return IRQ_NONE;
> > >
> > > - ir = m_can_read(cdev, M_CAN_IR);
> > > + /* For m_can_pci, the interrupt line is interpreted as edge-triggered,
> > > + * but the m_can controller generates them as level-triggered. We must
> > > + * observe that IR is 0 at least once to be sure that the next
> > > + * interrupt will generate an edge.
> > > + */
> >
> > Could you please remove this hardware specific comment? As mentioned
> > above this will be independent of any specific hardware.
>
> Ok.
>
>
> >
> > > + while ((ir_read = m_can_read(cdev, M_CAN_IR)) != 0) {
> > > + ir |= ir_read;
> > > +
> > > + /* ACK all irqs */
> > > + m_can_write(cdev, M_CAN_IR, ir);
> > > +
> > > + if (!cdev->is_edge_triggered)
> > > + break;
> > > + }
> > > +
> > > m_can_coalescing_update(cdev, ir);
> > > if (!ir)
> > > return IRQ_NONE;
> > >
> > > - /* ACK all irqs */
> > > - m_can_write(cdev, M_CAN_IR, ir);
> > > -
> > > if (cdev->ops->clear_interrupts)
> > > cdev->ops->clear_interrupts(cdev);
> > >
> > > diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
> > > index 92b2bd8628e6b..8c17eb94d2f98 100644
> > > --- a/drivers/net/can/m_can/m_can.h
> > > +++ b/drivers/net/can/m_can/m_can.h
> > > @@ -99,6 +99,7 @@ struct m_can_classdev {
> > > int pm_clock_support;
> > > int pm_wake_source;
> > > int is_peripheral;
> > > + bool is_edge_triggered;
> >
> > To avoid confusion could you rename it to irq_edge_triggered or
> > something similar, to make clear that it is not about the chip but the
> > way the interrupt line is connected?
>
> Will do.
>
> >
> > Also I am not sure it is possible, but could you use
> > irq_get_trigger_type() to see if it is a level or edge based interrupt?
> > Then we wouldn't need this additional parameter at all and could just
> > detect it in m_can.c.
>
> Unfortunately that doesn't seem to work. irq_get_trigger_type() only returns a meaningful value
> after the IRQ has been requested. I thought about requesting the IRQ with IRQF_NO_AUTOEN and then
> filling in the irq_edge_triggered field before enabling the IRQ, but IRQF_NO_AUTOEN is incompatible
> with IRQF_SHARED.
The mentioned function works for me on ARM and DT even before
irq_request_threaded_irq() was called.
Also I am probably missing something here. Afer requesting the irq, the
interrupts are not enabled yet right? So can't you just request it and
check the triggertype immediately afterwards?
>
> Of course there are ways around this - checking irq_get_trigger_type() from the ISR itself, or
> adding more locking, but neither seems quite worthwhile to me. Would you agree with this?
>
> Maybe there is some other way to find out the trigger type that would be set when the IRQ is
> requested? I don't know what that would be however - so I'd just keep setting the flag statically
> for m_can_pci and leave a dynamic solution for future improvement.
No if it doesn't work easily the parameter is probably the best option.
Best
Markus
>
> Matthias
>
>
>
> >
> > Best
> > Markus
> >
> > >
> > > // Cached M_CAN_IE register content
> > > u32 active_interrupts;
> > > diff --git a/drivers/net/can/m_can/m_can_pci.c b/drivers/net/can/m_can/m_can_pci.c
> > > index d72fe771dfc7a..f98527981402a 100644
> > > --- a/drivers/net/can/m_can/m_can_pci.c
> > > +++ b/drivers/net/can/m_can/m_can_pci.c
> > > @@ -127,6 +127,7 @@ static int m_can_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
> > > mcan_class->pm_clock_support = 1;
> > > mcan_class->pm_wake_source = 0;
> > > mcan_class->can.clock.freq = id->driver_data;
> > > + mcan_class->is_edge_triggered = true;
> > > mcan_class->ops = &m_can_pci_ops;
> > >
> > > pci_set_drvdata(pci, mcan_class);
> > > --
> > > TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
> > > Amtsgericht München, HRB 105018
> > > Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
> > > https://www.tq-group.com/
> > >
>
> --
> TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
> Amtsgericht München, HRB 105018
> Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
> https://www.tq-group.com/
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 2/2] can: m_can: fix missed interrupts with m_can_pci
2024-09-19 11:27 ` [PATCH v2 2/2] can: m_can: fix missed interrupts with m_can_pci Matthias Schiffer
2024-09-23 8:03 ` Markus Schneider-Pargmann
@ 2024-09-23 10:17 ` Marc Kleine-Budde
2024-09-23 11:06 ` Matthias Schiffer
1 sibling, 1 reply; 11+ messages in thread
From: Marc Kleine-Budde @ 2024-09-23 10:17 UTC (permalink / raw)
To: Matthias Schiffer
Cc: Chandrasekar Ramakrishnan, Vincent Mailhol, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Martin Hundebøll,
Markus Schneider-Pargmann, Felipe Balbi (Intel),
Raymond Tan, Jarkko Nikula, linux-can, netdev, linux-kernel,
linux, lst
[-- Attachment #1: Type: text/plain, Size: 2051 bytes --]
On 19.09.2024 13:27:28, Matthias Schiffer wrote:
> The interrupt line of PCI devices is interpreted as edge-triggered,
> however the interrupt signal of the m_can controller integrated in Intel
> Elkhart Lake CPUs appears to be generated level-triggered.
>
> Consider the following sequence of events:
>
> - IR register is read, interrupt X is set
> - A new interrupt Y is triggered in the m_can controller
> - IR register is written to acknowledge interrupt X. Y remains set in IR
>
> As at no point in this sequence no interrupt flag is set in IR, the
> m_can interrupt line will never become deasserted, and no edge will ever
> be observed to trigger another run of the ISR. This was observed to
> result in the TX queue of the EHL m_can to get stuck under high load,
> because frames were queued to the hardware in m_can_start_xmit(), but
> m_can_finish_tx() was never run to account for their successful
> transmission.
>
> To fix the issue, repeatedly read and acknowledge interrupts at the
> start of the ISR until no interrupt flags are set, so the next incoming
> interrupt will also result in an edge on the interrupt line.
>
> Fixes: cab7ffc0324f ("can: m_can: add PCI glue driver for Intel Elkhart Lake")
> Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
My coworker Lucas pointed me to:
| https://wiki.linuxfoundation.org/networking/napi#non-level_sensitive_irqs
On the other hand, I would also like to convert the !peripteral part of
the driver to rx-offload. However, I am still looking for potential
customers for this task. I have talked to some TI and ST people at LPC,
maybe they are interested.
I think let's first fix edge sensitive IRQs, then rework the driver to
rx-offload.
regards,
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Embedded Linux | https://www.pengutronix.de |
Vertretung Nürnberg | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 2/2] can: m_can: fix missed interrupts with m_can_pci
2024-09-23 8:03 ` Markus Schneider-Pargmann
2024-09-23 9:32 ` Matthias Schiffer
@ 2024-09-23 10:30 ` Marc Kleine-Budde
2024-09-24 6:16 ` [PATCH DO_NOT_APPLY] net: can: m_can: Support tcan level with edge interrupts Markus Schneider-Pargmann
1 sibling, 1 reply; 11+ messages in thread
From: Marc Kleine-Budde @ 2024-09-23 10:30 UTC (permalink / raw)
To: Markus Schneider-Pargmann
Cc: Matthias Schiffer, Chandrasekar Ramakrishnan, Vincent Mailhol,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Martin Hundebøll, Felipe Balbi (Intel),
Raymond Tan, Jarkko Nikula, linux-can, netdev, linux-kernel,
linux
[-- Attachment #1: Type: text/plain, Size: 792 bytes --]
On 23.09.2024 10:03:24, Markus Schneider-Pargmann wrote:
> Hi Matthias,
>
> On Thu, Sep 19, 2024 at 01:27:28PM GMT, Matthias Schiffer wrote:
> > The interrupt line of PCI devices is interpreted as edge-triggered,
> > however the interrupt signal of the m_can controller integrated in Intel
>
> I have a similar patch though for a different setup (I didn't send it
> yet). I have a tcan chip wired to a pin that is only capable of edge
> interrupts.
Can you post your patch for completeness?
regards,
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Embedded Linux | https://www.pengutronix.de |
Vertretung Nürnberg | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 2/2] can: m_can: fix missed interrupts with m_can_pci
2024-09-23 10:07 ` Markus Schneider-Pargmann
@ 2024-09-23 10:53 ` Marc Kleine-Budde
0 siblings, 0 replies; 11+ messages in thread
From: Marc Kleine-Budde @ 2024-09-23 10:53 UTC (permalink / raw)
To: Markus Schneider-Pargmann
Cc: Matthias Schiffer, Chandrasekar Ramakrishnan, Vincent Mailhol,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Martin Hundebøll, Felipe Balbi (Intel),
Raymond Tan, Jarkko Nikula, linux-can, netdev, linux-kernel,
linux
[-- Attachment #1: Type: text/plain, Size: 2838 bytes --]
On 23.09.2024 12:07:33, Markus Schneider-Pargmann wrote:
> > > > diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
> > > > index 92b2bd8628e6b..8c17eb94d2f98 100644
> > > > --- a/drivers/net/can/m_can/m_can.h
> > > > +++ b/drivers/net/can/m_can/m_can.h
> > > > @@ -99,6 +99,7 @@ struct m_can_classdev {
> > > > int pm_clock_support;
> > > > int pm_wake_source;
> > > > int is_peripheral;
> > > > + bool is_edge_triggered;
> > >
> > > To avoid confusion could you rename it to irq_edge_triggered or
> > > something similar, to make clear that it is not about the chip but the
> > > way the interrupt line is connected?
> >
> > Will do.
> >
> > >
> > > Also I am not sure it is possible, but could you use
> > > irq_get_trigger_type() to see if it is a level or edge based interrupt?
> > > Then we wouldn't need this additional parameter at all and could just
> > > detect it in m_can.c.
> >
> > Unfortunately that doesn't seem to work. irq_get_trigger_type() only returns a meaningful value
> > after the IRQ has been requested. I thought about requesting the IRQ with IRQF_NO_AUTOEN and then
> > filling in the irq_edge_triggered field before enabling the IRQ, but IRQF_NO_AUTOEN is incompatible
> > with IRQF_SHARED.
>
> The mentioned function works for me on ARM and DT even before
> irq_request_threaded_irq() was called.
>
> Also I am probably missing something here. Afer requesting the irq, the
> interrupts are not enabled yet right? So can't you just request it and
> check the triggertype immediately afterwards?
You have to distinguish between the IRQ controller and the IRQ source in
your device. You must be able to handle IRQs right after request_irq().
If you IP core is in reset or has all IRQs masked, this will probably
not happen, but with shared IRQ one of the other IRQ sources on the IRQ
can trigger and your IRQ handler will be called. See
CONFIG_DEBUG_SHIRQ_FIXME and CONFIG_DEBUG_SHIRQ_FIXME.
> > Of course there are ways around this - checking irq_get_trigger_type() from the ISR itself, or
> > adding more locking, but neither seems quite worthwhile to me. Would you agree with this?
> >
> > Maybe there is some other way to find out the trigger type that would be set when the IRQ is
> > requested? I don't know what that would be however - so I'd just keep setting the flag statically
> > for m_can_pci and leave a dynamic solution for future improvement.
>
> No if it doesn't work easily the parameter is probably the best option.
regards,
Marc
--
Pengutronix e.K. | Marc Kleine-Budde |
Embedded Linux | https://www.pengutronix.de |
Vertretung Nürnberg | Phone: +49-5121-206917-129 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v2 2/2] can: m_can: fix missed interrupts with m_can_pci
2024-09-23 10:17 ` [PATCH v2 2/2] can: m_can: fix missed interrupts with m_can_pci Marc Kleine-Budde
@ 2024-09-23 11:06 ` Matthias Schiffer
0 siblings, 0 replies; 11+ messages in thread
From: Matthias Schiffer @ 2024-09-23 11:06 UTC (permalink / raw)
To: Marc Kleine-Budde
Cc: Chandrasekar Ramakrishnan, Vincent Mailhol, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Martin Hundebøll,
Markus Schneider-Pargmann, Felipe Balbi (Intel),
Raymond Tan, Jarkko Nikula, linux-can, netdev, linux-kernel,
linux, lst
On Mon, 2024-09-23 at 12:17 +0200, Marc Kleine-Budde wrote:
> On 19.09.2024 13:27:28, Matthias Schiffer wrote:
> > The interrupt line of PCI devices is interpreted as edge-triggered,
> > however the interrupt signal of the m_can controller integrated in Intel
> > Elkhart Lake CPUs appears to be generated level-triggered.
> >
> > Consider the following sequence of events:
> >
> > - IR register is read, interrupt X is set
> > - A new interrupt Y is triggered in the m_can controller
> > - IR register is written to acknowledge interrupt X. Y remains set in IR
> >
> > As at no point in this sequence no interrupt flag is set in IR, the
> > m_can interrupt line will never become deasserted, and no edge will ever
> > be observed to trigger another run of the ISR. This was observed to
> > result in the TX queue of the EHL m_can to get stuck under high load,
> > because frames were queued to the hardware in m_can_start_xmit(), but
> > m_can_finish_tx() was never run to account for their successful
> > transmission.
> >
> > To fix the issue, repeatedly read and acknowledge interrupts at the
> > start of the ISR until no interrupt flags are set, so the next incoming
> > interrupt will also result in an edge on the interrupt line.
> >
> > Fixes: cab7ffc0324f ("can: m_can: add PCI glue driver for Intel Elkhart Lake")
> > Signed-off-by: Matthias Schiffer <matthias.schiffer@ew.tq-group.com>
>
> My coworker Lucas pointed me to:
>
> > https://wiki.linuxfoundation.org/networking/napi#non-level_sensitive_irqs
>
Thanks. I don't think this is directly applicable here - in our case the lost TX complete interrupts
were the issue, not (only) the RX interrupts.
Matthias
> On the other hand, I would also like to convert the !peripteral part of
> the driver to rx-offload. However, I am still looking for potential
> customers for this task. I have talked to some TI and ST people at LPC,
> maybe they are interested.
>
> I think let's first fix edge sensitive IRQs, then rework the driver to
> rx-offload.
>
> regards,
> Marc
>
>
--
TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
Amtsgericht München, HRB 105018
Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
https://www.tq-group.com/
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH DO_NOT_APPLY] net: can: m_can: Support tcan level with edge interrupts
2024-09-23 10:30 ` Marc Kleine-Budde
@ 2024-09-24 6:16 ` Markus Schneider-Pargmann
0 siblings, 0 replies; 11+ messages in thread
From: Markus Schneider-Pargmann @ 2024-09-24 6:16 UTC (permalink / raw)
To: Matthias Schiffer, Marc Kleine-Budde
Cc: Chandrasekar Ramakrishnan, Vincent Mailhol, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Martin Hundebøll,
Felipe Balbi (Intel),
Raymond Tan, Jarkko Nikula, linux-can, netdev, linux-kernel,
linux, Markus Schneider-Pargmann
The tcan chip has a low level interrupt line that needs to be used.
There are some SoCs and components that do only support edge interrupts
on GPIOs. In the exact example someone wired the tcan chip to a am62
GPIO.
This patch creates a workaround for these situations, enabling the use
of tcan with a falling edge interrupt. Note that this is not the
preferred way to wire a tcan chip to the SoC.
I am detecting the situation by reading the IRQ type. If it is a level
interrupt everything stays the same. Otherwise these were my
considerations and solutions:
With falling edge interrupts we have following issues:
- While handling a IRQF_ONESHOT interrupt the interrupt may be masked as
long as the interrupt is handled. So if a new interrupt hits during
the handling of the interrupt may be lost as it is masked. With level
interrupts that is not a problem because the interrupt line is still
active/low after the handler is unmasked so it will jump back into
handling interrupts afterwards. With edge interrupts we will just
loose the interrupt at this point as we do not see the edge while the
interrupt is masked. Solution here is to remove the IRQF_ONESHOT flag
in case edge interrupts are used.
- Reading and clearing the interrupt register is not atomic. So the
interrupts we clear from the interrupt register may not result in a
completely cleared interrupt register and leave some unhandled
interrupt. Again this is fine for level based interrupts as they will
be causing a new call of the interrupt handler. With edge interrupts
we will be missing this interrupt. So we need to make sure that the
clearing of the interrupt register actually cleared it and the
interrupt line could have gone back to inactive/high. To do that the
interrupt register is read/cleared/handled repeatedly until it is 0.
Updating the interrupts for coalescing is only done once at the end with
all interrupts that were handled and not for every loop. We don't want
to change interrupts multiple times here.
Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com>
---
This is the draft that I had for edge interrupts. For am62 I will create
a followup patch that covers minor things like IRQF_ONESHOT removal etc.
Best
Markus
drivers/net/can/m_can/m_can.c | 114 +++++++++++++++++++++-------------
drivers/net/can/m_can/m_can.h | 1 +
2 files changed, 73 insertions(+), 42 deletions(-)
diff --git a/drivers/net/can/m_can/m_can.c b/drivers/net/can/m_can/m_can.c
index 663eb4247029..4b969f29ba55 100644
--- a/drivers/net/can/m_can/m_can.c
+++ b/drivers/net/can/m_can/m_can.c
@@ -1208,6 +1208,7 @@ static void m_can_coalescing_update(struct m_can_classdev *cdev, u32 ir)
static int m_can_interrupt_handler(struct m_can_classdev *cdev)
{
struct net_device *dev = cdev->net;
+ u32 all_interrupts = 0;
u32 ir;
int ret;
@@ -1215,56 +1216,75 @@ static int m_can_interrupt_handler(struct m_can_classdev *cdev)
return IRQ_NONE;
ir = m_can_read(cdev, M_CAN_IR);
- m_can_coalescing_update(cdev, ir);
- if (!ir)
+ all_interrupts |= ir;
+ if (!ir) {
+ m_can_coalescing_update(cdev, 0);
return IRQ_NONE;
-
- /* ACK all irqs */
- m_can_write(cdev, M_CAN_IR, ir);
-
- if (cdev->ops->clear_interrupts)
- cdev->ops->clear_interrupts(cdev);
-
- /* schedule NAPI in case of
- * - rx IRQ
- * - state change IRQ
- * - bus error IRQ and bus error reporting
- */
- if (ir & (IR_RF0N | IR_RF0W | IR_ERR_ALL_30X)) {
- cdev->irqstatus = ir;
- if (!cdev->is_peripheral) {
- m_can_disable_all_interrupts(cdev);
- napi_schedule(&cdev->napi);
- } else {
- ret = m_can_rx_handler(dev, NAPI_POLL_WEIGHT, ir);
- if (ret < 0)
- return ret;
- }
}
- if (cdev->version == 30) {
- if (ir & IR_TC) {
- /* Transmission Complete Interrupt*/
- u32 timestamp = 0;
- unsigned int frame_len;
+ do {
+ /* ACK all irqs */
+ m_can_write(cdev, M_CAN_IR, ir);
- if (cdev->is_peripheral)
- timestamp = m_can_get_timestamp(cdev);
- frame_len = m_can_tx_update_stats(cdev, 0, timestamp);
- m_can_finish_tx(cdev, 1, frame_len);
+ if (cdev->ops->clear_interrupts)
+ cdev->ops->clear_interrupts(cdev);
+
+ /* schedule NAPI in case of
+ * - rx IRQ
+ * - state change IRQ
+ * - bus error IRQ and bus error reporting
+ */
+ if (ir & (IR_RF0N | IR_RF0W | IR_ERR_ALL_30X)) {
+ cdev->irqstatus = ir;
+ if (!cdev->is_peripheral) {
+ m_can_disable_all_interrupts(cdev);
+ napi_schedule(&cdev->napi);
+ } else {
+ ret = m_can_rx_handler(dev, NAPI_POLL_WEIGHT, ir);
+ if (ret < 0)
+ return ret;
+ }
}
- } else {
- if (ir & (IR_TEFN | IR_TEFW)) {
- /* New TX FIFO Element arrived */
- ret = m_can_echo_tx_event(dev);
- if (ret != 0)
- return ret;
+
+ if (cdev->version == 30) {
+ if (ir & IR_TC) {
+ /* Transmission Complete Interrupt*/
+ u32 timestamp = 0;
+ unsigned int frame_len;
+
+ if (cdev->is_peripheral)
+ timestamp = m_can_get_timestamp(cdev);
+ frame_len = m_can_tx_update_stats(cdev, 0, timestamp);
+ m_can_finish_tx(cdev, 1, frame_len);
+ }
+ } else {
+ if (ir & (IR_TEFN | IR_TEFW)) {
+ /* New TX FIFO Element arrived */
+ ret = m_can_echo_tx_event(dev);
+ if (ret != 0)
+ return ret;
+ }
}
- }
+ if (!cdev->irq_type_edge)
+ break;
+
+
+ /* For edge interrupts we need to read the IR register again to
+ * check that everything is cleared. If it is not, we can not
+ * make sure the interrupt line is inactive again which is
+ * required at this point to not miss any new interrupts. So in
+ * case there are interrupts signaled in IR we repeat the
+ * interrupt handling.
+ */
+ ir = m_can_read(cdev, M_CAN_IR);
+ all_interrupts |= ir;
+ } while (ir);
if (cdev->is_peripheral)
can_rx_offload_threaded_irq_finish(&cdev->offload);
+ m_can_coalescing_update(cdev, all_interrupts);
+
return IRQ_HANDLED;
}
@@ -2009,6 +2029,11 @@ static enum hrtimer_restart hrtimer_callback(struct hrtimer *timer)
return HRTIMER_RESTART;
}
+static irqreturn_t m_can_hardirq(int irq, void *dev_id)
+{
+ return IRQ_WAKE_THREAD;
+}
+
static int m_can_open(struct net_device *dev)
{
struct m_can_classdev *cdev = netdev_priv(dev);
@@ -2034,6 +2059,9 @@ static int m_can_open(struct net_device *dev)
/* register interrupt handler */
if (cdev->is_peripheral) {
+ cdev->irq_type_edge = !(irq_get_trigger_type(dev->irq) &
+ IRQ_TYPE_LEVEL_MASK);
+
cdev->tx_wq = alloc_ordered_workqueue("mcan_wq",
WQ_FREEZABLE | WQ_MEM_RECLAIM);
if (!cdev->tx_wq) {
@@ -2046,9 +2074,11 @@ static int m_can_open(struct net_device *dev)
INIT_WORK(&cdev->tx_ops[i].work, m_can_tx_work_queue);
}
- err = request_threaded_irq(dev->irq, NULL, m_can_isr,
- IRQF_ONESHOT,
+ err = request_threaded_irq(dev->irq, m_can_hardirq, m_can_isr,
+ (cdev->irq_type_edge ? 0 : IRQF_ONESHOT),
dev->name, dev);
+ if (cdev->irq_type_edge)
+ netdev_info(dev, "Operating a level interrupt chip with an edge interrupt.\n");
} else if (dev->irq) {
err = request_irq(dev->irq, m_can_isr, IRQF_SHARED, dev->name,
dev);
diff --git a/drivers/net/can/m_can/m_can.h b/drivers/net/can/m_can/m_can.h
index 3a9edc292593..17de56056352 100644
--- a/drivers/net/can/m_can/m_can.h
+++ b/drivers/net/can/m_can/m_can.h
@@ -99,6 +99,7 @@ struct m_can_classdev {
int pm_clock_support;
int pm_wake_source;
int is_peripheral;
+ bool irq_type_edge;
// Cached M_CAN_IE register content
u32 active_interrupts;
--
2.45.2
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2024-09-24 6:21 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-09-19 11:27 [PATCH v2 1/2] can: m_can: set init flag earlier in probe Matthias Schiffer
2024-09-19 11:27 ` [PATCH v2 2/2] can: m_can: fix missed interrupts with m_can_pci Matthias Schiffer
2024-09-23 8:03 ` Markus Schneider-Pargmann
2024-09-23 9:32 ` Matthias Schiffer
2024-09-23 10:07 ` Markus Schneider-Pargmann
2024-09-23 10:53 ` Marc Kleine-Budde
2024-09-23 10:30 ` Marc Kleine-Budde
2024-09-24 6:16 ` [PATCH DO_NOT_APPLY] net: can: m_can: Support tcan level with edge interrupts Markus Schneider-Pargmann
2024-09-23 10:17 ` [PATCH v2 2/2] can: m_can: fix missed interrupts with m_can_pci Marc Kleine-Budde
2024-09-23 11:06 ` Matthias Schiffer
2024-09-23 8:12 ` [PATCH v2 1/2] can: m_can: set init flag earlier in probe Markus Schneider-Pargmann
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®