summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBhupesh Sharma <bhupesh.sharma@freescale.com>2015-06-15 11:14:10 (GMT)
committerZhengxiong Jin <Jason.Jin@freescale.com>2015-06-16 03:32:00 (GMT)
commit6e9c5a3e7af574a47c112881e7f7df7348d54a1e (patch)
tree2b47ddfdc18b332a8255bda036e2f07084b0f653
parentc466de88b5b0991aa9fe8b4ad900b242ebfe9cbf (diff)
downloadlinux-fsl-qoriq-6e9c5a3e7af574a47c112881e7f7df7348d54a1e.tar.xz
can: flexcan: Add support for non RX-FIFO mode
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@freescale.com> Signed-off-by: Sakar Arora <Sakar.Arora@freescale.com> Change-Id: I8b07e851b68fcca9716d02b14b6712c2da654ad5 Reviewed-on: http://git.am.freescale.net:8181/38095 Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> Reviewed-by: Zhengxiong Jin <Jason.Jin@freescale.com>
-rw-r--r--drivers/net/can/flexcan.c186
1 files changed, 135 insertions, 51 deletions
diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c
index 9ed4a54..87c1853 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)
@@ -255,6 +258,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;
@@ -306,8 +310,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.
*
@@ -454,7 +457,6 @@ static int flexcan_chip_softreset(struct flexcan_priv *priv)
return 0;
}
-
static int __flexcan_get_berr_counter(const struct net_device *dev,
struct can_berr_counter *bec)
{
@@ -716,12 +718,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);
@@ -737,14 +737,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;
@@ -755,7 +780,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++;
@@ -769,9 +798,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;
/*
@@ -783,12 +813,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 */
@@ -798,7 +841,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);
}
@@ -825,26 +874,44 @@ 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 */
@@ -940,7 +1007,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);
@@ -1037,8 +1109,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__,
@@ -1195,22 +1272,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);