diff options
Diffstat (limited to 'drivers/net/can/sja1000/sja1000.c')
| -rw-r--r-- | drivers/net/can/sja1000/sja1000.c | 120 |
1 files changed, 82 insertions, 38 deletions
diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c index aac5956e4a53..a8fa0d6516b9 100644 --- a/drivers/net/can/sja1000/sja1000.c +++ b/drivers/net/can/sja1000/sja1000.c @@ -206,7 +206,7 @@ static void sja1000_start(struct net_device *dev) { struct sja1000_priv *priv = netdev_priv(dev); - /* leave reset mode */ + /* enter reset mode */ if (priv->can.state != CAN_STATE_STOPPED) set_reset_mode(dev); @@ -387,6 +387,22 @@ static void sja1000_rx(struct net_device *dev) 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"); + + netif_tx_lock(dev); + + can_free_echo_skb(dev, 0, NULL); + sja1000_set_mode(dev, CAN_MODE_START); + + netif_tx_unlock(dev); + + return IRQ_HANDLED; +} + static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) { struct sja1000_priv *priv = netdev_priv(dev); @@ -397,10 +413,9 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) enum can_state rx_state, tx_state; unsigned int rxerr, txerr; uint8_t ecc, alc; + int ret = 0; skb = alloc_can_err_skb(dev, &cf); - if (skb == NULL) - return -ENOMEM; txerr = priv->read_reg(priv, SJA1000_TXERR); rxerr = priv->read_reg(priv, SJA1000_RXERR); @@ -408,11 +423,23 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) if (isrc & IRQ_DOI) { /* data overrun interrupt */ netdev_dbg(dev, "data overrun interrupt\n"); - cf->can_id |= CAN_ERR_CRTL; - cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + } + stats->rx_over_errors++; stats->rx_errors++; 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) { @@ -426,7 +453,7 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) else state = CAN_STATE_ERROR_ACTIVE; } - if (state != CAN_STATE_BUS_OFF) { + if (state != CAN_STATE_BUS_OFF && skb) { cf->can_id |= CAN_ERR_CNT; cf->data[6] = txerr; cf->data[7] = rxerr; @@ -434,33 +461,38 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) if (isrc & IRQ_BEI) { /* bus error interrupt */ priv->can.can_stats.bus_error++; - stats->rx_errors++; ecc = priv->read_reg(priv, SJA1000_ECC); + if (skb) { + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; - cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; - - /* set error type */ - switch (ecc & ECC_MASK) { - case ECC_BIT: - cf->data[2] |= CAN_ERR_PROT_BIT; - break; - case ECC_FORM: - cf->data[2] |= CAN_ERR_PROT_FORM; - break; - case ECC_STUFF: - cf->data[2] |= CAN_ERR_PROT_STUFF; - break; - default: - break; - } + /* set error type */ + switch (ecc & ECC_MASK) { + case ECC_BIT: + cf->data[2] |= CAN_ERR_PROT_BIT; + break; + case ECC_FORM: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case ECC_STUFF: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + default: + break; + } - /* set error location */ - cf->data[3] = ecc & ECC_SEG; + /* set error location */ + cf->data[3] = ecc & ECC_SEG; + } /* Error occurred during transmission? */ - if ((ecc & ECC_DIR) == 0) - cf->data[2] |= CAN_ERR_PROT_TX; + if ((ecc & ECC_DIR) == 0) { + stats->tx_errors++; + if (skb) + cf->data[2] |= CAN_ERR_PROT_TX; + } else { + stats->rx_errors++; + } } if (isrc & IRQ_EPI) { /* error passive interrupt */ @@ -476,8 +508,10 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) netdev_dbg(dev, "arbitration lost interrupt\n"); alc = priv->read_reg(priv, SJA1000_ALC); priv->can.can_stats.arbitration_lost++; - cf->can_id |= CAN_ERR_LOSTARB; - cf->data[0] = alc & 0x1f; + if (skb) { + cf->can_id |= CAN_ERR_LOSTARB; + cf->data[0] = alc & 0x1f; + } } if (state != priv->can.state) { @@ -490,9 +524,12 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status) can_bus_off(dev); } + if (!skb) + return -ENOMEM; + netif_rx(skb); - return 0; + return ret; } irqreturn_t sja1000_interrupt(int irq, void *dev_id) @@ -501,7 +538,8 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id) struct sja1000_priv *priv = netdev_priv(dev); struct net_device_stats *stats = &dev->stats; uint8_t isrc, status; - int n = 0; + irqreturn_t ret = 0; + int n = 0, err; if (priv->pre_irq) priv->pre_irq(priv); @@ -510,8 +548,8 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id) if (priv->read_reg(priv, SJA1000_IER) == IRQ_OFF) goto out; - while ((isrc = priv->read_reg(priv, SJA1000_IR)) && - (n < SJA1000_MAX_IRQ)) { + while ((n < SJA1000_MAX_IRQ) && + (isrc = priv->read_reg(priv, SJA1000_IR))) { status = priv->read_reg(priv, SJA1000_SR); /* check for absent controller due to hw unplug */ @@ -546,19 +584,25 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id) } if (isrc & (IRQ_DOI | IRQ_EI | IRQ_BEI | IRQ_EPI | IRQ_ALI)) { /* error interrupt */ - if (sja1000_err(dev, isrc, status)) + err = sja1000_err(dev, isrc, status); + if (err == IRQ_WAKE_THREAD) + ret = err; + if (err) break; } n++; } out: + if (!ret) + ret = (n) ? IRQ_HANDLED : IRQ_NONE; + if (priv->post_irq) priv->post_irq(priv); if (n >= SJA1000_MAX_IRQ) netdev_dbg(dev, "%d messages handled in ISR", n); - return (n) ? IRQ_HANDLED : IRQ_NONE; + return ret; } EXPORT_SYMBOL_GPL(sja1000_interrupt); @@ -577,8 +621,9 @@ static int sja1000_open(struct net_device *dev) /* register interrupt handler, if not done by the device driver */ if (!(priv->flags & SJA1000_CUSTOM_IRQ_HANDLER)) { - err = request_irq(dev->irq, sja1000_interrupt, priv->irq_flags, - dev->name, (void *)dev); + err = request_threaded_irq(dev->irq, sja1000_interrupt, + sja1000_reset_interrupt, + priv->irq_flags, dev->name, (void *)dev); if (err) { close_candev(dev); return -EAGAIN; @@ -652,7 +697,6 @@ static const struct net_device_ops sja1000_netdev_ops = { .ndo_open = sja1000_open, .ndo_stop = sja1000_close, .ndo_start_xmit = sja1000_start_xmit, - .ndo_change_mtu = can_change_mtu, }; static const struct ethtool_ops sja1000_ethtool_ops = { |
