diff options
| author | Oliver Hartkopp <socketcan@hartkopp.net> | 2012-02-15 11:51:56 -0500 |
|---|---|---|
| committer | Marc Kleine-Budde <mkl@pengutronix.de> | 2012-02-20 05:36:56 -0500 |
| commit | a7762b10c12a70c5dbf2253142764b728ac88c3a (patch) | |
| tree | dd2dba7c245118fee835fb72935ff885e7fddda8 | |
| parent | 64f0a836f600e9c31ffd511713ab5d328aa96ac8 (diff) | |
can: sja1000: fix isr hang when hw is unplugged under load
In the case of hotplug enabled devices (PCMCIA/PCIeC) the removal of the
hardware can cause an infinite loop in the common sja1000 isr.
Use the already retrieved status register to indicate a possible hardware
removal and double check by reading the mode register in sja1000_is_absent.
Cc: stable@kernel.org [3.2+]
Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
Acked-by: Wolfgang Grandegger <wg@grandegger.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
| -rw-r--r-- | drivers/net/can/sja1000/sja1000.c | 13 |
1 files changed, 12 insertions, 1 deletions
diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c index 04a3f1b756a8..192b0d118df4 100644 --- a/drivers/net/can/sja1000/sja1000.c +++ b/drivers/net/can/sja1000/sja1000.c | |||
| @@ -95,11 +95,16 @@ static void sja1000_write_cmdreg(struct sja1000_priv *priv, u8 val) | |||
| 95 | spin_unlock_irqrestore(&priv->cmdreg_lock, flags); | 95 | spin_unlock_irqrestore(&priv->cmdreg_lock, flags); |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | static int sja1000_is_absent(struct sja1000_priv *priv) | ||
| 99 | { | ||
| 100 | return (priv->read_reg(priv, REG_MOD) == 0xFF); | ||
| 101 | } | ||
| 102 | |||
| 98 | static int sja1000_probe_chip(struct net_device *dev) | 103 | static int sja1000_probe_chip(struct net_device *dev) |
| 99 | { | 104 | { |
| 100 | struct sja1000_priv *priv = netdev_priv(dev); | 105 | struct sja1000_priv *priv = netdev_priv(dev); |
| 101 | 106 | ||
| 102 | if (priv->reg_base && (priv->read_reg(priv, 0) == 0xFF)) { | 107 | if (priv->reg_base && sja1000_is_absent(priv)) { |
| 103 | printk(KERN_INFO "%s: probing @0x%lX failed\n", | 108 | printk(KERN_INFO "%s: probing @0x%lX failed\n", |
| 104 | DRV_NAME, dev->base_addr); | 109 | DRV_NAME, dev->base_addr); |
| 105 | return 0; | 110 | return 0; |
| @@ -493,6 +498,9 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id) | |||
| 493 | while ((isrc = priv->read_reg(priv, REG_IR)) && (n < SJA1000_MAX_IRQ)) { | 498 | while ((isrc = priv->read_reg(priv, REG_IR)) && (n < SJA1000_MAX_IRQ)) { |
| 494 | n++; | 499 | n++; |
| 495 | status = priv->read_reg(priv, REG_SR); | 500 | status = priv->read_reg(priv, REG_SR); |
| 501 | /* check for absent controller due to hw unplug */ | ||
| 502 | if (status == 0xFF && sja1000_is_absent(priv)) | ||
| 503 | return IRQ_NONE; | ||
| 496 | 504 | ||
| 497 | if (isrc & IRQ_WUI) | 505 | if (isrc & IRQ_WUI) |
| 498 | dev_warn(dev->dev.parent, "wakeup interrupt\n"); | 506 | dev_warn(dev->dev.parent, "wakeup interrupt\n"); |
| @@ -509,6 +517,9 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id) | |||
| 509 | while (status & SR_RBS) { | 517 | while (status & SR_RBS) { |
| 510 | sja1000_rx(dev); | 518 | sja1000_rx(dev); |
| 511 | status = priv->read_reg(priv, REG_SR); | 519 | status = priv->read_reg(priv, REG_SR); |
| 520 | /* check for absent controller */ | ||
| 521 | if (status == 0xFF && sja1000_is_absent(priv)) | ||
| 522 | return IRQ_NONE; | ||
| 512 | } | 523 | } |
| 513 | } | 524 | } |
| 514 | if (isrc & (IRQ_DOI | IRQ_EI | IRQ_BEI | IRQ_EPI | IRQ_ALI)) { | 525 | if (isrc & (IRQ_DOI | IRQ_EI | IRQ_BEI | IRQ_EPI | IRQ_ALI)) { |
