diff --git a/arm-linux/drivers/net/can/flexcan.c b/arm-linux/drivers/net/can/flexcan.c old mode 100644 new mode 100755 index 14c8a86..96e0b55 --- a/arm-linux/drivers/net/can/flexcan.c +++ b/arm-linux/drivers/net/can/flexcan.c @@ -43,6 +43,9 @@ /* 8 for RX fifo and 2 error handling */ #define FLEXCAN_NAPI_WEIGHT (8 + 2) +/* Buffers in the interrupt-read ring, 10ms@100us per. */ +#define FLEXCAN_READ_RING_SIZE (100) + /* FLEXCAN module configuration register (CANMCR) bits */ #define FLEXCAN_MCR_MDIS BIT(31) #define FLEXCAN_MCR_FRZ BIT(30) @@ -170,6 +173,14 @@ struct flexcan_regs { struct flexcan_mb cantxfg[64]; }; +struct flexcan_ring { + u32 input; /* Ring input index */ + u32 output; /* Ring output index */ + u32 full; /* ring full/wrapped flag */ + spinlock_t ring_lock; + struct can_frame frames[FLEXCAN_READ_RING_SIZE]; +}; + struct flexcan_priv { struct can_priv can; struct net_device *dev; @@ -182,6 +193,7 @@ struct flexcan_priv { struct clk *clk; struct flexcan_platform_data *pdata; struct gpio_sw *xcvr_switch; + struct flexcan_ring read_ring; }; static struct can_bittiming_const flexcan_bittiming_const = { @@ -532,12 +544,70 @@ static int flexcan_read_frame(struct net_device *dev) return 1; } +static int flexcan_read_frame_to_ring(struct net_device *dev) +{ + struct net_device_stats *stats = &dev->stats; + struct flexcan_priv *priv = netdev_priv(dev); + struct can_frame *cf; + + cf = &priv->read_ring.frames[priv->read_ring.input]; + + /* Read even if ring full to clear the interrupt request */ + flexcan_read_fifo(dev, cf); + + if (priv->read_ring.full) { + stats->rx_dropped++; + return 0; + } + priv->read_ring.input += 1; + if (priv->read_ring.input >= FLEXCAN_READ_RING_SIZE) { + priv->read_ring.input = 0; + } + if (priv->read_ring.input == priv->read_ring.output) { + priv->read_ring.full = 1; + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + + return 1; +} + +static int flexcan_read_frame_from_ring(struct net_device *dev) +{ + struct net_device_stats *stats = &dev->stats; + struct flexcan_priv *priv = netdev_priv(dev); + struct can_frame *cf; + struct sk_buff *skb; + unsigned long flags; + + skb = alloc_can_skb(dev, &cf); + if (unlikely(!skb)) { + stats->rx_dropped++; + return 1; /* was 0, but that's an infinite loop bug */ + } + /* Assumes that the ring-empty test already done */ + memcpy(cf, &priv->read_ring.frames[priv->read_ring.output], sizeof(*cf)); + + spin_lock_irqsave(&priv->read_ring.ring_lock, flags); + priv->read_ring.full = 0; + priv->read_ring.output += 1; + if (priv->read_ring.output >= FLEXCAN_READ_RING_SIZE) { + priv->read_ring.output = 0; + } + spin_unlock_irqrestore(&priv->read_ring.ring_lock, flags); + + netif_receive_skb(skb); + + return 1; +} + static int flexcan_poll(struct napi_struct *napi, int quota) { struct net_device *dev = napi->dev; - const struct flexcan_priv *priv = netdev_priv(dev); + struct flexcan_priv *priv = netdev_priv(dev); struct flexcan_regs __iomem *regs = priv->base; - u32 reg_iflag1, reg_esr; + u32 reg_esr; int work_done = 0; /* @@ -545,16 +615,16 @@ static int flexcan_poll(struct napi_struct *napi, int quota) * use saved value from irq handler. */ reg_esr = flexcan_read(®s->esr) | priv->reg_esr; + priv->reg_esr = 0; /* handle state changes */ work_done += flexcan_poll_state(dev, reg_esr); - /* handle RX-FIFO */ - reg_iflag1 = flexcan_read(®s->iflag1); - while (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE && + /* handle RX-RING */ + while (((priv->read_ring.full) || + (priv->read_ring.input != priv->read_ring.output)) && work_done < quota) { - work_done += flexcan_read_frame(dev); - reg_iflag1 = flexcan_read(®s->iflag1); + work_done += flexcan_read_frame_from_ring(dev); } /* report bus errors */ @@ -563,9 +633,6 @@ static int flexcan_poll(struct napi_struct *napi, int quota) if (work_done < quota) { napi_complete(napi); - /* enable IRQs */ - flexcan_write(FLEXCAN_IFLAG_DEFAULT, ®s->imask1); - flexcan_write(priv->reg_ctrl_default, ®s->ctrl); } return work_done; @@ -598,11 +665,14 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) * The error bits are cleared on read, * save them for later use. */ - priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS; - flexcan_write(FLEXCAN_IFLAG_DEFAULT & - ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->imask1); - flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL, - ®s->ctrl); + priv->reg_esr |= reg_esr & FLEXCAN_ESR_ERR_BUS; + /* + * Read the FIFO into the rings. + */ + while (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) { + flexcan_read_frame_to_ring(dev); + reg_iflag1 = flexcan_read(®s->iflag1); + } napi_schedule(&priv->napi); } @@ -1012,6 +1082,7 @@ static int __devinit flexcan_probe(struct platform_device *pdev) priv->dev = dev; priv->clk = clk; priv->pdata = pdev->dev.platform_data; + spin_lock_init(&priv->read_ring.ring_lock); netif_napi_add(dev, &priv->napi, flexcan_poll, FLEXCAN_NAPI_WEIGHT);