aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/layerscape/patches-5.4/802-can-0019-can-flexcan-add-CAN-FD-mode-support.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/layerscape/patches-5.4/802-can-0019-can-flexcan-add-CAN-FD-mode-support.patch')
-rw-r--r--target/linux/layerscape/patches-5.4/802-can-0019-can-flexcan-add-CAN-FD-mode-support.patch397
1 files changed, 397 insertions, 0 deletions
diff --git a/target/linux/layerscape/patches-5.4/802-can-0019-can-flexcan-add-CAN-FD-mode-support.patch b/target/linux/layerscape/patches-5.4/802-can-0019-can-flexcan-add-CAN-FD-mode-support.patch
new file mode 100644
index 0000000000..f762ed6392
--- /dev/null
+++ b/target/linux/layerscape/patches-5.4/802-can-0019-can-flexcan-add-CAN-FD-mode-support.patch
@@ -0,0 +1,397 @@
+From 2aea13a107090d05e968d7d2aa3f72380a3f1b4c Mon Sep 17 00:00:00 2001
+From: Joakim Zhang <qiangqing.zhang@nxp.com>
+Date: Fri, 12 Jul 2019 08:02:44 +0000
+Subject: [PATCH] can: flexcan: add CAN FD mode support
+
+This patch intends to add CAN FD mode support in driver, it means that
+payload size can extend up to 64 bytes.
+
+Bit timing always set in CBT register other than CTRL1 register when
+CANFD supports BRS, it will extend the range of all CAN bit timing
+variables (PRESDIV, PROPSEG, PSEG1, PSEG2 and RJW), which will improve
+the bit timing accuracy.
+
+Signed-off-by: Joakim Zhang <qiangqing.zhang@nxp.com>
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+---
+ drivers/net/can/flexcan.c | 247 ++++++++++++++++++++++++++++++++++++++++------
+ 1 file changed, 218 insertions(+), 29 deletions(-)
+
+--- a/drivers/net/can/flexcan.c
++++ b/drivers/net/can/flexcan.c
+@@ -52,6 +52,7 @@
+ #define FLEXCAN_MCR_IRMQ BIT(16)
+ #define FLEXCAN_MCR_LPRIO_EN BIT(13)
+ #define FLEXCAN_MCR_AEN BIT(12)
++#define FLEXCAN_MCR_FDEN BIT(11)
+ /* MCR_MAXMB: maximum used MBs is MAXMB + 1 */
+ #define FLEXCAN_MCR_MAXMB(x) ((x) & 0x7f)
+ #define FLEXCAN_MCR_IDAM_A (0x0 << 8)
+@@ -137,6 +138,26 @@
+ FLEXCAN_ESR_BOFF_INT | FLEXCAN_ESR_ERR_INT | \
+ FLEXCAN_ESR_WAK_INT)
+
++/* FLEXCAN Bit Timing register (CBT) bits */
++#define FLEXCAN_CBT_BTF BIT(31)
++#define FLEXCAN_CBT_EPRESDIV(x) (((x) & 0x3ff) << 21)
++#define FLEXCAN_CBT_ERJW(x) (((x) & 0x0f) << 16)
++#define FLEXCAN_CBT_EPROPSEG(x) (((x) & 0x3f) << 10)
++#define FLEXCAN_CBT_EPSEG1(x) (((x) & 0x1f) << 5)
++#define FLEXCAN_CBT_EPSEG2(x) ((x) & 0x1f)
++
++/* FLEXCAN FD control register (FDCTRL) bits */
++#define FLEXCAN_FDCTRL_FDRATE BIT(31)
++#define FLEXCAN_FDCTRL_MBDSR1(x) (((x) & 0x3) << 19)
++#define FLEXCAN_FDCTRL_MBDSR0(x) (((x) & 0x3) << 16)
++
++/* FLEXCAN FD Bit Timing register (FDCBT) bits */
++#define FLEXCAN_FDCBT_FPRESDIV(x) (((x) & 0x3ff) << 20)
++#define FLEXCAN_FDCBT_FRJW(x) (((x) & 0x07) << 16)
++#define FLEXCAN_FDCBT_FPROPSEG(x) (((x) & 0x1f) << 10)
++#define FLEXCAN_FDCBT_FPSEG1(x) (((x) & 0x07) << 5)
++#define FLEXCAN_FDCBT_FPSEG2(x) ((x) & 0x07)
++
+ /* FLEXCAN interrupt flag register (IFLAG) bits */
+ /* Errata ERR005829 step7: Reserve first valid MB */
+ #define FLEXCAN_TX_MB_RESERVED_OFF_FIFO 8
+@@ -161,6 +182,9 @@
+ #define FLEXCAN_MB_CODE_TX_DATA (0xc << 24)
+ #define FLEXCAN_MB_CODE_TX_TANSWER (0xe << 24)
+
++#define FLEXCAN_MB_CNT_EDL BIT(31)
++#define FLEXCAN_MB_CNT_BRS BIT(30)
++#define FLEXCAN_MB_CNT_ESI BIT(29)
+ #define FLEXCAN_MB_CNT_SRR BIT(22)
+ #define FLEXCAN_MB_CNT_IDE BIT(21)
+ #define FLEXCAN_MB_CNT_RTR BIT(20)
+@@ -192,6 +216,7 @@
+ #define FLEXCAN_QUIRK_BROKEN_PERR_STATE BIT(6) /* No interrupt for error passive */
+ #define FLEXCAN_QUIRK_DEFAULT_BIG_ENDIAN BIT(7) /* default to BE register access */
+ #define FLEXCAN_QUIRK_SETUP_STOP_MODE BIT(8) /* Setup stop mode to support wakeup */
++#define FLEXCAN_QUIRK_TIMESTAMP_SUPPORT_FD BIT(9) /* Use timestamp then support can fd mode */
+
+ /* Structure of the message buffer */
+ struct flexcan_mb {
+@@ -225,7 +250,8 @@ struct flexcan_regs {
+ u32 crcr; /* 0x44 */
+ u32 rxfgmask; /* 0x48 */
+ u32 rxfir; /* 0x4c */
+- u32 _reserved3[12]; /* 0x50 */
++ u32 cbt; /* 0x50 */
++ u32 _reserved3[11]; /* 0x54 */
+ u8 mb[2][512]; /* 0x80 */
+ /* FIFO-mode:
+ * MB
+@@ -250,6 +276,10 @@ struct flexcan_regs {
+ u32 rerrdr; /* 0xaf4 */
+ u32 rerrsynr; /* 0xaf8 */
+ u32 errsr; /* 0xafc */
++ u32 _reserved7[64]; /* 0xb00 */
++ u32 fdctrl; /* 0xc00 */
++ u32 fdcbt; /* 0xc04 */
++ u32 fdcrc; /* 0xc08 */
+ };
+
+ struct flexcan_devtype_data {
+@@ -337,6 +367,30 @@ static const struct can_bittiming_const
+ .brp_inc = 1,
+ };
+
++static const struct can_bittiming_const flexcan_fd_bittiming_const = {
++ .name = DRV_NAME,
++ .tseg1_min = 2,
++ .tseg1_max = 96,
++ .tseg2_min = 2,
++ .tseg2_max = 32,
++ .sjw_max = 16,
++ .brp_min = 1,
++ .brp_max = 1024,
++ .brp_inc = 1,
++};
++
++static const struct can_bittiming_const flexcan_fd_data_bittiming_const = {
++ .name = DRV_NAME,
++ .tseg1_min = 2,
++ .tseg1_max = 39,
++ .tseg2_min = 2,
++ .tseg2_max = 8,
++ .sjw_max = 4,
++ .brp_min = 1,
++ .brp_max = 1024,
++ .brp_inc = 1,
++};
++
+ /* 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.
+@@ -631,7 +685,7 @@ static netdev_tx_t flexcan_start_xmit(st
+ struct canfd_frame *cfd = (struct canfd_frame *)skb->data;
+ u32 can_id;
+ u32 data;
+- u32 ctrl = FLEXCAN_MB_CODE_TX_DATA | (cfd->len << 16);
++ u32 ctrl = FLEXCAN_MB_CODE_TX_DATA | ((can_len2dlc(cfd->len)) << 16);
+ int i;
+
+ if (can_dropped_invalid_skb(dev, skb))
+@@ -649,6 +703,9 @@ static netdev_tx_t flexcan_start_xmit(st
+ if (cfd->can_id & CAN_RTR_FLAG)
+ ctrl |= FLEXCAN_MB_CNT_RTR;
+
++ if (can_is_canfd_skb(skb))
++ ctrl |= FLEXCAN_MB_CNT_EDL;
++
+ for (i = 0; i < cfd->len; i += sizeof(u32)) {
+ data = be32_to_cpup((__be32 *)&cfd->data[i]);
+ priv->write(data, &priv->tx_mb->data[i / sizeof(u32)]);
+@@ -859,7 +916,10 @@ static struct sk_buff *flexcan_mailbox_r
+ reg_ctrl = priv->read(&mb->can_ctrl);
+ }
+
+- skb = alloc_can_skb(offload->dev, (struct can_frame **)&cfd);
++ if (reg_ctrl & FLEXCAN_MB_CNT_EDL)
++ skb = alloc_canfd_skb(offload->dev, &cfd);
++ else
++ skb = alloc_can_skb(offload->dev, (struct can_frame **)&cfd);
+ if (unlikely(!skb)) {
+ skb = ERR_PTR(-ENOMEM);
+ goto mark_as_read;
+@@ -874,9 +934,17 @@ static struct sk_buff *flexcan_mailbox_r
+ else
+ cfd->can_id = (reg_id >> 18) & CAN_SFF_MASK;
+
+- if (reg_ctrl & FLEXCAN_MB_CNT_RTR)
+- cfd->can_id |= CAN_RTR_FLAG;
+- cfd->len = get_can_dlc((reg_ctrl >> 16) & 0xf);
++ if (reg_ctrl & FLEXCAN_MB_CNT_EDL) {
++ cfd->len = can_dlc2len(get_canfd_dlc((reg_ctrl >> 16) & 0xf));
++ } else {
++ cfd->len = get_can_dlc((reg_ctrl >> 16) & 0xf);
++
++ if (reg_ctrl & FLEXCAN_MB_CNT_RTR)
++ cfd->can_id |= CAN_RTR_FLAG;
++ }
++
++ if (reg_ctrl & FLEXCAN_MB_CNT_ESI)
++ cfd->flags |= CANFD_ESI;
+
+ for (i = 0; i < cfd->len; i += sizeof(u32)) {
+ __be32 data = cpu_to_be32(priv->read(&mb->data[i / sizeof(u32)]));
+@@ -1021,27 +1089,14 @@ static irqreturn_t flexcan_irq(int irq,
+
+ static void flexcan_set_bittiming(struct net_device *dev)
+ {
+- const struct flexcan_priv *priv = netdev_priv(dev);
+- const struct can_bittiming *bt = &priv->can.bittiming;
++ struct flexcan_priv *priv = netdev_priv(dev);
++ struct can_bittiming *bt = &priv->can.bittiming;
++ struct can_bittiming *dbt = &priv->can.data_bittiming;
+ struct flexcan_regs __iomem *regs = priv->regs;
+- u32 reg;
++ u32 reg, reg_cbt, reg_fdcbt;
+
+ reg = priv->read(&regs->ctrl);
+- reg &= ~(FLEXCAN_CTRL_PRESDIV(0xff) |
+- FLEXCAN_CTRL_RJW(0x3) |
+- FLEXCAN_CTRL_PSEG1(0x7) |
+- FLEXCAN_CTRL_PSEG2(0x7) |
+- FLEXCAN_CTRL_PROPSEG(0x7) |
+- FLEXCAN_CTRL_LPB |
+- FLEXCAN_CTRL_SMP |
+- FLEXCAN_CTRL_LOM);
+-
+- reg |= FLEXCAN_CTRL_PRESDIV(bt->brp - 1) |
+- FLEXCAN_CTRL_PSEG1(bt->phase_seg1 - 1) |
+- FLEXCAN_CTRL_PSEG2(bt->phase_seg2 - 1) |
+- FLEXCAN_CTRL_RJW(bt->sjw - 1) |
+- FLEXCAN_CTRL_PROPSEG(bt->prop_seg - 1);
+-
++ reg &= ~(FLEXCAN_CTRL_LPB | FLEXCAN_CTRL_SMP | FLEXCAN_CTRL_LOM);
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
+ reg |= FLEXCAN_CTRL_LPB;
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+@@ -1052,9 +1107,102 @@ static void flexcan_set_bittiming(struct
+ netdev_dbg(dev, "writing ctrl=0x%08x\n", reg);
+ priv->write(reg, &regs->ctrl);
+
+- /* print chip status */
+- netdev_dbg(dev, "%s: mcr=0x%08x ctrl=0x%08x\n", __func__,
+- priv->read(&regs->mcr), priv->read(&regs->ctrl));
++ if (priv->can.ctrlmode_supported & CAN_CTRLMODE_FD) {
++ reg_cbt = priv->read(&regs->cbt);
++ reg_cbt &= ~(FLEXCAN_CBT_EPRESDIV(0x3ff) |
++ FLEXCAN_CBT_EPSEG1(0x1f) |
++ FLEXCAN_CBT_EPSEG2(0x1f) |
++ FLEXCAN_CBT_ERJW(0x1f) |
++ FLEXCAN_CBT_EPROPSEG(0x3f) |
++ FLEXCAN_CBT_BTF);
++
++ /* CBT[EPSEG1] is 5 bit long and CBT[EPROPSEG] is 6 bit long.
++ * The can_calc_bittiming tries to divide the tseg1 equally
++ * between phase_seg1 and prop_seg, which may not fit in CBT
++ * register. Therefore, if phase_seg1 is more than possible
++ * value, increase prop_seg and decrease phase_seg1
++ */
++ if (bt->phase_seg1 > 0x20) {
++ bt->prop_seg += (bt->phase_seg1 - 0x20);
++ bt->phase_seg1 = 0x20;
++ }
++
++ reg_cbt = FLEXCAN_CBT_EPRESDIV(bt->brp - 1) |
++ FLEXCAN_CBT_EPSEG1(bt->phase_seg1 - 1) |
++ FLEXCAN_CBT_EPSEG2(bt->phase_seg2 - 1) |
++ FLEXCAN_CBT_ERJW(bt->sjw - 1) |
++ FLEXCAN_CBT_EPROPSEG(bt->prop_seg - 1) |
++ FLEXCAN_CBT_BTF;
++ priv->write(reg_cbt, &regs->cbt);
++
++ netdev_dbg(dev, "bt: prediv %d seg1 %d seg2 %d rjw %d propseg %d\n",
++ bt->brp - 1, bt->phase_seg1 - 1, bt->phase_seg2 - 1,
++ bt->sjw - 1, bt->prop_seg - 1);
++
++ if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
++ reg_fdcbt = priv->read(&regs->fdcbt);
++ reg_fdcbt &= ~(FLEXCAN_FDCBT_FPRESDIV(0x3ff) |
++ FLEXCAN_FDCBT_FPSEG1(0x07) |
++ FLEXCAN_FDCBT_FPSEG2(0x07) |
++ FLEXCAN_FDCBT_FRJW(0x07) |
++ FLEXCAN_FDCBT_FPROPSEG(0x1f));
++
++ /* FDCBT[FPSEG1] is 3 bit long and FDCBT[FPROPSEG] is 5 bit long.
++ * The can_calc_bittiming tries to divide the tseg1 equally
++ * between phase_seg1 and prop_seg, which may not fit in FDCBT
++ * register. Therefore, if phase_seg1 is more than possible
++ * value, increase prop_seg and decrease phase_seg1
++ */
++ if (dbt->phase_seg1 > 0x8) {
++ dbt->prop_seg += (dbt->phase_seg1 - 0x8);
++ dbt->phase_seg1 = 0x8;
++ }
++
++ reg_fdcbt = FLEXCAN_FDCBT_FPRESDIV(dbt->brp - 1) |
++ FLEXCAN_FDCBT_FPSEG1(dbt->phase_seg1 - 1) |
++ FLEXCAN_FDCBT_FPSEG2(dbt->phase_seg2 - 1) |
++ FLEXCAN_FDCBT_FRJW(dbt->sjw - 1) |
++ FLEXCAN_FDCBT_FPROPSEG(dbt->prop_seg);
++ priv->write(reg_fdcbt, &regs->fdcbt);
++
++ if (bt->brp != dbt->brp)
++ netdev_warn(dev, "Warning!! data brp = %d and brp = %d don't match.\n"
++ "flexcan may not work. consider using different bitrate or data bitrate\n",
++ dbt->brp, bt->brp);
++
++ netdev_dbg(dev, "fdbt: prediv %d seg1 %d seg2 %d rjw %d propseg %d\n",
++ dbt->brp - 1, dbt->phase_seg1 - 1, dbt->phase_seg2 - 1,
++ dbt->sjw - 1, dbt->prop_seg);
++
++ netdev_dbg(dev, "%s: mcr=0x%08x ctrl=0x%08x cbt=0x%08x fdcbt=0x%08x\n",
++ __func__, priv->read(&regs->mcr),
++ priv->read(&regs->ctrl),
++ priv->read(&regs->cbt),
++ priv->read(&regs->fdcbt));
++ }
++ } else {
++ reg = priv->read(&regs->ctrl);
++ reg &= ~(FLEXCAN_CTRL_PRESDIV(0xff) |
++ FLEXCAN_CTRL_RJW(0x3) |
++ FLEXCAN_CTRL_PSEG1(0x7) |
++ FLEXCAN_CTRL_PSEG2(0x7) |
++ FLEXCAN_CTRL_PROPSEG(0x7));
++
++ reg |= FLEXCAN_CTRL_PRESDIV(bt->brp - 1) |
++ FLEXCAN_CTRL_PSEG1(bt->phase_seg1 - 1) |
++ FLEXCAN_CTRL_PSEG2(bt->phase_seg2 - 1) |
++ FLEXCAN_CTRL_RJW(bt->sjw - 1) |
++ FLEXCAN_CTRL_PROPSEG(bt->prop_seg - 1);
++ priv->write(reg, &regs->ctrl);
++
++ netdev_dbg(dev, "bt: prediv %d seg1 %d seg2 %d rjw %d propseg %d\n",
++ bt->brp - 1, bt->phase_seg1 - 1, bt->phase_seg2 - 1,
++ bt->sjw - 1, bt->prop_seg - 1);
++
++ /* print chip status */
++ netdev_dbg(dev, "%s: mcr=0x%08x ctrl=0x%08x\n", __func__,
++ priv->read(&regs->mcr), priv->read(&regs->ctrl));
++ }
+ }
+
+ /* flexcan_chip_start
+@@ -1066,7 +1214,7 @@ static int flexcan_chip_start(struct net
+ {
+ struct flexcan_priv *priv = netdev_priv(dev);
+ struct flexcan_regs __iomem *regs = priv->regs;
+- u32 reg_mcr, reg_ctrl, reg_ctrl2, reg_mecr;
++ u32 reg_mcr, reg_ctrl, reg_ctrl2, reg_mecr, reg_fdctrl;
+ u64 reg_imask;
+ int err, i;
+ struct flexcan_mb __iomem *mb;
+@@ -1163,6 +1311,26 @@ static int flexcan_chip_start(struct net
+ netdev_dbg(dev, "%s: writing ctrl=0x%08x", __func__, reg_ctrl);
+ priv->write(reg_ctrl, &regs->ctrl);
+
++ /* FDCTRL */
++ if (priv->can.ctrlmode_supported & CAN_CTRLMODE_FD) {
++ reg_fdctrl = priv->read(&regs->fdctrl) & ~FLEXCAN_FDCTRL_FDRATE;
++ reg_fdctrl &= ~(FLEXCAN_FDCTRL_MBDSR1(0x3) | FLEXCAN_FDCTRL_MBDSR0(0x3));
++ reg_mcr = priv->read(&regs->mcr) & ~FLEXCAN_MCR_FDEN;
++
++ /* support BRS when set CAN FD mode
++ * 64 bytes payload per MB and 7 MBs per RAM block by default
++ * enable CAN FD mode
++ */
++ if (priv->can.ctrlmode & CAN_CTRLMODE_FD) {
++ reg_fdctrl |= FLEXCAN_FDCTRL_FDRATE;
++ reg_fdctrl |= FLEXCAN_FDCTRL_MBDSR1(0x3) | FLEXCAN_FDCTRL_MBDSR0(0x3);
++ reg_mcr |= FLEXCAN_MCR_FDEN;
++ }
++
++ priv->write(reg_fdctrl, &regs->fdctrl);
++ priv->write(reg_mcr, &regs->mcr);
++ }
++
+ if ((priv->devtype_data->quirks & FLEXCAN_QUIRK_ENABLE_EACEN_RRS)) {
+ reg_ctrl2 = priv->read(&regs->ctrl2);
+ reg_ctrl2 |= FLEXCAN_CTRL2_EACEN | FLEXCAN_CTRL2_RRS;
+@@ -1288,6 +1456,12 @@ static int flexcan_open(struct net_devic
+ struct flexcan_priv *priv = netdev_priv(dev);
+ int err;
+
++ if ((priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) &&
++ (priv->can.ctrlmode & CAN_CTRLMODE_FD)) {
++ netdev_err(dev, "three samples mode and fd mode can't be used together\n");
++ return -EINVAL;
++ }
++
+ err = pm_runtime_get_sync(priv->dev);
+ if (err < 0)
+ return err;
+@@ -1300,7 +1474,10 @@ static int flexcan_open(struct net_devic
+ if (err)
+ goto out_close;
+
+- priv->mb_size = sizeof(struct flexcan_mb) + CAN_MAX_DLEN;
++ if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
++ priv->mb_size = sizeof(struct flexcan_mb) + CANFD_MAX_DLEN;
++ else
++ priv->mb_size = sizeof(struct flexcan_mb) + CAN_MAX_DLEN;
+ priv->mb_count = (sizeof(priv->regs->mb[0]) / priv->mb_size) +
+ (sizeof(priv->regs->mb[1]) / priv->mb_size);
+
+@@ -1645,6 +1822,18 @@ static int flexcan_probe(struct platform
+ priv->devtype_data = devtype_data;
+ priv->reg_xceiver = reg_xceiver;
+
++ if (priv->devtype_data->quirks & FLEXCAN_QUIRK_TIMESTAMP_SUPPORT_FD) {
++ if (priv->devtype_data->quirks & FLEXCAN_QUIRK_USE_OFF_TIMESTAMP) {
++ priv->can.ctrlmode_supported |= CAN_CTRLMODE_FD;
++ priv->can.bittiming_const = &flexcan_fd_bittiming_const;
++ priv->can.data_bittiming_const = &flexcan_fd_data_bittiming_const;
++ } else {
++ dev_err(&pdev->dev, "can fd mode can't work on fifo mode\n");
++ err = -EINVAL;
++ goto failed_register;
++ }
++ }
++
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);