From mboxrd@z Thu Jan 1 00:00:00 1970 From: Marc Kleine-Budde Subject: [PATCH 09/10] can: flexcan: add support for rx-fifo based software FIFO implementation Date: Mon, 9 May 2016 12:52:33 +0200 Message-ID: <1462791154-13375-10-git-send-email-mkl@pengutronix.de> References: <1462791154-13375-1-git-send-email-mkl@pengutronix.de> Return-path: Received: from metis.ext.4.pengutronix.de ([92.198.50.35]:53437 "EHLO metis.ext.4.pengutronix.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751420AbcEIKwr (ORCPT ); Mon, 9 May 2016 06:52:47 -0400 In-Reply-To: <1462791154-13375-1-git-send-email-mkl@pengutronix.de> Sender: linux-can-owner@vger.kernel.org List-ID: To: linux-can@vger.kernel.org Cc: Marc Kleine-Budde 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 a software based RX-FIFO bases on the generic rx-fifo infrastructure. Signed-off-by: Marc Kleine-Budde --- drivers/net/can/flexcan.c | 189 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 163 insertions(+), 26 deletions(-) diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index b1135012ced3..9c7a9ce5b308 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -3,7 +3,8 @@ * * Copyright (c) 2005-2006 Varma Electronics Oy * Copyright (c) 2009 Sascha Hauer, Pengutronix - * Copyright (c) 2010 Marc Kleine-Budde, Pengutronix + * Copyright (c) 2010-2016 Pengutronix, Marc Kleine-Budde + * Copyright (c) 2014 David Jander, Protonic Holland * * Based on code originally by Andrey Volkov * @@ -59,6 +60,7 @@ #define FLEXCAN_MCR_IRMQ BIT(16) #define FLEXCAN_MCR_LPRIO_EN BIT(13) #define FLEXCAN_MCR_AEN BIT(12) +/* MCR_MAXMB: maximum used MBs is MAXMB + 1 */ #define FLEXCAN_MCR_MAXMB(x) ((x) & 0x7f) #define FLEXCAN_MCR_IDAM_A (0x0 << 8) #define FLEXCAN_MCR_IDAM_B (0x1 << 8) @@ -146,12 +148,19 @@ /* Errata ERR005829 step7: Reserve first valid MB */ #define FLEXCAN_TX_MB_RESERVED_HW_FIFO 8 #define FLEXCAN_TX_MB_HW_FIFO 9 +#define FLEXCAN_TX_MB_RESERVED_SW_FIFO 0 +#define FLEXCAN_TX_MB_SW_FIFO 1 +#define FLEXCAN_RX_MB_LOW_FIRST (FLEXCAN_TX_MB_SW_FIFO + 1) +#define FLEXCAN_RX_MB_HIGH_FIRST 32 +#define FLEXCAN_RX_MB_HIGH_LAST 63 #define FLEXCAN_IFLAG_MB(x) BIT(x) #define FLEXCAN_IFLAG_RX_FIFO_OVERFLOW BIT(7) #define FLEXCAN_IFLAG_RX_FIFO_WARN BIT(6) #define FLEXCAN_IFLAG_RX_FIFO_AVAILABLE BIT(5) /* 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_EMPTY (0x4 << 24) #define FLEXCAN_MB_CODE_RX_FULL (0x2 << 24) @@ -189,6 +198,7 @@ #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_DISABLE_MECR BIT(4) /* Disble Memory error detection */ +#define FLEXCAN_QUIRK_USE_SW_FIFO BIT(5) /* Use SW-FIFO */ /* Structure of the message buffer */ struct flexcan_mb { @@ -265,6 +275,7 @@ struct flexcan_priv { u32 poll_esr; /* used in flexcan_poll_bus_err */ u32 reg_ctrl_default; u32 reg_imask1_default; + u32 reg_imask2_default; struct clk *clk_ipg; struct clk *clk_per; @@ -652,6 +663,49 @@ static unsigned int flexcan_poll_state(struct can_rx_fifo *fifo) return 1; } +static void do_mailbox_enable(struct flexcan_mb __iomem *mb) +{ + u32 reg_ctrl; + u32 code; + + do { + reg_ctrl = flexcan_read(&mb->can_ctrl); + } while (reg_ctrl & FLEXCAN_MB_CODE_RX_BUSY_BIT); + + code = reg_ctrl & FLEXCAN_MB_CODE_MASK; + if (code == FLEXCAN_MB_CODE_RX_INACTIVE) { + flexcan_write(FLEXCAN_MB_CODE_RX_EMPTY, &mb->can_ctrl); + } +} + +static void flexcan_mailbox_enable(struct can_rx_fifo *fifo, unsigned int n) +{ + struct flexcan_priv *priv = rx_fifo_to_priv(fifo); + struct flexcan_regs __iomem *regs = priv->regs; + + do_mailbox_enable(®s->mb[n]); + + /* unlock mailbox */ + flexcan_read(®s->timer); +} + +static void flexcan_mailbox_enable_mask(struct can_rx_fifo *fifo, u64 mask) +{ + struct flexcan_priv *priv = rx_fifo_to_priv(fifo); + struct flexcan_regs __iomem *regs = priv->regs; + unsigned int i; + + for (i = priv->fifo.low_first; i <= priv->fifo.high_last; i++) { + if (!(mask & BIT_ULL(i))) + continue; + + do_mailbox_enable(®s->mb[i]); + } + + /* unlock mailbox */ + flexcan_read(®s->timer); +} + static unsigned int flexcan_mailbox_read(struct can_rx_fifo *fifo, struct can_frame *cf, unsigned int n) { @@ -660,11 +714,32 @@ static unsigned int flexcan_mailbox_read(struct can_rx_fifo *fifo, struct flexcan_mb __iomem *mb = ®s->mb[n]; u32 reg_ctrl, reg_id, reg_iflag1; - reg_iflag1 = flexcan_read(®s->iflag1); - if (!(reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE)) - return 0; + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_SW_FIFO) { + u32 code; + + 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 */ + fifo->dev->stats.rx_over_errors++; + fifo->dev->stats.rx_errors++; + } + } else { + reg_iflag1 = flexcan_read(®s->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); reg_id = flexcan_read(&mb->can_id); if (reg_ctrl & FLEXCAN_MB_CNT_IDE) cf->can_id = ((reg_id >> 0) & CAN_EFF_MASK) | CAN_EFF_FLAG; @@ -679,8 +754,18 @@ static unsigned int flexcan_mailbox_read(struct can_rx_fifo *fifo, *(__be32 *)(cf->data + 4) = cpu_to_be32(flexcan_read(&mb->data[1])); /* mark as read */ - flexcan_write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->iflag1); - flexcan_read(®s->timer); + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_SW_FIFO) { + /* Clear IRQ and lock MB */ + if (n < 32) + flexcan_write(BIT(n), ®s->iflag1); + else + flexcan_write(BIT(n - 32), ®s->iflag2); + + flexcan_write(FLEXCAN_MB_CODE_RX_INACTIVE, &mb->can_ctrl); + } else { + flexcan_write(FLEXCAN_IFLAG_RX_FIFO_AVAILABLE, ®s->iflag1); + flexcan_read(®s->timer); + } return 1; } @@ -721,14 +806,27 @@ static irqreturn_t flexcan_irq(int irq, void *dev_id) } /* reception interrupt */ - if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) - can_rx_fifo_irq_offload_simple(&priv->fifo); + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_SW_FIFO) { + u32 reg_iflag2; + u64 reg_iflag; - /* FIFO overflow */ - if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) { - flexcan_write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, ®s->iflag1); - dev->stats.rx_over_errors++; - dev->stats.rx_errors++; + reg_iflag2 = flexcan_read(®s->iflag2); + reg_iflag = (reg_iflag1 & (priv->reg_imask1_default & + ~FLEXCAN_IFLAG_MB(priv->tx_mb_idx))) | + ((u64)(reg_iflag2 & priv->reg_imask2_default) << 32); + + if (reg_iflag) + can_rx_fifo_irq_offload(&priv->fifo, reg_iflag); + } else { + if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_AVAILABLE) + can_rx_fifo_irq_offload_simple(&priv->fifo); + + /* FIFO overflow */ + if (reg_iflag1 & FLEXCAN_IFLAG_RX_FIFO_OVERFLOW) { + flexcan_write(FLEXCAN_IFLAG_RX_FIFO_OVERFLOW, ®s->iflag1); + dev->stats.rx_over_errors++; + dev->stats.rx_errors++; + } } /* transmission complete interrupt */ @@ -823,10 +921,17 @@ static int flexcan_chip_start(struct net_device *dev) */ reg_mcr = flexcan_read(®s->mcr); reg_mcr &= ~FLEXCAN_MCR_MAXMB(0xff); - reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_FEN | FLEXCAN_MCR_HALT | - FLEXCAN_MCR_SUPV | FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_SRX_DIS | - FLEXCAN_MCR_IRMQ | FLEXCAN_MCR_IDAM_C | - FLEXCAN_MCR_MAXMB(priv->tx_mb_idx); + reg_mcr |= FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT | FLEXCAN_MCR_SUPV | + FLEXCAN_MCR_WRN_EN | FLEXCAN_MCR_SRX_DIS | FLEXCAN_MCR_IRMQ | + FLEXCAN_MCR_IDAM_C | FLEXCAN_MCR_MAXMB(priv->tx_mb_idx); + + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_SW_FIFO) { + reg_mcr &= ~FLEXCAN_MCR_FEN; + reg_mcr |= FLEXCAN_MCR_MAXMB(priv->fifo.high_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); flexcan_write(reg_mcr, ®s->mcr); @@ -875,6 +980,12 @@ static int flexcan_chip_start(struct net_device *dev) ®s->mb[i].can_ctrl); } + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_SW_FIFO) { + for (i = priv->fifo.low_first; i <= priv->fifo.high_last; i++) + flexcan_write(FLEXCAN_MB_CODE_RX_EMPTY, + ®s->mb[i].can_ctrl); + } + /* Errata ERR005829: mark first TX mailbox as INACTIVE */ flexcan_write(FLEXCAN_MB_CODE_TX_INACTIVE, &priv->tx_mb_reserved->can_ctrl); @@ -933,6 +1044,7 @@ static int flexcan_chip_start(struct net_device *dev) disable_irq(dev->irq); flexcan_write(priv->reg_ctrl_default, ®s->ctrl); flexcan_write(priv->reg_imask1_default, ®s->imask1); + flexcan_write(priv->reg_imask2_default, ®s->imask2); enable_irq(dev->irq); /* print chip status */ @@ -962,6 +1074,7 @@ static void flexcan_chip_stop(struct net_device *dev) flexcan_chip_disable(priv); /* Disable all interrupts */ + flexcan_write(0, ®s->imask2); flexcan_write(0, ®s->imask1); flexcan_write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL, ®s->ctrl); @@ -1094,8 +1207,9 @@ static int register_flexcandev(struct net_device *dev) flexcan_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. + * featuring a RX hardware FIFO (although this driver doesn't + * make use of it on some cores). Older cores, found on some + * Coldfire derivates are not tested. */ reg = flexcan_read(®s->mcr); if (!(reg & FLEXCAN_MCR_FEN)) { @@ -1217,13 +1331,17 @@ static int flexcan_probe(struct platform_device *pdev) priv->devtype_data = devtype_data; priv->reg_xceiver = reg_xceiver; - priv->tx_mb_idx = FLEXCAN_TX_MB_HW_FIFO; - priv->tx_mb_reserved = ®s->mb[FLEXCAN_TX_MB_RESERVED_HW_FIFO]; + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_SW_FIFO) { + priv->tx_mb_idx = FLEXCAN_TX_MB_SW_FIFO; + priv->tx_mb_reserved = ®s->mb[FLEXCAN_TX_MB_RESERVED_SW_FIFO]; + } else { + priv->tx_mb_idx = FLEXCAN_TX_MB_HW_FIFO; + priv->tx_mb_reserved = ®s->mb[FLEXCAN_TX_MB_RESERVED_HW_FIFO]; + } priv->tx_mb = ®s->mb[priv->tx_mb_idx]; - priv->reg_imask1_default = FLEXCAN_IFLAG_RX_FIFO_OVERFLOW | - FLEXCAN_IFLAG_RX_FIFO_AVAILABLE | - FLEXCAN_IFLAG_MB(priv->tx_mb_idx); + priv->reg_imask1_default = FLEXCAN_IFLAG_MB(priv->tx_mb_idx); + priv->reg_imask2_default = 0; priv->fifo.poll_pre_read = flexcan_poll_state; priv->fifo.poll_post_read = flexcan_poll_bus_err; @@ -1231,7 +1349,26 @@ static int flexcan_probe(struct platform_device *pdev) flexcan_poll_error_interrupts_enable; priv->fifo.mailbox_read = flexcan_mailbox_read; - err = can_rx_fifo_add_simple(dev, &priv->fifo, FLEXCAN_NAPI_WEIGHT); + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_SW_FIFO) { + u64 imask; + + priv->fifo.low_first = FLEXCAN_RX_MB_LOW_FIRST; + priv->fifo.high_first = FLEXCAN_RX_MB_HIGH_FIRST; + priv->fifo.high_last = FLEXCAN_RX_MB_HIGH_LAST; + priv->fifo.mailbox_enable_mask = flexcan_mailbox_enable_mask; + priv->fifo.mailbox_enable = flexcan_mailbox_enable; + + imask = ~0LLU >> (64 + priv->fifo.low_first - priv->fifo.high_last - 1) + << priv->fifo.low_first; + priv->reg_imask1_default |= imask; + priv->reg_imask2_default |= imask >> 32; + + err = can_rx_fifo_add(dev, &priv->fifo); + } else { + priv->reg_imask1_default |= FLEXCAN_IFLAG_RX_FIFO_OVERFLOW | + FLEXCAN_IFLAG_RX_FIFO_AVAILABLE; + err = can_rx_fifo_add_simple(dev, &priv->fifo, FLEXCAN_NAPI_WEIGHT); + } if (err) goto failed_fifo; -- 2.8.1