diff options
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.patch | 397 |
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(®s->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, ®s->ctrl); + +- /* print chip status */ +- netdev_dbg(dev, "%s: mcr=0x%08x ctrl=0x%08x\n", __func__, +- priv->read(®s->mcr), priv->read(®s->ctrl)); ++ if (priv->can.ctrlmode_supported & CAN_CTRLMODE_FD) { ++ reg_cbt = priv->read(®s->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, ®s->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(®s->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, ®s->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(®s->mcr), ++ priv->read(®s->ctrl), ++ priv->read(®s->cbt), ++ priv->read(®s->fdcbt)); ++ } ++ } else { ++ reg = priv->read(®s->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, ®s->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(®s->mcr), priv->read(®s->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, ®s->ctrl); + ++ /* FDCTRL */ ++ if (priv->can.ctrlmode_supported & CAN_CTRLMODE_FD) { ++ reg_fdctrl = priv->read(®s->fdctrl) & ~FLEXCAN_FDCTRL_FDRATE; ++ reg_fdctrl &= ~(FLEXCAN_FDCTRL_MBDSR1(0x3) | FLEXCAN_FDCTRL_MBDSR0(0x3)); ++ reg_mcr = priv->read(®s->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, ®s->fdctrl); ++ priv->write(reg_mcr, ®s->mcr); ++ } ++ + if ((priv->devtype_data->quirks & FLEXCAN_QUIRK_ENABLE_EACEN_RRS)) { + reg_ctrl2 = priv->read(®s->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); |