From 283a7551144df8e275cdb5080432ad25775d116e Mon Sep 17 00:00:00 2001 From: uziel Date: Mon, 11 Nov 2024 16:40:13 +0800 Subject: [PATCH] rk3588 can canfd driver --- drivers/net/can/Kconfig | 1 + drivers/net/can/Makefile | 1 + drivers/net/can/rockchip/Kconfig | 27 + drivers/net/can/rockchip/Makefile | 8 + drivers/net/can/rockchip/rk3562_can.c | 1086 ++++++++++++++++++ drivers/net/can/rockchip/rockchip_can.c | 814 ++++++++++++++ drivers/net/can/rockchip/rockchip_canfd.c | 1226 +++++++++++++++++++++ 7 files changed, 3163 insertions(+) create mode 100644 drivers/net/can/rockchip/Kconfig create mode 100644 drivers/net/can/rockchip/Makefile create mode 100644 drivers/net/can/rockchip/rk3562_can.c create mode 100644 drivers/net/can/rockchip/rockchip_can.c create mode 100644 drivers/net/can/rockchip/rockchip_canfd.c diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 64561f6c2..48ebcf4e3 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -182,6 +182,7 @@ source "drivers/net/can/m_can/Kconfig" source "drivers/net/can/mscan/Kconfig" source "drivers/net/can/peak_canfd/Kconfig" source "drivers/net/can/rcar/Kconfig" +source "drivers/net/can/rockchip/Kconfig" source "drivers/net/can/sja1000/Kconfig" source "drivers/net/can/softing/Kconfig" source "drivers/net/can/spi/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index bcd331707..456dd7aea 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_CAN_SLCAN) += slcan.o obj-y += dev/ obj-y += rcar/ +obj-y += rockchip/ obj-y += spi/ obj-y += usb/ obj-y += softing/ diff --git a/drivers/net/can/rockchip/Kconfig b/drivers/net/can/rockchip/Kconfig new file mode 100644 index 000000000..cf287aa6e --- /dev/null +++ b/drivers/net/can/rockchip/Kconfig @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0 +config CAN_ROCKCHIP + tristate "Rockchip CAN controller" + depends on ARCH_ROCKCHIP + help + Say Y here if you want to use CAN controller found on Rockchip SoCs. + + To compile this driver as a module, choose M here: the module will + be called rockchip_can. + +config CANFD_ROCKCHIP + tristate "Rockchip CANFD controller" + depends on ARCH_ROCKCHIP + help + Say Y here if you want to use CANFD controller found on Rockchip SoCs. + + To compile this driver as a module, choose M here: the module will + be called rockchip_canfd. + +config CAN_RK3562 + tristate "RK3562 CAN controller" + depends on ARCH_ROCKCHIP + help + Say Y here if you want to use CAN controller found on RK3562 SoCs. + + To compile this driver as a module, choose M here: the module will + be called rk3562_can. diff --git a/drivers/net/can/rockchip/Makefile b/drivers/net/can/rockchip/Makefile new file mode 100644 index 000000000..49934fdab --- /dev/null +++ b/drivers/net/can/rockchip/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the rockchip can and canfd controller driver. +# + +obj-$(CONFIG_CAN_ROCKCHIP) += rockchip_can.o +obj-$(CONFIG_CANFD_ROCKCHIP) += rockchip_canfd.o +obj-$(CONFIG_CAN_RK3562) += rk3562_can.o diff --git a/drivers/net/can/rockchip/rk3562_can.c b/drivers/net/can/rockchip/rk3562_can.c new file mode 100644 index 000000000..ef7d4c9f2 --- /dev/null +++ b/drivers/net/can/rockchip/rk3562_can.c @@ -0,0 +1,1086 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2022 Rockchip Electronics Co. Ltd. + * Rk3562 CAN driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* registers definition */ +enum rk3562_can_reg { + CAN_MODE = 0x00, + CAN_CMD = 0x04, + CAN_STATE = 0x08, + CAN_INT = 0x0c, + CAN_INT_MASK = 0x10, + CAN_LOSTARB_CODE = 0x28, + CAN_ERR_CODE = 0x2c, + CAN_RX_ERR_CNT = 0x34, + CAN_TX_ERR_CNT = 0x38, + CAN_IDCODE = 0x3c, + CAN_IDMASK = 0x40, + CAN_NBTP = 0x100, + CAN_DBTP = 0x104, + CAN_TDCR = 0x108, + CAN_TSCC = 0x10c, + CAN_TSCV = 0x110, + CAN_TXEFC = 0x114, + CAN_RXFC = 0x118, + CAN_AFC = 0x11c, + CAN_IDCODE0 = 0x120, + CAN_IDMASK0 = 0x124, + CAN_IDCODE1 = 0x128, + CAN_IDMASK1 = 0x12c, + CAN_IDCODE2 = 0x130, + CAN_IDMASK2 = 0x134, + CAN_IDCODE3 = 0x138, + CAN_IDMASK3 = 0x13c, + CAN_IDCODE4 = 0x140, + CAN_IDMASK4 = 0x144, + CAN_TXFIC = 0x200, + CAN_TXID = 0x204, + CAN_TXDAT0 = 0x208, + CAN_TXDAT1 = 0x20c, + CAN_TXDAT2 = 0x210, + CAN_TXDAT3 = 0x214, + CAN_TXDAT4 = 0x218, + CAN_TXDAT5 = 0x21c, + CAN_TXDAT6 = 0x220, + CAN_TXDAT7 = 0x224, + CAN_TXDAT8 = 0x228, + CAN_TXDAT9 = 0x22c, + CAN_TXDAT10 = 0x230, + CAN_TXDAT11 = 0x234, + CAN_TXDAT12 = 0x238, + CAN_TXDAT13 = 0x23c, + CAN_TXDAT14 = 0x240, + CAN_TXDAT15 = 0x244, + CAN_RXFIC = 0x300, + CAN_RXID = 0x304, + CAN_RXTS = 0x308, + CAN_RXDAT0 = 0x30c, + CAN_RXDAT1 = 0x310, + CAN_RXDAT2 = 0x314, + CAN_RXDAT3 = 0x318, + CAN_RXDAT4 = 0x31c, + CAN_RXDAT5 = 0x320, + CAN_RXDAT6 = 0x324, + CAN_RXDAT7 = 0x328, + CAN_RXDAT8 = 0x32c, + CAN_RXDAT9 = 0x330, + CAN_RXDAT10 = 0x334, + CAN_RXDAT11 = 0x338, + CAN_RXDAT12 = 0x33c, + CAN_RXDAT13 = 0x340, + CAN_RXDAT14 = 0x344, + CAN_RXDAT15 = 0x348, + CAN_RXFRD = 0x400, + CAN_TXEFRD = 0x500, + CAN_SPACR_CTRL = 0x700, + CAN_FIFO_INFO = 0x704, + CAN_TXCRC_DEBUG = 0x7c4, +}; + +enum { + ROCKCHIP_RK3562_CAN_MODE = 0, + ROCKCHIP_RK3562_CANFD_MODE, +}; + +#define DATE_LENGTH_12_BYTE (0x9) +#define DATE_LENGTH_16_BYTE (0xa) +#define DATE_LENGTH_20_BYTE (0xb) +#define DATE_LENGTH_24_BYTE (0xc) +#define DATE_LENGTH_32_BYTE (0xd) +#define DATE_LENGTH_48_BYTE (0xe) +#define DATE_LENGTH_64_BYTE (0xf) + +#define CAN_TX0_REQ BIT(0) +#define CAN_TX1_REQ BIT(1) +#define CAN_TX_REQ_FULL ((CAN_TX0_REQ) | (CAN_TX1_REQ)) + +#define MODE_FDOE BIT(15) +#define MODE_BRSD BIT(13) +#define MODE_SPACE_RX BIT(12) +#define MODE_AUTO_RETX BIT(10) +#define MODE_RXSORT BIT(7) +#define MODE_TXORDER BIT(6) +#define MODE_RXSTX BIT(5) +#define MODE_LBACK BIT(4) +#define MODE_SILENT BIT(3) +#define MODE_SELF_TEST BIT(2) +#define MODE_SLEEP BIT(1) +#define RESET_MODE 0 +#define WORK_MODE BIT(0) + +#define RX_FINISH_INT BIT(0) +#define TX_FINISH_INT BIT(1) +#define ERR_WARN_INT BIT(2) +#define RX_BUF_OV_INT BIT(3) +#define PASSIVE_ERR_INT BIT(4) +#define TX_LOSTARB_INT BIT(5) +#define BUS_ERR_INT BIT(6) +#define RX_FIFO_FULL_INT BIT(7) +#define RX_FIFO_OV_INT BIT(8) +#define BUS_OFF_INT BIT(9) +#define BUS_OFF_RECOVERY_INT BIT(10) +#define TSC_OV_INT BIT(11) +#define TXE_FIFO_OV_INT BIT(12) +#define TXE_FIFO_FULL_INT BIT(13) +#define WAKEUP_INT BIT(14) +#define TIMEOUT_INT BIT(15) + +#define ERR_TYPE_MASK GENMASK(28, 26) +#define ERR_TYPE_SHIFT 26 +#define BIT_ERR 0 +#define STUFF_ERR 1 +#define FORM_ERR 2 +#define ACK_ERR 3 +#define CRC_ERR 4 +#define ERR_DIR_RX BIT(25) +#define ERR_TX_ERR_MASK GENMASK(24, 16) +#define ERR_LOC_MASK GENMASK(15, 0) + +/* Nominal Bit Timing & Prescaler Register (NBTP) */ +#define NBTP_MODE_3_SAMPLES BIT(31) +#define NBTP_NSJW_SHIFT 24 +#define NBTP_NSJW_MASK (0x7f << NBTP_NSJW_SHIFT) +#define NBTP_NBRP_SHIFT 16 +#define NBTP_NBRP_MASK (0xff << NBTP_NBRP_SHIFT) +#define NBTP_NTSEG2_SHIFT 8 +#define NBTP_NTSEG2_MASK (0x7f << NBTP_NTSEG2_SHIFT) +#define NBTP_NTSEG1_SHIFT 0 +#define NBTP_NTSEG1_MASK (0x7f << NBTP_NTSEG1_SHIFT) + +/* Data Bit Timing & Prescaler Register (DBTP) */ +#define DBTP_MODE_3_SAMPLES BIT(21) +#define DBTP_DSJW_SHIFT 17 +#define DBTP_DSJW_MASK (0xf << DBTP_DSJW_SHIFT) +#define DBTP_DBRP_SHIFT 9 +#define DBTP_DBRP_MASK (0xff << DBTP_DBRP_SHIFT) +#define DBTP_DTSEG2_SHIFT 5 +#define DBTP_DTSEG2_MASK (0xf << DBTP_DTSEG2_SHIFT) +#define DBTP_DTSEG1_SHIFT 0 +#define DBTP_DTSEG1_MASK (0x1f << DBTP_DTSEG1_SHIFT) + +/* Transmitter Delay Compensation Register (TDCR) */ +#define TDCR_TDCO_SHIFT 1 +#define TDCR_TDCO_MASK (0x3f << TDCR_TDCO_SHIFT) +#define TDCR_TDC_ENABLE BIT(0) + +#define TX_FD_ENABLE BIT(5) +#define TX_FD_BRS_ENABLE BIT(4) + +#define FIFO_ENABLE BIT(0) +#define FIFO_FIXED_DATA BIT(2) +#define RX_FIFO_CNT0_SHIFT 4 +#define RX_FIFO_CNT0_MASK (0x7 << RX_FIFO_CNT0_SHIFT) +#define RX_FIFO_CNT1_SHIFT 5 +#define RX_FIFO_CNT1_MASK (0x7 << RX_FIFO_CNT1_SHIFT) +#define RX_FIFO_CNT2_SHIFT 24 +#define RX_FIFO_CNT2_MASK (0xff << RX_FIFO_CNT2_SHIFT) + +#define FORMAT_SHIFT 7 +#define FORMAT_MASK (0x1 << FORMAT_SHIFT) +#define RTR_SHIFT 6 +#define RTR_MASK (0x1 << RTR_SHIFT) +#define FDF_SHIFT 5 +#define FDF_MASK (0x1 << FDF_SHIFT) +#define BRS_SHIFT 4 +#define BRS_MASK (0x1 << BRS_SHIFT) +#define DLC_SHIFT 0 +#define DLC_MASK (0xF << DLC_SHIFT) + +#define CAN_RF_SIZE 0x48 +#define CAN_TEF_SIZE 0x8 +#define CAN_TXEFRD_OFFSET(n) (CAN_TXEFRD + CAN_TEF_SIZE * (n)) +#define CAN_RXFRD_OFFSET(n) (CAN_RXFRD + CAN_RF_SIZE * (n)) + +#define CAN_RX_FILTER_MASK 0x1fffffff +#define CAN_SPACE_CTRL_INIT 0x0a010100 +#define CAN_BUSOFF_FLAG 0x20 + +#define CAN_FIFO_INFO_CNT_MASK 0xff + +#define DRV_NAME "rk3562_can" + +/* rk3562_can private data structure */ + +struct rk3562_can { + struct can_priv can; + struct device *dev; + struct napi_struct napi; + struct clk_bulk_data *clks; + int num_clks; + struct reset_control *reset; + void __iomem *base; + u32 irqstatus; + unsigned long mode; + int rx_fifo_shift; + u32 rx_fifo_mask; + int rx_fifo_depth; + int rx_max_data; +}; + +static inline u32 rk3562_can_read(const struct rk3562_can *priv, + enum rk3562_can_reg reg) +{ + return readl(priv->base + reg); +} + +static inline void rk3562_can_write(const struct rk3562_can *priv, + enum rk3562_can_reg reg, u32 val) +{ + writel(val, priv->base + reg); +} + +static const struct can_bittiming_const rk3562_can_bittiming_const = { + .name = DRV_NAME, + .tseg1_min = 1, + .tseg1_max = 128, + .tseg2_min = 1, + .tseg2_max = 128, + .sjw_max = 128, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 2, +}; + +static const struct can_bittiming_const rk3562_can_data_bittiming_const = { + .name = DRV_NAME, + .tseg1_min = 1, + .tseg1_max = 32, + .tseg2_min = 1, + .tseg2_max = 16, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 2, +}; + +static int set_reset_mode(struct net_device *ndev) +{ + struct rk3562_can *rcan = netdev_priv(ndev); + + reset_control_assert(rcan->reset); + udelay(2); + reset_control_deassert(rcan->reset); + + rk3562_can_write(rcan, CAN_MODE, 0); + + netdev_dbg(ndev, "%s MODE=0x%08x\n", __func__, + rk3562_can_read(rcan, CAN_MODE)); + + return 0; +} + +static int set_normal_mode(struct net_device *ndev) +{ + struct rk3562_can *rcan = netdev_priv(ndev); + u32 val; + + val = rk3562_can_read(rcan, CAN_MODE); + val |= WORK_MODE; + rk3562_can_write(rcan, CAN_MODE, val); + + netdev_dbg(ndev, "%s MODE=0x%08x\n", __func__, + rk3562_can_read(rcan, CAN_MODE)); + return 0; +} + +/* bittiming is called in reset_mode only */ +static int rk3562_can_set_bittiming(struct net_device *ndev) +{ + struct rk3562_can *rcan = netdev_priv(ndev); + const struct can_bittiming *bt = &rcan->can.bittiming; + const struct can_bittiming *dbt = &rcan->can.data_bittiming; + u16 brp, sjw, tseg1, tseg2; + u32 reg_btp; + + brp = (bt->brp >> 1) - 1; + sjw = bt->sjw - 1; + tseg1 = bt->prop_seg + bt->phase_seg1 - 1; + tseg2 = bt->phase_seg2 - 1; + reg_btp = (brp << NBTP_NBRP_SHIFT) | (sjw << NBTP_NSJW_SHIFT) | + (tseg1 << NBTP_NTSEG1_SHIFT) | + (tseg2 << NBTP_NTSEG2_SHIFT); + + if (rcan->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + reg_btp |= NBTP_MODE_3_SAMPLES; + + rk3562_can_write(rcan, CAN_NBTP, reg_btp); + + if (rcan->can.ctrlmode & CAN_CTRLMODE_FD) { + reg_btp = 0; + brp = (dbt->brp >> 1) - 1; + sjw = dbt->sjw - 1; + tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1; + tseg2 = dbt->phase_seg2 - 1; + + if (dbt->bitrate > 2200000) { + u32 tdco; + + /* Equation based on Bosch's ROCKCHIP_CAN User Manual's + * Transmitter Delay Compensation Section + */ + tdco = (rcan->can.clock.freq / dbt->bitrate) * 2 / 3; + /* Max valid TDCO value is 63 */ + if (tdco > 63) + tdco = 63; + + rk3562_can_write(rcan, CAN_TDCR, + (tdco << TDCR_TDCO_SHIFT) | + TDCR_TDC_ENABLE); + } + + reg_btp |= (brp << DBTP_DBRP_SHIFT) | + (sjw << DBTP_DSJW_SHIFT) | + (tseg1 << DBTP_DTSEG1_SHIFT) | + (tseg2 << DBTP_DTSEG2_SHIFT); + + if (rcan->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + reg_btp |= DBTP_MODE_3_SAMPLES; + + rk3562_can_write(rcan, CAN_DBTP, reg_btp); + } + + netdev_dbg(ndev, "%s NBTP=0x%08x, DBTP=0x%08x, TDCR=0x%08x\n", __func__, + rk3562_can_read(rcan, CAN_NBTP), + rk3562_can_read(rcan, CAN_DBTP), + rk3562_can_read(rcan, CAN_TDCR)); + return 0; +} + +static int rk3562_can_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct rk3562_can *rcan = netdev_priv(ndev); + int err; + + err = pm_runtime_get_sync(rcan->dev); + if (err < 0) { + netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", + __func__, err); + return err; + } + + bec->rxerr = rk3562_can_read(rcan, CAN_RX_ERR_CNT); + bec->txerr = rk3562_can_read(rcan, CAN_TX_ERR_CNT); + + pm_runtime_put(rcan->dev); + + netdev_dbg(ndev, "%s RX_ERR_CNT=0x%08x, TX_ERR_CNT=0x%08x\n", __func__, + rk3562_can_read(rcan, CAN_RX_ERR_CNT), + rk3562_can_read(rcan, CAN_TX_ERR_CNT)); + + return 0; +} + +static int rk3562_can_start(struct net_device *ndev) +{ + struct rk3562_can *rcan = netdev_priv(ndev); + u32 val; + + /* we need to enter the reset mode */ + set_reset_mode(ndev); + + rk3562_can_write(rcan, CAN_INT_MASK, 0); + + /* RECEIVING FILTER, accept all */ + rk3562_can_write(rcan, CAN_IDCODE, 0); + rk3562_can_write(rcan, CAN_IDMASK, CAN_RX_FILTER_MASK); + rk3562_can_write(rcan, CAN_IDCODE0, 0); + rk3562_can_write(rcan, CAN_IDMASK0, CAN_RX_FILTER_MASK); + rk3562_can_write(rcan, CAN_IDCODE1, 0); + rk3562_can_write(rcan, CAN_IDMASK1, CAN_RX_FILTER_MASK); + rk3562_can_write(rcan, CAN_IDCODE2, 0); + rk3562_can_write(rcan, CAN_IDMASK2, CAN_RX_FILTER_MASK); + rk3562_can_write(rcan, CAN_IDCODE3, 0); + rk3562_can_write(rcan, CAN_IDMASK3, CAN_RX_FILTER_MASK); + rk3562_can_write(rcan, CAN_IDCODE4, 0); + rk3562_can_write(rcan, CAN_IDMASK4, CAN_RX_FILTER_MASK); + + /* set mode */ + val = rk3562_can_read(rcan, CAN_MODE); + + /* rx fifo enable */ + rk3562_can_write(rcan, CAN_RXFC, + rk3562_can_read(rcan, CAN_RXFC) | FIFO_ENABLE | + (rcan->rx_max_data << 3) | FIFO_FIXED_DATA); + + /* Mode */ + val |= MODE_FDOE; + + /* Loopback Mode */ + if (rcan->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + val |= MODE_SELF_TEST | MODE_LBACK; + + /* Listen-only mode */ + if (rcan->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + val |= MODE_SILENT; + + rk3562_can_write(rcan, CAN_MODE, val); + + rk3562_can_set_bittiming(ndev); + + rk3562_can_write(rcan, CAN_SPACR_CTRL, CAN_SPACE_CTRL_INIT); + + set_normal_mode(ndev); + + rcan->can.state = CAN_STATE_ERROR_ACTIVE; + + netdev_dbg(ndev, "%s MODE=0x%08x, INT_MASK=0x%08x\n", __func__, + rk3562_can_read(rcan, CAN_MODE), + rk3562_can_read(rcan, CAN_INT_MASK)); + + return 0; +} + +static int rk3562_can_stop(struct net_device *ndev) +{ + struct rk3562_can *rcan = netdev_priv(ndev); + + rcan->can.state = CAN_STATE_STOPPED; + /* we need to enter reset mode */ + set_reset_mode(ndev); + + /* disable all interrupts */ + rk3562_can_write(rcan, CAN_INT_MASK, 0xffff); + + netdev_dbg(ndev, "%s MODE=0x%08x, INT_MASK=0x%08x\n", __func__, + rk3562_can_read(rcan, CAN_MODE), + rk3562_can_read(rcan, CAN_INT_MASK)); + return 0; +} + +static int rk3562_can_set_mode(struct net_device *ndev, + enum can_mode mode) +{ + int err; + + switch (mode) { + case CAN_MODE_START: + err = rk3562_can_start(ndev); + if (err) { + netdev_err(ndev, "starting CAN controller failed!\n"); + return err; + } + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +/* transmit a CAN message + * message layout in the sk_buff should be like this: + * xx xx xx xx ff ll 00 11 22 33 44 55 66 77 + * [ can_id ] [flags] [len] [can data (up to 8 bytes] + */ +static netdev_tx_t rk3562_can_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct rk3562_can *rcan = netdev_priv(ndev); + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + u32 id, dlc; + u32 cmd = CAN_TX0_REQ; + int i; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(ndev); + + if (rk3562_can_read(rcan, CAN_CMD) & CAN_TX0_REQ) + cmd = CAN_TX1_REQ; + + /* Watch carefully on the bit sequence */ + if (cf->can_id & CAN_EFF_FLAG) { + /* Extended CAN ID format */ + id = cf->can_id & CAN_EFF_MASK; + dlc = can_len2dlc(cf->len) & DLC_MASK; + dlc |= FORMAT_MASK; + + /* Extended frames remote TX request */ + if (cf->can_id & CAN_RTR_FLAG) + dlc |= RTR_MASK; + } else { + /* Standard CAN ID format */ + id = cf->can_id & CAN_SFF_MASK; + dlc = can_len2dlc(cf->len) & DLC_MASK; + + /* Standard frames remote TX request */ + if (cf->can_id & CAN_RTR_FLAG) + dlc |= RTR_MASK; + } + + if ((rcan->can.ctrlmode & CAN_CTRLMODE_FD) && can_is_canfd_skb(skb)) { + dlc |= TX_FD_ENABLE; + if (cf->flags & CANFD_BRS) + dlc |= TX_FD_BRS_ENABLE; + } + + rk3562_can_write(rcan, CAN_TXID, id); + rk3562_can_write(rcan, CAN_TXFIC, dlc); + + for (i = 0; i < cf->len; i += 4) + rk3562_can_write(rcan, CAN_TXDAT0 + i, + *(u32 *)(cf->data + i)); + + can_put_echo_skb(skb, ndev, 0); + + rk3562_can_write(rcan, CAN_CMD, cmd); + + return NETDEV_TX_OK; +} + +static int rk3562_can_rx(struct net_device *ndev) +{ + struct rk3562_can *rcan = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct canfd_frame *cf; + struct sk_buff *skb; + u32 id_rk3562_can, dlc; + int i = 0; + u32 __maybe_unused ts, ret; + u32 data[16] = {0}; + + dlc = rk3562_can_read(rcan, CAN_RXFRD); + id_rk3562_can = rk3562_can_read(rcan, CAN_RXFRD); + for (i = 0; i < (rcan->rx_max_data - 2); i++) + data[i] = rk3562_can_read(rcan, CAN_RXFRD); + + /* create zero'ed CAN frame buffer */ + if (dlc & FDF_MASK) + skb = alloc_canfd_skb(ndev, &cf); + else + skb = alloc_can_skb(ndev, (struct can_frame **)&cf); + if (!skb) { + stats->rx_dropped++; + return 1; + } + + /* Change CAN data length format to socketCAN data format */ + if (dlc & FDF_MASK) + cf->len = can_dlc2len(dlc & DLC_MASK); + else + cf->len = get_can_dlc(dlc & DLC_MASK); + + /* Change CAN ID format to socketCAN ID format */ + if (dlc & FORMAT_MASK) { + /* The received frame is an Extended format frame */ + cf->can_id = id_rk3562_can; + cf->can_id |= CAN_EFF_FLAG; + if (dlc & RTR_MASK) + cf->can_id |= CAN_RTR_FLAG; + } else { + /* The received frame is a standard format frame */ + cf->can_id = id_rk3562_can; + if (dlc & RTR_MASK) + cf->can_id |= CAN_RTR_FLAG; + } + + if (dlc & BRS_MASK) + cf->flags |= CANFD_BRS; + + if (!(cf->can_id & CAN_RTR_FLAG)) { + /* Change CAN data format to socketCAN data format */ + for (i = 0; i < cf->len; i += 4) + *(u32 *)(cf->data + i) = data[i / 4]; + } + + stats->rx_packets++; + stats->rx_bytes += cf->len; + netif_rx(skb); + + can_led_event(ndev, CAN_LED_EVENT_RX); + + return 1; +} + +/* rk3562_can_rx_poll - Poll routine for rx packets (NAPI) + * @napi: napi structure pointer + * @quota: Max number of rx packets to be processed. + * + * This is the poll routine for rx part. + * It will process the packets maximux quota value. + * + * Return: number of packets received + */ +static int rk3562_can_rx_poll(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct rk3562_can *rcan = netdev_priv(ndev); + int work_done = 0; + u32 cnt; + + quota = (rk3562_can_read(rcan, CAN_RXFC) & rcan->rx_fifo_mask) >> + rcan->rx_fifo_shift; + cnt = rk3562_can_read(rcan, CAN_FIFO_INFO) & CAN_FIFO_INFO_CNT_MASK; + cnt = cnt / rcan->rx_max_data; + if (cnt != quota) + quota = cnt; + + while (work_done < quota) + work_done += rk3562_can_rx(ndev); + + if (work_done) + can_led_event(ndev, CAN_LED_EVENT_RX); + + if (work_done < rcan->rx_fifo_depth) { + napi_complete_done(napi, work_done); + rk3562_can_write(rcan, CAN_INT_MASK, 0); + } + + return work_done; +} + +static int rk3562_can_err(struct net_device *ndev, u32 isr) +{ + struct rk3562_can *rcan = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + unsigned int rxerr, txerr; + u32 sta_reg, err_code; + + skb = alloc_can_err_skb(ndev, &cf); + + rxerr = rk3562_can_read(rcan, CAN_RX_ERR_CNT); + txerr = rk3562_can_read(rcan, CAN_TX_ERR_CNT); + sta_reg = rk3562_can_read(rcan, CAN_STATE); + err_code = rk3562_can_read(rcan, CAN_ERR_CODE); + + if ((isr & TX_LOSTARB_INT) || + ((!(err_code & ERR_DIR_RX)) && + (err_code & ERR_TX_ERR_MASK))) { + err_code = rk3562_can_read(rcan, CAN_ERR_CODE); + err_code = rk3562_can_read(rcan, CAN_CMD); + rk3562_can_write(rcan, CAN_CMD, CAN_TX0_REQ); + } + + if (skb) { + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + + if (isr & BUS_OFF_INT) { + rcan->can.state = CAN_STATE_BUS_OFF; + rcan->can.can_stats.bus_off++; + cf->can_id |= CAN_ERR_BUSOFF; + } else if (isr & ERR_WARN_INT) { + rcan->can.can_stats.error_warning++; + rcan->can.state = CAN_STATE_ERROR_WARNING; + /* error warning state */ + if (likely(skb)) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + } else if (isr & PASSIVE_ERR_INT) { + rcan->can.can_stats.error_passive++; + rcan->can.state = CAN_STATE_ERROR_PASSIVE; + /* error passive state */ + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + + if (rcan->can.state >= CAN_STATE_BUS_OFF || + ((sta_reg & CAN_BUSOFF_FLAG) == CAN_BUSOFF_FLAG)) + can_bus_off(ndev); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + return 0; +} + +static irqreturn_t rk3562_can_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct rk3562_can *rcan = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + u32 err_int = ERR_WARN_INT | RX_BUF_OV_INT | PASSIVE_ERR_INT | + TX_LOSTARB_INT | BUS_ERR_INT | BUS_OFF_INT; + u32 isr; + u32 dlc = 0; + + isr = rk3562_can_read(rcan, CAN_INT); + if (isr & RX_FINISH_INT) { + rk3562_can_write(rcan, CAN_INT_MASK, + RX_FINISH_INT | TIMEOUT_INT); + napi_schedule(&rcan->napi); + } + if (isr & TX_FINISH_INT) { + dlc = rk3562_can_read(rcan, CAN_TXFIC); + /* transmission complete interrupt */ + if (dlc & FDF_MASK) + stats->tx_bytes += can_dlc2len(dlc & DLC_MASK); + else + stats->tx_bytes += (dlc & DLC_MASK); + stats->tx_packets++; + rk3562_can_write(rcan, CAN_CMD, 0); + can_get_echo_skb(ndev, 0); + netif_wake_queue(ndev); + can_led_event(ndev, CAN_LED_EVENT_TX); + } + + if (isr & err_int) { + /* error interrupt */ + if (rk3562_can_err(ndev, isr)) + netdev_err(ndev, "can't allocate buffer - clearing pending interrupts\n"); + } + + rk3562_can_write(rcan, CAN_INT, isr); + return IRQ_HANDLED; +} + +static int rk3562_can_open(struct net_device *ndev) +{ + struct rk3562_can *rcan = netdev_priv(ndev); + int err; + + /* common open */ + err = open_candev(ndev); + if (err) + return err; + + err = pm_runtime_get_sync(rcan->dev); + if (err < 0) { + netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", + __func__, err); + goto exit; + } + + err = rk3562_can_start(ndev); + if (err) { + netdev_err(ndev, "could not start CAN peripheral\n"); + goto exit_can_start; + } + + can_led_event(ndev, CAN_LED_EVENT_OPEN); + napi_enable(&rcan->napi); + netif_start_queue(ndev); + + netdev_dbg(ndev, "%s\n", __func__); + return 0; + +exit_can_start: + pm_runtime_put(rcan->dev); +exit: + close_candev(ndev); + return err; +} + +static int rk3562_can_close(struct net_device *ndev) +{ + struct rk3562_can *rcan = netdev_priv(ndev); + + netif_stop_queue(ndev); + napi_disable(&rcan->napi); + rk3562_can_stop(ndev); + close_candev(ndev); + can_led_event(ndev, CAN_LED_EVENT_STOP); + pm_runtime_put(rcan->dev); + + netdev_dbg(ndev, "%s\n", __func__); + return 0; +} + +static const struct net_device_ops rk3562_can_netdev_ops = { + .ndo_open = rk3562_can_open, + .ndo_stop = rk3562_can_close, + .ndo_start_xmit = rk3562_can_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +/** + * rk3562_can_suspend - Suspend method for the driver + * @dev: Address of the device structure + * + * Put the driver into low power mode. + * Return: 0 on success and failure value on error + */ +static int __maybe_unused rk3562_can_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + + if (netif_running(ndev)) { + netif_stop_queue(ndev); + netif_device_detach(ndev); + rk3562_can_stop(ndev); + } + + return pm_runtime_force_suspend(dev); +} + +/** + * rk3562_can_resume - Resume from suspend + * @dev: Address of the device structure + * + * Resume operation after suspend. + * Return: 0 on success and failure value on error + */ +static int __maybe_unused rk3562_can_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) { + dev_err(dev, "pm_runtime_force_resume failed on resume\n"); + return ret; + } + + if (netif_running(ndev)) { + ret = rk3562_can_start(ndev); + if (ret) { + dev_err(dev, "rk3562_can_chip_start failed on resume\n"); + return ret; + } + + netif_device_attach(ndev); + netif_start_queue(ndev); + } + + return 0; +} + +/** + * rk3562_can_runtime_suspend - Runtime suspend method for the driver + * @dev: Address of the device structure + * + * Put the driver into low power mode. + * Return: 0 always + */ +static int __maybe_unused rk3562_can_runtime_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct rk3562_can *rcan = netdev_priv(ndev); + + clk_bulk_disable_unprepare(rcan->num_clks, rcan->clks); + + return 0; +} + +/** + * rk3562_can_runtime_resume - Runtime resume from suspend + * @dev: Address of the device structure + * + * Resume operation after suspend. + * Return: 0 on success and failure value on error + */ +static int __maybe_unused rk3562_can_runtime_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct rk3562_can *rcan = netdev_priv(ndev); + int ret; + + ret = clk_bulk_prepare_enable(rcan->num_clks, rcan->clks); + if (ret) { + dev_err(dev, "Cannot enable clock.\n"); + return ret; + } + + return 0; +} + +static const struct dev_pm_ops rk3562_can_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(rk3562_can_suspend, rk3562_can_resume) + SET_RUNTIME_PM_OPS(rk3562_can_runtime_suspend, + rk3562_can_runtime_resume, NULL) +}; + +static const struct of_device_id rk3562_can_of_match[] = { + { + .compatible = "rockchip,rk3562-can", + .data = (void *)ROCKCHIP_RK3562_CAN_MODE + }, + { + .compatible = "rockchip,rk3562-canfd", + .data = (void *)ROCKCHIP_RK3562_CANFD_MODE + }, + {}, +}; +MODULE_DEVICE_TABLE(of, rk3562_can_of_match); + +static int rk3562_can_probe(struct platform_device *pdev) +{ + struct net_device *ndev; + struct rk3562_can *rcan; + struct resource *res; + void __iomem *addr; + int err, irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "could not get a valid irq\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(addr)) + return -EBUSY; + + ndev = alloc_candev(sizeof(struct rk3562_can), 1); + if (!ndev) { + dev_err(&pdev->dev, "could not allocate memory for CANFD device\n"); + return -ENOMEM; + } + rcan = netdev_priv(ndev); + + /* register interrupt handler */ + err = devm_request_irq(&pdev->dev, irq, rk3562_can_interrupt, + 0, ndev->name, ndev); + if (err) { + dev_err(&pdev->dev, "request_irq err: %d\n", err); + return err; + } + + rcan->reset = devm_reset_control_array_get(&pdev->dev, false, false); + if (IS_ERR(rcan->reset)) { + if (PTR_ERR(rcan->reset) != -EPROBE_DEFER) + dev_err(&pdev->dev, "failed to get canfd reset lines\n"); + return PTR_ERR(rcan->reset); + } + rcan->num_clks = devm_clk_bulk_get_all(&pdev->dev, &rcan->clks); + if (rcan->num_clks < 1) + return -ENODEV; + + rcan->mode = (unsigned long)of_device_get_match_data(&pdev->dev); + + rcan->base = addr; + rcan->can.clock.freq = clk_get_rate(rcan->clks[0].clk); + rcan->dev = &pdev->dev; + rcan->can.state = CAN_STATE_STOPPED; + switch (rcan->mode) { + case ROCKCHIP_RK3562_CAN_MODE: + rcan->can.bittiming_const = &rk3562_can_bittiming_const; + rcan->can.do_set_mode = rk3562_can_set_mode; + rcan->can.do_get_berr_counter = rk3562_can_get_berr_counter; + rcan->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_3_SAMPLES; + rcan->rx_fifo_shift = RX_FIFO_CNT2_SHIFT; + rcan->rx_fifo_mask = RX_FIFO_CNT2_MASK; + rcan->rx_fifo_depth = 32; + rcan->rx_max_data = 4; + break; + case ROCKCHIP_RK3562_CANFD_MODE: + rcan->can.bittiming_const = &rk3562_can_bittiming_const; + rcan->can.data_bittiming_const = &rk3562_can_data_bittiming_const; + rcan->can.do_set_mode = rk3562_can_set_mode; + rcan->can.do_get_berr_counter = rk3562_can_get_berr_counter; + rcan->can.do_set_bittiming = rk3562_can_set_bittiming; + rcan->can.do_set_data_bittiming = rk3562_can_set_bittiming; + rcan->can.ctrlmode = CAN_CTRLMODE_FD; + /* IFI CANFD can do both Bosch FD and ISO FD */ + rcan->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_FD; + rcan->rx_fifo_shift = RX_FIFO_CNT2_SHIFT; + rcan->rx_fifo_mask = RX_FIFO_CNT2_MASK; + rcan->rx_fifo_depth = 7; + rcan->rx_max_data = 18; + break; + default: + return -EINVAL; + } + + ndev->netdev_ops = &rk3562_can_netdev_ops; + ndev->irq = irq; + ndev->flags |= IFF_ECHO; + rcan->can.restart_ms = 1; + + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + + pm_runtime_enable(&pdev->dev); + err = pm_runtime_get_sync(&pdev->dev); + if (err < 0) { + dev_err(&pdev->dev, "%s: pm_runtime_get failed(%d)\n", + __func__, err); + goto err_pmdisable; + } + netif_napi_add(ndev, &rcan->napi, rk3562_can_rx_poll, + rcan->rx_fifo_depth); + err = register_candev(ndev); + if (err) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto err_disableclks; + } + + devm_can_led_init(ndev); + + return 0; + +err_disableclks: + pm_runtime_put(&pdev->dev); +err_pmdisable: + pm_runtime_disable(&pdev->dev); + free_candev(ndev); + + return err; +} + +static int rk3562_can_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct rk3562_can *rcan = netdev_priv(ndev); + + unregister_netdev(ndev); + pm_runtime_disable(&pdev->dev); + netif_napi_del(&rcan->napi); + free_candev(ndev); + + return 0; +} + +static struct platform_driver rk3562_can_driver = { + .driver = { + .name = DRV_NAME, + .pm = &rk3562_can_dev_pm_ops, + .of_match_table = rk3562_can_of_match, + }, + .probe = rk3562_can_probe, + .remove = rk3562_can_remove, +}; +module_platform_driver(rk3562_can_driver); + +MODULE_AUTHOR("Elaine Zhang "); +MODULE_DESCRIPTION("RK3562 CAN Drivers"); diff --git a/drivers/net/can/rockchip/rockchip_can.c b/drivers/net/can/rockchip/rockchip_can.c new file mode 100644 index 000000000..89949fcbc --- /dev/null +++ b/drivers/net/can/rockchip/rockchip_can.c @@ -0,0 +1,814 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020 Rockchip Electronics Co. Ltd. + * Rockchip CAN driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "rockchip_can" + +#define CAN_MODE 0x00 +#define RESET_MODE 0 +#define WORK_MODE BIT(0) +#define SELF_TEST_EN BIT(2) +#define MODE_AUTO_RETX BIT(10) + +#define CAN_CMD 0x04 +#define TX_REQ BIT(0) + +#define CAN_STATE 0x08 +#define RX_BUF_FULL BIT(0) +#define TX_BUF_FULL BIT(1) +#define RX_PERIOD BIT(2) +#define TX_PERIOD BIT(3) +#define ERR_WARN BIT(4) +#define BUS_OFF BIT(5) + +#define CAN_INT 0x0C + +#define CAN_INT_MASK 0x10 +#define RX_FINISH BIT(0) +#define TX_FINISH BIT(1) +#define ERR_WARN_INT BIT(2) +#define RX_BUF_OV BIT(3) +#define PASSIVE_ERR BIT(4) +#define TX_LOSTARB BIT(5) +#define BUS_ERR_INT BIT(6) + +/* Bit Timing Register */ +#define CAN_BTT 0x18 +#define MODE_3_SAMPLES BIT(16) +#define BT_SJW_SHIFT 14 +#define BT_SJW_MASK GENMASK(15, 14) +#define BT_BRP_SHIFT 8 +#define BT_BRP_MASK GENMASK(13, 8) +#define BT_TSEG2_SHIFT 4 +#define BT_TSEG2_MASK GENMASK(6, 4) +#define BT_TSEG1_SHIFT 0 +#define BT_TSEG1_MASK GENMASK(3, 0) + +#define CAN_LOSTARB_CODE 0x28 + +#define CAN_ERR_CODE 0x2c +#define ERR_TYPE_MASK GENMASK(24, 22) +#define ERR_TYPE_SHIFT 22 +#define BIT_ERR 0 +#define STUFF_ERR 1 +#define FORM_ERR 2 +#define ACK_ERR 3 +#define CRC_ERR 4 +#define ERR_DIR_RX BIT(21) +#define ERR_LOC_MASK GENMASK(13, 0) + +#define CAN_RX_ERR_CNT 0x34 + +#define CAN_TX_ERR_CNT 0x38 + +#define CAN_ID 0x3c + +#define CAN_ID_MASK 0x40 + +#define CAN_TX_FRM_INFO 0x50 +#define CAN_EFF BIT(7) +#define CAN_RTR BIT(6) +#define CAN_DLC_MASK GENMASK(3, 0) +#define CAN_DLC(x) ((x) & GENMASK(3, 0)) + +#define CAN_TX_ID 0x54 +#define CAN_TX_ID_MASK 0x1fffffff + +#define CAN_TX_DATA1 0x58 + +#define CAN_TX_DATA2 0x5c + +#define CAN_RX_FRM_INFO 0x60 + +#define CAN_RX_ID 0x64 + +#define CAN_RX_DATA1 0x68 + +#define CAN_RX_DATA2 0x6c + +#define CAN_RX_FILTER_MASK 0x1fffffff + +#define CAN_VERSION 0x70 + +struct rockchip_can { + struct can_priv can; + void __iomem *base; + struct device *dev; + struct clk_bulk_data *clks; + int num_clks; + struct reset_control *reset; +}; + +static const struct can_bittiming_const rockchip_can_bittiming_const = { + .name = DRV_NAME, + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 128, + .brp_inc = 2, +}; + +static void rockchip_can_write_cmdreg(struct rockchip_can *rcan, u8 val) +{ + writel(val, rcan->base + CAN_CMD); +} + +static int set_reset_mode(struct net_device *ndev) +{ + struct rockchip_can *rcan = netdev_priv(ndev); + + reset_control_assert(rcan->reset); + udelay(2); + reset_control_deassert(rcan->reset); + + writel(0, rcan->base + CAN_MODE); + + return 0; +} + +static int set_normal_mode(struct net_device *ndev) +{ + struct rockchip_can *rcan = netdev_priv(ndev); + u32 val; + + val = readl(rcan->base + CAN_MODE); + val |= WORK_MODE | MODE_AUTO_RETX; + writel(val, rcan->base + CAN_MODE); + + return 0; +} + +/* bittiming is called in reset_mode only */ +static int rockchip_can_set_bittiming(struct net_device *ndev) +{ + struct rockchip_can *rcan = netdev_priv(ndev); + struct can_bittiming *bt = &rcan->can.bittiming; + u32 cfg; + + cfg = ((bt->sjw - 1) << BT_SJW_SHIFT) | + (((bt->brp >> 1) - 1) << BT_BRP_SHIFT) | + ((bt->phase_seg2 - 1) << BT_TSEG2_SHIFT) | + ((bt->prop_seg + bt->phase_seg1 - 1)); + if (rcan->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + cfg |= MODE_3_SAMPLES; + + writel(cfg, rcan->base + CAN_BTT); + + netdev_dbg(ndev, "setting BITTIMING=0x%08x brp: %d bitrate:%d\n", + cfg, bt->brp, bt->bitrate); + + return 0; +} + +static int rockchip_can_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct rockchip_can *rcan = netdev_priv(ndev); + int err; + + err = pm_runtime_get_sync(rcan->dev); + if (err < 0) { + netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", + __func__, err); + return err; + } + + bec->rxerr = readl(rcan->base + CAN_RX_ERR_CNT); + bec->txerr = readl(rcan->base + CAN_TX_ERR_CNT); + + pm_runtime_put(rcan->dev); + + netdev_dbg(ndev, "%s\n", __func__); + return 0; +} + +static int rockchip_can_start(struct net_device *ndev) +{ + struct rockchip_can *rcan = netdev_priv(ndev); + + /* we need to enter the reset mode */ + set_reset_mode(ndev); + + writel(0, rcan->base + CAN_INT_MASK); + + /* RECEIVING FILTER, accept all */ + writel(0, rcan->base + CAN_ID); + writel(CAN_RX_FILTER_MASK, rcan->base + CAN_ID_MASK); + + rockchip_can_set_bittiming(ndev); + + set_normal_mode(ndev); + + rcan->can.state = CAN_STATE_ERROR_ACTIVE; + + netdev_dbg(ndev, "%s\n", __func__); + return 0; +} + +static int rockchip_can_stop(struct net_device *ndev) +{ + struct rockchip_can *rcan = netdev_priv(ndev); + u32 val; + + rcan->can.state = CAN_STATE_STOPPED; + /* we need to enter reset mode */ + set_reset_mode(ndev); + + /* disable all interrupts */ + val = RX_FINISH | TX_FINISH | ERR_WARN_INT | + RX_BUF_OV | PASSIVE_ERR | TX_LOSTARB | + BUS_ERR_INT; + + writel(val, rcan->base + CAN_INT_MASK); + netdev_dbg(ndev, "%s\n", __func__); + return 0; +} + +static int rockchip_can_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int err; + + netdev_dbg(ndev, "can set mode: 0x%x\n", mode); + + switch (mode) { + case CAN_MODE_START: + err = rockchip_can_start(ndev); + if (err) { + netdev_err(ndev, "starting CAN controller failed!\n"); + return err; + } + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +/* transmit a CAN message + * message layout in the sk_buff should be like this: + * xx xx xx xx ff ll 00 11 22 33 44 55 66 77 + * [ can_id ] [flags] [len] [can data (up to 8 bytes] + */ +static netdev_tx_t rockchip_can_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct rockchip_can *rcan = netdev_priv(ndev); + struct can_frame *cf = (struct can_frame *)skb->data; + canid_t id; + u8 dlc; + u32 fi; + u32 data1 = 0, data2 = 0; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(ndev); + + id = cf->can_id; + dlc = cf->can_dlc; + fi = dlc; + + if (id & CAN_RTR_FLAG) { + fi |= CAN_RTR; + fi &= ~CAN_DLC_MASK; + } + + if (id & CAN_EFF_FLAG) + fi |= CAN_EFF; + + rockchip_can_write_cmdreg(rcan, 0); + + writel(id & CAN_TX_ID_MASK, rcan->base + CAN_TX_ID); + if (!(id & CAN_RTR_FLAG)) { + data1 = le32_to_cpup((__le32 *)&cf->data[0]); + data2 = le32_to_cpup((__le32 *)&cf->data[4]); + writel(data1, rcan->base + CAN_TX_DATA1); + writel(data2, rcan->base + CAN_TX_DATA2); + } + + writel(fi, rcan->base + CAN_TX_FRM_INFO); + can_put_echo_skb(skb, ndev, 0, 0); + + rockchip_can_write_cmdreg(rcan, TX_REQ); + netdev_dbg(ndev, "TX: can_id:0x%08x dlc: %d mode: 0x%08x data: 0x%08x 0x%08x\n", + cf->can_id, cf->can_dlc, rcan->can.ctrlmode, data1, data2); + + return NETDEV_TX_OK; +} + +static void rockchip_can_rx(struct net_device *ndev) +{ + struct rockchip_can *rcan = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + canid_t id; + u8 fi; + u32 data1 = 0, data2 = 0; + + /* create zero'ed CAN frame buffer */ + skb = alloc_can_skb(ndev, &cf); + if (!skb) + return; + + fi = readl(rcan->base + CAN_RX_FRM_INFO); + cf->can_dlc = can_cc_dlc2len(fi & CAN_DLC_MASK); + id = readl(rcan->base + CAN_RX_ID); + if (fi & CAN_EFF) + id |= CAN_EFF_FLAG; + + /* remote frame ? */ + if (fi & CAN_RTR) { + id |= CAN_RTR_FLAG; + } else { + data1 = readl(rcan->base + CAN_RX_DATA1); + data2 = readl(rcan->base + CAN_RX_DATA2); + } + + cf->can_id = id; + *(__le32 *)(cf->data + 0) = cpu_to_le32(data1); + *(__le32 *)(cf->data + 4) = cpu_to_le32(data2); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + can_led_event(ndev, CAN_LED_EVENT_RX); + + netdev_dbg(ndev, "%s can_id:0x%08x fi: 0x%08x dlc: %d data: 0x%08x 0x%08x\n", + __func__, cf->can_id, fi, cf->can_dlc, + data1, data2); +} + +static void rockchip_can_clean_rx_info(struct rockchip_can *rcan) +{ + readl(rcan->base + CAN_RX_FRM_INFO); + readl(rcan->base + CAN_RX_ID); + readl(rcan->base + CAN_RX_DATA1); + readl(rcan->base + CAN_RX_DATA2); +} + +static int rockchip_can_err(struct net_device *ndev, u8 isr) +{ + struct rockchip_can *rcan = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + enum can_state state = rcan->can.state; + enum can_state rx_state, tx_state; + struct can_frame *cf; + struct sk_buff *skb; + unsigned int rxerr, txerr; + u32 ecc, alc; + u32 sta_reg; + + skb = alloc_can_err_skb(ndev, &cf); + + rxerr = readl(rcan->base + CAN_RX_ERR_CNT); + txerr = readl(rcan->base + CAN_TX_ERR_CNT); + sta_reg = readl(rcan->base + CAN_STATE); + + if (skb) { + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + + if (isr & RX_BUF_OV) { + /* data overrun interrupt */ + netdev_dbg(ndev, "data overrun interrupt\n"); + if (likely(skb)) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + } + stats->rx_over_errors++; + stats->rx_errors++; + + /* reset the CAN IP by entering reset mode + * ignoring timeout error + */ + set_reset_mode(ndev); + set_normal_mode(ndev); + } + + if (isr & ERR_WARN_INT) { + /* error warning interrupt */ + netdev_dbg(ndev, "error warning interrupt\n"); + + if (sta_reg & BUS_OFF) + state = CAN_STATE_BUS_OFF; + else if (sta_reg & ERR_WARN) + state = CAN_STATE_ERROR_WARNING; + else + state = CAN_STATE_ERROR_ACTIVE; + } + + if (isr & BUS_ERR_INT) { + /* bus error interrupt */ + netdev_dbg(ndev, "bus error interrupt\n"); + rcan->can.can_stats.bus_error++; + stats->rx_errors++; + + if (likely(skb)) { + ecc = readl(rcan->base + CAN_ERR_CODE); + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + switch ((ecc & ERR_TYPE_MASK) >> ERR_TYPE_SHIFT) { + case BIT_ERR: + cf->data[2] |= CAN_ERR_PROT_BIT; + break; + case FORM_ERR: + cf->data[2] |= CAN_ERR_PROT_FORM; + break; + case STUFF_ERR: + cf->data[2] |= CAN_ERR_PROT_STUFF; + break; + default: + cf->data[3] = ecc & ERR_LOC_MASK; + break; + } + /* error occurred during transmission? */ + if ((ecc & ERR_DIR_RX) == 0) + cf->data[2] |= CAN_ERR_PROT_TX; + } + } + + if (isr & PASSIVE_ERR) { + /* error passive interrupt */ + netdev_dbg(ndev, "error passive interrupt\n"); + if (state == CAN_STATE_ERROR_PASSIVE) + state = CAN_STATE_ERROR_WARNING; + else + state = CAN_STATE_ERROR_PASSIVE; + } + if (isr & TX_LOSTARB) { + /* arbitration lost interrupt */ + netdev_dbg(ndev, "arbitration lost interrupt\n"); + alc = readl(rcan->base + CAN_LOSTARB_CODE); + rcan->can.can_stats.arbitration_lost++; + stats->tx_errors++; + if (likely(skb)) { + cf->can_id |= CAN_ERR_LOSTARB; + cf->data[0] = alc; + } + } + + if (state != rcan->can.state) { + tx_state = txerr >= rxerr ? state : 0; + rx_state = txerr <= rxerr ? state : 0; + + if (likely(skb)) + can_change_state(ndev, cf, tx_state, rx_state); + else + rcan->can.state = state; + if (state == CAN_STATE_BUS_OFF) + can_bus_off(ndev); + } + + if (likely(skb)) { + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } else { + return -ENOMEM; + } + + return 0; +} + +static irqreturn_t rockchip_can_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct rockchip_can *rcan = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + u8 err_int = ERR_WARN_INT | RX_BUF_OV | PASSIVE_ERR | + TX_LOSTARB | BUS_ERR_INT; + u8 isr; + + isr = readl(rcan->base + CAN_INT); + if (isr & TX_FINISH) { + /* transmission complete interrupt */ + stats->tx_bytes += readl(rcan->base + CAN_TX_FRM_INFO) & + CAN_DLC_MASK; + stats->tx_packets++; + rockchip_can_write_cmdreg(rcan, 0); + can_get_echo_skb(ndev, 0, 0); + netif_wake_queue(ndev); + can_led_event(ndev, CAN_LED_EVENT_TX); + } + + if (isr & RX_FINISH) + rockchip_can_rx(ndev); + + if (isr & err_int) { + rockchip_can_clean_rx_info(rcan); + if (rockchip_can_err(ndev, isr)) + netdev_err(ndev, "can't allocate buffer - clearing pending interrupts\n"); + } + + writel(isr, rcan->base + CAN_INT); + rockchip_can_clean_rx_info(rcan); + netdev_dbg(ndev, "isr: 0x%x\n", isr); + return IRQ_HANDLED; +} + +static int rockchip_can_open(struct net_device *ndev) +{ + struct rockchip_can *rcan = netdev_priv(ndev); + int err; + + /* common open */ + err = open_candev(ndev); + if (err) + return err; + + err = pm_runtime_get_sync(rcan->dev); + if (err < 0) { + netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", + __func__, err); + goto exit; + } + + err = rockchip_can_start(ndev); + if (err) { + netdev_err(ndev, "could not start CAN peripheral\n"); + goto exit_can_start; + } + + can_led_event(ndev, CAN_LED_EVENT_OPEN); + netif_start_queue(ndev); + + netdev_dbg(ndev, "%s\n", __func__); + return 0; + +exit_can_start: + pm_runtime_put(rcan->dev); +exit: + close_candev(ndev); + return err; +} + +static int rockchip_can_close(struct net_device *ndev) +{ + struct rockchip_can *rcan = netdev_priv(ndev); + + netif_stop_queue(ndev); + rockchip_can_stop(ndev); + close_candev(ndev); + can_led_event(ndev, CAN_LED_EVENT_STOP); + pm_runtime_put(rcan->dev); + + netdev_dbg(ndev, "%s\n", __func__); + return 0; +} + +static const struct net_device_ops rockchip_can_netdev_ops = { + .ndo_open = rockchip_can_open, + .ndo_stop = rockchip_can_close, + .ndo_start_xmit = rockchip_can_start_xmit, +}; + +/** + * rockchip_can_suspend - Suspend method for the driver + * @dev: Address of the device structure + * + * Put the driver into low power mode. + * Return: 0 on success and failure value on error + */ +static int __maybe_unused rockchip_can_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + + if (netif_running(ndev)) { + netif_stop_queue(ndev); + netif_device_detach(ndev); + rockchip_can_stop(ndev); + } + + return pm_runtime_force_suspend(dev); +} + +/** + * rockchip_can_resume - Resume from suspend + * @dev: Address of the device structure + * + * Resume operation after suspend. + * Return: 0 on success and failure value on error + */ +static int __maybe_unused rockchip_can_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) { + dev_err(dev, "pm_runtime_force_resume failed on resume\n"); + return ret; + } + + if (netif_running(ndev)) { + ret = rockchip_can_start(ndev); + if (ret) { + dev_err(dev, "rockchip_can_chip_start failed on resume\n"); + return ret; + } + + netif_device_attach(ndev); + netif_start_queue(ndev); + } + + return 0; +} + +/** + * rockchip_can_runtime_suspend - Runtime suspend method for the driver + * @dev: Address of the device structure + * + * Put the driver into low power mode. + * Return: 0 always + */ +static int __maybe_unused rockchip_can_runtime_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct rockchip_can *rcan = netdev_priv(ndev); + + clk_bulk_disable_unprepare(rcan->num_clks, rcan->clks); + + return 0; +} + +/** + * rockchip_can_runtime_resume - Runtime resume from suspend + * @dev: Address of the device structure + * + * Resume operation after suspend. + * Return: 0 on success and failure value on error + */ +static int __maybe_unused rockchip_can_runtime_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct rockchip_can *rcan = netdev_priv(ndev); + int ret; + + ret = clk_bulk_prepare_enable(rcan->num_clks, rcan->clks); + if (ret) { + dev_err(dev, "Cannot enable clock.\n"); + return ret; + } + + return 0; +} + +static const struct dev_pm_ops rockchip_can_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(rockchip_can_suspend, rockchip_can_resume) + SET_RUNTIME_PM_OPS(rockchip_can_runtime_suspend, + rockchip_can_runtime_resume, NULL) +}; + +static const struct of_device_id rockchip_can_of_match[] = { + {.compatible = "rockchip,can-1.0"}, + {}, +}; +MODULE_DEVICE_TABLE(of, rockchip_can_of_match); + +static int rockchip_can_probe(struct platform_device *pdev) +{ + struct net_device *ndev; + struct rockchip_can *rcan; + struct resource *res; + void __iomem *addr; + int err, irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "could not get a valid irq\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(addr)) + return -EBUSY; + + ndev = alloc_candev(sizeof(struct rockchip_can), 1); + if (!ndev) { + dev_err(&pdev->dev, "could not allocate memory for CAN device\n"); + return -ENOMEM; + } + + ndev->netdev_ops = &rockchip_can_netdev_ops; + ndev->irq = irq; + ndev->flags |= IFF_ECHO; + + rcan = netdev_priv(ndev); + + /* register interrupt handler */ + err = devm_request_irq(&pdev->dev, ndev->irq, rockchip_can_interrupt, + 0, ndev->name, ndev); + if (err) { + dev_err(&pdev->dev, "request_irq err: %d\n", err); + return err; + } + + rcan->reset = devm_reset_control_array_get(&pdev->dev, false, false); + if (IS_ERR(rcan->reset)) { + if (PTR_ERR(rcan->reset) != -EPROBE_DEFER) + dev_err(&pdev->dev, "failed to get rcan reset lines\n"); + return PTR_ERR(rcan->reset); + } + + rcan->num_clks = devm_clk_bulk_get_all(&pdev->dev, &rcan->clks); + if (rcan->num_clks < 1) { + dev_err(&pdev->dev, "bus clock not found\n"); + return -ENODEV; + } + + rcan->dev = &pdev->dev; + rcan->can.clock.freq = clk_get_rate(rcan->clks[0].clk); + rcan->can.bittiming_const = &rockchip_can_bittiming_const; + rcan->can.do_set_mode = rockchip_can_set_mode; + rcan->can.do_get_berr_counter = rockchip_can_get_berr_counter; + rcan->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_3_SAMPLES; + rcan->base = addr; + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + + pm_runtime_enable(&pdev->dev); + err = pm_runtime_get_sync(&pdev->dev); + if (err < 0) { + dev_err(&pdev->dev, "%s: pm_runtime_get failed(%d)\n", + __func__, err); + goto err_pmdisable; + } + + err = register_candev(ndev); + if (err) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto err_disableclks; + } + + devm_can_led_init(ndev); + + pm_runtime_put(&pdev->dev); + + return 0; + +err_disableclks: + pm_runtime_put(&pdev->dev); +err_pmdisable: + pm_runtime_disable(&pdev->dev); + free_candev(ndev); + + return err; +} + +static int rockchip_can_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + + unregister_netdev(ndev); + pm_runtime_disable(&pdev->dev); + free_candev(ndev); + + return 0; +} + +static struct platform_driver rockchip_can_driver = { + .driver = { + .name = DRV_NAME, + .pm = &rockchip_can_dev_pm_ops, + .of_match_table = rockchip_can_of_match, + }, + .probe = rockchip_can_probe, + .remove = rockchip_can_remove, +}; +module_platform_driver(rockchip_can_driver); + +MODULE_AUTHOR("Andy Yan "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Rockchip CAN Drivers"); diff --git a/drivers/net/can/rockchip/rockchip_canfd.c b/drivers/net/can/rockchip/rockchip_canfd.c new file mode 100644 index 000000000..24dca7894 --- /dev/null +++ b/drivers/net/can/rockchip/rockchip_canfd.c @@ -0,0 +1,1226 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020 Rockchip Electronics Co. Ltd. + * Rockchip CANFD driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* registers definition */ +enum rockchip_canfd_reg { + CAN_MODE = 0x00, + CAN_CMD = 0x04, + CAN_STATE = 0x08, + CAN_INT = 0x0c, + CAN_INT_MASK = 0x10, + CAN_LOSTARB_CODE = 0x28, + CAN_ERR_CODE = 0x2c, + CAN_RX_ERR_CNT = 0x34, + CAN_TX_ERR_CNT = 0x38, + CAN_IDCODE = 0x3c, + CAN_IDMASK = 0x40, + CAN_TX_CHECK_FIC = 0x50, + CAN_NBTP = 0x100, + CAN_DBTP = 0x104, + CAN_TDCR = 0x108, + CAN_TSCC = 0x10c, + CAN_TSCV = 0x110, + CAN_TXEFC = 0x114, + CAN_RXFC = 0x118, + CAN_AFC = 0x11c, + CAN_IDCODE0 = 0x120, + CAN_IDMASK0 = 0x124, + CAN_IDCODE1 = 0x128, + CAN_IDMASK1 = 0x12c, + CAN_IDCODE2 = 0x130, + CAN_IDMASK2 = 0x134, + CAN_IDCODE3 = 0x138, + CAN_IDMASK3 = 0x13c, + CAN_IDCODE4 = 0x140, + CAN_IDMASK4 = 0x144, + CAN_TXFIC = 0x200, + CAN_TXID = 0x204, + CAN_TXDAT0 = 0x208, + CAN_TXDAT1 = 0x20c, + CAN_TXDAT2 = 0x210, + CAN_TXDAT3 = 0x214, + CAN_TXDAT4 = 0x218, + CAN_TXDAT5 = 0x21c, + CAN_TXDAT6 = 0x220, + CAN_TXDAT7 = 0x224, + CAN_TXDAT8 = 0x228, + CAN_TXDAT9 = 0x22c, + CAN_TXDAT10 = 0x230, + CAN_TXDAT11 = 0x234, + CAN_TXDAT12 = 0x238, + CAN_TXDAT13 = 0x23c, + CAN_TXDAT14 = 0x240, + CAN_TXDAT15 = 0x244, + CAN_RXFIC = 0x300, + CAN_RXID = 0x304, + CAN_RXTS = 0x308, + CAN_RXDAT0 = 0x30c, + CAN_RXDAT1 = 0x310, + CAN_RXDAT2 = 0x314, + CAN_RXDAT3 = 0x318, + CAN_RXDAT4 = 0x31c, + CAN_RXDAT5 = 0x320, + CAN_RXDAT6 = 0x324, + CAN_RXDAT7 = 0x328, + CAN_RXDAT8 = 0x32c, + CAN_RXDAT9 = 0x330, + CAN_RXDAT10 = 0x334, + CAN_RXDAT11 = 0x338, + CAN_RXDAT12 = 0x33c, + CAN_RXDAT13 = 0x340, + CAN_RXDAT14 = 0x344, + CAN_RXDAT15 = 0x348, + CAN_RXFRD = 0x400, + CAN_TXEFRD = 0x500, +}; + +enum { + ROCKCHIP_CANFD_MODE = 0, + ROCKCHIP_CAN_MODE, + ROCKCHIP_RK3568_CAN_MODE, + ROCKCHIP_RK3568_CAN_MODE_V2, +}; + +#define DATE_LENGTH_12_BYTE (0x9) +#define DATE_LENGTH_16_BYTE (0xa) +#define DATE_LENGTH_20_BYTE (0xb) +#define DATE_LENGTH_24_BYTE (0xc) +#define DATE_LENGTH_32_BYTE (0xd) +#define DATE_LENGTH_48_BYTE (0xe) +#define DATE_LENGTH_64_BYTE (0xf) + +#define CAN_TX0_REQ BIT(0) +#define CAN_TX1_REQ BIT(1) +#define CAN_TX_REQ_FULL ((CAN_TX0_REQ) | (CAN_TX1_REQ)) + +#define MODE_FDOE BIT(15) +#define MODE_BRSD BIT(13) +#define MODE_SPACE_RX BIT(12) +#define MODE_AUTO_RETX BIT(10) +#define MODE_RXSORT BIT(7) +#define MODE_TXORDER BIT(6) +#define MODE_RXSTX BIT(5) +#define MODE_LBACK BIT(4) +#define MODE_SILENT BIT(3) +#define MODE_SELF_TEST BIT(2) +#define MODE_SLEEP BIT(1) +#define RESET_MODE 0 +#define WORK_MODE BIT(0) + +#define RX_FINISH_INT BIT(0) +#define TX_FINISH_INT BIT(1) +#define ERR_WARN_INT BIT(2) +#define RX_BUF_OV_INT BIT(3) +#define PASSIVE_ERR_INT BIT(4) +#define TX_LOSTARB_INT BIT(5) +#define BUS_ERR_INT BIT(6) +#define RX_FIFO_FULL_INT BIT(7) +#define RX_FIFO_OV_INT BIT(8) +#define BUS_OFF_INT BIT(9) +#define BUS_OFF_RECOVERY_INT BIT(10) +#define TSC_OV_INT BIT(11) +#define TXE_FIFO_OV_INT BIT(12) +#define TXE_FIFO_FULL_INT BIT(13) +#define WAKEUP_INT BIT(14) + +#define ERR_TYPE_MASK GENMASK(28, 26) +#define ERR_TYPE_SHIFT 26 +#define BIT_ERR 0 +#define STUFF_ERR 1 +#define FORM_ERR 2 +#define ACK_ERR 3 +#define CRC_ERR 4 +#define ERR_DIR_RX BIT(25) +#define ERR_LOC_MASK GENMASK(15, 0) + +/* Nominal Bit Timing & Prescaler Register (NBTP) */ +#define NBTP_MODE_3_SAMPLES BIT(31) +#define NBTP_NSJW_SHIFT 24 +#define NBTP_NSJW_MASK (0x7f << NBTP_NSJW_SHIFT) +#define NBTP_NBRP_SHIFT 16 +#define NBTP_NBRP_MASK (0xff << NBTP_NBRP_SHIFT) +#define NBTP_NTSEG2_SHIFT 8 +#define NBTP_NTSEG2_MASK (0x7f << NBTP_NTSEG2_SHIFT) +#define NBTP_NTSEG1_SHIFT 0 +#define NBTP_NTSEG1_MASK (0x7f << NBTP_NTSEG1_SHIFT) + +/* Data Bit Timing & Prescaler Register (DBTP) */ +#define DBTP_MODE_3_SAMPLES BIT(21) +#define DBTP_DSJW_SHIFT 17 +#define DBTP_DSJW_MASK (0xf << DBTP_DSJW_SHIFT) +#define DBTP_DBRP_SHIFT 9 +#define DBTP_DBRP_MASK (0xff << DBTP_DBRP_SHIFT) +#define DBTP_DTSEG2_SHIFT 5 +#define DBTP_DTSEG2_MASK (0xf << DBTP_DTSEG2_SHIFT) +#define DBTP_DTSEG1_SHIFT 0 +#define DBTP_DTSEG1_MASK (0x1f << DBTP_DTSEG1_SHIFT) + +/* Transmitter Delay Compensation Register (TDCR) */ +#define TDCR_TDCO_SHIFT 1 +#define TDCR_TDCO_MASK (0x3f << TDCR_TDCO_SHIFT) +#define TDCR_TDC_ENABLE BIT(0) + +#define TX_FD_ENABLE BIT(5) +#define TX_FD_BRS_ENABLE BIT(4) + +#define FIFO_ENABLE BIT(0) +#define RX_FIFO_CNT0_SHIFT 4 +#define RX_FIFO_CNT0_MASK (0x7 << RX_FIFO_CNT0_SHIFT) +#define RX_FIFO_CNT1_SHIFT 5 +#define RX_FIFO_CNT1_MASK (0x7 << RX_FIFO_CNT1_SHIFT) + +#define FORMAT_SHIFT 7 +#define FORMAT_MASK (0x1 << FORMAT_SHIFT) +#define RTR_SHIFT 6 +#define RTR_MASK (0x1 << RTR_SHIFT) +#define FDF_SHIFT 5 +#define FDF_MASK (0x1 << FDF_SHIFT) +#define BRS_SHIFT 4 +#define BRS_MASK (0x1 << BRS_SHIFT) +#define DLC_SHIFT 0 +#define DLC_MASK (0xF << DLC_SHIFT) + +#define CAN_RF_SIZE 0x48 +#define CAN_TEF_SIZE 0x8 +#define CAN_TXEFRD_OFFSET(n) (CAN_TXEFRD + CAN_TEF_SIZE * (n)) +#define CAN_RXFRD_OFFSET(n) (CAN_RXFRD + CAN_RF_SIZE * (n)) + +#define CAN_RX_FILTER_MASK 0x1fffffff +#define NOACK_ERR_FLAG 0xc200800 +#define CAN_BUSOFF_FLAG 0x20 + +#define DRV_NAME "rockchip_canfd" + +/* rockchip_canfd private data structure */ + +struct rockchip_canfd { + struct can_priv can; + struct device *dev; + struct napi_struct napi; + struct clk_bulk_data *clks; + int num_clks; + struct reset_control *reset; + void __iomem *base; + u32 irqstatus; + unsigned long mode; + int rx_fifo_shift; + u32 rx_fifo_mask; + bool txtorx; + u32 tx_invalid[4]; + struct delayed_work tx_err_work; + u32 delay_time_ms; +}; + +static inline u32 rockchip_canfd_read(const struct rockchip_canfd *priv, + enum rockchip_canfd_reg reg) +{ + return readl(priv->base + reg); +} + +static inline void rockchip_canfd_write(const struct rockchip_canfd *priv, + enum rockchip_canfd_reg reg, u32 val) +{ + writel(val, priv->base + reg); +} + +static const struct can_bittiming_const rockchip_canfd_bittiming_const = { + .name = DRV_NAME, + .tseg1_min = 1, + .tseg1_max = 128, + .tseg2_min = 1, + .tseg2_max = 128, + .sjw_max = 128, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 2, +}; + +static const struct can_bittiming_const rockchip_canfd_data_bittiming_const = { + .name = DRV_NAME, + .tseg1_min = 1, + .tseg1_max = 32, + .tseg2_min = 1, + .tseg2_max = 16, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 2, +}; + +static int set_reset_mode(struct net_device *ndev) +{ + struct rockchip_canfd *rcan = netdev_priv(ndev); + + reset_control_assert(rcan->reset); + udelay(2); + reset_control_deassert(rcan->reset); + + rockchip_canfd_write(rcan, CAN_MODE, 0); + + netdev_dbg(ndev, "%s MODE=0x%08x\n", __func__, + rockchip_canfd_read(rcan, CAN_MODE)); + + return 0; +} + +static int set_normal_mode(struct net_device *ndev) +{ + struct rockchip_canfd *rcan = netdev_priv(ndev); + u32 val; + + val = rockchip_canfd_read(rcan, CAN_MODE); + val |= WORK_MODE; + rockchip_canfd_write(rcan, CAN_MODE, val); + + netdev_dbg(ndev, "%s MODE=0x%08x\n", __func__, + rockchip_canfd_read(rcan, CAN_MODE)); + return 0; +} + +/* bittiming is called in reset_mode only */ +static int rockchip_canfd_set_bittiming(struct net_device *ndev) +{ + struct rockchip_canfd *rcan = netdev_priv(ndev); + const struct can_bittiming *bt = &rcan->can.bittiming; + const struct can_bittiming *dbt = &rcan->can.data_bittiming; + u16 brp, sjw, tseg1, tseg2; + u32 reg_btp; + + brp = (bt->brp >> 1) - 1; + sjw = bt->sjw - 1; + tseg1 = bt->prop_seg + bt->phase_seg1 - 1; + tseg2 = bt->phase_seg2 - 1; + reg_btp = (brp << NBTP_NBRP_SHIFT) | (sjw << NBTP_NSJW_SHIFT) | + (tseg1 << NBTP_NTSEG1_SHIFT) | + (tseg2 << NBTP_NTSEG2_SHIFT); + + if (rcan->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + reg_btp |= NBTP_MODE_3_SAMPLES; + + rockchip_canfd_write(rcan, CAN_NBTP, reg_btp); + + if (rcan->can.ctrlmode & CAN_CTRLMODE_FD) { + reg_btp = 0; + brp = (dbt->brp >> 1) - 1; + sjw = dbt->sjw - 1; + tseg1 = dbt->prop_seg + dbt->phase_seg1 - 1; + tseg2 = dbt->phase_seg2 - 1; + + if (dbt->bitrate > 2200000) { + u32 tdco; + + /* Equation based on Bosch's ROCKCHIP_CAN User Manual's + * Transmitter Delay Compensation Section + */ + tdco = (rcan->can.clock.freq / dbt->bitrate) * 2 / 3; + /* Max valid TDCO value is 63 */ + if (tdco > 63) + tdco = 63; + + rockchip_canfd_write(rcan, CAN_TDCR, + (tdco << TDCR_TDCO_SHIFT) | + TDCR_TDC_ENABLE); + } + + reg_btp |= (brp << DBTP_DBRP_SHIFT) | + (sjw << DBTP_DSJW_SHIFT) | + (tseg1 << DBTP_DTSEG1_SHIFT) | + (tseg2 << DBTP_DTSEG2_SHIFT); + + if (rcan->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + reg_btp |= DBTP_MODE_3_SAMPLES; + + rockchip_canfd_write(rcan, CAN_DBTP, reg_btp); + } + if (bt->bitrate > 200000) + rcan->delay_time_ms = 1; + else if (bt->bitrate > 50000) + rcan->delay_time_ms = 5; + else + rcan->delay_time_ms = 20; + + netdev_dbg(ndev, "%s NBTP=0x%08x, DBTP=0x%08x, TDCR=0x%08x\n", __func__, + rockchip_canfd_read(rcan, CAN_NBTP), + rockchip_canfd_read(rcan, CAN_DBTP), + rockchip_canfd_read(rcan, CAN_TDCR)); + return 0; +} + +static int rockchip_canfd_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct rockchip_canfd *rcan = netdev_priv(ndev); + int err; + + err = pm_runtime_get_sync(rcan->dev); + if (err < 0) { + netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", + __func__, err); + return err; + } + + bec->rxerr = rockchip_canfd_read(rcan, CAN_RX_ERR_CNT); + bec->txerr = rockchip_canfd_read(rcan, CAN_TX_ERR_CNT); + + pm_runtime_put(rcan->dev); + + netdev_dbg(ndev, "%s RX_ERR_CNT=0x%08x, TX_ERR_CNT=0x%08x\n", __func__, + rockchip_canfd_read(rcan, CAN_RX_ERR_CNT), + rockchip_canfd_read(rcan, CAN_TX_ERR_CNT)); + + return 0; +} + +static int rockchip_canfd_start(struct net_device *ndev) +{ + struct rockchip_canfd *rcan = netdev_priv(ndev); + u32 val; + + /* we need to enter the reset mode */ + set_reset_mode(ndev); + + rockchip_canfd_write(rcan, CAN_INT_MASK, 0); + + /* RECEIVING FILTER, accept all */ + rockchip_canfd_write(rcan, CAN_IDCODE, 0); + rockchip_canfd_write(rcan, CAN_IDMASK, CAN_RX_FILTER_MASK); + rockchip_canfd_write(rcan, CAN_IDCODE0, 0); + rockchip_canfd_write(rcan, CAN_IDMASK0, CAN_RX_FILTER_MASK); + rockchip_canfd_write(rcan, CAN_IDCODE1, 0); + rockchip_canfd_write(rcan, CAN_IDMASK1, CAN_RX_FILTER_MASK); + rockchip_canfd_write(rcan, CAN_IDCODE2, 0); + rockchip_canfd_write(rcan, CAN_IDMASK2, CAN_RX_FILTER_MASK); + rockchip_canfd_write(rcan, CAN_IDCODE3, 0); + rockchip_canfd_write(rcan, CAN_IDMASK3, CAN_RX_FILTER_MASK); + rockchip_canfd_write(rcan, CAN_IDCODE4, 0); + rockchip_canfd_write(rcan, CAN_IDMASK4, CAN_RX_FILTER_MASK); + + /* set mode */ + val = rockchip_canfd_read(rcan, CAN_MODE); + + /* rx fifo enable */ + rockchip_canfd_write(rcan, CAN_RXFC, + rockchip_canfd_read(rcan, CAN_RXFC) | FIFO_ENABLE); + + /* Mode */ + val |= MODE_FDOE; + + /* Loopback Mode */ + if (rcan->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + val |= MODE_SELF_TEST | MODE_LBACK; + + /* Listen-only mode */ + if (rcan->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + val |= MODE_SILENT; + + rockchip_canfd_write(rcan, CAN_MODE, val); + + rockchip_canfd_set_bittiming(ndev); + + set_normal_mode(ndev); + + rcan->can.state = CAN_STATE_ERROR_ACTIVE; + + netdev_dbg(ndev, "%s MODE=0x%08x, INT_MASK=0x%08x\n", __func__, + rockchip_canfd_read(rcan, CAN_MODE), + rockchip_canfd_read(rcan, CAN_INT_MASK)); + + return 0; +} + +static int rockchip_canfd_stop(struct net_device *ndev) +{ + struct rockchip_canfd *rcan = netdev_priv(ndev); + + rcan->can.state = CAN_STATE_STOPPED; + /* we need to enter reset mode */ + set_reset_mode(ndev); + + /* disable all interrupts */ + rockchip_canfd_write(rcan, CAN_INT_MASK, 0xffff); + + netdev_dbg(ndev, "%s MODE=0x%08x, INT_MASK=0x%08x\n", __func__, + rockchip_canfd_read(rcan, CAN_MODE), + rockchip_canfd_read(rcan, CAN_INT_MASK)); + return 0; +} + +static int rockchip_canfd_set_mode(struct net_device *ndev, + enum can_mode mode) +{ + int err; + + switch (mode) { + case CAN_MODE_START: + err = rockchip_canfd_start(ndev); + if (err) { + netdev_err(ndev, "starting CAN controller failed!\n"); + return err; + } + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static void rockchip_canfd_tx_err_delay_work(struct work_struct *work) +{ + struct rockchip_canfd *rcan = + container_of(work, struct rockchip_canfd, tx_err_work.work); + u32 mode, err_code; + + mode = rockchip_canfd_read(rcan, CAN_MODE); + err_code = rockchip_canfd_read(rcan, CAN_ERR_CODE); + if ((err_code & NOACK_ERR_FLAG) == NOACK_ERR_FLAG) { + rockchip_canfd_write(rcan, CAN_MODE, + rockchip_canfd_read(rcan, CAN_MODE) | MODE_SPACE_RX); + rockchip_canfd_write(rcan, CAN_CMD, CAN_TX0_REQ); + rockchip_canfd_write(rcan, CAN_MODE, + rockchip_canfd_read(rcan, CAN_MODE) & (~MODE_SPACE_RX)); + schedule_delayed_work(&rcan->tx_err_work, msecs_to_jiffies(rcan->delay_time_ms)); + } else { + rockchip_canfd_write(rcan, CAN_MODE, 0); + rockchip_canfd_write(rcan, CAN_MODE, mode); + rockchip_canfd_write(rcan, CAN_MODE, + rockchip_canfd_read(rcan, CAN_MODE) | MODE_SPACE_RX); + rockchip_canfd_write(rcan, CAN_CMD, CAN_TX0_REQ); + rockchip_canfd_write(rcan, CAN_MODE, + rockchip_canfd_read(rcan, CAN_MODE) & (~MODE_SPACE_RX)); + schedule_delayed_work(&rcan->tx_err_work, msecs_to_jiffies(rcan->delay_time_ms)); + } +} + +/* transmit a CAN message + * message layout in the sk_buff should be like this: + * xx xx xx xx ff ll 00 11 22 33 44 55 66 77 + * [ can_id ] [flags] [len] [can data (up to 8 bytes] + */ +static netdev_tx_t rockchip_canfd_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct rockchip_canfd *rcan = netdev_priv(ndev); + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + u32 id, dlc; + u32 cmd = CAN_TX0_REQ; + int i; + unsigned long flags; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(ndev); + + if (rockchip_canfd_read(rcan, CAN_CMD) & CAN_TX0_REQ) + cmd = CAN_TX1_REQ; + + /* Watch carefully on the bit sequence */ + if (cf->can_id & CAN_EFF_FLAG) { + /* Extended CAN ID format */ + id = cf->can_id & CAN_EFF_MASK; + dlc = can_fd_len2dlc(cf->len) & DLC_MASK; + dlc |= FORMAT_MASK; + + /* Extended frames remote TX request */ + if (cf->can_id & CAN_RTR_FLAG) + dlc |= RTR_MASK; + } else { + /* Standard CAN ID format */ + id = cf->can_id & CAN_SFF_MASK; + dlc = can_fd_len2dlc(cf->len) & DLC_MASK; + + /* Standard frames remote TX request */ + if (cf->can_id & CAN_RTR_FLAG) + dlc |= RTR_MASK; + } + + if ((rcan->can.ctrlmode & CAN_CTRLMODE_FD) && can_is_canfd_skb(skb)) { + dlc |= TX_FD_ENABLE; + if (cf->flags & CANFD_BRS) + dlc |= TX_FD_BRS_ENABLE; + } + + if (rcan->txtorx && rcan->mode <= ROCKCHIP_RK3568_CAN_MODE && cf->can_id & CAN_EFF_FLAG) + rockchip_canfd_write(rcan, CAN_MODE, + rockchip_canfd_read(rcan, CAN_MODE) | MODE_RXSTX); + else + rockchip_canfd_write(rcan, CAN_MODE, + rockchip_canfd_read(rcan, CAN_MODE) & (~MODE_RXSTX)); + + if (!rcan->txtorx && rcan->mode <= ROCKCHIP_RK3568_CAN_MODE && cf->can_id & CAN_EFF_FLAG) { + /* Two frames are sent consecutively. + * Before the first frame is tx finished, + * the register of the second frame is configured. + * Don't be interrupted in the middle. + */ + local_irq_save(flags); + rockchip_canfd_write(rcan, CAN_TXID, rcan->tx_invalid[1]); + rockchip_canfd_write(rcan, CAN_TXFIC, rcan->tx_invalid[0]); + rockchip_canfd_write(rcan, CAN_TXDAT0, rcan->tx_invalid[2]); + rockchip_canfd_write(rcan, CAN_TXDAT1, rcan->tx_invalid[3]); + rockchip_canfd_write(rcan, CAN_CMD, CAN_TX0_REQ); + rockchip_canfd_write(rcan, CAN_TXID, id); + rockchip_canfd_write(rcan, CAN_TXFIC, dlc); + for (i = 0; i < cf->len; i += 4) + rockchip_canfd_write(rcan, CAN_TXDAT0 + i, + *(u32 *)(cf->data + i)); + can_put_echo_skb(skb, ndev, 0, 0); + rockchip_canfd_write(rcan, CAN_CMD, CAN_TX1_REQ); + local_irq_restore(flags); + return NETDEV_TX_OK; + } + + rockchip_canfd_write(rcan, CAN_TXID, id); + rockchip_canfd_write(rcan, CAN_TXFIC, dlc); + + for (i = 0; i < cf->len; i += 4) + rockchip_canfd_write(rcan, CAN_TXDAT0 + i, + *(u32 *)(cf->data + i)); + + can_put_echo_skb(skb, ndev, 0, 0); + rockchip_canfd_write(rcan, CAN_MODE, + rockchip_canfd_read(rcan, CAN_MODE) | MODE_SPACE_RX); + rockchip_canfd_write(rcan, CAN_CMD, cmd); + rockchip_canfd_write(rcan, CAN_MODE, + rockchip_canfd_read(rcan, CAN_MODE) & (~MODE_SPACE_RX)); + schedule_delayed_work(&rcan->tx_err_work, msecs_to_jiffies(rcan->delay_time_ms)); + return NETDEV_TX_OK; +} + +static int rockchip_canfd_rx(struct net_device *ndev) +{ + struct rockchip_canfd *rcan = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct canfd_frame *cf; + struct sk_buff *skb; + u32 id_rockchip_canfd, dlc; + int i = 0; + u32 __maybe_unused ts, ret; + u32 data[16]; + + dlc = rockchip_canfd_read(rcan, CAN_RXFRD); + id_rockchip_canfd = rockchip_canfd_read(rcan, CAN_RXFRD); + ts = rockchip_canfd_read(rcan, CAN_RXFRD); + for (i = 0; i < ARRAY_SIZE(data); i++) + data[i] = rockchip_canfd_read(rcan, CAN_RXFRD); + + if (rcan->mode <= ROCKCHIP_RK3568_CAN_MODE) { + /* may be an empty frame */ + if (!dlc && !id_rockchip_canfd) + return 1; + + if (rcan->txtorx) { + if (rockchip_canfd_read(rcan, CAN_TX_CHECK_FIC) & FORMAT_MASK) { + ret = rockchip_canfd_read(rcan, CAN_TXID) & CAN_SFF_MASK; + if ((id_rockchip_canfd == ret) && !(dlc & FORMAT_MASK)) + rockchip_canfd_write(rcan, CAN_TX_CHECK_FIC, + ts | CAN_TX0_REQ); + return 1; + } + } + } + + /* create zero'ed CAN frame buffer */ + if (dlc & FDF_MASK) + skb = alloc_canfd_skb(ndev, &cf); + else + skb = alloc_can_skb(ndev, (struct can_frame **)&cf); + if (!skb) { + stats->rx_dropped++; + return 1; + } + + /* Change CAN data length format to socketCAN data format */ + if (dlc & FDF_MASK) + cf->len = can_fd_dlc2len(dlc & DLC_MASK); + else + cf->len = can_cc_dlc2len(dlc & DLC_MASK); + + /* Change CAN ID format to socketCAN ID format */ + if (dlc & FORMAT_MASK) { + /* The received frame is an Extended format frame */ + cf->can_id = id_rockchip_canfd; + cf->can_id |= CAN_EFF_FLAG; + if (dlc & RTR_MASK) + cf->can_id |= CAN_RTR_FLAG; + } else { + /* The received frame is a standard format frame */ + cf->can_id = id_rockchip_canfd; + if (dlc & RTR_MASK) + cf->can_id |= CAN_RTR_FLAG; + } + + if (dlc & BRS_MASK) + cf->flags |= CANFD_BRS; + + if (!(cf->can_id & CAN_RTR_FLAG)) { + /* Change CAN data format to socketCAN data format */ + for (i = 0; i < cf->len; i += 4) + *(u32 *)(cf->data + i) = data[i / 4]; + } + + stats->rx_packets++; + stats->rx_bytes += cf->len; + netif_rx(skb); + + can_led_event(ndev, CAN_LED_EVENT_RX); + + return 1; +} + +static int rockchip_canfd_get_rx_fifo_cnt(struct net_device *ndev) +{ + struct rockchip_canfd *rcan = netdev_priv(ndev); + int quota = 0; + + if (read_poll_timeout_atomic(rockchip_canfd_read, quota, + (quota & rcan->rx_fifo_mask) >> rcan->rx_fifo_shift, + 0, 500000, false, rcan, CAN_RXFC)) + netdev_dbg(ndev, "Warning: get fifo cnt failed\n"); + + quota = (quota & rcan->rx_fifo_mask) >> rcan->rx_fifo_shift; + + return quota; +} + +/* rockchip_canfd_rx_poll - Poll routine for rx packets (NAPI) + * @napi: napi structure pointer + * @quota: Max number of rx packets to be processed. + * + * This is the poll routine for rx part. + * It will process the packets maximux quota value. + * + * Return: number of packets received + */ +static int rockchip_canfd_rx_poll(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct rockchip_canfd *rcan = netdev_priv(ndev); + int work_done = 0; + + quota = rockchip_canfd_get_rx_fifo_cnt(ndev); + if (quota) { + while (work_done < quota) + work_done += rockchip_canfd_rx(ndev); + } + + if (work_done) + can_led_event(ndev, CAN_LED_EVENT_RX); + + if (work_done < 6) { + napi_complete_done(napi, work_done); + rockchip_canfd_write(rcan, CAN_INT_MASK, 0); + } + + return work_done; +} + +static int rockchip_canfd_err(struct net_device *ndev, u32 isr) +{ + struct rockchip_canfd *rcan = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + unsigned int rxerr, txerr; + u32 sta_reg; + + skb = alloc_can_err_skb(ndev, &cf); + + rxerr = rockchip_canfd_read(rcan, CAN_RX_ERR_CNT); + txerr = rockchip_canfd_read(rcan, CAN_TX_ERR_CNT); + sta_reg = rockchip_canfd_read(rcan, CAN_STATE); + + if (skb) { + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + + if (isr & BUS_OFF_INT) { + rcan->can.state = CAN_STATE_BUS_OFF; + rcan->can.can_stats.bus_off++; + cf->can_id |= CAN_ERR_BUSOFF; + } else if (isr & ERR_WARN_INT) { + rcan->can.can_stats.error_warning++; + rcan->can.state = CAN_STATE_ERROR_WARNING; + /* error warning state */ + if (likely(skb)) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + } else if (isr & PASSIVE_ERR_INT) { + rcan->can.can_stats.error_passive++; + rcan->can.state = CAN_STATE_ERROR_PASSIVE; + /* error passive state */ + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (txerr > rxerr) ? + CAN_ERR_CRTL_TX_WARNING : + CAN_ERR_CRTL_RX_WARNING; + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + + if (rcan->can.state >= CAN_STATE_BUS_OFF || + ((sta_reg & CAN_BUSOFF_FLAG) == CAN_BUSOFF_FLAG)) { + cancel_delayed_work(&rcan->tx_err_work); + netif_stop_queue(ndev); + rockchip_canfd_stop(ndev); + can_free_echo_skb(ndev, 0, NULL); + rockchip_canfd_start(ndev); + netif_start_queue(ndev); + } + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + return 0; +} + +static irqreturn_t rockchip_canfd_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct rockchip_canfd *rcan = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + u32 err_int = ERR_WARN_INT | RX_BUF_OV_INT | PASSIVE_ERR_INT | + BUS_ERR_INT | BUS_OFF_INT; + u32 isr; + u32 dlc = 0; + u32 quota, work_done = 0; + + isr = rockchip_canfd_read(rcan, CAN_INT); + if (isr & TX_FINISH_INT) { + cancel_delayed_work(&rcan->tx_err_work); + dlc = rockchip_canfd_read(rcan, CAN_TXFIC); + /* transmission complete interrupt */ + if (dlc & FDF_MASK) + stats->tx_bytes += can_fd_dlc2len(dlc & DLC_MASK); + else + stats->tx_bytes += (dlc & DLC_MASK); + stats->tx_packets++; + if (rcan->txtorx && rcan->mode <= ROCKCHIP_RK3568_CAN_MODE && dlc & FORMAT_MASK) { + rockchip_canfd_write(rcan, CAN_TX_CHECK_FIC, FORMAT_MASK); + quota = rockchip_canfd_get_rx_fifo_cnt(ndev); + if (quota) { + while (work_done < quota) + work_done += rockchip_canfd_rx(ndev); + } + if (rockchip_canfd_read(rcan, CAN_TX_CHECK_FIC) & CAN_TX0_REQ) + rockchip_canfd_write(rcan, CAN_CMD, CAN_TX1_REQ); + rockchip_canfd_write(rcan, CAN_TX_CHECK_FIC, 0); + } + if (read_poll_timeout_atomic(rockchip_canfd_read, quota, + !(quota & 0x3), + 0, 5000000, false, rcan, CAN_CMD)) + netdev_err(ndev, "Warning: wait tx req timeout!\n"); + rockchip_canfd_write(rcan, CAN_CMD, 0); + can_get_echo_skb(ndev, 0, NULL); + netif_wake_queue(ndev); + can_led_event(ndev, CAN_LED_EVENT_TX); + } + + if (isr & RX_FINISH_INT) { + if (rcan->mode == ROCKCHIP_RK3568_CAN_MODE_V2) { + rockchip_canfd_write(rcan, CAN_INT_MASK, 0x1); + napi_schedule(&rcan->napi); + } else { + work_done = 0; + quota = (rockchip_canfd_read(rcan, CAN_RXFC) & + rcan->rx_fifo_mask) >> + rcan->rx_fifo_shift; + if (quota) { + while (work_done < quota) + work_done += rockchip_canfd_rx(ndev); + } + } + } + + if (isr & err_int) { + /* error interrupt */ + if (rockchip_canfd_err(ndev, isr)) + netdev_err(ndev, "can't allocate buffer - clearing pending interrupts\n"); + } + + rockchip_canfd_write(rcan, CAN_INT, isr); + return IRQ_HANDLED; +} + +static int rockchip_canfd_open(struct net_device *ndev) +{ + struct rockchip_canfd *rcan = netdev_priv(ndev); + int err; + + /* common open */ + err = open_candev(ndev); + if (err) + return err; + + err = pm_runtime_get_sync(rcan->dev); + if (err < 0) { + netdev_err(ndev, "%s: pm_runtime_get failed(%d)\n", + __func__, err); + goto exit; + } + + err = rockchip_canfd_start(ndev); + if (err) { + netdev_err(ndev, "could not start CAN peripheral\n"); + goto exit_can_start; + } + + can_led_event(ndev, CAN_LED_EVENT_OPEN); + if (rcan->mode == ROCKCHIP_RK3568_CAN_MODE_V2) + napi_enable(&rcan->napi); + netif_start_queue(ndev); + + netdev_dbg(ndev, "%s\n", __func__); + return 0; + +exit_can_start: + pm_runtime_put(rcan->dev); +exit: + close_candev(ndev); + return err; +} + +static int rockchip_canfd_close(struct net_device *ndev) +{ + struct rockchip_canfd *rcan = netdev_priv(ndev); + + netif_stop_queue(ndev); + if (rcan->mode == ROCKCHIP_RK3568_CAN_MODE_V2) + napi_disable(&rcan->napi); + rockchip_canfd_stop(ndev); + close_candev(ndev); + can_led_event(ndev, CAN_LED_EVENT_STOP); + pm_runtime_put(rcan->dev); + cancel_delayed_work_sync(&rcan->tx_err_work); + + netdev_dbg(ndev, "%s\n", __func__); + return 0; +} + +static const struct net_device_ops rockchip_canfd_netdev_ops = { + .ndo_open = rockchip_canfd_open, + .ndo_stop = rockchip_canfd_close, + .ndo_start_xmit = rockchip_canfd_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +/** + * rockchip_canfd_suspend - Suspend method for the driver + * @dev: Address of the device structure + * + * Put the driver into low power mode. + * Return: 0 on success and failure value on error + */ +static int __maybe_unused rockchip_canfd_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + + if (netif_running(ndev)) { + netif_stop_queue(ndev); + netif_device_detach(ndev); + rockchip_canfd_stop(ndev); + } + + return pm_runtime_force_suspend(dev); +} + +/** + * rockchip_canfd_resume - Resume from suspend + * @dev: Address of the device structure + * + * Resume operation after suspend. + * Return: 0 on success and failure value on error + */ +static int __maybe_unused rockchip_canfd_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) { + dev_err(dev, "pm_runtime_force_resume failed on resume\n"); + return ret; + } + + if (netif_running(ndev)) { + ret = rockchip_canfd_start(ndev); + if (ret) { + dev_err(dev, "rockchip_canfd_chip_start failed on resume\n"); + return ret; + } + + netif_device_attach(ndev); + netif_start_queue(ndev); + } + + return 0; +} + +/** + * rockchip_canfd_runtime_suspend - Runtime suspend method for the driver + * @dev: Address of the device structure + * + * Put the driver into low power mode. + * Return: 0 always + */ +static int __maybe_unused rockchip_canfd_runtime_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct rockchip_canfd *rcan = netdev_priv(ndev); + + clk_bulk_disable_unprepare(rcan->num_clks, rcan->clks); + + return 0; +} + +/** + * rockchip_canfd_runtime_resume - Runtime resume from suspend + * @dev: Address of the device structure + * + * Resume operation after suspend. + * Return: 0 on success and failure value on error + */ +static int __maybe_unused rockchip_canfd_runtime_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct rockchip_canfd *rcan = netdev_priv(ndev); + int ret; + + ret = clk_bulk_prepare_enable(rcan->num_clks, rcan->clks); + if (ret) { + dev_err(dev, "Cannot enable clock.\n"); + return ret; + } + + return 0; +} + +static const struct dev_pm_ops rockchip_canfd_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(rockchip_canfd_suspend, rockchip_canfd_resume) + SET_RUNTIME_PM_OPS(rockchip_canfd_runtime_suspend, + rockchip_canfd_runtime_resume, NULL) +}; + +static const struct of_device_id rockchip_canfd_of_match[] = { + { + .compatible = "rockchip,canfd-1.0", + .data = (void *)ROCKCHIP_CANFD_MODE + }, + { + .compatible = "rockchip,can-2.0", + .data = (void *)ROCKCHIP_CAN_MODE + }, + { + .compatible = "rockchip,rk3568-can-2.0", + .data = (void *)ROCKCHIP_RK3568_CAN_MODE + }, + {}, +}; +MODULE_DEVICE_TABLE(of, rockchip_canfd_of_match); + +static int rockchip_canfd_probe(struct platform_device *pdev) +{ + struct net_device *ndev; + struct rockchip_canfd *rcan; + struct resource *res; + void __iomem *addr; + int err, irq; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "could not get a valid irq\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + addr = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(addr)) + return -EBUSY; + + ndev = alloc_candev(sizeof(struct rockchip_canfd), 1); + if (!ndev) { + dev_err(&pdev->dev, "could not allocate memory for CANFD device\n"); + return -ENOMEM; + } + rcan = netdev_priv(ndev); + + /* register interrupt handler */ + err = devm_request_irq(&pdev->dev, irq, rockchip_canfd_interrupt, + 0, ndev->name, ndev); + if (err) { + dev_err(&pdev->dev, "request_irq err: %d\n", err); + return err; + } + + rcan->reset = devm_reset_control_array_get(&pdev->dev, false, false); + if (IS_ERR(rcan->reset)) { + if (PTR_ERR(rcan->reset) != -EPROBE_DEFER) + dev_err(&pdev->dev, "failed to get canfd reset lines\n"); + return PTR_ERR(rcan->reset); + } + rcan->num_clks = devm_clk_bulk_get_all(&pdev->dev, &rcan->clks); + if (rcan->num_clks < 1) + return -ENODEV; + + rcan->mode = (unsigned long)of_device_get_match_data(&pdev->dev); + + if ((cpu_is_rk3566() || cpu_is_rk3568()) && (rockchip_get_cpu_version() == 3)) + rcan->mode = ROCKCHIP_RK3568_CAN_MODE_V2; + + rcan->base = addr; + rcan->can.clock.freq = clk_get_rate(rcan->clks[0].clk); + rcan->dev = &pdev->dev; + rcan->can.state = CAN_STATE_STOPPED; + switch (rcan->mode) { + case ROCKCHIP_CANFD_MODE: + rcan->can.bittiming_const = &rockchip_canfd_bittiming_const; + rcan->can.data_bittiming_const = &rockchip_canfd_data_bittiming_const; + rcan->can.do_set_mode = rockchip_canfd_set_mode; + rcan->can.do_get_berr_counter = rockchip_canfd_get_berr_counter; + rcan->can.do_set_bittiming = rockchip_canfd_set_bittiming; + rcan->can.do_set_data_bittiming = rockchip_canfd_set_bittiming; + rcan->can.ctrlmode = CAN_CTRLMODE_FD; + /* IFI CANFD can do both Bosch FD and ISO FD */ + rcan->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_FD; + rcan->rx_fifo_shift = RX_FIFO_CNT0_SHIFT; + rcan->rx_fifo_mask = RX_FIFO_CNT0_MASK; + break; + case ROCKCHIP_CAN_MODE: + case ROCKCHIP_RK3568_CAN_MODE: + case ROCKCHIP_RK3568_CAN_MODE_V2: + rcan->can.bittiming_const = &rockchip_canfd_bittiming_const; + rcan->can.do_set_mode = rockchip_canfd_set_mode; + rcan->can.do_get_berr_counter = rockchip_canfd_get_berr_counter; + rcan->can.ctrlmode_supported = CAN_CTRLMODE_BERR_REPORTING | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_3_SAMPLES; + rcan->rx_fifo_shift = RX_FIFO_CNT0_SHIFT; + rcan->rx_fifo_mask = RX_FIFO_CNT0_MASK; + break; + default: + return -EINVAL; + } + + if (rcan->mode == ROCKCHIP_CAN_MODE) { + rcan->rx_fifo_shift = RX_FIFO_CNT1_SHIFT; + rcan->rx_fifo_mask = RX_FIFO_CNT1_MASK; + } + + if (device_property_read_u32_array(&pdev->dev, + "rockchip,tx-invalid-info", + rcan->tx_invalid, 4)) + rcan->txtorx = 1; + + if (rcan->mode == ROCKCHIP_RK3568_CAN_MODE_V2) { + rcan->txtorx = 0; + netif_napi_add(ndev, &rcan->napi, rockchip_canfd_rx_poll, 6); + } + + ndev->netdev_ops = &rockchip_canfd_netdev_ops; + ndev->irq = irq; + ndev->flags |= IFF_ECHO; + rcan->can.restart_ms = 1; + + irq_set_affinity_hint(irq, get_cpu_mask(num_online_cpus() - 1)); + + INIT_DELAYED_WORK(&rcan->tx_err_work, rockchip_canfd_tx_err_delay_work); + + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + + pm_runtime_enable(&pdev->dev); + err = pm_runtime_get_sync(&pdev->dev); + if (err < 0) { + dev_err(&pdev->dev, "%s: pm_runtime_get failed(%d)\n", + __func__, err); + goto err_pmdisable; + } + + err = register_candev(ndev); + if (err) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto err_disableclks; + } + + devm_can_led_init(ndev); + + return 0; + +err_disableclks: + pm_runtime_put(&pdev->dev); +err_pmdisable: + pm_runtime_disable(&pdev->dev); + free_candev(ndev); + + return err; +} + +static int rockchip_canfd_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct rockchip_canfd *rcan = netdev_priv(ndev); + + unregister_netdev(ndev); + pm_runtime_disable(&pdev->dev); + if (rcan->mode == ROCKCHIP_RK3568_CAN_MODE_V2) + netif_napi_del(&rcan->napi); + free_candev(ndev); + + return 0; +} + +static struct platform_driver rockchip_canfd_driver = { + .driver = { + .name = DRV_NAME, + .pm = &rockchip_canfd_dev_pm_ops, + .of_match_table = rockchip_canfd_of_match, + }, + .probe = rockchip_canfd_probe, + .remove = rockchip_canfd_remove, +}; +module_platform_driver(rockchip_canfd_driver); + +MODULE_AUTHOR("Elaine Zhang "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Rockchip CANFD Drivers");