[PATCH v2 5/5] can: flexcan: Add support for non RX-FIFO mode

Bhupesh Sharma bhupesh.sharma at freescale.com
Thu May 14 04:33:35 PDT 2015


This patch adds support for non RX-FIFO (legacy) mode in
the flexcan driver.

On certain SoCs, the RX-FIFO support might be broken, as
a result we need to fall-back on the legacy (non RX-FIFO)
mode to receive CAN frames.

Signed-off-by: Bhupesh Sharma <bhupesh.sharma at freescale.com>
Signed-off-by: Sakar Arora <Sakar.Arora at freescale.com>
---
 drivers/net/can/flexcan.c |  188 +++++++++++++++++++++++++++++++++------------
 1 file changed, 138 insertions(+), 50 deletions(-)

diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index b0222ae..3240472 100644
--- a/drivers/net/can/flexcan.c
+++ b/drivers/net/can/flexcan.c
@@ -156,6 +156,9 @@
 #define FLEXCAN_IFLAG_DEFAULT \
 	(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW | FLEXCAN_IFLAG_RX_FIFO_AVAILABLE | \
 	 FLEXCAN_IFLAG_BUF(FLEXCAN_TX_BUF_ID))
+#define FLEXCAN_IFLAG_DEFAULT_RX_MB_MODE \
+	 (FLEXCAN_IFLAG_BUF(FLEXCAN_TX_BUF_ID))
+#define FLEXCAN_IFLAG_RX_MB_RXMASK	((1 << FLEXCAN_TX_BUF_RESERVED) - 1)
 
 /* FLEXCAN message buffers */
 #define FLEXCAN_MB_CNT_CODE(x)		(((x) & 0xf) << 24)
@@ -198,6 +201,8 @@
 #define FLEXCAN_HAS_V10_FEATURES	BIT(1) /* For core version >= 10 */
 #define FLEXCAN_HAS_BROKEN_ERR_STATE	BIT(2) /* [TR]WRN_INT not connected */
 #define FLEXCAN_HAS_MECR_FEATURES	BIT(3) /* Memory error detection */
+#define FLEXCAN_HAS_ONLY_LEGACY_RX_SUPPORT \
+	 BIT(4) /* No RX FIFO mode support */
 
 /* Structure of the message buffer */
 struct flexcan_mb {
@@ -252,6 +257,7 @@ struct flexcan_priv {
 	void __iomem *base;
 	u32 reg_esr;
 	u32 reg_ctrl_default;
+	u32 rx_msg_buf;
 
 	struct clk *clk_ipg;
 	struct clk *clk_per;
@@ -303,8 +309,7 @@ static const struct can_bittiming_const flexcan_bittiming_const = {
 	.brp_inc = 1,
 };
 
-/*
- * FlexCAN module is essentially modelled as a little-endian IP in most
+/* FlexCAN module is essentially modelled as a little-endian IP in most
  * SoCs, i.e the registers as well as the message buffer areas are
  * implemented in a little-endian fashion.
  *
@@ -646,12 +651,10 @@ static int flexcan_poll_state(struct net_device *dev, u32 reg_esr)
 	return 1;
 }
 
-static void flexcan_read_fifo(const struct net_device *dev,
-			      struct can_frame *cf)
+static void flexcan_read_can_frame(const struct flexcan_priv *priv,
+				   struct flexcan_mb __iomem *mb,
+				   struct can_frame *cf)
 {
-	const struct flexcan_priv *priv = netdev_priv(dev);
-	struct flexcan_regs __iomem *regs = priv->base;
-	struct flexcan_mb __iomem *mb = &regs->cantxfg[0];
 	u32 reg_ctrl, reg_id;
 
 	reg_ctrl = priv->read(&mb->can_ctrl);
@@ -667,14 +670,39 @@ static void flexcan_read_fifo(const struct net_device *dev,
 
 	*(__be32 *)(cf->data + 0) = cpu_to_be32(priv->read(&mb->data[0]));
 	*(__be32 *)(cf->data + 4) = cpu_to_be32(priv->read(&mb->data[1]));
+}
+
+static void flexcan_read_fifo(const struct net_device *dev,
+			      struct can_frame *cf)
+{
+	const struct flexcan_priv *priv = netdev_priv(dev);
+	struct flexcan_regs __iomem *regs = priv->base;
+	struct flexcan_mb __iomem *mb = &regs->cantxfg[0];
+
+	flexcan_read_can_frame(priv, mb, cf);
 
 	/* mark as read */
 	priv->write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, &regs->iflag1);
 	priv->read(&regs->timer);
 }
 
+static void flexcan_read_msg_buf(const struct net_device *dev,
+				 struct can_frame *cf, u32 msg_buf)
+{
+	const struct flexcan_priv *priv = netdev_priv(dev);
+	struct flexcan_regs __iomem *regs = priv->base;
+	struct flexcan_mb __iomem *mb = &regs->cantxfg[msg_buf];
+
+	flexcan_read_can_frame(priv, mb, cf);
+
+	/* mark as read */
+	priv->write(BIT(msg_buf), &regs->iflag1);
+	priv->read(&regs->timer);
+}
+
 static int flexcan_read_frame(struct net_device *dev)
 {
+	const struct flexcan_priv *priv = netdev_priv(dev);
 	struct net_device_stats *stats = &dev->stats;
 	struct can_frame *cf;
 	struct sk_buff *skb;
@@ -685,7 +713,11 @@ static int flexcan_read_frame(struct net_device *dev)
 		return 0;
 	}
 
-	flexcan_read_fifo(dev, cf);
+	if (priv->devtype_data->features & FLEXCAN_HAS_ONLY_LEGACY_RX_SUPPORT)
+		flexcan_read_msg_buf(dev, cf, priv->rx_msg_buf);
+	else
+		flexcan_read_fifo(dev, cf);
+
 	netif_receive_skb(skb);
 
 	stats->rx_packets++;
@@ -699,9 +731,10 @@ static int flexcan_read_frame(struct net_device *dev)
 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;
+	unsigned long iflag1;
 	int work_done = 0;
 
 	/*
@@ -713,12 +746,25 @@ static int flexcan_poll(struct napi_struct *napi, int quota)
 	/* handle state changes */
 	work_done += flexcan_poll_state(dev, reg_esr);
 
-	/* handle RX-FIFO */
 	reg_iflag1 = priv->read(&regs->iflag1);
-	while (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE &&
-	       work_done < quota) {
-		work_done += flexcan_read_frame(dev);
-		reg_iflag1 = priv->read(&regs->iflag1);
+
+	if (priv->devtype_data->features & FLEXCAN_HAS_ONLY_LEGACY_RX_SUPPORT) {
+		/* handle legacy RX mode */
+		iflag1 = reg_iflag1 & FLEXCAN_IFLAG_RX_MB_RXMASK;
+		while ((reg_iflag1 & FLEXCAN_IFLAG_RX_MB_RXMASK) &&
+		       work_done < quota) {
+			priv->rx_msg_buf = find_first_bit(&iflag1,
+						(FLEXCAN_TX_BUF_ID - 1));
+			work_done += flexcan_read_frame(dev);
+			reg_iflag1 = priv->read(&regs->iflag1);
+		}
+	} else {
+		/* handle RX-FIFO */
+		while (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE &&
+		       work_done < quota) {
+			work_done += flexcan_read_frame(dev);
+			reg_iflag1 = priv->read(&regs->iflag1);
+		}
 	}
 
 	/* report bus errors */
@@ -728,7 +774,13 @@ static int flexcan_poll(struct napi_struct *napi, int quota)
 	if (work_done < quota) {
 		napi_complete(napi);
 		/* enable IRQs */
-		priv->write(FLEXCAN_IFLAG_DEFAULT, &regs->imask1);
+		if (priv->devtype_data->features &
+		    FLEXCAN_HAS_ONLY_LEGACY_RX_SUPPORT)
+			priv->write(FLEXCAN_IFLAG_DEFAULT_RX_MB_MODE |
+				    FLEXCAN_IFLAG_RX_MB_RXMASK, &regs->imask1);
+		else
+			priv->write(FLEXCAN_IFLAG_DEFAULT, &regs->imask1);
+
 		priv->write(priv->reg_ctrl_default, &regs->ctrl);
 	}
 
@@ -755,26 +807,45 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
 	 * - state change IRQ
 	 * - bus error IRQ and bus error reporting is activated
 	 */
-	if ((reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) ||
-	    (reg_esr & FLEXCAN_ESR_ERR_STATE) ||
-	    flexcan_has_and_handle_berr(priv, reg_esr)) {
-		/*
-		 * The error bits are cleared on read,
-		 * save them for later use.
-		 */
-		priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS;
-		priv->write(FLEXCAN_IFLAG_DEFAULT &
-			~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, &regs->imask1);
-		priv->write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
-		       &regs->ctrl);
-		napi_schedule(&priv->napi);
-	}
+	if (priv->devtype_data->features & FLEXCAN_HAS_ONLY_LEGACY_RX_SUPPORT) {
+		if ((reg_iflag1 & FLEXCAN_IFLAG_RX_MB_RXMASK) ||
+		    (reg_esr & FLEXCAN_ESR_ERR_STATE) ||
+		    flexcan_has_and_handle_berr(priv, reg_esr)) {
+			/* The error bits are cleared on read,
+			 * save them for later use.
+			 */
+			priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS;
+			priv->write(FLEXCAN_IFLAG_DEFAULT_RX_MB_MODE &
+				    ~FLEXCAN_IFLAG_RX_MB_RXMASK, &regs->imask1);
+			priv->write(priv->reg_ctrl_default &
+				    ~FLEXCAN_CTRL_ERR_ALL, &regs->ctrl);
+
+			napi_schedule(&priv->napi);
+		}
+	} else {
+		if ((reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) ||
+		    (reg_esr & FLEXCAN_ESR_ERR_STATE) ||
+		    flexcan_has_and_handle_berr(priv, reg_esr)) {
+			/*
+			 * The error bits are cleared on read,
+			 * save them for later use.
+			 */
+			priv->reg_esr = reg_esr & FLEXCAN_ESR_ERR_BUS;
+			priv->write(FLEXCAN_IFLAG_DEFAULT &
+				    ~FLEXCAN_IFLAG_RX_FIFO_AVAILABLE,
+				    &regs->imask1);
+			priv->write(priv->reg_ctrl_default &
+				    ~FLEXCAN_CTRL_ERR_ALL, &regs->ctrl);
+			napi_schedule(&priv->napi);
+		}
 
-	/* FIFO overflow */
-	if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) {
-		priv->write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, &regs->iflag1);
-		dev->stats.rx_over_errors++;
-		dev->stats.rx_errors++;
+		/* FIFO overflow */
+		if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) {
+			priv->write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW,
+				    &regs->iflag1);
+			dev->stats.rx_over_errors++;
+			dev->stats.rx_errors++;
+		}
 	}
 
 	/* transmission complete interrupt */
@@ -869,7 +940,12 @@ static int flexcan_chip_start(struct net_device *dev)
 	 */
 	reg_mcr = priv->read(&regs->mcr);
 	reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff);
-	reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT |
+	if (priv->devtype_data->features & FLEXCAN_HAS_ONLY_LEGACY_RX_SUPPORT)
+		reg_mcr &= ~FLEXCAN_MCR_FEN;
+	else
+		reg_mcr |= FLEXCAN_MCR_FEN;
+
+	reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT |
 		FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN |
 		FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_SRX_DIS |
 		FLEXCAN_MCR_MAXMB(FLEXCAN_TX_BUF_ID);
@@ -966,8 +1042,13 @@ static int flexcan_chip_start(struct net_device *dev)
 
 	priv->can.state = CAN_STATE_ERROR_ACTIVE;
 
-	/* enable FIFO interrupts */
-	priv->write(FLEXCAN_IFLAG_DEFAULT, &regs->imask1);
+	if (priv->devtype_data->features & FLEXCAN_HAS_ONLY_LEGACY_RX_SUPPORT)
+		/* enable mb interrupts */
+		priv->write(FLEXCAN_IFLAG_DEFAULT_RX_MB_MODE |
+			    FLEXCAN_IFLAG_RX_MB_RXMASK, &regs->imask1);
+	else
+		/* enable FIFO interrupts */
+		priv->write(FLEXCAN_IFLAG_DEFAULT, &regs->imask1);
 
 	/* print chip status */
 	netdev_dbg(dev, "%s: reading mcr=0x%08x ctrl=0x%08x\n", __func__,
@@ -1125,22 +1206,29 @@ static int register_flexcandev(struct net_device *dev)
 	if (err)
 		goto out_chip_disable;
 
-	/* set freeze, halt and activate FIFO, restrict register access */
+	/* set freeze, halt and activate FIFO/legacy mode, restrict
+	 * register access
+	 */
 	reg = priv->read(&regs->mcr);
-	reg |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT |
-		FLEXCAN_MCR_FEN | FLEXCAN_MCR_SUPV;
+	if (priv->devtype_data->features & FLEXCAN_HAS_ONLY_LEGACY_RX_SUPPORT)
+		reg &= ~FLEXCAN_MCR_FEN;
+	else
+		reg |= FLEXCAN_MCR_FEN;
+
+	reg |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT | FLEXCAN_MCR_SUPV;
 	priv->write(reg, &regs->mcr);
 
-	/*
-	 * Currently we only support newer versions of this core
-	 * featuring a RX FIFO. Older cores found on some Coldfire
-	 * derivates are not yet supported.
-	 */
-	reg = priv->read(&regs->mcr);
-	if (!(reg & FLEXCAN_MCR_FEN)) {
-		netdev_err(dev, "Could not enable RX FIFO, unsupported core\n");
-		err = -ENODEV;
-		goto out_chip_disable;
+	if (priv->devtype_data->features & FLEXCAN_HAS_ONLY_LEGACY_RX_SUPPORT) {
+		/* Legacy RX mode*/
+		netdev_info(dev, "Legacy mode (non RX-FIFO) enabled\n");
+	} else {
+		/* RX FIFO mode */
+		reg = priv->read(&regs->mcr);
+		if (!(reg & FLEXCAN_MCR_FEN)) {
+			netdev_err(dev, "Could not enable RX FIFO, unsupported core\n");
+			err = -ENODEV;
+			goto out_chip_disable;
+		}
 	}
 
 	err = register_candev(dev);
-- 
1.7.9.5





More information about the linux-arm-kernel mailing list