can: Fix SJA1000 command register writes on SMP systems
[safe/jmp/linux-2.6] / drivers / net / can / sja1000 / sja1000.c
index 08ebee7..85f7cbf 100644 (file)
@@ -60,7 +60,6 @@
 #include <linux/skbuff.h>
 #include <linux/delay.h>
 
-#include <linux/can.h>
 #include <linux/can/dev.h>
 #include <linux/can/error.h>
 
@@ -84,6 +83,20 @@ static struct can_bittiming_const sja1000_bittiming_const = {
        .brp_inc = 1,
 };
 
+static void sja1000_write_cmdreg(struct sja1000_priv *priv, u8 val)
+{
+       unsigned long flags;
+
+       /*
+        * The command register needs some locking and time to settle
+        * the write_reg() operation - especially on SMP systems.
+        */
+       spin_lock_irqsave(&priv->cmdreg_lock, flags);
+       priv->write_reg(priv, REG_CMR, val);
+       priv->read_reg(priv, REG_SR);
+       spin_unlock_irqrestore(&priv->cmdreg_lock, flags);
+}
+
 static int sja1000_probe_chip(struct net_device *dev)
 {
        struct sja1000_priv *priv = netdev_priv(dev);
@@ -130,8 +143,12 @@ static void set_normal_mode(struct net_device *dev)
                /* check reset bit */
                if ((status & MOD_RM) == 0) {
                        priv->can.state = CAN_STATE_ERROR_ACTIVE;
-                       /* enable all interrupts */
-                       priv->write_reg(priv, REG_IER, IRQ_ALL);
+                       /* enable interrupts */
+                       if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
+                               priv->write_reg(priv, REG_IER, IRQ_ALL);
+                       else
+                               priv->write_reg(priv, REG_IER,
+                                               IRQ_ALL & ~IRQ_BEI);
                        return;
                }
 
@@ -203,6 +220,17 @@ static int sja1000_set_bittiming(struct net_device *dev)
        return 0;
 }
 
+static int sja1000_get_berr_counter(const struct net_device *dev,
+                                   struct can_berr_counter *bec)
+{
+       struct sja1000_priv *priv = netdev_priv(dev);
+
+       bec->txerr = priv->read_reg(priv, REG_TXERR);
+       bec->rxerr = priv->read_reg(priv, REG_RXERR);
+
+       return 0;
+}
+
 /*
  * initialize SJA1000 chip:
  *   - reset chip
@@ -238,10 +266,10 @@ static void chipset_init(struct net_device *dev)
  * xx xx xx xx  ff      ll   00 11 22 33 44 55 66 77
  * [  can-id ] [flags] [len] [can data (up to 8 bytes]
  */
-static int sja1000_start_xmit(struct sk_buff *skb, struct net_device *dev)
+static netdev_tx_t sja1000_start_xmit(struct sk_buff *skb,
+                                           struct net_device *dev)
 {
        struct sja1000_priv *priv = netdev_priv(dev);
-       struct net_device_stats *stats = &dev->stats;
        struct can_frame *cf = (struct can_frame *)skb->data;
        uint8_t fi;
        uint8_t dlc;
@@ -249,6 +277,9 @@ static int sja1000_start_xmit(struct sk_buff *skb, struct net_device *dev)
        uint8_t dreg;
        int i;
 
+       if (can_dropped_invalid_skb(dev, skb))
+               return NETDEV_TX_OK;
+
        netif_stop_queue(dev);
 
        fi = dlc = cf->can_dlc;
@@ -275,14 +306,11 @@ static int sja1000_start_xmit(struct sk_buff *skb, struct net_device *dev)
        for (i = 0; i < dlc; i++)
                priv->write_reg(priv, dreg++, cf->data[i]);
 
-       stats->tx_bytes += dlc;
-       dev->trans_start = jiffies;
-
        can_put_echo_skb(skb, dev, 0);
 
-       priv->write_reg(priv, REG_CMR, CMD_TR);
+       sja1000_write_cmdreg(priv, CMD_TR);
 
-       return 0;
+       return NETDEV_TX_OK;
 }
 
 static void sja1000_rx(struct net_device *dev)
@@ -294,17 +322,14 @@ static void sja1000_rx(struct net_device *dev)
        uint8_t fi;
        uint8_t dreg;
        canid_t id;
-       uint8_t dlc;
        int i;
 
-       skb = dev_alloc_skb(sizeof(struct can_frame));
+       /* create zero'ed CAN frame buffer */
+       skb = alloc_can_skb(dev, &cf);
        if (skb == NULL)
                return;
-       skb->dev = dev;
-       skb->protocol = htons(ETH_P_CAN);
 
        fi = priv->read_reg(priv, REG_FI);
-       dlc = fi & 0x0F;
 
        if (fi & FI_FF) {
                /* extended frame format (EFF) */
@@ -321,27 +346,23 @@ static void sja1000_rx(struct net_device *dev)
                    | (priv->read_reg(priv, REG_ID2) >> 5);
        }
 
-       if (fi & FI_RTR)
+       if (fi & FI_RTR) {
                id |= CAN_RTR_FLAG;
+       } else {
+               cf->can_dlc = get_can_dlc(fi & 0x0F);
+               for (i = 0; i < cf->can_dlc; i++)
+                       cf->data[i] = priv->read_reg(priv, dreg++);
+       }
 
-       cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
-       memset(cf, 0, sizeof(struct can_frame));
        cf->can_id = id;
-       cf->can_dlc = dlc;
-       for (i = 0; i < dlc; i++)
-               cf->data[i] = priv->read_reg(priv, dreg++);
-
-       while (i < 8)
-               cf->data[i++] = 0;
 
        /* release receive buffer */
-       priv->write_reg(priv, REG_CMR, CMD_RRB);
+       sja1000_write_cmdreg(priv, CMD_RRB);
 
        netif_rx(skb);
 
-       dev->last_rx = jiffies;
        stats->rx_packets++;
-       stats->rx_bytes += dlc;
+       stats->rx_bytes += cf->can_dlc;
 }
 
 static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
@@ -353,15 +374,9 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
        enum can_state state = priv->can.state;
        uint8_t ecc, alc;
 
-       skb = dev_alloc_skb(sizeof(struct can_frame));
+       skb = alloc_can_err_skb(dev, &cf);
        if (skb == NULL)
                return -ENOMEM;
-       skb->dev = dev;
-       skb->protocol = htons(ETH_P_CAN);
-       cf = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
-       memset(cf, 0, sizeof(struct can_frame));
-       cf->can_id = CAN_ERR_FLAG;
-       cf->can_dlc = CAN_ERR_DLC;
 
        if (isrc & IRQ_DOI) {
                /* data overrun interrupt */
@@ -370,7 +385,7 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
                cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
                stats->rx_over_errors++;
                stats->rx_errors++;
-               priv->write_reg(priv, REG_CMR, CMD_CDO);        /* clear bit */
+               sja1000_write_cmdreg(priv, CMD_CDO);    /* clear bit */
        }
 
        if (isrc & IRQ_EI) {
@@ -427,7 +442,7 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
                dev_dbg(dev->dev.parent, "arbitration lost interrupt\n");
                alc = priv->read_reg(priv, REG_ALC);
                priv->can.can_stats.arbitration_lost++;
-               stats->rx_errors++;
+               stats->tx_errors++;
                cf->can_id |= CAN_ERR_LOSTARB;
                cf->data[0] = alc & 0x1f;
        }
@@ -448,13 +463,14 @@ static int sja1000_err(struct net_device *dev, uint8_t isrc, uint8_t status)
                                CAN_ERR_CRTL_TX_PASSIVE :
                                CAN_ERR_CRTL_RX_PASSIVE;
                }
+               cf->data[6] = txerr;
+               cf->data[7] = rxerr;
        }
 
        priv->can.state = state;
 
        netif_rx(skb);
 
-       dev->last_rx = jiffies;
        stats->rx_packets++;
        stats->rx_bytes += cf->can_dlc;
 
@@ -485,6 +501,7 @@ irqreturn_t sja1000_interrupt(int irq, void *dev_id)
 
                if (isrc & IRQ_TI) {
                        /* transmission complete interrupt */
+                       stats->tx_bytes += priv->read_reg(priv, REG_FI) & 0xf;
                        stats->tx_packets++;
                        can_get_echo_skb(dev, 0);
                        netif_wake_queue(dev);
@@ -528,7 +545,7 @@ 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,
+               err = request_irq(dev->irq, sja1000_interrupt, priv->irq_flags,
                                  dev->name, (void *)dev);
                if (err) {
                        close_candev(dev);
@@ -567,7 +584,8 @@ struct net_device *alloc_sja1000dev(int sizeof_priv)
        struct net_device *dev;
        struct sja1000_priv *priv;
 
-       dev = alloc_candev(sizeof(struct sja1000_priv) + sizeof_priv);
+       dev = alloc_candev(sizeof(struct sja1000_priv) + sizeof_priv,
+               SJA1000_ECHO_SKB_MAX);
        if (!dev)
                return NULL;
 
@@ -577,6 +595,9 @@ struct net_device *alloc_sja1000dev(int sizeof_priv)
        priv->can.bittiming_const = &sja1000_bittiming_const;
        priv->can.do_set_bittiming = sja1000_set_bittiming;
        priv->can.do_set_mode = sja1000_set_mode;
+       priv->can.do_get_berr_counter = sja1000_get_berr_counter;
+       priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES |
+               CAN_CTRLMODE_BERR_REPORTING;
 
        if (sizeof_priv)
                priv->priv = (void *)priv + sizeof(struct sja1000_priv);