can: sja1000: Prevent overrun stalls with a soft reset on Renesas SoCs
In their RZN1 SoC, Renesas put a CAN controller supposed to act very similarly to the original Philips sja1000. In practice, while flooding the bus with another device, we discovered that the controller very often after an overrun situation would just refuse any new frame, drop them all and trigger over and over again the overrun interrupt, even though the buffer would have been totally emptied. The controller acts like if its internal buffer offsets (where it writes and where the host reads) where totally screwed-up. Renesas manual mentions a single action to perform in order to resynchronize the read and write offsets within the buffer: performing a soft reset. Performing a soft reset takes a bit of time and involves small delays, so better do that in a threaded handler rather than inside the hard IRQ handler. Add platform data to recognize the platforms which need this workaround, and when the faulty situation is diagnosed, stop what is being performed and request the threaded handler to be executed in order to perform the reset. Tested-by: Jérémie Dautheribes <jeremie.dautheribes@bootlin.com> # 5.10 Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> Link: https://lore.kernel.org/all/20230616134553.2786391-2-miquel.raynal@bootlin.com Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
parent
af7647a0b4
commit
717c6ec241
|
@ -387,6 +387,16 @@ static void sja1000_rx(struct net_device *dev)
|
||||||
netif_rx(skb);
|
netif_rx(skb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static irqreturn_t sja1000_reset_interrupt(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
struct net_device *dev = (struct net_device *)dev_id;
|
||||||
|
|
||||||
|
netdev_dbg(dev, "performing a soft reset upon overrun\n");
|
||||||
|
sja1000_start(dev);
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
|
static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
|
||||||
{
|
{
|
||||||
struct sja1000_priv *priv = netdev_priv(dev);
|
struct sja1000_priv *priv = netdev_priv(dev);
|
||||||
|
@ -397,6 +407,7 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
|
||||||
enum can_state rx_state, tx_state;
|
enum can_state rx_state, tx_state;
|
||||||
unsigned int rxerr, txerr;
|
unsigned int rxerr, txerr;
|
||||||
uint8_t ecc, alc;
|
uint8_t ecc, alc;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
skb = alloc_can_err_skb(dev, &cf);
|
skb = alloc_can_err_skb(dev, &cf);
|
||||||
if (skb == NULL)
|
if (skb == NULL)
|
||||||
|
@ -413,6 +424,15 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
|
||||||
stats->rx_over_errors++;
|
stats->rx_over_errors++;
|
||||||
stats->rx_errors++;
|
stats->rx_errors++;
|
||||||
sja1000_write_cmdreg(priv, CMD_CDO); /* clear bit */
|
sja1000_write_cmdreg(priv, CMD_CDO); /* clear bit */
|
||||||
|
|
||||||
|
/* Some controllers needs additional handling upon overrun
|
||||||
|
* condition: the controller may sometimes be totally confused
|
||||||
|
* and refuse any new frame while its buffer is empty. The only
|
||||||
|
* way to re-sync the read vs. write buffer offsets is to
|
||||||
|
* stop any current handling and perform a reset.
|
||||||
|
*/
|
||||||
|
if (priv->flags & SJA1000_QUIRK_RESET_ON_OVERRUN)
|
||||||
|
ret = IRQ_WAKE_THREAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isrc & IRQ_EI) {
|
if (isrc & IRQ_EI) {
|
||||||
|
@ -492,7 +512,7 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
|
||||||
|
|
||||||
netif_rx(skb);
|
netif_rx(skb);
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
irqreturn_t sja1000_interrupt(int irq, void *dev_id)
|
irqreturn_t sja1000_interrupt(int irq, void *dev_id)
|
||||||
|
@ -548,6 +568,8 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id)
|
||||||
if (isrc & (IRQ_DOI | IRQ_EI | IRQ_BEI | IRQ_EPI | IRQ_ALI)) {
|
if (isrc & (IRQ_DOI | IRQ_EI | IRQ_BEI | IRQ_EPI | IRQ_ALI)) {
|
||||||
/* error interrupt */
|
/* error interrupt */
|
||||||
err = sja1000_err(dev, isrc, status);
|
err = sja1000_err(dev, isrc, status);
|
||||||
|
if (err == IRQ_WAKE_THREAD)
|
||||||
|
ret = err;
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -582,8 +604,9 @@ static int sja1000_open(struct net_device *dev)
|
||||||
|
|
||||||
/* register interrupt handler, if not done by the device driver */
|
/* register interrupt handler, if not done by the device driver */
|
||||||
if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER)) {
|
if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER)) {
|
||||||
err = request_irq(dev->irq, sja1000_interrupt, priv->irq_flags,
|
err = request_threaded_irq(dev->irq, sja1000_interrupt,
|
||||||
dev->name, (void *)dev);
|
sja1000_reset_interrupt,
|
||||||
|
priv->irq_flags, dev->name, (void *)dev);
|
||||||
if (err) {
|
if (err) {
|
||||||
close_candev(dev);
|
close_candev(dev);
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
|
|
@ -147,6 +147,7 @@
|
||||||
*/
|
*/
|
||||||
#define SJA1000_CUSTOM_IRQ_HANDLER BIT(0)
|
#define SJA1000_CUSTOM_IRQ_HANDLER BIT(0)
|
||||||
#define SJA1000_QUIRK_NO_CDR_REG BIT(1)
|
#define SJA1000_QUIRK_NO_CDR_REG BIT(1)
|
||||||
|
#define SJA1000_QUIRK_RESET_ON_OVERRUN BIT(2)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SJA1000 private data structure
|
* SJA1000 private data structure
|
||||||
|
|
|
@ -106,7 +106,7 @@ static void sp_technologic_init(struct sja1000_priv *priv, struct device_node *o
|
||||||
|
|
||||||
static void sp_rzn1_init(struct sja1000_priv *priv, struct device_node *of)
|
static void sp_rzn1_init(struct sja1000_priv *priv, struct device_node *of)
|
||||||
{
|
{
|
||||||
priv->flags = SJA1000_QUIRK_NO_CDR_REG;
|
priv->flags = SJA1000_QUIRK_NO_CDR_REG | SJA1000_QUIRK_RESET_ON_OVERRUN;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sp_populate(struct sja1000_priv *priv,
|
static void sp_populate(struct sja1000_priv *priv,
|
||||||
|
@ -277,6 +277,9 @@ static int sp_probe(struct platform_device *pdev)
|
||||||
priv->irq_flags = IRQF_SHARED;
|
priv->irq_flags = IRQF_SHARED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (priv->flags & SJA1000_QUIRK_RESET_ON_OVERRUN)
|
||||||
|
priv->irq_flags |= IRQF_ONESHOT;
|
||||||
|
|
||||||
dev->irq = irq;
|
dev->irq = irq;
|
||||||
priv->reg_base = addr;
|
priv->reg_base = addr;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue