Commit 04769802 authored by Marc Kleine-Budde's avatar Marc Kleine-Budde

Merge patch series "can: sja1000: Prepare the use of a threaded handler"

Miquel Raynal provides a series for the sja1000 driver to work around
overrun stalls with a soft reset on Renesas SoCs.

Link: https://lore.kernel.org/all/20230616134553.2786391-1-miquel.raynal@bootlin.comSigned-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parents cf8462a8 717c6ec2
...@@ -387,6 +387,16 @@ static void sja1000_rx(struct net_device *dev) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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)
...@@ -501,7 +521,8 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id) ...@@ -501,7 +521,8 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id)
struct sja1000_priv *priv = netdev_priv(dev); struct sja1000_priv *priv = netdev_priv(dev);
struct net_device_stats *stats = &dev->stats; struct net_device_stats *stats = &dev->stats;
uint8_t isrc, status; uint8_t isrc, status;
int n = 0; irqreturn_t ret = 0;
int n = 0, err;
if (priv->pre_irq) if (priv->pre_irq)
priv->pre_irq(priv); priv->pre_irq(priv);
...@@ -546,19 +567,25 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id) ...@@ -546,19 +567,25 @@ 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 */
if (sja1000_err(dev, isrc, status)) err = sja1000_err(dev, isrc, status);
if (err == IRQ_WAKE_THREAD)
ret = err;
if (err)
break; break;
} }
n++; n++;
} }
out: out:
if (!ret)
ret = (n) ? IRQ_HANDLED : IRQ_NONE;
if (priv->post_irq) if (priv->post_irq)
priv->post_irq(priv); priv->post_irq(priv);
if (n >= SJA1000_MAX_IRQ) if (n >= SJA1000_MAX_IRQ)
netdev_dbg(dev, "%d messages handled in ISR", n); netdev_dbg(dev, "%d messages handled in ISR", n);
return (n) ? IRQ_HANDLED : IRQ_NONE; return ret;
} }
EXPORT_SYMBOL_GPL(sja1000_interrupt); EXPORT_SYMBOL_GPL(sja1000_interrupt);
...@@ -577,8 +604,9 @@ static int sja1000_open(struct net_device *dev) ...@@ -577,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 @@ ...@@ -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 ...@@ -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) ...@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment