[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 = ®s->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 = ®s->cantxfg[0];
+
+ flexcan_read_can_frame(priv, mb, cf);
/* mark as read */
priv->write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->iflag1);
priv->read(®s->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 = ®s->cantxfg[msg_buf];
+
+ flexcan_read_can_frame(priv, mb, cf);
+
+ /* mark as read */
+ priv->write(BIT(msg_buf), ®s->iflag1);
+ priv->read(®s->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(®s->iflag1);
- while (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE &&
- work_done < quota) {
- work_done += flexcan_read_frame(dev);
- reg_iflag1 = priv->read(®s->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(®s->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(®s->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, ®s->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, ®s->imask1);
+ else
+ priv->write(FLEXCAN_IFLAG_DEFAULT, ®s->imask1);
+
priv->write(priv->reg_ctrl_default, ®s->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, ®s->imask1);
- priv->write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
- ®s->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, ®s->imask1);
+ priv->write(priv->reg_ctrl_default &
+ ~FLEXCAN_CTRL_ERR_ALL, ®s->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,
+ ®s->imask1);
+ priv->write(priv->reg_ctrl_default &
+ ~FLEXCAN_CTRL_ERR_ALL, ®s->ctrl);
+ napi_schedule(&priv->napi);
+ }
- /* FIFO overflow */
- if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) {
- priv->write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, ®s->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,
+ ®s->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(®s->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, ®s->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, ®s->imask1);
+ else
+ /* enable FIFO interrupts */
+ priv->write(FLEXCAN_IFLAG_DEFAULT, ®s->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(®s->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, ®s->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(®s->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(®s->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