From patchwork Tue Dec 17 02:39:07 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Elaine Zhang X-Patchwork-Id: 13910919 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 2ADC3E7717F for ; Tue, 17 Dec 2024 02:40:40 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-Id:Date:Subject:Cc:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=X/mYaZWmOVYu7CSnx3Uehe5JVadU1Wx/NuktocGLqA8=; b=XIkmFz79eB8+rH to8ZpRa5ylZkWRIxetQpwtPU50qoQcmTuwdJNrwddq32Hm/smD4EBCSWyfjo+P4mV3zylJSOoYL6g BtWneMV6flStYCX0BDxmKCB8/XqaeCGjqbbgZl40sJ16uRNz2pgP9OvpQDUXoLJQ9s7hLwZaDTKhF vgDPqaqgNd73aprJ+yAUy/xv7GPRAPb04BQDeUNIWOCylVEtHkxmbZSlG+pQNPF4lDHl8jhIjrjQQ iEhOLa7U1ZSk1KCeCEXmrflykP82cYFrbQxExfyLoKM9N0Aqc6ypclAGz4oi/zhfNjLIGeFVcjTj/ 6axRjDNeRaRpmjRoi68w==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.98 #2 (Red Hat Linux)) id 1tNNVj-0000000Bza5-01gj; Tue, 17 Dec 2024 02:40:35 +0000 Received: from mail-m19731102.qiye.163.com ([220.197.31.102]) by bombadil.infradead.org with esmtps (Exim 4.98 #2 (Red Hat Linux)) id 1tNNUd-0000000BzSs-0svn; Tue, 17 Dec 2024 02:39:29 +0000 Received: from rockchip.. (unknown [58.22.7.114]) by smtp.qiye.163.com (Hmail) with ESMTP id 5ec156b5; Tue, 17 Dec 2024 10:39:20 +0800 (GMT+08:00) From: Elaine Zhang To: zhangqing@rock-chips.com, mkl@pengutronix.de, kernel@pengutronix.de, mailhol.vincent@wanadoo.fr, heiko@sntech.de, cl@rock-chips.com, kever.yang@rock-chips.com Cc: linux-can@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, devicetree@vger.kernel.org Subject: [PATCH 1/2] net: can: rockchip: add can for RK3576 Soc Date: Tue, 17 Dec 2024 10:39:07 +0800 Message-Id: <20241217023908.1292999-2-zhangqing@rock-chips.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20241217023908.1292999-1-zhangqing@rock-chips.com> References: <20241217023908.1292999-1-zhangqing@rock-chips.com> MIME-Version: 1.0 X-HM-Spam-Status: e1kfGhgUHx5ZQUpXWQgPGg8OCBgUHx5ZQUlOS1dZFg8aDwILHllBWSg2Ly tZV1koWUFDSUNOT01LS0k3V1ktWUFJV1kPCRoVCBIfWUFZQxgZS1ZPQxpKGEIYQkxCHkNWFRQJFh oXVRMBExYaEhckFA4PWVdZGBILWUFZTkNVSUlVTFVKSk9ZV1kWGg8SFR0UWUFZT0tIVUpLSUhCSE NVSktLVUpCS0tZBg++ X-HM-Tid: 0a93d27bfbc403a3kunm5ec156b5 X-HM-MType: 1 X-HM-Sender-Digest: e1kMHhlZQR0aFwgeV1kSHx4VD1lBWUc6MTo6OBw4IzIQNh8JIxQWUT8R EwMwFBpVSlVKTEhPT0tISk1JTElOVTMWGhIXVQETGhUcChIVHDsJFBgQVhgTEgsIVRgUFkVZV1kS C1lBWU5DVUlJVUxVSkpPWVdZCAFZQU9LT0xDNwY+ DKIM-Signature: a=rsa-sha256; b=azx7fMPDXIziAlna74NjoYfUCaP4OlVl9zpgBfIGrCnJDMNAiQaiDAiSOB37MV3kHZfRshSMN7VKtv6GiBSwFZcFxtHQM+kFOHmJibvF/mo5OoL1GAbY0tAPiFVNTlXKnmNCWXaxQmu4Ji576XhDcHQlaGzpKJJICWxTsxEjLLI=; c=relaxed/relaxed; s=default; d=rock-chips.com; v=1; bh=+RMCZ32UVDPZmcPliHegw6GzYj0kHvPAL0Oysy8tf8I=; h=date:mime-version:subject:message-id:from; X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20241216_183927_658741_D06C2752 X-CRM114-Status: GOOD ( 16.35 ) X-BeenThere: linux-rockchip@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Upstream kernel work for Rockchip platforms List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "Linux-rockchip" Errors-To: linux-rockchip-bounces+linux-rockchip=archiver.kernel.org@lists.infradead.org Is new controller: Support CAN and CANFD protocol. Support DMA. Signed-off-by: Elaine Zhang --- drivers/net/can/rockchip/Kconfig | 10 + drivers/net/can/rockchip/Makefile | 1 + drivers/net/can/rockchip/rk3576_canfd.c | 1349 +++++++++++++++++++++++ 3 files changed, 1360 insertions(+) create mode 100644 drivers/net/can/rockchip/rk3576_canfd.c diff --git a/drivers/net/can/rockchip/Kconfig b/drivers/net/can/rockchip/Kconfig index d203c530551f..78c2990e374e 100644 --- a/drivers/net/can/rockchip/Kconfig +++ b/drivers/net/can/rockchip/Kconfig @@ -8,3 +8,13 @@ config CAN_ROCKCHIP_CANFD help Say Y here if you want to use CAN-FD controller found on Rockchip SoCs. + +config CANFD_RK3576 + tristate "RK3576 CANFD controller" + depends on OF + depends on ARCH_ROCKCHIP || COMPILE_TEST + help + Say Y here if you want to use CANFD controller found on RK3576 SoCs. + + To compile this driver as a module, choose M here: the module will + be called rk3576_canfd. diff --git a/drivers/net/can/rockchip/Makefile b/drivers/net/can/rockchip/Makefile index 3760d3e1baa3..151f9e0ee6aa 100644 --- a/drivers/net/can/rockchip/Makefile +++ b/drivers/net/can/rockchip/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_CAN_ROCKCHIP_CANFD) += rockchip_canfd.o +obj-$(CONFIG_CANFD_RK3576) += rk3576_canfd.o rockchip_canfd-objs := rockchip_canfd-objs += rockchip_canfd-core.o diff --git a/drivers/net/can/rockchip/rk3576_canfd.c b/drivers/net/can/rockchip/rk3576_canfd.c new file mode 100644 index 000000000000..663f6e3a68ab --- /dev/null +++ b/drivers/net/can/rockchip/rk3576_canfd.c @@ -0,0 +1,1349 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 Rockchip Electronics Co., Ltd. + * Rk3576 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 rk3576_canfd_reg { + CANFD_MODE = 0x00, + CANFD_CMD = 0x04, + CANFD_STATE = 0x08, + CANFD_INT = 0x0c, + CANFD_INT_MASK = 0x10, + CANFD_NBTP = 0x100, + CANFD_DBTP = 0x104, + CANFD_TDCR = 0x108, + CANFD_BRS_CFG = 0x10c, + CANFD_DMA_CTRL = 0x11c, + CANFD_TXFIC = 0x200, + CANFD_TXID = 0x204, + CANFD_TXDAT0 = 0x208, + CANFD_TXDAT1 = 0x20c, + CANFD_TXDAT2 = 0x210, + CANFD_TXDAT3 = 0x214, + CANFD_TXDAT4 = 0x218, + CANFD_TXDAT5 = 0x21c, + CANFD_TXDAT6 = 0x220, + CANFD_TXDAT7 = 0x224, + CANFD_TXDAT8 = 0x228, + CANFD_TXDAT9 = 0x22c, + CANFD_TXDAT10 = 0x230, + CANFD_TXDAT11 = 0x234, + CANFD_TXDAT12 = 0x238, + CANFD_TXDAT13 = 0x23c, + CANFD_TXDAT14 = 0x240, + CANFD_TXDAT15 = 0x244, + CANFD_RXFIC = 0x300, + CANFD_RXID = 0x304, + CANFD_RXTS = 0x308, + CANFD_RXDAT0 = 0x30c, + CANFD_RXDAT1 = 0x310, + CANFD_RXDAT2 = 0x314, + CANFD_RXDAT3 = 0x318, + CANFD_RXDAT4 = 0x31c, + CANFD_RXDAT5 = 0x320, + CANFD_RXDAT6 = 0x324, + CANFD_RXDAT7 = 0x328, + CANFD_RXDAT8 = 0x32c, + CANFD_RXDAT9 = 0x330, + CANFD_RXDAT10 = 0x334, + CANFD_RXDAT11 = 0x338, + CANFD_RXDAT12 = 0x33c, + CANFD_RXDAT13 = 0x340, + CANFD_RXDAT14 = 0x344, + CANFD_RXDAT15 = 0x348, + CANFD_RXFRD = 0x400, + CANFD_STR_CTL = 0x600, + CANFD_STR_STATE = 0x604, + CANFD_STR_TIMEOUT = 0x608, + CANFD_STR_WTM = 0x60c, + CANFD_EXTM_START_ADDR = 0x610, + CANFD_EXTM_SIZE = 0x614, + CANFD_EXTM_WADDR = 0x618, + CANFD_EXTM_RADDR = 0x61c, + CANFD_EXTM_AHB_TXTHR = 0x620, + CANFD_EXTM_LEFT_CNT = 0x624, + CANFD_ATF0 = 0x700, + CANFD_ATF1 = 0x704, + CANFD_ATF2 = 0x708, + CANFD_ATF3 = 0x70c, + CANFD_ATF4 = 0x710, + CANFD_ATFM0 = 0x714, + CANFD_ATFM1 = 0x718, + CANFD_ATFM2 = 0x71c, + CANFD_ATFM3 = 0x720, + CANFD_ATFM4 = 0x724, + CANFD_ATF_DLC = 0x728, + CANFD_ATF_CTL = 0x72c, + CANFD_SPACE_CTRL = 0x800, + CANFD_AUTO_RETX_CFG = 0x808, + CANFD_AUTO_RETX_STATE0 = 0x80c, + CANFD_AUTO_RETX_STATE1 = 0x810, + CANFD_OLF_CFG = 0x814, + CANFD_RXINT_CTRL = 0x818, + CANFD_RXINT_TIMEOUT = 0x81c, + CANFD_OTHER_CFG = 0x820, + CANFD_WAVE_FILTER_CFG = 0x824, + CANFD_RBC_CFG = 0x828, + CANFD_TXCRC_CFG = 0x82c, + CANFD_BUSOFFRCY_CFG = 0x830, + CANFD_BUSOFF_RCY_THR = 0x834, + CANFD_ERROR_CODE = 0x900, + CANFD_ERROR_MASK = 0x904, + CANFD_RXERRORCNT = 0x910, + CANFD_TXERRORCNT = 0x914, + CANFD_RX_RXSRAM_RDATA = 0xc00, + CANFD_RTL_VERSION = 0xf0c, +}; + +#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 CANFD_TX0_REQ BIT(0) +#define CANFD_TX1_REQ BIT(1) +#define CANFD_TX_REQ_FULL ((CANFD_TX0_REQ) | (CANFD_TX1_REQ)) + +#define MODE_PASS_ERR BIT(10) +#define MODE_DIS_PEE BIT(9) +#define MODE_RETT BIT(8) +#define MODE_AUTO_BUS_ON BIT(7) +#define MODE_COVER 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 RETX_TIME_LIMIT_CNT 0x12c /* 300 */ +#define RETX_TIME_LIMIT_SHIFT 3 +#define RETX_LIMIT_EN BIT(1) +#define AUTO_RETX_EN BIT(0) + +#define ERR_WARNING_STATE BIT(3) +#define BUS_OFF_STATE BIT(4) + +#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_STR_FULL_INT BIT(7) +#define RX_STR_OV_INT BIT(8) +#define BUS_OFF_INT BIT(9) +#define BUS_OFF_RECOVERY_INT BIT(10) +#define WAKEUP_INT BIT(11) +#define AUTO_RETX_FAIL_INT BIT(12) +#define MFI_INT BIT(13) +#define MFI_TIMEOUT BIT(14) +#define RX_STR_TIMEOUT_INT BIT(15) +#define BUSINT_INT BIT(16) +#define ISM_WTM_INT BIT(17) +#define ESM_WTM_INT BIT(18) +#define BUSOFF_RCY_INT BIT(19) + +#define INT_ENABLE BIT(0) + +#define ERR_PHASE BIT(28) +#define ARB_PHASE 0 +#define DATA_PHASE 1 +#define ERR_TYPE_MASK GENMASK(27, 25) +#define ERR_TYPE_SHIFT 25 +#define BIT_ERROR 0 +#define BIT_STUFF_ERROR 1 +#define FORM_ERROR 2 +#define ACK_ERROR 3 +#define CRC_ERROR 4 +#define TRANSMIT_ACK_EOF BIT(24) +#define TRANSMIT_CRC BIT(23) +#define TRANSMIT_STUFF_COUNT BIT(22) +#define TRANSMIT_DATA BIT(21) +#define TRANSMIT_SOF_DLC BIT(20) +#define TRANSMIT_IDLE BIT(19) +#define RECEIVE_ERROR BIT(18) +#define RECEIVE_OVERLOAD BIT(17) +#define RECEIVE_SPACE BIT(16) +#define RECEIVE_EOF BIT(15) +#define RECEIVE_ACK_LIM BIT(14) +#define RECEIVE_ACK_SLOT BIT(13) +#define RECEIVE_CRC_LIM BIT(12) +#define RECEIVE_CRC BIT(11) +#define RECEIVE_STUFF_COUNT BIT(10) +#define RECEIVE_DATA BIT(9) +#define RECEIVE_DLC BIT(8) +#define RECEIVE_BRS_ESI BIT(7) +#define RECEIVE_RES BIT(6) +#define RECEIVE_FDF BIT(5) +#define RECEIVE_ID2_RTR BIT(4) +#define RECEIVE_SOF_IDE BIT(3) +#define RECEIVE_BUS_IDLE BIT(2) +#define RECEIVE_BUS_INT BIT(1) +#define RECEIVE_STOP BIT(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 (0xff << NBTP_NTSEG1_SHIFT) + +/* Data Bit Timing & Prescaler Register (DBTP) */ +#define DBTP_BRS_TSEG1_SHIFT 24 +#define DBTP_BRS_TSEG1_MASK (0xff << DBTP_BRS_TSEG1_SHIFT) +#define DBTP_BRS_MODE BIT(23) +#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 RX_DMA_ENABLE BIT(9) + +#define TX_FD_ENABLE BIT(5) +#define TX_FD_BRS_ENABLE BIT(4) + +#define TX_FORMAT_SHIFT 7 +#define TX_FORMAT_MASK (0x1 << TX_FORMAT_SHIFT) +#define TX_RTR_SHIFT 6 +#define TX_RTR_MASK (0x1 << TX_RTR_SHIFT) +#define TX_FDF_SHIFT 5 +#define TX_FDF_MASK (0x1 << TX_FDF_SHIFT) +#define TX_BRS_SHIFT 4 +#define TX_BRS_MASK (0x1 << TX_BRS_SHIFT) +#define TX_DLC_SHIFT 0 +#define TX_DLC_MASK (0xF << TX_DLC_SHIFT) + +#define RX_FORMAT_SHIFT 23 +#define RX_FORMAT_MASK (0x1 << RX_FORMAT_SHIFT) +#define RX_RTR_SHIFT 22 +#define RX_RTR_MASK (0x1 << RX_RTR_SHIFT) +#define RX_FDF_SHIFT 21 +#define RX_FDF_MASK (0x1 << RX_FDF_SHIFT) +#define RX_BRS_SHIFT 20 +#define RX_BRS_MASK (0x1 << RX_BRS_SHIFT) +#define RX_DLC_SHIFT 24 +#define RX_DLC_MASK (0xF << RX_DLC_SHIFT) +#define RX_ISM_LEN_SHIFT 8 +#define RX_ISM_LEN_MASK (0xF << RX_ISM_LEN_SHIFT) + +#define BUFFER_MODE_ENABLE BIT(0) +#define EXT_STORAGE_MODE BIT(1) +#define ISM_SEL_SHIFT 2 +#define ISM_SEL_MASK (0x3 << ISM_SEL_SHIFT) +#define RX_STORAGE_RESET BIT(4) +#define ESM_SEL_SHIFT 6 +#define ESM_SEL_MASK (0x3 << ESM_SEL_SHIFT) +#define STORAGE_TIMEOUT_MODE BIT(8) + +#define INTM_CNT_SHIFT 17 +#define INTM_CNT_MASK (0x1ff << INTM_CNT_SHIFT) +#define INTM_LEFT_CNT_SHIFT 8 +#define INTM_LEFT_CNT_MASK (0x1ff << INTM_LEFT_CNT_SHIFT) +#define EXTM_FULL BIT(3) +#define EXTM_EMPTY BIT(2) +#define INTM_FULL BIT(1) +#define INTM_EMPTY BIT(0) + +#define EXTTM_LEFT_CNT_SHIFT 0 +#define EXTTM_LEFT_CNT_MASK (0x3fffff << EXTTM_LEFT_CNT_SHIFT) + +#define ISM_WATERMASK_CAN 0x6c /* word */ +#define ISM_WATERMASK_CANFD 0x6c /* word */ +#define ESM_WATERMASK (0x50 << 8) /* word */ + +#define BUSOFF_RCY_MODE_EN BIT(8) +#define BUSOFF_RCY_TIME_CLR BIT(9) +#define BUSOFF_RCY_CNT_FAST 4 +#define BUSOFF_RCY_CNT_SLOW 5 +#define BUSOFF_RCY_TIME_FAST 0x3d0900 /* 40ms : cnt * (1 / can_clk) */ +#define BUSOFF_RCY_TIME_SLOW 0x1312d00 /* 200ms : cnt * (1 / can_clk) */ + +#define ATF_MASK BIT(31) +#define ATF_RTR_EN BIT(30) +#define ATF_RTR BIT(29) + +#define ATF_DLC_MODE BIT(5) +#define ATF_DLC_EN BIT(4) +#define ATF_DLC_SHIFT 0 +#define ATF_DLC_MASK (0xf << ATF_DLC_SHIFT) + +#define ATF_DIS(n) BIT(n) + +#define ACK_ERROR_MASK BIT(4) +#define FORM_ERROR_MASK BIT(3) +#define CRC_ERROR_MASK BIT(2) +#define STUFF_ERROR_MASK BIT(1) +#define BIT_ERROR_MASK BIT(0) + +#define SRAM_MAX_DEPTH 256 /* word */ +#define EXT_MEM_SIZE 0x2000 /* 8KByte */ + +#define CANFD_FILTER_MASK 0x1fffffff + +#define CANFD_FIFO_CNT_MASK 0xff + +#define CANBUSOFF_RCY_SLOW 200 /* ms */ +#define CANBUSOFF_RCY_FAST 30 /* ms */ + +#define DRV_NAME "rk3576_canfd" + +enum rk3576_canfd_atf_mode { + CANFD_ATF_MASK_MODE = 0, + CANFD_ATF_LIST_MODE, +}; + +enum rk3576_canfd_storage_mode { + CANFD_DATA_FLEXIBLE = 0, + CANFD_DATA_CAN_FIXED, + CANFD_DATA_CANFD_FIXED, +}; + +/* rk3576_canfd private data structure */ + +struct rk3576_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; + int rx_fifo_depth; + int rx_max_data; + bool use_dma; + u32 dma_size; + int quota; + struct dma_chan *rxchan; + u32 *rxbuf; + dma_addr_t rx_dma_src_addr; + dma_addr_t rx_dma_dst_addr; +}; + +static inline u32 rk3576_canfd_read(const struct rk3576_canfd *priv, + enum rk3576_canfd_reg reg) +{ + return readl(priv->base + reg); +} + +static inline void rk3576_canfd_write(const struct rk3576_canfd *priv, + enum rk3576_canfd_reg reg, u32 val) +{ + writel(val, priv->base + reg); +} + +static const struct can_bittiming_const rk3576_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 rk3576_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 rk3576_canfd *rcan = netdev_priv(ndev); + + reset_control_assert(rcan->reset); + udelay(2); + reset_control_deassert(rcan->reset); + + rk3576_canfd_write(rcan, CANFD_MODE, 0); + + netdev_dbg(ndev, "%s MODE=0x%08x\n", __func__, + rk3576_canfd_read(rcan, CANFD_MODE)); + + return 0; +} + +static int set_normal_mode(struct net_device *ndev) +{ + struct rk3576_canfd *rcan = netdev_priv(ndev); + u32 val; + + val = rk3576_canfd_read(rcan, CANFD_MODE); + val |= WORK_MODE; + rk3576_canfd_write(rcan, CANFD_MODE, val); + + netdev_dbg(ndev, "%s MODE=0x%08x\n", __func__, + rk3576_canfd_read(rcan, CANFD_MODE)); + return 0; +} + +/* bittiming is called in reset_mode only */ +static int rk3576_canfd_set_bittiming(struct net_device *ndev) +{ + struct rk3576_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; + + rk3576_canfd_write(rcan, CANFD_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 (sjw < 2) + sjw = 2; + + if (dbt->bitrate > 2200000) { + u32 tdco; + + /* Equation based on Bosch's ROCKCHIP_CANFD User Manual's + * Transmitter Delay Compensation Section + */ + tdco = ((1 + 1 + tseg1) * (brp + 1)) - 2; + /* Max valid TDCO value is 63 */ + if (tdco > 63) + tdco = 63; + rk3576_canfd_write(rcan, CANFD_TDCR, + (tdco << TDCR_TDCO_SHIFT) | + TDCR_TDC_ENABLE); + } else { + rk3576_canfd_write(rcan, CANFD_TDCR, 0); + } + + reg_btp |= (brp << DBTP_DBRP_SHIFT) | + (sjw << DBTP_DSJW_SHIFT) | + (tseg1 << DBTP_DTSEG1_SHIFT) | + (tseg2 << DBTP_DTSEG2_SHIFT) | + DBTP_BRS_MODE | + ((tseg1 / 2) << DBTP_BRS_TSEG1_SHIFT); + + if (rcan->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + reg_btp |= DBTP_MODE_3_SAMPLES; + + rk3576_canfd_write(rcan, CANFD_DBTP, reg_btp); + } + + netdev_dbg(ndev, "%s NBTP=0x%08x, DBTP=0x%08x, TDCR=0x%08x\n", __func__, + rk3576_canfd_read(rcan, CANFD_NBTP), + rk3576_canfd_read(rcan, CANFD_DBTP), + rk3576_canfd_read(rcan, CANFD_TDCR)); + return 0; +} + +static int rk3576_canfd_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + struct rk3576_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 = rk3576_canfd_read(rcan, CANFD_RXERRORCNT); + bec->txerr = rk3576_canfd_read(rcan, CANFD_TXERRORCNT); + + pm_runtime_put(rcan->dev); + + netdev_dbg(ndev, "%s RX_ERR_CNT=0x%08x, TX_ERR_CNT=0x%08x\n", __func__, + rk3576_canfd_read(rcan, CANFD_RXERRORCNT), + rk3576_canfd_read(rcan, CANFD_TXERRORCNT)); + + return 0; +} + +static int rk3576_canfd_atf_config(const struct net_device *ndev, int mode) +{ + struct rk3576_canfd *rcan = netdev_priv(ndev); + u32 id[10] = {0}; + u32 dlc = 0, dlc_over = 0; + + switch (mode) { + case CANFD_ATF_MASK_MODE: + rk3576_canfd_write(rcan, CANFD_ATF0, id[0]); + rk3576_canfd_write(rcan, CANFD_ATF1, id[1]); + rk3576_canfd_write(rcan, CANFD_ATF2, id[2]); + rk3576_canfd_write(rcan, CANFD_ATF3, id[3]); + rk3576_canfd_write(rcan, CANFD_ATF4, id[4]); + rk3576_canfd_write(rcan, CANFD_ATFM0, 0x7fff); + rk3576_canfd_write(rcan, CANFD_ATFM1, 0x7fff); + rk3576_canfd_write(rcan, CANFD_ATFM2, 0x7fff); + rk3576_canfd_write(rcan, CANFD_ATFM3, 0x7fff); + rk3576_canfd_write(rcan, CANFD_ATFM4, 0x7fff); + break; + case CANFD_ATF_LIST_MODE: + rk3576_canfd_write(rcan, CANFD_ATF0, id[0]); + rk3576_canfd_write(rcan, CANFD_ATF1, id[1]); + rk3576_canfd_write(rcan, CANFD_ATF2, id[2]); + rk3576_canfd_write(rcan, CANFD_ATF3, id[3]); + rk3576_canfd_write(rcan, CANFD_ATF4, id[4]); + rk3576_canfd_write(rcan, CANFD_ATFM0, id[5] | (1 << 31)); + rk3576_canfd_write(rcan, CANFD_ATFM1, id[6] | (1 << 31)); + rk3576_canfd_write(rcan, CANFD_ATFM2, id[7] | (1 << 31)); + rk3576_canfd_write(rcan, CANFD_ATFM3, id[8] | (1 << 31)); + rk3576_canfd_write(rcan, CANFD_ATFM4, id[9] | (1 << 31)); + break; + default: + rk3576_canfd_write(rcan, CANFD_ATF_CTL, 0xffff); + return -EINVAL; + } + + if (dlc) { + if (dlc_over) + rk3576_canfd_write(rcan, CANFD_ATF_DLC, dlc | (1 << 4)); + else + rk3576_canfd_write(rcan, CANFD_ATF_DLC, dlc | (1 << 4) | (1 << 5)); + } + rk3576_canfd_write(rcan, CANFD_ATF_CTL, 0); + + return 0; +} + +static int rk3576_canfd_start(struct net_device *ndev) +{ + struct rk3576_canfd *rcan = netdev_priv(ndev); + u32 val, ism = 0, water = 0; + + /* we need to enter the reset mode */ + set_reset_mode(ndev); + + rk3576_canfd_write(rcan, CANFD_INT_MASK, INT_ENABLE); + rk3576_canfd_atf_config(ndev, CANFD_ATF_MASK_MODE); + + /* set mode */ + val = rk3576_canfd_read(rcan, CANFD_MODE); + + if (rcan->rx_max_data > 4) { + ism = CANFD_DATA_CANFD_FIXED; + water = ISM_WATERMASK_CANFD; + } else { + ism = CANFD_DATA_CAN_FIXED; + water = ISM_WATERMASK_CAN; + } + + /* internal sram mode */ + rk3576_canfd_write(rcan, CANFD_STR_CTL, + (ism << ISM_SEL_SHIFT) | STORAGE_TIMEOUT_MODE); + rk3576_canfd_write(rcan, CANFD_STR_WTM, water); + + /* Loopback Mode */ + if (rcan->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { + val |= MODE_LBACK; + rk3576_canfd_write(rcan, CANFD_ERROR_MASK, ACK_ERROR_MASK); + } else if (rcan->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) { + val |= MODE_SILENT; + rk3576_canfd_write(rcan, CANFD_ERROR_MASK, ACK_ERROR_MASK); + } else { + rk3576_canfd_write(rcan, CANFD_ERROR_MASK, 0); + } + + rk3576_canfd_write(rcan, CANFD_AUTO_RETX_CFG, + AUTO_RETX_EN | RETX_LIMIT_EN | + (RETX_TIME_LIMIT_CNT << RETX_TIME_LIMIT_SHIFT)); + + rk3576_canfd_write(rcan, CANFD_MODE, val); + if (rcan->use_dma) + rk3576_canfd_write(rcan, CANFD_DMA_CTRL, RX_DMA_ENABLE); + + rk3576_canfd_write(rcan, CANFD_BRS_CFG, 0x7); + + rk3576_canfd_write(rcan, CANFD_BUSOFFRCY_CFG, BUSOFF_RCY_MODE_EN | BUSOFF_RCY_CNT_FAST); + rk3576_canfd_write(rcan, CANFD_BUSOFF_RCY_THR, BUSOFF_RCY_TIME_FAST); + + rk3576_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__, + rk3576_canfd_read(rcan, CANFD_MODE), + rk3576_canfd_read(rcan, CANFD_INT_MASK)); + + return 0; +} + +static int rk3576_canfd_stop(struct net_device *ndev) +{ + struct rk3576_canfd *rcan = netdev_priv(ndev); + + rcan->can.state = CAN_STATE_STOPPED; + /* we need to enter reset mode */ + set_reset_mode(ndev); + + /* disable all interrupts */ + rk3576_canfd_write(rcan, CANFD_INT_MASK, 0xffffffff); + + netdev_dbg(ndev, "%s MODE=0x%08x, INT_MASK=0x%08x\n", __func__, + rk3576_canfd_read(rcan, CANFD_MODE), + rk3576_canfd_read(rcan, CANFD_INT_MASK)); + return 0; +} + +static int rk3576_canfd_set_mode(struct net_device *ndev, + enum can_mode mode) +{ + int err; + + switch (mode) { + case CAN_MODE_START: + err = rk3576_canfd_start(ndev); + if (err) { + netdev_err(ndev, "starting CANFD controller failed!\n"); + return err; + } + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +/* transmit a CANFD 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 rk3576_canfd_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct rk3576_canfd *rcan = netdev_priv(ndev); + struct canfd_frame *cf = (struct canfd_frame *)skb->data; + u32 id, dlc; + u32 cmd = CANFD_TX0_REQ; + int i; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(ndev); + + if (rk3576_canfd_read(rcan, CANFD_CMD) & CANFD_TX0_REQ) + cmd = CANFD_TX1_REQ; + + /* Watch carefully on the bit sequence */ + if (cf->can_id & CAN_EFF_FLAG) { + /* Extended CANFD ID format */ + id = cf->can_id & CAN_EFF_MASK; + dlc = can_fd_len2dlc(cf->len) & TX_DLC_MASK; + dlc |= TX_FORMAT_MASK; + + /* Extended frames remote TX request */ + if (cf->can_id & CAN_RTR_FLAG) + dlc |= TX_RTR_MASK; + } else { + /* Standard CANFD ID format */ + id = cf->can_id & CAN_SFF_MASK; + dlc = can_fd_len2dlc(cf->len) & TX_DLC_MASK; + + /* Standard frames remote TX request */ + if (cf->can_id & CAN_RTR_FLAG) + dlc |= TX_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; + } + + rk3576_canfd_write(rcan, CANFD_TXID, id); + rk3576_canfd_write(rcan, CANFD_TXFIC, dlc); + + for (i = 0; i < cf->len; i += 4) + rk3576_canfd_write(rcan, CANFD_TXDAT0 + i, + *(u32 *)(cf->data + i)); + + can_put_echo_skb(skb, ndev, 0, 0); + + rk3576_canfd_write(rcan, CANFD_CMD, cmd); + + return NETDEV_TX_OK; +} + +static int rk3576_canfd_rx(struct net_device *ndev, u32 addr) +{ + struct rk3576_canfd *rcan = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct canfd_frame *cf; + struct sk_buff *skb; + u32 id_rk3576_canfd, dlc; + int i = 0; + u32 __maybe_unused ts, ret; + u32 data[16] = {0}; + + if (rcan->use_dma) { + dlc = readl(rcan->rxbuf + addr * rcan->rx_max_data); + id_rk3576_canfd = readl(rcan->rxbuf + 1 + addr * rcan->rx_max_data); + for (i = 0; i < (rcan->rx_max_data - 2); i++) + data[i] = readl(rcan->rxbuf + 2 + i + addr * rcan->rx_max_data); + } else { + dlc = rk3576_canfd_read(rcan, addr); + id_rk3576_canfd = rk3576_canfd_read(rcan, addr); + for (i = 0; i < (rcan->rx_max_data - 2); i++) + data[i] = rk3576_canfd_read(rcan, addr); + } + + /* create zero'ed CANFD frame buffer */ + if (dlc & RX_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 CANFD data length format to SocketCAN data format */ + if (dlc & RX_FDF_MASK) + cf->len = can_fd_dlc2len((dlc & RX_DLC_MASK) >> RX_DLC_SHIFT); + else + cf->len = can_cc_dlc2len((dlc & RX_DLC_MASK) >> RX_DLC_SHIFT); + + /* Change CANFD ID format to SocketCAN ID format */ + if (dlc & RX_FORMAT_MASK) { + /* The received frame is an Extended format frame */ + cf->can_id = id_rk3576_canfd; + cf->can_id |= CAN_EFF_FLAG; + if (dlc & RX_RTR_MASK) + cf->can_id |= CAN_RTR_FLAG; + } else { + /* The received frame is a standard format frame */ + cf->can_id = id_rk3576_canfd; + if (dlc & RX_RTR_MASK) + cf->can_id |= CAN_RTR_FLAG; + } + + if (dlc & RX_BRS_MASK) + cf->flags |= CANFD_BRS; + + if (!(cf->can_id & CAN_RTR_FLAG)) { + /* Change CANFD 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); + + return 1; +} + +/* rk3576_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 rk3576_canfd_rx_poll(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct rk3576_canfd *rcan = netdev_priv(ndev); + int work_done = 0, cnt = 0; + + if (rcan->use_dma) { + while (work_done < rcan->quota) + work_done += rk3576_canfd_rx(ndev, work_done); + + if (work_done <= rcan->rx_fifo_depth) { + napi_complete_done(napi, work_done); + rk3576_canfd_write(rcan, CANFD_INT_MASK, INT_ENABLE); + } + } else { + quota = (rk3576_canfd_read(rcan, CANFD_STR_STATE) & rcan->rx_fifo_mask) >> + rcan->rx_fifo_shift; + quota = quota / rcan->rx_max_data; + cnt = (rk3576_canfd_read(rcan, CANFD_STR_STATE) & INTM_CNT_MASK) >> INTM_CNT_SHIFT; + if (quota != cnt) + quota = ((rk3576_canfd_read(rcan, CANFD_STR_STATE) & rcan->rx_fifo_mask) >> + rcan->rx_fifo_shift) / rcan->rx_max_data; + + while (work_done < quota) + work_done += rk3576_canfd_rx(ndev, CANFD_RXFRD); + + if (work_done <= rcan->rx_fifo_depth) { + napi_complete_done(napi, work_done); + rk3576_canfd_write(rcan, CANFD_INT_MASK, INT_ENABLE); + } + } + return work_done; +} + +static void rk3576_canfd_rx_dma_callback(void *data) +{ + struct rk3576_canfd *rcan = data; + + napi_schedule(&rcan->napi); +} + +static int rk3576_canfd_rx_dma(struct rk3576_canfd *rcan) +{ + struct dma_async_tx_descriptor *rxdesc = NULL; + int quota = 0, cnt = 0; + + quota = (rk3576_canfd_read(rcan, CANFD_STR_STATE) & rcan->rx_fifo_mask) >> + rcan->rx_fifo_shift; + quota = quota / rcan->rx_max_data; + cnt = (rk3576_canfd_read(rcan, CANFD_STR_STATE) & INTM_CNT_MASK) >> INTM_CNT_SHIFT; + + if (quota != cnt) + quota = ((rk3576_canfd_read(rcan, CANFD_STR_STATE) & rcan->rx_fifo_mask) >> + rcan->rx_fifo_shift) / rcan->rx_max_data; + + rcan->quota = quota; + if (rcan->quota == 0) + return 1; + + rxdesc = dmaengine_prep_slave_single(rcan->rxchan, rcan->rx_dma_dst_addr, + rcan->dma_size * rcan->quota, DMA_DEV_TO_MEM, 0); + if (!rxdesc) + return -EBUSY; + + rxdesc->callback = rk3576_canfd_rx_dma_callback; + rxdesc->callback_param = rcan; + + dmaengine_submit(rxdesc); + dma_async_issue_pending(rcan->rxchan); + return 1; +} + +static int rk3576_canfd_err(struct net_device *ndev, u32 isr) +{ + struct rk3576_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 = rk3576_canfd_read(rcan, CANFD_RXERRORCNT); + txerr = rk3576_canfd_read(rcan, CANFD_TXERRORCNT); + sta_reg = rk3576_canfd_read(rcan, CANFD_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 & 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 (sta_reg & ERR_WARNING_STATE) { + 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; + } + } + + if (isr & BUSOFF_RCY_INT) { + rk3576_canfd_write(rcan, CANFD_INT_MASK, 0xffff); + rk3576_canfd_write(rcan, CANFD_INT, isr); + napi_schedule(&rcan->napi); + rk3576_canfd_write(rcan, CANFD_BUSOFFRCY_CFG, BUSOFF_RCY_TIME_CLR); + rk3576_canfd_write(rcan, CANFD_BUSOFF_RCY_THR, BUSOFF_RCY_TIME_SLOW); + rk3576_canfd_write(rcan, CANFD_BUSOFFRCY_CFG, + BUSOFF_RCY_MODE_EN | BUSOFF_RCY_CNT_SLOW); + rk3576_canfd_write(rcan, CANFD_INT_MASK, INT_ENABLE); + netif_stop_queue(ndev); + can_free_echo_skb(ndev, 0, NULL); + netif_start_queue(ndev); + } + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + + return 0; +} + +static irqreturn_t rk3576_canfd_interrupt(int irq, void *dev_id) +{ + struct net_device *ndev = (struct net_device *)dev_id; + struct rk3576_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 | BUSOFF_RCY_INT | + BUS_OFF_RECOVERY_INT | RX_STR_FULL_INT; + u32 isr; + + isr = rk3576_canfd_read(rcan, CANFD_INT); + if ((isr & RX_STR_TIMEOUT_INT) || (isr & ISM_WTM_INT) || (isr & RX_STR_FULL_INT)) { + rk3576_canfd_write(rcan, CANFD_INT_MASK, + ISM_WTM_INT | RX_STR_TIMEOUT_INT | + RX_FINISH_INT); + if (rcan->use_dma) + rk3576_canfd_rx_dma(rcan); + else + napi_schedule(&rcan->napi); + } + + if (isr & TX_FINISH_INT) { + rk3576_canfd_write(rcan, CANFD_CMD, 0); + stats->tx_bytes += can_get_echo_skb(ndev, 0, NULL); + stats->tx_packets++; + netif_wake_queue(ndev); + } + + if (isr & err_int) { + /* error interrupt */ + if (rk3576_canfd_err(ndev, isr)) + netdev_err(ndev, "can't allocate buffer - clearing pending interrupts\n"); + } + + rk3576_canfd_write(rcan, CANFD_INT, isr); + return IRQ_HANDLED; +} + +static int rk3576_canfd_open(struct net_device *ndev) +{ + struct rk3576_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 = rk3576_canfd_start(ndev); + if (err) { + netdev_err(ndev, "could not start CANFD peripheral\n"); + goto exit_can_start; + } + + 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 rk3576_canfd_close(struct net_device *ndev) +{ + struct rk3576_canfd *rcan = netdev_priv(ndev); + + netif_stop_queue(ndev); + napi_disable(&rcan->napi); + rk3576_canfd_stop(ndev); + close_candev(ndev); + pm_runtime_put(rcan->dev); + + netdev_dbg(ndev, "%s\n", __func__); + return 0; +} + +static const struct net_device_ops rk3576_canfd_netdev_ops = { + .ndo_open = rk3576_canfd_open, + .ndo_stop = rk3576_canfd_close, + .ndo_start_xmit = rk3576_canfd_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +/** + * rk3576_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 rk3576_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); + rk3576_canfd_stop(ndev); + } + + return pm_runtime_force_suspend(dev); +} + +/** + * rk3576_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 rk3576_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 = rk3576_canfd_start(ndev); + if (ret) { + dev_err(dev, "rk3576_canfd_chip_start failed on resume\n"); + return ret; + } + + netif_device_attach(ndev); + netif_start_queue(ndev); + } + + return 0; +} + +/** + * rk3576_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 rk3576_canfd_runtime_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct rk3576_canfd *rcan = netdev_priv(ndev); + + clk_bulk_disable_unprepare(rcan->num_clks, rcan->clks); + + return 0; +} + +/** + * rk3576_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 rk3576_canfd_runtime_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct rk3576_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 rk3576_canfd_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(rk3576_canfd_suspend, rk3576_canfd_resume) + SET_RUNTIME_PM_OPS(rk3576_canfd_runtime_suspend, + rk3576_canfd_runtime_resume, NULL) +}; + +static const struct of_device_id rk3576_canfd_of_match[] = { + { + .compatible = "rockchip,rk3576-canfd", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, rk3576_canfd_of_match); + +static void rk3576_canfd_dma_init(struct rk3576_canfd *rcan) +{ + struct dma_slave_config rxconf = { + .direction = DMA_DEV_TO_MEM, + .src_addr = rcan->rx_dma_src_addr, + .src_addr_width = 4, + .dst_addr_width = 4, + .src_maxburst = 16, + }; + + rcan->rxbuf = dma_alloc_coherent(rcan->dev, rcan->dma_size * rcan->rx_fifo_depth, + &rcan->rx_dma_dst_addr, GFP_KERNEL); + if (!rcan->rxbuf) { + rcan->use_dma = 0; + return; + } + dmaengine_slave_config(rcan->rxchan, &rxconf); +} + +static int rk3576_canfd_probe(struct platform_device *pdev) +{ + struct net_device *ndev; + struct rk3576_canfd *rcan; + struct resource *res; + void __iomem *addr; + int err, irq; + u32 val = 0; + + 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 rk3576_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, rk3576_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_exclusive(&pdev->dev); + 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; + + rcan->can.bittiming_const = &rk3576_canfd_bittiming_const; + rcan->can.data_bittiming_const = &rk3576_canfd_data_bittiming_const; + rcan->can.do_set_mode = rk3576_canfd_set_mode; + rcan->can.do_get_berr_counter = rk3576_canfd_get_berr_counter; + rcan->can.do_set_bittiming = rk3576_canfd_set_bittiming; + rcan->can.do_set_data_bittiming = rk3576_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_LISTENONLY | + CAN_CTRLMODE_FD; + + rcan->rx_fifo_shift = INTM_LEFT_CNT_SHIFT; + rcan->rx_fifo_mask = INTM_LEFT_CNT_MASK; + + /* rx-max-data only 4 Words or 18 words are supported */ + if (device_property_read_u32_array(&pdev->dev, "rockchip,rx-max-data", &val, 1)) + rcan->rx_max_data = 18; + else + rcan->rx_max_data = val; + + if (rcan->rx_max_data != 4 && rcan->rx_max_data != 18) { + rcan->rx_max_data = 18; + dev_warn(&pdev->dev, "rx_max_data is invalid, set to 18 words!\n"); + } + rcan->rx_fifo_depth = SRAM_MAX_DEPTH / rcan->rx_max_data; + + rcan->rxchan = dma_request_chan(&pdev->dev, "rx"); + if (IS_ERR(rcan->rxchan)) { + dev_warn(&pdev->dev, "Failed to request rxchan\n"); + rcan->rxchan = NULL; + rcan->use_dma = 0; + } else { + rcan->rx_dma_src_addr = res->start + CANFD_RXFRD; + rcan->dma_size = rcan->rx_max_data * 4; + rcan->use_dma = 1; + } + if (rcan->use_dma) + rk3576_canfd_dma_init(rcan); + + ndev->netdev_ops = &rk3576_canfd_netdev_ops; + ndev->irq = irq; + ndev->flags |= IFF_ECHO; + + 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, rk3576_canfd_rx_poll); + err = register_candev(ndev); + if (err) { + dev_err(&pdev->dev, "registering %s failed (err=%d)\n", + DRV_NAME, err); + goto err_disableclks; + } + dev_info(&pdev->dev, "CAN info: use_dma = %d, rx_max_data = %d, fifo_depth = %d\n", + rcan->use_dma, rcan->rx_max_data, rcan->rx_fifo_depth); + return 0; + +err_disableclks: + pm_runtime_put(&pdev->dev); +err_pmdisable: + if (rcan->rxbuf) { + dma_free_coherent(rcan->dev, rcan->dma_size * rcan->rx_fifo_depth, rcan->rxbuf, + rcan->rx_dma_dst_addr); + rcan->rxbuf = NULL; + } + if (rcan->rxchan) + dma_release_channel(rcan->rxchan); + pm_runtime_disable(&pdev->dev); + free_candev(ndev); + + return err; +} + +static void rk3576_canfd_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct rk3576_canfd *rcan = netdev_priv(ndev); + + if (rcan->rxbuf) { + dma_free_coherent(rcan->dev, rcan->dma_size * rcan->rx_fifo_depth, rcan->rxbuf, + rcan->rx_dma_dst_addr); + rcan->rxbuf = NULL; + } + + if (rcan->rxchan) + dma_release_channel(rcan->rxchan); + + unregister_netdev(ndev); + pm_runtime_disable(&pdev->dev); + netif_napi_del(&rcan->napi); + free_candev(ndev); +} + +static struct platform_driver rk3576_canfd_driver = { + .driver = { + .name = DRV_NAME, + .pm = &rk3576_canfd_dev_pm_ops, + .of_match_table = rk3576_canfd_of_match, + }, + .probe = rk3576_canfd_probe, + .remove = rk3576_canfd_remove, +}; +module_platform_driver(rk3576_canfd_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Elaine Zhang "); +MODULE_DESCRIPTION("RK3576 CANFD Drivers");