| 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 { |
| @@ -336,6 +366,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. |
| @@ -638,7 +692,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)) |
| @@ -656,6 +710,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)]); |
| @@ -866,7 +923,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; |
| @@ -881,9 +941,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)])); |
| @@ -1028,27 +1096,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) |
| @@ -1059,9 +1114,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 |
| @@ -1073,7 +1221,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; |
| @@ -1172,6 +1320,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; |
| @@ -1312,6 +1480,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) { |
| pm_runtime_put_noidle(priv->dev); |
| @@ -1330,7 +1504,10 @@ static int flexcan_open(struct net_devic |
| if (err) |
| goto out_transceiver_disable; |
| |
| - 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); |
| |
| @@ -1682,6 +1859,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); |