Commit b3cf53e9 authored by Marc Kleine-Budde's avatar Marc Kleine-Budde

can: flexcan: add support for timestamp based rx-offload

The flexcan IP core has 64 mailboxes. For now they are configured for
RX as a hardware FIFO. This FIFO has a fixed depth of 6 CAN frames. In
some high load scenarios it turns out thas this buffer is too small.

In order to have a buffer larger than the 6 frames FIFO, this patch adds
support for timestamp based offloading via the generic rx-offload
infrastructure.
Signed-off-by: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 9eb7aa89
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
* *
* Copyright (c) 2005-2006 Varma Electronics Oy * Copyright (c) 2005-2006 Varma Electronics Oy
* Copyright (c) 2009 Sascha Hauer, Pengutronix * Copyright (c) 2009 Sascha Hauer, Pengutronix
* Copyright (c) 2010 Marc Kleine-Budde, Pengutronix * Copyright (c) 2010-2017 Pengutronix, Marc Kleine-Budde <kernel@pengutronix.de>
* Copyright (c) 2014 David Jander, Protonic Holland
* *
* Based on code originally by Andrey Volkov <avolkov@varma-el.com> * Based on code originally by Andrey Volkov <avolkov@varma-el.com>
* *
...@@ -59,6 +60,7 @@ ...@@ -59,6 +60,7 @@
#define FLEXCAN_MCR_IRMQ BIT(16) #define FLEXCAN_MCR_IRMQ BIT(16)
#define FLEXCAN_MCR_LPRIO_EN BIT(13) #define FLEXCAN_MCR_LPRIO_EN BIT(13)
#define FLEXCAN_MCR_AEN BIT(12) #define FLEXCAN_MCR_AEN BIT(12)
/* MCR_MAXMB: maximum used MBs is MAXMB + 1 */
#define FLEXCAN_MCR_MAXMB(x) ((x) & 0x7f) #define FLEXCAN_MCR_MAXMB(x) ((x) & 0x7f)
#define FLEXCAN_MCR_IDAM_A (0x0 << 8) #define FLEXCAN_MCR_IDAM_A (0x0 << 8)
#define FLEXCAN_MCR_IDAM_B (0x1 << 8) #define FLEXCAN_MCR_IDAM_B (0x1 << 8)
...@@ -146,12 +148,18 @@ ...@@ -146,12 +148,18 @@
/* Errata ERR005829 step7: Reserve first valid MB */ /* Errata ERR005829 step7: Reserve first valid MB */
#define FLEXCAN_TX_MB_RESERVED_OFF_FIFO 8 #define FLEXCAN_TX_MB_RESERVED_OFF_FIFO 8
#define FLEXCAN_TX_MB_OFF_FIFO 9 #define FLEXCAN_TX_MB_OFF_FIFO 9
#define FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP 0
#define FLEXCAN_TX_MB_OFF_TIMESTAMP 1
#define FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST (FLEXCAN_TX_MB_OFF_TIMESTAMP + 1)
#define FLEXCAN_RX_MB_OFF_TIMESTAMP_LAST 63
#define FLEXCAN_IFLAG_MB(x) BIT(x) #define FLEXCAN_IFLAG_MB(x) BIT(x)
#define FLEXCAN_IFLAG_RX_FIFO_OVERFLOW BIT(7) #define FLEXCAN_IFLAG_RX_FIFO_OVERFLOW BIT(7)
#define FLEXCAN_IFLAG_RX_FIFO_WARN BIT(6) #define FLEXCAN_IFLAG_RX_FIFO_WARN BIT(6)
#define FLEXCAN_IFLAG_RX_FIFO_AVAILABLE BIT(5) #define FLEXCAN_IFLAG_RX_FIFO_AVAILABLE BIT(5)
/* FLEXCAN message buffers */ /* FLEXCAN message buffers */
#define FLEXCAN_MB_CODE_MASK (0xf << 24)
#define FLEXCAN_MB_CODE_RX_BUSY_BIT (0x1 << 24)
#define FLEXCAN_MB_CODE_RX_INACTIVE (0x0 << 24) #define FLEXCAN_MB_CODE_RX_INACTIVE (0x0 << 24)
#define FLEXCAN_MB_CODE_RX_EMPTY (0x4 << 24) #define FLEXCAN_MB_CODE_RX_EMPTY (0x4 << 24)
#define FLEXCAN_MB_CODE_RX_FULL (0x2 << 24) #define FLEXCAN_MB_CODE_RX_FULL (0x2 << 24)
...@@ -189,6 +197,7 @@ ...@@ -189,6 +197,7 @@
#define FLEXCAN_QUIRK_DISABLE_RXFG BIT(2) /* Disable RX FIFO Global mask */ #define FLEXCAN_QUIRK_DISABLE_RXFG BIT(2) /* Disable RX FIFO Global mask */
#define FLEXCAN_QUIRK_ENABLE_EACEN_RRS BIT(3) /* Enable EACEN and RRS bit in ctrl2 */ #define FLEXCAN_QUIRK_ENABLE_EACEN_RRS BIT(3) /* Enable EACEN and RRS bit in ctrl2 */
#define FLEXCAN_QUIRK_DISABLE_MECR BIT(4) /* Disble Memory error detection */ #define FLEXCAN_QUIRK_DISABLE_MECR BIT(4) /* Disble Memory error detection */
#define FLEXCAN_QUIRK_USE_OFF_TIMESTAMP BIT(5) /* Use timestamp based offloading */
/* Structure of the message buffer */ /* Structure of the message buffer */
struct flexcan_mb { struct flexcan_mb {
...@@ -263,6 +272,7 @@ struct flexcan_priv { ...@@ -263,6 +272,7 @@ struct flexcan_priv {
u8 tx_mb_idx; u8 tx_mb_idx;
u32 reg_ctrl_default; u32 reg_ctrl_default;
u32 reg_imask1_default; u32 reg_imask1_default;
u32 reg_imask2_default;
struct clk *clk_ipg; struct clk *clk_ipg;
struct clk *clk_per; struct clk *clk_per;
...@@ -624,11 +634,32 @@ static unsigned int flexcan_mailbox_read(struct can_rx_offload *offload, ...@@ -624,11 +634,32 @@ static unsigned int flexcan_mailbox_read(struct can_rx_offload *offload,
struct flexcan_mb __iomem *mb = &regs->mb[n]; struct flexcan_mb __iomem *mb = &regs->mb[n];
u32 reg_ctrl, reg_id, reg_iflag1; u32 reg_ctrl, reg_id, reg_iflag1;
reg_iflag1 = flexcan_read(&regs->iflag1); if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
if (!(reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE)) u32 code;
return 0;
do {
reg_ctrl = flexcan_read(&mb->can_ctrl);
} while (reg_ctrl & FLEXCAN_MB_CODE_RX_BUSY_BIT);
/* is this MB empty? */
code = reg_ctrl & FLEXCAN_MB_CODE_MASK;
if ((code != FLEXCAN_MB_CODE_RX_FULL) &&
(code != FLEXCAN_MB_CODE_RX_OVERRUN))
return 0;
if (code == FLEXCAN_MB_CODE_RX_OVERRUN) {
/* This MB was overrun, we lost data */
offload->dev->stats.rx_over_errors++;
offload->dev->stats.rx_errors++;
}
} else {
reg_iflag1 = flexcan_read(&regs->iflag1);
if (!(reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE))
return 0;
reg_ctrl = flexcan_read(&mb->can_ctrl);
}
reg_ctrl = flexcan_read(&mb->can_ctrl);
/* increase timstamp to full 32 bit */ /* increase timstamp to full 32 bit */
*timestamp = reg_ctrl << 16; *timestamp = reg_ctrl << 16;
...@@ -646,12 +677,33 @@ static unsigned int flexcan_mailbox_read(struct can_rx_offload *offload, ...@@ -646,12 +677,33 @@ static unsigned int flexcan_mailbox_read(struct can_rx_offload *offload,
*(__be32 *)(cf->data + 4) = cpu_to_be32(flexcan_read(&mb->data[1])); *(__be32 *)(cf->data + 4) = cpu_to_be32(flexcan_read(&mb->data[1]));
/* mark as read */ /* mark as read */
flexcan_write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, &regs->iflag1); if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
flexcan_read(&regs->timer); /* Clear IRQ */
if (n < 32)
flexcan_write(BIT(n), &regs->iflag1);
else
flexcan_write(BIT(n - 32), &regs->iflag2);
} else {
flexcan_write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, &regs->iflag1);
flexcan_read(&regs->timer);
}
return 1; return 1;
} }
static inline u64 flexcan_read_reg_iflag_rx(struct flexcan_priv *priv)
{
struct flexcan_regs __iomem *regs = priv->regs;
u32 iflag1, iflag2;
iflag2 = flexcan_read(&regs->iflag2) & priv->reg_imask2_default;
iflag1 = flexcan_read(&regs->iflag1) & priv->reg_imask1_default &
~FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
return (u64)iflag2 << 32 | iflag1;
}
static irqreturn_t flexcan_irq(int irq, void *dev_id) static irqreturn_t flexcan_irq(int irq, void *dev_id)
{ {
struct net_device *dev = dev_id; struct net_device *dev = dev_id;
...@@ -664,17 +716,30 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) ...@@ -664,17 +716,30 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id)
reg_iflag1 = flexcan_read(&regs->iflag1); reg_iflag1 = flexcan_read(&regs->iflag1);
/* reception interrupt */ /* reception interrupt */
if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) { if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
handled = IRQ_HANDLED; u64 reg_iflag;
can_rx_offload_irq_offload_fifo(&priv->offload); int ret;
}
while ((reg_iflag = flexcan_read_reg_iflag_rx(priv))) {
handled = IRQ_HANDLED;
ret = can_rx_offload_irq_offload_timestamp(&priv->offload,
reg_iflag);
if (!ret)
break;
}
} else {
if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) {
handled = IRQ_HANDLED;
can_rx_offload_irq_offload_fifo(&priv->offload);
}
/* FIFO overflow interrupt */ /* FIFO overflow interrupt */
if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) { if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) {
handled = IRQ_HANDLED; handled = IRQ_HANDLED;
flexcan_write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, &regs->iflag1); flexcan_write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, &regs->iflag1);
dev->stats.rx_over_errors++; dev->stats.rx_over_errors++;
dev->stats.rx_errors++; dev->stats.rx_errors++;
}
} }
/* transmission complete interrupt */ /* transmission complete interrupt */
...@@ -787,10 +852,17 @@ static int flexcan_chip_start(struct net_device *dev) ...@@ -787,10 +852,17 @@ static int flexcan_chip_start(struct net_device *dev)
*/ */
reg_mcr = flexcan_read(&regs->mcr); reg_mcr = flexcan_read(&regs->mcr);
reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff); reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff);
reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT | reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT | FLEXCAN_MCR_SUPV |
FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_IRMQ |
FLEXCAN_MCR_IRMQ | FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_IDAM_C;
FLEXCAN_MCR_MAXMB(priv->tx_mb_idx);
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
reg_mcr &= ~FLEXCAN_MCR_FEN;
reg_mcr |= FLEXCAN_MCR_MAXMB(priv->offload.mb_last);
} else {
reg_mcr |= FLEXCAN_MCR_FEN |
FLEXCAN_MCR_MAXMB(priv->tx_mb_idx);
}
netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr); netdev_dbg(dev, "%s: writing mcr=0x%08x", __func__, reg_mcr);
flexcan_write(reg_mcr, &regs->mcr); flexcan_write(reg_mcr, &regs->mcr);
...@@ -839,6 +911,12 @@ static int flexcan_chip_start(struct net_device *dev) ...@@ -839,6 +911,12 @@ static int flexcan_chip_start(struct net_device *dev)
&regs->mb[i].can_ctrl); &regs->mb[i].can_ctrl);
} }
if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
for (i = priv->offload.mb_first; i <= priv->offload.mb_last; i++)
flexcan_write(FLEXCAN_MB_CODE_RX_EMPTY,
&regs->mb[i].can_ctrl);
}
/* Errata ERR005829: mark first TX mailbox as INACTIVE */ /* Errata ERR005829: mark first TX mailbox as INACTIVE */
flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE, flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE,
&priv->tx_mb_reserved->can_ctrl); &priv->tx_mb_reserved->can_ctrl);
...@@ -897,6 +975,7 @@ static int flexcan_chip_start(struct net_device *dev) ...@@ -897,6 +975,7 @@ static int flexcan_chip_start(struct net_device *dev)
disable_irq(dev->irq); disable_irq(dev->irq);
flexcan_write(priv->reg_ctrl_default, &regs->ctrl); flexcan_write(priv->reg_ctrl_default, &regs->ctrl);
flexcan_write(priv->reg_imask1_default, &regs->imask1); flexcan_write(priv->reg_imask1_default, &regs->imask1);
flexcan_write(priv->reg_imask2_default, &regs->imask2);
enable_irq(dev->irq); enable_irq(dev->irq);
/* print chip status */ /* print chip status */
...@@ -926,6 +1005,7 @@ static void flexcan_chip_stop(struct net_device *dev) ...@@ -926,6 +1005,7 @@ static void flexcan_chip_stop(struct net_device *dev)
flexcan_chip_disable(priv); flexcan_chip_disable(priv);
/* Disable all interrupts */ /* Disable all interrupts */
flexcan_write(0, &regs->imask2);
flexcan_write(0, &regs->imask1); flexcan_write(0, &regs->imask1);
flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL, flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
&regs->ctrl); &regs->ctrl);
...@@ -1058,8 +1138,9 @@ static int register_flexcandev(struct net_device *dev) ...@@ -1058,8 +1138,9 @@ static int register_flexcandev(struct net_device *dev)
flexcan_write(reg, &regs->mcr); flexcan_write(reg, &regs->mcr);
/* Currently we only support newer versions of this core /* Currently we only support newer versions of this core
* featuring a RX FIFO. Older cores found on some Coldfire * featuring a RX hardware FIFO (although this driver doesn't
* derivates are not yet supported. * make use of it on some cores). Older cores, found on some
* Coldfire derivates are not tested.
*/ */
reg = flexcan_read(&regs->mcr); reg = flexcan_read(&regs->mcr);
if (!(reg & FLEXCAN_MCR_FEN)) { if (!(reg & FLEXCAN_MCR_FEN)) {
...@@ -1183,17 +1264,36 @@ static int flexcan_probe(struct platform_device *pdev) ...@@ -1183,17 +1264,36 @@ static int flexcan_probe(struct platform_device *pdev)
priv->devtype_data = devtype_data; priv->devtype_data = devtype_data;
priv->reg_xceiver = reg_xceiver; priv->reg_xceiver = reg_xceiver;
priv->tx_mb_idx = FLEXCAN_TX_MB_OFF_FIFO; if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
priv->tx_mb_reserved = &regs->mb[FLEXCAN_TX_MB_RESERVED_OFF_FIFO]; priv->tx_mb_idx = FLEXCAN_TX_MB_OFF_TIMESTAMP;
priv->tx_mb_reserved = &regs->mb[FLEXCAN_TX_MB_RESERVED_OFF_TIMESTAMP];
} else {
priv->tx_mb_idx = FLEXCAN_TX_MB_OFF_FIFO;
priv->tx_mb_reserved = &regs->mb[FLEXCAN_TX_MB_RESERVED_OFF_FIFO];
}
priv->tx_mb = &regs->mb[priv->tx_mb_idx]; priv->tx_mb = &regs->mb[priv->tx_mb_idx];
priv->reg_imask1_default = FLEXCAN_IFLAG_RX_FIFO_OVERFLOW | priv->reg_imask1_default = FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
FLEXCAN_IFLAG_RX_FIFO_AVAILABLE | priv->reg_imask2_default = 0;
FLEXCAN_IFLAG_MB(priv->tx_mb_idx);
priv->offload.mailbox_read = flexcan_mailbox_read; priv->offload.mailbox_read = flexcan_mailbox_read;
err = can_rx_offload_add_fifo(dev, &priv->offload, FLEXCAN_NAPI_WEIGHT); if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
u64 imask;
priv->offload.mb_first = FLEXCAN_RX_MB_OFF_TIMESTAMP_FIRST;
priv->offload.mb_last = FLEXCAN_RX_MB_OFF_TIMESTAMP_LAST;
imask = GENMASK_ULL(priv->offload.mb_last, priv->offload.mb_first);
priv->reg_imask1_default |= imask;
priv->reg_imask2_default |= imask >> 32;
err = can_rx_offload_add_timestamp(dev, &priv->offload);
} else {
priv->reg_imask1_default |= FLEXCAN_IFLAG_RX_FIFO_OVERFLOW |
FLEXCAN_IFLAG_RX_FIFO_AVAILABLE;
err = can_rx_offload_add_fifo(dev, &priv->offload, FLEXCAN_NAPI_WEIGHT);
}
if (err) if (err)
goto failed_offload; goto failed_offload;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment