From d7250e2bf5ef090076df1498ab0f411d6e9d7314 Mon Sep 17 00:00:00 2001 From: yuanxia Date: Tue, 21 May 2024 16:39:58 +0800 Subject: [PATCH 1/5] dt-bindings: can: Add bindings for Phytium CAN This patch documents the DT bindings for Phytium CAN controller. Signed-off-by: yuanxia Signed-off-by: Chen Baozi Signed-off-by: Wang Yinfeng --- .../bindings/net/can/phytium,can.yaml | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 Documentation/devicetree/bindings/net/can/phytium,can.yaml diff --git a/Documentation/devicetree/bindings/net/can/phytium,can.yaml b/Documentation/devicetree/bindings/net/can/phytium,can.yaml new file mode 100644 index 0000000000000..dd4b8000830ca --- /dev/null +++ b/Documentation/devicetree/bindings/net/can/phytium,can.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/can/phytium,can.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: + Phytium CAN controller + +maintainers: + - Chen Baozi + +properties: + compatible: + enum: + - phytium,can + - phytium,canfd + + reg: + maxItems: 1 + description: Should contain the controller registers location and length + + interrupts: + maxItems: 1 + description: Should contain IRQ line for the controller + + clocks: + maxItems: 1 + description: Input clock used by the controller + + clock-names: + maxItems: 1 + description: Input clock name, should be "can_clk" + const: can_clk + + tx-fifo-depth: + $ref: "/schemas/types.yaml#/definitions/uint32" + description: Indicates the length of TX FIFO + + rx-fifo-depth: + $ref: "/schemas/types.yaml#/definitions/uint32" + description: Indicates the length of RX FIFO + + extend_brp: + description: | + Indicates to apply the extend BRP parameter of bit timming for + early version of CAN controller + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - tx-fifo-depth + - rx-fifo-depth + +allOf: + - $ref: can-controller.yaml# + +examples: + - | + + can0: can@2800a000 { + compatible = "phytium,canfd"; + reg = <0x0 0x2800a000 0x0 0x1000>; + interrupts = ; + clocks = <&sysclk_600mhz>; + clock-names = "can_clk"; + tx-fifo-depth = <64>; + rx-fifo-depth = <64>; + }; From c97ab8479d6e2d3edeeaeed2bb1e2445c3ed7a5e Mon Sep 17 00:00:00 2001 From: yuanxia Date: Tue, 21 May 2024 16:41:07 +0800 Subject: [PATCH 2/5] arm64: can: Add Phytium CAN controller support This patch adds Phytium CAN controller support. Signed-off-by: yuanxia Signed-off-by: Cheng Quan Signed-off-by: Li Zhengguang Signed-off-by: Wu Jinyong Signed-off-by: Chen Baozi Signed-off-by: Wang Yinfeng --- drivers/net/can/Kconfig | 1 + drivers/net/can/Makefile | 2 +- drivers/net/can/phytium/Kconfig | 34 + drivers/net/can/phytium/Makefile | 8 + drivers/net/can/phytium/phytium_can.c | 1146 +++++++++++++++++ drivers/net/can/phytium/phytium_can.h | 66 + drivers/net/can/phytium/phytium_can_pci.c | 135 ++ .../net/can/phytium/phytium_can_platform.c | 228 ++++ 8 files changed, 1619 insertions(+), 1 deletion(-) create mode 100644 drivers/net/can/phytium/Kconfig create mode 100644 drivers/net/can/phytium/Makefile create mode 100644 drivers/net/can/phytium/phytium_can.c create mode 100644 drivers/net/can/phytium/phytium_can.h create mode 100644 drivers/net/can/phytium/phytium_can_pci.c create mode 100644 drivers/net/can/phytium/phytium_can_platform.c diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index f8cde9f9f554d..7183316606652 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -221,6 +221,7 @@ source "drivers/net/can/ifi_canfd/Kconfig" source "drivers/net/can/m_can/Kconfig" source "drivers/net/can/mscan/Kconfig" source "drivers/net/can/peak_canfd/Kconfig" +source "drivers/net/can/phytium/Kconfig" source "drivers/net/can/rcar/Kconfig" source "drivers/net/can/sja1000/Kconfig" source "drivers/net/can/softing/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index ff8f76295d13b..99eb7ea48207d 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -31,5 +31,5 @@ obj-$(CONFIG_CAN_SJA1000) += sja1000/ obj-$(CONFIG_CAN_SUN4I) += sun4i_can.o obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o obj-$(CONFIG_CAN_XILINXCAN) += xilinx_can.o - +obj-$(CONFIG_CAN_PHYTIUM) += phytium/ subdir-ccflags-$(CONFIG_CAN_DEBUG_DEVICES) += -DDEBUG diff --git a/drivers/net/can/phytium/Kconfig b/drivers/net/can/phytium/Kconfig new file mode 100644 index 0000000000000..a23216d23c226 --- /dev/null +++ b/drivers/net/can/phytium/Kconfig @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: GPL-2.0-only +menuconfig CAN_PHYTIUM + tristate "Phytium CAN support" + help + Say Y here if you want support for Phytium CAN controller framework. + This is common support for devices that embed the Phytium CAN IP. + + To compile this driver as a module, choose M here: the module will + be called phytium_can. + +if CAN_PHYTIUM + +config CAN_PHYTIUM_PLATFORM + tristate "Phytium CAN support for io-mapped devices" + depends on HAS_IOMEM + help + Say Y here is you want to support for IO Mapped Phytium CAN controller. + This support is for devices that have the Phytium CAN controller IP + embedded into the device and the IP is IO Mapped to the processor. + + To compile this driver as a module, choose M here: the module will + be called phytium_can_platform. + +config CAN_PHYTIUM_PCI + tristate "Phytium CAN support for PCI devices" + depends on PCI + help + Say Y here is you want to support for Phytium CAN controller connected + to the PCI bus. This support is for devices that have the Phytium CAN + controller IP embedded into a PCI device. + + To compile this driver as a module, choose M here: the module will + be called phytium_can_pci. +endif diff --git a/drivers/net/can/phytium/Makefile b/drivers/net/can/phytium/Makefile new file mode 100644 index 0000000000000..7ef554fca58e4 --- /dev/null +++ b/drivers/net/can/phytium/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the Phytium CAN controller drivers. +# +# + +obj-$(CONFIG_CAN_PHYTIUM) += phytium_can.o +obj-$(CONFIG_CAN_PHYTIUM_PLATFORM) += phytium_can_platform.o +obj-$(CONFIG_CAN_PHYTIUM_PCI) += phytium_can_pci.o diff --git a/drivers/net/can/phytium/phytium_can.c b/drivers/net/can/phytium/phytium_can.c new file mode 100644 index 0000000000000..21bf74a1d66f2 --- /dev/null +++ b/drivers/net/can/phytium/phytium_can.c @@ -0,0 +1,1146 @@ +// SPDX-License-Identifier: GPL-2.0 +/* CAN bus driver for Phytium CAN controller + * + * Copyright (C) 2021-2023, Phytium Technology Co., Ltd. + */ + +#include + +#include "phytium_can.h" + +/* register definition */ +enum phytium_can_reg { + CAN_CTRL = 0x00, /* Global control register */ + CAN_INTR = 0x04, /* Interrupt register */ + CAN_ARB_RATE_CTRL = 0x08, /* Arbitration rate control register */ + CAN_DAT_RATE_CTRL = 0x0c, /* Data rate control register */ + CAN_ACC_ID0 = 0x10, /* Acceptance identifier0 register */ + CAN_ACC_ID1 = 0x14, /* Acceptance identifier1 register */ + CAN_ACC_ID2 = 0x18, /* Acceptance identifier2 register */ + CAN_ACC_ID3 = 0x1c, /* Acceptance identifier3 register */ + CAN_ACC_ID0_MASK = 0x20, /* Acceptance identifier0 mask register */ + CAN_ACC_ID1_MASK = 0x24, /* Acceptance identifier1 mask register */ + CAN_ACC_ID2_MASK = 0x28, /* Acceptance identifier2 mask register */ + CAN_ACC_ID3_MASK = 0x2c, /* Acceptance identifier3 mask register */ + CAN_XFER_STS = 0x30, /* Transfer status register */ + CAN_ERROR_CNT = 0x34, /* Error counter register */ + CAN_FIFO_CNT = 0x38, /* FIFO counter register */ + CAN_DMA_CTRL = 0x3c, /* DMA request control register */ + CAN_XFER_EN = 0x40, /* Transfer enable register */ + CAN_INTR1 = 0x44, /* Interrupt register 1 */ + CAN_FRM_INFO = 0x48, /* Frame valid number register */ + CAN_TIME_OUT = 0x4c, /* Timeout register */ + CAN_TIME_OUT_CNT = 0x50, /* Timeout counter register */ + CAN_INTR2 = 0x54, /* Interrupt register 2 */ + CAN_TX_FIFO = 0x100, /* TX FIFO shadow register */ + CAN_RX_FIFO = 0x200, /* RX FIFO shadow register */ + CAN_RX_INFO_FIFO = 0x300, /* RX information FIFO shadow register */ + CAN_PIDR4 = 0xfd0, /* Peripheral Identification Register 4 */ + CAN_PIDR0 = 0xfe0, /* Peripheral Identification Register 0 */ + CAN_PIDR1 = 0xfe4, /* Peripheral Identification Register 1 */ + CAN_PIDR2 = 0xfe8, /* Peripheral Identification Register 2 */ + CAN_PIDR3 = 0xfec, /* Peripheral Identification Register 3 */ + CAN_CIDR0 = 0xff0, /* Component Identification Register 0 */ + CAN_CIDR1 = 0xff4, /* Component Identification Register 1 */ + CAN_CIDR2 = 0xff8, /* Component Identification Register 2 */ + CAN_CIDR3 = 0xffc, /* Component Identification Register 3 */ +}; + +/* Global control register (CTRL) */ +#define CTRL_XFER BIT(0) /* Transfer enable */ +#define CTRL_TXREQ BIT(1) /* Transmit request */ +#define CTRL_AIME BIT(2) /* Acceptance identifier mask enable */ +#define CTRL_TTS BIT(3) /* Transmit trigger strategy */ +#define CTRL_RST BIT(7) /* Write 1 to soft reset and self clear */ +#define CTRL_RFEIDF BIT(8) /* Allow RX frame end interrupt during ID filtered frame */ +#define CTRL_RFEDT BIT(9) /* Allow RX frame end interrupt during TX frame */ +#define CTRL_IOF BIT(10) /* Ignore overload flag internally */ +#define CTRL_FDCRC BIT(11) /* CANFD CRC mode */ + +/* Interrupt register (INTR) */ +#define INTR_BOIS BIT(0) /* Bus off interrupt status */ +#define INTR_PWIS BIT(1) /* Passive warning interrupt status */ +#define INTR_PEIS BIT(2) /* Passive error interrupt status */ +#define INTR_RFIS BIT(3) /* RX FIFO full interrupt status */ +#define INTR_TFIS BIT(4) /* TX FIFO empty interrupt status */ +#define INTR_REIS BIT(5) /* RX frame end interrupt status */ +#define INTR_TEIS BIT(6) /* TX frame end interrupt status */ +#define INTR_EIS BIT(7) /* Error interrupt status */ +#define INTR_BOIE BIT(8) /* Bus off interrupt enable */ +#define INTR_PWIE BIT(9) /* Passive warning interrupt enable */ +#define INTR_PEIE BIT(10) /* Passive error interrupt enable */ +#define INTR_RFIE BIT(11) /* RX FIFO full interrupt enable */ +#define INTR_TFIE BIT(12) /* TX FIFO empty interrupt enable */ +#define INTR_REIE BIT(13) /* RX frame end interrupt enable */ +#define INTR_TEIE BIT(14) /* TX frame end interrupt enable */ +#define INTR_EIE BIT(15) /* Error interrupt enable */ +#define INTR_BOIC BIT(16) /* Bus off interrupt clear */ +#define INTR_PWIC BIT(17) /* Passive warning interrupt clear */ +#define INTR_PEIC BIT(18) /* Passive error interrupt clear */ +#define INTR_RFIC BIT(19) /* RX FIFO full interrupt clear */ +#define INTR_TFIC BIT(20) /* TX FIFO empty interrupt clear */ +#define INTR_REIC BIT(21) /* RX frame end interrupt clear */ +#define INTR_TEIC BIT(22) /* TX frame end interrupt clear */ +#define INTR_EIC BIT(23) /* Error interrupt clear */ + +#define INTR_STATUS_MASK (INTR_BOIS | INTR_PWIS | INTR_PEIS | INTR_RFIS | \ + INTR_TFIS | INTR_REIS | INTR_TEIS | INTR_EIS) +#define INTR_EN_MASK (INTR_BOIE | INTR_PWIE | INTR_PEIE | INTR_RFIE | \ + INTR_REIE | INTR_TEIE | INTR_EIE) +#define INTR_CLEAR_MASK (INTR_BOIC | INTR_PWIC | INTR_PEIC | INTR_RFIC | \ + INTR_TFIC | INTR_REIC | INTR_TEIC | INTR_EIC) + +/* Arbitration rate control register (ARB_RATE_CTRL) */ +#define ARB_RATE_CTRL_ARJW GENMASK(1, 0) /* Arbitration field resync jump width */ +#define ARB_RATE_CTRL_APRS GENMASK(4, 2) /* Arbitration field propagation segment */ +#define ARB_RATE_CTRL_APH1S GENMASK(7, 5) /* Arbitration field phase1 segment */ +#define ARB_RATE_CTRL_APH2S GENMASK(10, 8) /* Arbitration field phase2 segment */ +#define ARB_RATE_CTRL_APD GENMASK(28, 16) /* Arbitration field prescaler divider */ + +/* Data rate control register (DAT_RATE_CTRL) */ +#define DAT_RATE_CTRL_DRJW GENMASK(1, 0) /* Data field resync jump width */ +#define DAT_RATE_CTRL_DPRS GENMASK(4, 2) /* Data field propagation segment */ +#define DAT_RATE_CTRL_DPH1S GENMASK(7, 5) /* Data field phase1 segment */ +#define DAT_RATE_CTRL_DPH2S GENMASK(10, 8) /* Data field phase2 segment */ +#define DAT_RATE_CTRL_DPD GENMASK(28, 16) /* Data field prescaler divider */ + +/* Acceptance identifierX register (ACC_IDX) */ +#define ACC_IDX_AID_MASK GENMASK(28, 0) /* Acceptance identifier */ + +/* Acceptance identifier0 mask register (ACC_ID0_MASK) */ +#define ACC_IDX_MASK_AID_MASK GENMASK(28, 0) /* Acceptance identifier mask */ + +/* Transfer status register (XFER_STS) */ +#define XFER_STS_FRAS GENMASK(2, 0) /* Frame status */ +#define XFER_STS_FIES GENMASK(7, 3) /* Field status */ +#define XFER_STS_FIES_IDLE (0x0) /* idle */ +#define XFER_STS_FIES_ARBITRATION (0x1) /* arbitration */ +#define XFER_STS_FIES_TX_CTRL (0x2) /* transmit control */ +#define XFER_STS_FIES_TX_DATA (0x3) /* transmit data */ +#define XFER_STS_FIES_TX_CRC (0x4) /* transmit crc */ +#define XFER_STS_FIES_TX_FRM (0x5) /* transmit frame */ +#define XFER_STS_FIES_RX_CTRL (0x6) /* receive control */ +#define XFER_STS_FIES_RX_DATA (0x7) /* receive data */ +#define XFER_STS_FIES_RX_CRC (0x8) /* receive crc */ +#define XFER_STS_FIES_RX_FRM (0x9) /* receive frame */ +#define XFER_STS_FIES_INTERMISSION (0xa) /* intermission */ +#define XFER_STS_FIES_TX_SUSPD (0xb) /* transmit suspend */ +#define XFER_STS_FIES_BUS_IDLE (0xc) /* bus idle */ +#define XFER_STS_FIES_OVL_FLAG (0xd) /* overload flag */ +#define XFER_STS_FIES_OVL_DLM (0xe) /* overload delimiter */ +#define XFER_STS_FIES_ERR_FLAG (0xf) /* error flag */ +#define XFER_STS_FIES_ERR_DLM (0x10) /* error delimiter */ +#define XFER_STS_FIES_BUS_OFF (0x11) /* bus off */ +#define XFER_STS_TS BIT(8) /* Transmit status */ +#define XFER_STS_RS BIT(9) /* Receive status */ +#define XFER_STS_XFERS BIT(10) /* Transfer status */ + +/* Error counter register (ERR_CNT) */ +#define ERR_CNT_REC GENMASK(8, 0) /* Receive error counter */ +#define ERR_CNT_TEC GENMASK(24, 16) /* Transmit error counter */ + +/* FIFO counter register (FIFO_CNT) */ +#define FIFO_CNT_RFN GENMASK(6, 0) /* Receive FIFO valid data number */ +#define FIFO_CNT_TFN GENMASK(22, 16) /* Transmit FIFO valid data number */ + +/* DMA request control register (DMA_CTRL) */ +#define DMA_CTRL_RFTH GENMASK(5, 0) /* Receive FIFO DMA request threshold */ +#define DMA_CTRL_RFRE BIT(6) /* Receive FIFO DMA request enable */ +#define DMA_CTRL_TFTH GENMASK(21, 16) /* Transmit FIFO DMA request threshold */ +#define DMA_CTRL_TFRE BIT(22) /* Transmit FIFO DMA request enable */ + +/* Transfer enable register (XFER_EN) */ +#define XFER_EN_XFER BIT(0) /* Transfer enable */ + +/* Interrupt register 1 (INTR1) */ +#define INTR1_RF1IS BIT(0) /* RX FIFO 1/4 interrupt status */ +#define INTR1_RF2IS BIT(1) /* RX FIFO 1/2 interrupt status */ +#define INTR1_RF3IS BIT(2) /* RX FIFO 3/4 interrupt status */ +#define INTR1_RF4IS BIT(3) /* RX FIFO full interrupt status */ +#define INTR1_TF1IS BIT(4) /* TX FIFO 1/4 interrupt status */ +#define INTR1_TF2IS BIT(5) /* TX FIFO 1/2 interrupt status */ +#define INTR1_TF3IS BIT(6) /* TX FIFO 3/4 interrupt status */ +#define INTR1_TF4IS BIT(7) /* TX FIFO empty interrupt status */ +#define INTR1_RF1IE BIT(8) /* RX FIFO 1/4 interrupt enable */ +#define INTR1_RF2IE BIT(9) /* RX FIFO 1/2 interrupt enable */ +#define INTR1_RF3IE BIT(10) /* RX FIFO 3/4 interrupt enable */ +#define INTR1_RF4IE BIT(11) /* RX FIFO full interrupt enable */ +#define INTR1_TF1IE BIT(12) /* TX FIFO 1/4 interrupt enable */ +#define INTR1_TF2IE BIT(13) /* TX FIFO 1/2 interrupt enable */ +#define INTR1_TF3IE BIT(14) /* TX FIFO 3/4 interrupt enable */ +#define INTR1_TF4IE BIT(15) /* TX FIFO empty interrupt enable */ +#define INTR1_RF1IC BIT(16) /* RX FIFO 1/4 interrupt clear */ +#define INTR1_RF2IC BIT(17) /* RX FIFO 1/2 interrupt clear */ +#define INTR1_RF3IC BIT(18) /* RX FIFO 3/4 interrupt clear */ +#define INTR1_RF4IC BIT(19) /* RX FIFO full interrupt clear */ +#define INTR1_TF1IC BIT(20) /* TX FIFO 1/4 interrupt clear */ +#define INTR1_TF2IC BIT(21) /* TX FIFO 1/2 interrupt clear */ +#define INTR1_TF3IC BIT(22) /* TX FIFO 3/4 interrupt clear */ +#define INTR1_TF4IC BIT(23) /* TX FIFO empty interrupt clear */ +#define INTR1_RF1RIS BIT(24) /* RX FIFO 1/4 raw interrupt status */ +#define INTR1_RF2RIS BIT(25) /* RX FIFO 1/2 raw interrupt status */ +#define INTR1_RF3RIS BIT(26) /* RX FIFO 3/4 raw interrupt status */ +#define INTR1_RF4RIS BIT(27) /* RX FIFO full raw interrupt status */ +#define INTR1_TF1RIS BIT(28) /* TX FIFO 1/4 raw interrupt status */ +#define INTR1_TF2RIS BIT(29) /* TX FIFO 1/2 raw interrupt status */ +#define INTR1_TF3RIS BIT(30) /* TX FIFO 3/4 raw interrupt status */ +#define INTR1_TF4RIS BIT(31) /* TX FIFO empty raw interrupt status */ + +/* Frame valid number register (FRM_INFO) */ +#define FRM_INFO_RXFC GENMASK(5, 0) /* Valid frame number in RX FIFO */ +#define FRM_INFO_SSPD GENMASK(31, 16) /* Secondary sample point delay */ + +/* Interrupt register 2 (INTR2) */ +#define INTR2_TOIS BIT(0) /* RX FIFO time out interrupt status */ +#define INTR2_TOIM BIT(8) /* RX FIFO time out interrupt mask */ +#define INTR2_TOIC BIT(16) /* RX FIFO time out interrupt clear */ +#define INTR2_TORIS BIT(24) /* RX FIFO time out raw interrupt status */ + +/* RX information FIFO shadow register (RX_INFO_FIFO) */ +#define RX_INFO_FIFO_WNORF GENMASK(4, 0) /* Word (4-byte) number of current receive frame */ +#define RX_INFO_FIFO_RORF BIT(5) /* RTR value of current receive frame */ +#define RX_INFO_FIFO_FORF BIT(6) /* FDF value of current receive frame */ +#define RX_INFO_FIFO_IORF BIT(7) /* IDE value of current receive frame */ + +/* Arbitration Bits */ +#define CAN_ID1_MASK GENMASK(31, 21) /* Base identifer */ +/* Standard Remote Transmission Request */ +#define CAN_ID1_RTR_MASK BIT(20) +/* Extended Substitute remote TXreq */ +#define CAN_ID2_SRR_MASK BIT(20) +#define CAN_IDE_MASK BIT(19) /* IDentifier extension flag */ +#define CAN_ID2_MASK GENMASK(18, 1) /* Identifier extension */ +/* Extended frames remote TX request */ +#define CAN_ID2_RTR_MASK BIT(0) +#define CAN_ID1_FDF_MASK BIT(18) +#define CAN_ID1_DLC_MASK GENMASK(17, 14) +#define CANFD_ID1_BRS_MASK BIT(16) +#define CANFD_ID1_ESI_MASK BIT(15) +#define CANFD_ID1_DLC_MASK GENMASK(14, 11) + +#define CAN_ID2_FDF_MASK BIT(31) +#define CAN_ID2_DLC_MASK GENMASK(29, 26) +#define CANFD_ID2_BRS_MASK BIT(29) +#define CANFD_ID2_ESI_MASK BIT(28) +#define CANFD_ID2_DLC_MASK GENMASK(27, 24) + +#define CAN_ID1_DLC_OFF 14 +#define CANFD_ID1_DLC_OFF 11 +#define CAN_ID2_DLC_OFF 26 +#define CANFD_ID2_DLC_OFF 24 + +#define CAN_IDR_ID1_SHIFT 21 /* Standard Messg Identifier */ +#define CAN_IDR_ID2_SHIFT 1 /* Extended Message Identifier */ +#define CAN_IDR_SDLC_SHIFT 14 +#define CAN_IDR_EDLC_SHIFT 26 + +/* CANFD Standard msg padding 1 */ +#define CANFD_IDR_PAD_MASK 0x000007FF +#define CAN_IDR_PAD_MASK 0x00003FFF /* Standard msg padding 1 */ + +/** + * phytium_can_set_reg_bits - set a bit value to the device register + * @cdev: Driver private data structure + * @reg: Register offset + * @bs: The bit mask + * + * Read data from the particular CAN register + * Return: value read from the CAN register + */ +static void +phytium_can_set_reg_bits(const struct phytium_can_dev *cdev, + enum phytium_can_reg reg, u32 bs) +{ + u32 val = readl(cdev->base + reg); + + val |= bs; + writel(val, cdev->base + reg); +} + +/** + * phytium_can_clr_reg_bits - clear a bit value to the device register + * @cdev: Driver private data structure + * @reg: Register offset + * @bs: The bit mask + * + * Read data from the particular CAN register + * Return: value read from the CAN register + */ +static void +phytium_can_clr_reg_bits(const struct phytium_can_dev *cdev, + enum phytium_can_reg reg, u32 bs) +{ + u32 val = readl(cdev->base + reg); + + val &= ~bs; + writel(val, cdev->base + reg); +} + +static inline u32 phytium_can_read(const struct phytium_can_dev *cdev, enum phytium_can_reg reg) +{ + return readl(cdev->base + reg); +} + +static inline void phytium_can_write(const struct phytium_can_dev *cdev, enum phytium_can_reg reg, + u32 val) +{ + writel(val, cdev->base + reg); +} + +static inline void phytium_can_enable_all_interrupts(struct phytium_can_dev *cdev) +{ + phytium_can_write(cdev, CAN_INTR, INTR_EN_MASK); +} + +static inline void phytium_can_disable_all_interrupt(struct phytium_can_dev *cdev) +{ + phytium_can_write(cdev, CAN_INTR, 0x0); +} + +static int phytium_can_get_berr_counter(const struct net_device *dev, + struct can_berr_counter *bec) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + + bec->rxerr = phytium_can_read(cdev, CAN_ERROR_CNT) & ERR_CNT_REC; + bec->txerr = (phytium_can_read(cdev, CAN_ERROR_CNT) & ERR_CNT_TEC) >> 16; + + return 0; +} + +static int phytium_can_read_fifo(struct net_device *dev) +{ + struct net_device_stats *stats = &dev->stats; + struct phytium_can_dev *cdev = netdev_priv(dev); + struct canfd_frame *cf; + struct sk_buff *skb; + u32 id, dlc, i; + + /* Read the frame header from FIFO */ + id = phytium_can_read(cdev, CAN_RX_FIFO); + id = be32_to_cpup(&id); + if (id & CAN_IDE_MASK) { + /* Received an extended frame */ + dlc = phytium_can_read(cdev, CAN_RX_FIFO); + dlc = be32_to_cpup(&dlc); + if (dlc & CAN_ID2_FDF_MASK) + skb = alloc_canfd_skb(dev, &cf); + else + skb = alloc_can_skb(dev, (struct can_frame **)&cf); + + if (unlikely(!skb)) { + stats->rx_dropped++; + return 0; + } + + if (dlc & CAN_ID2_FDF_MASK) { + /* CAN FD extended frame */ + if (dlc & CANFD_ID2_BRS_MASK) + cf->flags |= CANFD_BRS; + if (dlc & CANFD_ID2_ESI_MASK) + cf->flags |= CANFD_ESI; + cf->len = can_fd_dlc2len((dlc & CANFD_ID2_DLC_MASK) >> CANFD_ID2_DLC_OFF); + } else { + /* CAN extended frame */ + cf->len = can_cc_dlc2len((dlc & CAN_ID2_DLC_MASK) >> CAN_ID2_DLC_OFF); + } + + cf->can_id = (id & CAN_ID1_MASK) >> 3; + cf->can_id |= (id & CAN_ID2_MASK) >> 1; + cf->can_id |= CAN_EFF_FLAG; + + if (id & CAN_ID2_RTR_MASK) + cf->can_id |= CAN_RTR_FLAG; + } else { + /* Received a standard frame */ + if (id & CAN_ID1_FDF_MASK) + skb = alloc_canfd_skb(dev, &cf); + else + skb = alloc_can_skb(dev, (struct can_frame **)&cf); + + if (unlikely(!skb)) { + stats->rx_dropped++; + return 0; + } + + if (id & CAN_ID1_FDF_MASK) { + /* CAN FD extended frame */ + if (id & CANFD_ID1_BRS_MASK) + cf->flags |= CANFD_BRS; + if (id & CANFD_ID1_ESI_MASK) + cf->flags |= CANFD_ESI; + cf->len = can_fd_dlc2len((id & CANFD_ID1_DLC_MASK) >> CANFD_ID1_DLC_OFF); + } else { + /* CAN extended frame */ + cf->len = can_cc_dlc2len((id & CAN_ID1_DLC_MASK) >> CAN_ID1_DLC_OFF); + } + + cf->can_id = (id & CAN_ID1_MASK) >> 21; + + if (id & CAN_ID1_RTR_MASK) + cf->can_id |= CAN_RTR_FLAG; + } + + if (!(cf->can_id & CAN_RTR_FLAG)) + /* Receive data frames */ + for (i = 0; i < cf->len; i += 4) + *(__be32 *)(cf->data + i) = phytium_can_read(cdev, CAN_RX_FIFO); + + stats->rx_packets++; + stats->rx_bytes += cf->len; + netif_receive_skb(skb); + + return 1; +} + +static int phytium_can_do_rx_poll(struct net_device *dev, int quota) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + u32 rxfs, pkts = 0; + int isr; + + isr = cdev->isr; + + rxfs = phytium_can_read(cdev, CAN_FIFO_CNT) & FIFO_CNT_RFN; + if (!rxfs) { + netdev_dbg(dev, "no messages in RX FIFO\n"); + return 0; + } + + while ((rxfs != 0) && (quota > 0)) { + if (isr & INTR_REIS) { + pkts += phytium_can_read_fifo(dev); + quota--; + } else { + break; + } + rxfs = phytium_can_read(cdev, CAN_FIFO_CNT) & FIFO_CNT_RFN; + netdev_dbg(dev, "Next received %d frame again.\n", rxfs); + } + + return pkts; +} + +static int phytium_can_rx_handler(struct net_device *dev, int quota) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + int work_done = 0; + u32 isr; + + isr = cdev->isr | phytium_can_read(cdev, CAN_INTR); + if (!isr) + goto end; + + /* Handle RX IRQ */ + if (isr & INTR_REIS) { + int rx_work_or_err; + + rx_work_or_err = phytium_can_do_rx_poll(dev, (quota - work_done)); + if (rx_work_or_err < 0) + return rx_work_or_err; + + work_done += rx_work_or_err; + } + +end: + return 0; +} + +static int phytium_can_poll(struct napi_struct *napi, int quota) +{ + struct net_device *dev = napi->dev; + struct phytium_can_dev *cdev = netdev_priv(dev); + int work_done; + + netdev_dbg(dev, "The receive processing is going on !\n"); + + work_done = phytium_can_rx_handler(dev, quota); + + /* Don't re-enable interrupts if the driver had a fatal error + * (e.g., FIFO read failure) + */ + if (work_done >= 0 && work_done < quota) { + napi_complete_done(napi, work_done); + phytium_can_enable_all_interrupts(cdev); + } + + return work_done; +} + +static void phytium_can_write_frame(struct phytium_can_dev *cdev) +{ + struct canfd_frame *cf = (struct canfd_frame *)cdev->tx_skb->data; + struct net_device *dev = cdev->net; + struct sk_buff *skb = cdev->tx_skb; + u32 i, id, dlc = 0, frame_head[2] = {0, 0}; + u32 data_len; + + data_len = can_fd_len2dlc(cf->len); + cdev->tx_skb = NULL; + + /* Watch carefully on the bit sequence */ + if (cf->can_id & CAN_EFF_FLAG) { + /* Extended CAN ID format */ + id = ((cf->can_id & CAN_EFF_MASK) << 1) & CAN_ID2_MASK; + id |= (((cf->can_id & CAN_EFF_MASK) >> + (CAN_EFF_ID_BITS - CAN_SFF_ID_BITS)) << + CAN_IDR_ID1_SHIFT) & CAN_ID1_MASK; + + /* The substibute remote TX request bit should be "1" + * for extended frames as in the Phytium CAN datasheet + */ + id |= CAN_IDE_MASK | CAN_ID2_SRR_MASK; + + if (cf->can_id & CAN_RTR_FLAG) + /* Extended frames remote TX request */ + id |= CAN_ID2_RTR_MASK; + if ((cdev->can.ctrlmode & CAN_CTRLMODE_FD) && + can_is_canfd_skb(skb)) + dlc = data_len << CANFD_ID2_DLC_OFF; + else + dlc = data_len << CAN_ID2_DLC_OFF; + + if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) { + dlc |= CAN_ID2_FDF_MASK; + if (cf->flags & CANFD_BRS) + dlc |= CANFD_ID2_BRS_MASK; + if (cf->flags & CANFD_ESI) + dlc |= CANFD_ID2_ESI_MASK; + } + + frame_head[0] = cpu_to_be32p(&id); + frame_head[1] = cpu_to_be32p(&dlc); + + /* Write the Frame to Phytium CAN TX FIFO */ + phytium_can_write(cdev, CAN_TX_FIFO, frame_head[0]); + phytium_can_write(cdev, CAN_TX_FIFO, frame_head[1]); + netdev_dbg(dev, "Write atbitration field [0]:0x%x [1]:0x%x\n", + frame_head[0], frame_head[1]); + } else { + /* Standard CAN ID format */ + id = ((cf->can_id & CAN_SFF_MASK) << CAN_IDR_ID1_SHIFT) + & CAN_ID1_MASK; + + if (cf->can_id & CAN_RTR_FLAG) + /* Standard frames remote TX request */ + id |= CAN_ID1_RTR_MASK; + + if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) + dlc = (data_len << CANFD_ID1_DLC_OFF) + | CANFD_IDR_PAD_MASK; + else + dlc = (data_len << CAN_ID1_DLC_OFF) | CAN_IDR_PAD_MASK; + + id |= dlc; + + if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) { + id |= CAN_ID1_FDF_MASK; + if (cf->flags & CANFD_BRS) + id |= CANFD_ID1_BRS_MASK; + if (cf->flags & CANFD_ESI) + id |= CANFD_ID1_ESI_MASK; + } + + frame_head[0] = cpu_to_be32p(&id); + /* Write the Frame to Phytium CAN TX FIFO */ + phytium_can_write(cdev, CAN_TX_FIFO, frame_head[0]); + netdev_dbg(dev, "Write atbitration field [0] 0x%x\n", + frame_head[0]); + } + + if (!(cf->can_id & CAN_RTR_FLAG)) { + netdev_dbg(dev, "Write CAN data frame\n"); + for (i = 0; i < cf->len; i += 4) { + phytium_can_write(cdev, CAN_TX_FIFO, + *(__be32 *)(cf->data + i)); + netdev_dbg(dev, "[%d]:%x\n", i, + *(__be32 *)(cf->data + i)); + } + } + + can_put_echo_skb(skb, dev, cdev->tx_head % cdev->tx_max, 0); + cdev->tx_head++; + + netif_stop_queue(dev); + /* trigger transmission */ + phytium_can_clr_reg_bits(cdev, CAN_CTRL, CTRL_XFER); + phytium_can_set_reg_bits(cdev, CAN_CTRL, CTRL_TXREQ | CTRL_XFER); + + netdev_dbg(dev, "Trigger send message!\n"); +} + +static netdev_tx_t phytium_can_tx_handler(struct phytium_can_dev *cdev) +{ + struct net_device *dev = cdev->net; + u32 tx_fifo_used; + + /* Check if the TX buffer is full */ + tx_fifo_used = (phytium_can_read(cdev, CAN_FIFO_CNT) & FIFO_CNT_TFN) >> 16; + if (tx_fifo_used == cdev->tx_max) { + netif_stop_queue(dev); + netdev_err(dev, "BUG!, TX FIFO full when queue awake!\n"); + return NETDEV_TX_BUSY; + } + + if (cdev->tx_head == cdev->tx_tail) { + cdev->tx_head = 0; + cdev->tx_tail = 0; + } + + phytium_can_write_frame(cdev); + + return NETDEV_TX_OK; +} + +/** + * phytium_can_tx_interrupt - Tx Done Isr + * @ndev: net_device pointer + * @isr: Interrupt status register value + */ +static void phytium_can_tx_interrupt(struct net_device *ndev, u32 isr) +{ + struct phytium_can_dev *cdev = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + + while ((cdev->tx_head - cdev->tx_tail > 0) && (isr & INTR_TEIS)) { + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_TEIC | INTR_REIC); + stats->tx_bytes = can_get_echo_skb(ndev, cdev->tx_tail % cdev->tx_max, NULL); + cdev->tx_tail++; + stats->tx_packets++; + isr = (phytium_can_read(cdev, CAN_INTR) & INTR_STATUS_MASK); + } + + phytium_can_clr_reg_bits(cdev, CAN_CTRL, CTRL_XFER); + phytium_can_clr_reg_bits(cdev, CAN_CTRL, CTRL_TXREQ); + phytium_can_set_reg_bits(cdev, CAN_CTRL, CTRL_XFER); + netdev_dbg(ndev, "Finish transform packets %lu\n", stats->tx_packets); + netdev_dbg(ndev, "\n-------------------\n"); + netif_wake_queue(ndev); +} + +static void phytium_can_err_interrupt(struct net_device *ndev, u32 isr) +{ + struct phytium_can_dev *cdev = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 txerr = 0, rxerr = 0; + + skb = alloc_can_err_skb(ndev, &cf); + + rxerr = phytium_can_read(cdev, CAN_ERROR_CNT) & ERR_CNT_REC; + txerr = ((phytium_can_read(cdev, CAN_ERROR_CNT) & ERR_CNT_TEC) >> 16); + + if (isr & INTR_BOIS) { + netdev_dbg(ndev, "%s: txerr :%u rxerr :%u\n", + __func__, txerr, rxerr); + cdev->can.state = CAN_STATE_BUS_OFF; + cdev->can.can_stats.bus_off++; + /* Leave device in Config Mode in bus-off state */ + phytium_can_write(cdev, CAN_CTRL, CTRL_RST); + can_bus_off(ndev); + if (skb) + cf->can_id |= CAN_ERR_BUSOFF; + } else if ((isr & INTR_PEIS) == INTR_PEIS) { + netdev_dbg(ndev, "%s: txerr :%u rxerr :%u\n", + __func__, txerr, rxerr); + cdev->can.state = CAN_STATE_ERROR_PASSIVE; + cdev->can.can_stats.error_passive++; + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (rxerr > 127) ? + CAN_ERR_CRTL_RX_PASSIVE : + CAN_ERR_CRTL_TX_PASSIVE; + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + } else if (isr & INTR_PWIS) { + netdev_dbg(ndev, "%s: txerr :%u rxerr :%u\n", + __func__, txerr, rxerr); + cdev->can.state = CAN_STATE_ERROR_WARNING; + cdev->can.can_stats.error_warning++; + if (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; + } + } + + /* Check for RX FIFO Overflow interrupt */ + if (isr & INTR_RFIS) { + stats->rx_over_errors++; + stats->rx_errors++; + + if (skb) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] |= CAN_ERR_CRTL_RX_OVERFLOW; + } + } + + if (skb) { + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + netif_rx(skb); + } +} + +/** + * phytium_can_isr - CAN Isr + * @irq: irq number + * @dev_id: device id poniter + * + * This is the phytium CAN Isr. It checks for the type of interrupt + * and invokes the corresponding ISR. + * + * Return: + * * IRQ_NONE - If CAN device is in sleep mode, IRQ_HANDLED otherwise + */ +static irqreturn_t phytium_can_isr(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct phytium_can_dev *cdev = netdev_priv(dev); + u32 isr; + + /* Get the interrupt status */ + isr = phytium_can_read(cdev, CAN_INTR) & INTR_STATUS_MASK; + if (!isr) + return IRQ_NONE; + + /* Check for FIFO full interrupt and alarm */ + if ((isr & INTR_RFIS)) { + netdev_dbg(dev, "rx_fifo is full!.\n"); + isr &= (~INTR_RFIS); + phytium_can_clr_reg_bits(cdev, CAN_INTR, INTR_RFIE); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_RFIC); + napi_schedule(&cdev->napi); + } + + /* Check for the type of error interrupt and Processing it */ + if (isr & (INTR_EIS | INTR_RFIS | INTR_BOIS | INTR_PEIS)) { + phytium_can_clr_reg_bits(cdev, CAN_INTR, (INTR_EIE + | INTR_RFIE | INTR_BOIE | INTR_PEIE)); + phytium_can_err_interrupt(dev, isr); + phytium_can_set_reg_bits(cdev, CAN_INTR, (INTR_EIC + | INTR_RFIC | INTR_BOIC | INTR_PEIC)); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_EN_MASK); + return IRQ_HANDLED; + } + + /* Check for Tx interrupt and Processing it */ + if ((isr & INTR_TEIS)) { + isr &= (~INTR_REIS); + phytium_can_tx_interrupt(dev, isr); + } + + /* Check for the type of receive interrupt and Processing it */ + if (isr & (INTR_REIS)) { + cdev->isr = (isr & INTR_REIS); + phytium_can_clr_reg_bits(cdev, CAN_INTR, INTR_REIE); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_REIC); + napi_schedule(&cdev->napi); + } + + return IRQ_HANDLED; +} + +/** + * phytium_can_set_bittiming - CAN set bit timing routine + * @dev: Pointer to net_device structure + * + * This is the driver set bittiming routine. + * Return: 0 on success and failure value on error + */ +static int phytium_can_set_bittiming(struct net_device *dev) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + const struct can_bittiming *bt = &cdev->can.bittiming; + const struct can_bittiming *dbt = &cdev->can.data_bittiming; + u32 btr, dbtr; + u32 is_config_mode; + + /** + * Check whether Phytium CAN is in configuration mode. + * It cannot set bit timing if Phytium CAN is not in configuration mode. + */ + is_config_mode = phytium_can_read(cdev, CAN_CTRL) & CTRL_XFER; + if (is_config_mode) { + netdev_alert(dev, "BUG! Cannot set bittiming - CAN is not in config mode\n"); + return -EPERM; + } + + /* Setting Baud Rate prescalar value in BRPR Register */ + btr = (bt->brp - 1) << 16; + + /* Setting Time Segment 1 in BTR Register */ + btr |= (bt->prop_seg - 1) << 2; + + btr |= (bt->phase_seg1 - 1) << 5; + + /* Setting Time Segment 2 in BTR Register */ + btr |= (bt->phase_seg2 - 1) << 8; + + /* Setting Synchronous jump width in BTR Register */ + btr |= (bt->sjw - 1); + + dbtr = (dbt->brp - 1) << 16; + dbtr |= (dbt->prop_seg - 1) << 2; + dbtr |= (dbt->phase_seg1 - 1) << 5; + dbtr |= (dbt->phase_seg2 - 1) << 8; + dbtr |= (dbt->sjw - 1); + + if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) { + phytium_can_write(cdev, CAN_ARB_RATE_CTRL, btr); + phytium_can_write(cdev, CAN_DAT_RATE_CTRL, dbtr); + } else { + phytium_can_write(cdev, CAN_ARB_RATE_CTRL, btr); + phytium_can_write(cdev, CAN_DAT_RATE_CTRL, btr); + } + + netdev_dbg(dev, "DAT=0x%08x, ARB=0x%08x\n", + phytium_can_read(cdev, CAN_DAT_RATE_CTRL), + phytium_can_read(cdev, CAN_ARB_RATE_CTRL)); + + return 0; +} + +/** + * phytium_can_start - This the drivers start routine + * @dev: Pointer to net_device structure + * + * This is the drivers start routine. + * Based on the State of the CAN device it puts + * the CAN device into a proper mode. + * + * Return: 0 on success and failure value on error + */ +static void phytium_can_start(struct net_device *dev) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + u32 ctrl; + + /* Disable transfer */ + ctrl = phytium_can_read(cdev, CAN_CTRL); + ctrl &= ~CTRL_XFER; + phytium_can_write(cdev, CAN_CTRL, ctrl); + + /* XXX: If CANFD, reset the controller */ + phytium_can_write(cdev, CAN_CTRL, (ctrl | CTRL_RST)); + + /* Bittiming setup */ + phytium_can_set_bittiming(dev); + + /* Acceptance identifier mask setup */ + phytium_can_write(cdev, CAN_ACC_ID0_MASK, ACC_IDX_MASK_AID_MASK); + phytium_can_write(cdev, CAN_ACC_ID1_MASK, ACC_IDX_MASK_AID_MASK); + phytium_can_write(cdev, CAN_ACC_ID2_MASK, ACC_IDX_MASK_AID_MASK); + phytium_can_write(cdev, CAN_ACC_ID3_MASK, ACC_IDX_MASK_AID_MASK); + ctrl |= CTRL_AIME; + + if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) + ctrl |= CTRL_IOF | CTRL_FDCRC; + + phytium_can_write(cdev, CAN_CTRL, ctrl); + + cdev->can.state = CAN_STATE_ERROR_ACTIVE; + + phytium_can_enable_all_interrupts(cdev); + + if (cdev->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + ctrl |= CTRL_XFER; + else + ctrl |= CTRL_XFER | CTRL_TXREQ; + + phytium_can_write(cdev, CAN_CTRL, ctrl); +} + +/** + * phytium_can_stop - Driver stop routine + * @dev: Pointer to net_device structure + * + * This is the drivers stop routine. It will disable the + * interrupts and put the device into configuration mode. + */ +static void phytium_can_stop(struct net_device *dev) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + u32 ctrl; + + /* Disable all interrupts */ + phytium_can_disable_all_interrupt(cdev); + + /* Disable transfer and switch to receive-only mode */ + ctrl = phytium_can_read(cdev, CAN_CTRL); + ctrl &= ~(CTRL_XFER | CTRL_TXREQ); + phytium_can_write(cdev, CAN_CTRL, ctrl); + + /* Set the state as STOPPED */ + cdev->can.state = CAN_STATE_STOPPED; +} + +static void phytium_can_clean(struct net_device *dev) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + + if (cdev->tx_skb) { + dev->stats.tx_errors++; + can_free_echo_skb(cdev->net, 0, NULL); + cdev->tx_skb = NULL; + } +} + +static int phytium_can_set_mode(struct net_device *dev, enum can_mode mode) +{ + switch (mode) { + case CAN_MODE_START: + phytium_can_clean(dev); + phytium_can_start(dev); + netif_wake_queue(dev); + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +/** + * phytium_can_open - Driver open routine + * @dev: Pointer to net_device structure + * + * This is the driver open routine. + * Return: 0 on success and failure value on error + */ +static int phytium_can_open(struct net_device *dev) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + int ret; + + /* Start clock */ + ret = pm_runtime_resume(cdev->dev); + if (ret) + return ret; + + /* Open the CAN device */ + ret = open_candev(dev); + if (ret) { + netdev_err(dev, "failed to open can device\n"); + goto disable_clk; + } + + /* Register interrupt handler */ + ret = request_irq(dev->irq, phytium_can_isr, + IRQF_SHARED, dev->name, dev); + if (ret < 0) { + netdev_err(dev, "failed to request interrupt\n"); + goto fail; + } + + /* Start the controller */ + phytium_can_start(dev); + + napi_enable(&cdev->napi); + netif_start_queue(dev); + + return 0; + +fail: + close_candev(dev); +disable_clk: + pm_runtime_put_sync(cdev->dev); + return ret; +} + +/** + * phytium_can_close - Driver close routine + * @dev: Pointer to net_device structure + * + * Return: 0 always + */ +static int phytium_can_close(struct net_device *dev) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + + netif_stop_queue(dev); + napi_disable(&cdev->napi); + + phytium_can_stop(dev); + free_irq(dev->irq, dev); + pm_runtime_put_sync(cdev->dev); + + close_candev(dev); + + return 0; +} + +/** + * phytium_can_start_xmit - Starts the transmission + * + * Return: 0 on success. + */ +static netdev_tx_t phytium_can_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct phytium_can_dev *cdev = netdev_priv(dev); + + if (can_dropped_invalid_skb(dev, skb)) + return NETDEV_TX_OK; + + cdev->tx_skb = skb; + + return phytium_can_tx_handler(cdev); +} + +static const struct net_device_ops phytium_can_netdev_ops = { + .ndo_open = phytium_can_open, + .ndo_stop = phytium_can_close, + .ndo_start_xmit = phytium_can_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static int register_phytium_can_dev(struct net_device *dev) +{ + dev->flags |= IFF_ECHO; + dev->netdev_ops = &phytium_can_netdev_ops; + + return register_candev(dev); +} + +static int phytium_can_dev_setup(struct phytium_can_dev *cdev) +{ + struct net_device *dev = cdev->net; + + netif_napi_add(dev, &cdev->napi, phytium_can_poll); + + cdev->can.do_set_mode = phytium_can_set_mode; + cdev->can.do_get_berr_counter = phytium_can_get_berr_counter; + + cdev->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_BERR_REPORTING; + cdev->can.bittiming_const = cdev->bit_timing; + + if (cdev->fdmode) { + cdev->can.ctrlmode_supported |= CAN_CTRLMODE_FD; + dev->mtu = CANFD_MTU; + cdev->can.ctrlmode = CAN_CTRLMODE_FD; + cdev->can.data_bittiming_const = cdev->bit_timing; + } + + return 0; +} + +struct phytium_can_dev *phytium_can_allocate_dev(struct device *dev, int sizeof_priv, + int tx_fifo_depth) +{ + struct phytium_can_dev *cdev = NULL; + struct net_device *net_dev; + + /* Allocate the can device struct */ + net_dev = alloc_candev(sizeof_priv, tx_fifo_depth); + if (!net_dev) { + dev_err(dev, "Failed to allocate CAN device.\n"); + goto out; + } + + cdev = netdev_priv(net_dev); + cdev->net = net_dev; + cdev->dev = dev; + SET_NETDEV_DEV(net_dev, dev); + +out: + return cdev; +} +EXPORT_SYMBOL(phytium_can_allocate_dev); + +void phytium_can_free_dev(struct net_device *net) +{ + free_candev(net); +} +EXPORT_SYMBOL(phytium_can_free_dev); + +int phytium_can_register(struct phytium_can_dev *cdev) +{ + int ret; + + ret = pm_runtime_resume(cdev->dev); + if (ret) + return ret; + + ret = phytium_can_dev_setup(cdev); + if (ret) + goto fail; + + ret = register_phytium_can_dev(cdev->net); + if (ret) { + dev_err(cdev->dev, "registering %s failed (err=%d)\n", + cdev->net->name, ret); + goto fail; + } + + dev_info(cdev->dev, "%s device registered (irq=%d)\n", + KBUILD_MODNAME, cdev->net->irq); + + /* Probe finished + * Stop clocks. They will be reactivated once the device is opened. + */ + pm_runtime_put_sync(cdev->dev); + + return 0; + +fail: + pm_runtime_put_sync(cdev->dev); + return ret; +} +EXPORT_SYMBOL(phytium_can_register); + +void phytium_can_unregister(struct phytium_can_dev *cdev) +{ + unregister_candev(cdev->net); +} +EXPORT_SYMBOL(phytium_can_unregister); + +int phytium_can_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct phytium_can_dev *cdev = netdev_priv(ndev); + + if (netif_running(ndev)) { + netif_stop_queue(ndev); + netif_device_detach(ndev); + phytium_can_stop(ndev); + pm_runtime_put_sync(cdev->dev); + } + + cdev->can.state = CAN_STATE_SLEEPING; + + return 0; +} +EXPORT_SYMBOL(phytium_can_suspend); + +int phytium_can_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct phytium_can_dev *cdev = netdev_priv(ndev); + int ret; + + cdev->can.state = CAN_STATE_ERROR_ACTIVE; + + if (netif_running(ndev)) { + ret = pm_runtime_resume(cdev->dev); + if (ret) + return ret; + + phytium_can_start(ndev); + netif_device_attach(ndev); + netif_start_queue(ndev); + } + + return 0; +} +EXPORT_SYMBOL(phytium_can_resume); + +MODULE_AUTHOR("Cheng Quan "); +MODULE_AUTHOR("Chen Baozi "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("CAN bus driver for Phytium CAN controller"); + diff --git a/drivers/net/can/phytium/phytium_can.h b/drivers/net/can/phytium/phytium_can.h new file mode 100644 index 0000000000000..edb74df010ca3 --- /dev/null +++ b/drivers/net/can/phytium/phytium_can.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Phytium CAN controller driver + * + * Copyright (C) 2021-2023, Phytium Technology Co., Ltd. + */ + +#ifndef _PHYTIUM_CAN_H_ +#define _PHYTIUM_CAN_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum phytium_can_ip_type { + PHYTIUM_CAN = 0, + PHYTIUM_CANFD, +}; + +struct phytium_can_devtype { + enum phytium_can_ip_type cantype; + const struct can_bittiming_const *bittiming_const; +}; + +struct phytium_can_dev { + struct can_priv can; + unsigned int tx_head; + unsigned int tx_tail; + unsigned int tx_max; + struct napi_struct napi; + struct net_device *net; + struct device *dev; + struct clk *clk; + + struct sk_buff *tx_skb; + + const struct can_bittiming_const *bit_timing; + + int fdmode; + u32 isr; + u32 tx_fifo_depth; + + void __iomem *base; +}; + +struct phytium_can_dev *phytium_can_allocate_dev(struct device *dev, int sizeof_priv, + int tx_fifo_depth); +void phytium_can_free_dev(struct net_device *net); + +int phytium_can_register(struct phytium_can_dev *cdev); +void phytium_can_unregister(struct phytium_can_dev *cdev); + +int phytium_can_suspend(struct device *dev); +int phytium_can_resume(struct device *dev); +#endif /* _PHYTIUM_CAN_H_ */ diff --git a/drivers/net/can/phytium/phytium_can_pci.c b/drivers/net/can/phytium/phytium_can_pci.c new file mode 100644 index 0000000000000..73e88980f1723 --- /dev/null +++ b/drivers/net/can/phytium/phytium_can_pci.c @@ -0,0 +1,135 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Platform CAN bus driver for Phytium CAN controller + * + * Copyright (C) 2021-2023, Phytium Technology Co., Ltd. + */ + +#include + +#include "phytium_can.h" + +struct phytium_can_pci_config { + const struct phytium_can_devtype *devtype; + unsigned int clock_freq; + unsigned int tx_fifo_depth; +}; + +#define cdev2priv(dev) container_of(dev, struct phytium_can_pci, cdev) + +struct phytium_can_pci { + struct phytium_can_dev cdev; + + void __iomem *base; +}; + +static const struct can_bittiming_const phytium_bittiming_const_8192 = { + .name = "phytium_can", + .tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 16, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 8, + .sjw_max = 4, /* Synchronisation jump width */ + .brp_min = 1, /* Bit-rate prescaler */ + .brp_max = 8192, + .brp_inc = 2, +}; + +static const struct phytium_can_devtype phytium_can_pci = { + .cantype = PHYTIUM_CAN, + .bittiming_const = &phytium_bittiming_const_8192, +}; + +static const struct phytium_can_pci_config phytium_can_pci_data = { + .devtype = &phytium_can_pci, + .clock_freq = 600000000, + .tx_fifo_depth = 64, +}; + +static int phytium_can_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + const struct phytium_can_pci_config *cfg; + struct phytium_can_dev *cdev; + struct phytium_can_pci *priv; + int ret; + + cfg = (const struct phytium_can_pci_config *)id->driver_data; + + ret = pcim_enable_device(pdev); + if (ret) + goto err; + + ret = pcim_iomap_regions(pdev, 0x1, pci_name(pdev)); + if (ret) + goto err; + + cdev = phytium_can_allocate_dev(&pdev->dev, sizeof(struct phytium_can_pci), + cfg->tx_fifo_depth); + if (!cdev) + return -ENOMEM; + + priv = cdev2priv(cdev); + priv->base = pcim_iomap_table(pdev)[0]; + + cdev->dev = &pdev->dev; + cdev->fdmode = cfg->devtype->cantype; + cdev->bit_timing = cfg->devtype->bittiming_const; + cdev->can.clock.freq = cfg->clock_freq; + cdev->tx_fifo_depth = cfg->tx_fifo_depth; + + cdev->base = priv->base; + cdev->net->irq = pdev->irq; + + pci_set_drvdata(pdev, cdev->net); + + pm_runtime_enable(cdev->dev); + ret = phytium_can_register(cdev); + if (ret) + goto err; + + return 0; +err: + return ret; +} + +static void phytium_can_pci_remove(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct phytium_can_dev *cdev = netdev_priv(dev); + + phytium_can_unregister(cdev); + phytium_can_free_dev(cdev->net); +} + +static __maybe_unused int phytium_can_pci_suspend(struct device *dev) +{ + return phytium_can_suspend(dev); +} + +static __maybe_unused int phytium_can_pci_resume(struct device *dev) +{ + return phytium_can_resume(dev); +} + +static SIMPLE_DEV_PM_OPS(phytium_can_pci_pm_ops, + phytium_can_pci_suspend, phytium_can_pci_resume); + +static const struct pci_device_id phytium_can_pci_id_table[] = { + { PCI_VDEVICE(PHYTIUM, 0xdc2d), (kernel_ulong_t)&phytium_can_pci_data, }, + { /* sentinel */ }, +}; + +static struct pci_driver phytium_can_pci_driver = { + .name = KBUILD_MODNAME, + .probe = phytium_can_pci_probe, + .remove = phytium_can_pci_remove, + .id_table = phytium_can_pci_id_table, + .driver = { + .pm = &phytium_can_pci_pm_ops, + }, +}; + +module_pci_driver(phytium_can_pci_driver); + +MODULE_AUTHOR("Cheng Quan +#include +#include + +#include "phytium_can.h" + +#define cdev2priv(dev) container_of(dev, struct phytium_can_plat, cdev) + +struct phytium_can_plat { + struct phytium_can_dev cdev; + struct phytium_can_devtype *devtype; + + int irq; + void __iomem *reg_base; +}; + +static const struct can_bittiming_const phytium_bittiming_const_512 = { + .name = "phytium_can", + .tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 16, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 8, + .sjw_max = 4, /* Synchronisation jump width */ + .brp_min = 1, /* Bit-rate prescaler */ + .brp_max = 512, + .brp_inc = 2, +}; + +static const struct can_bittiming_const phytium_bittiming_const_8192 = { + .name = "phytium_can", + .tseg1_min = 1, /* Time segment 1 = prop_seg + phase_seg1 */ + .tseg1_max = 16, + .tseg2_min = 1, /* Time segment 2 = phase_seg2 */ + .tseg2_max = 8, + .sjw_max = 4, /* Synchronisation jump width */ + .brp_min = 1, /* Bit-rate prescaler */ + .brp_max = 8192, + .brp_inc = 2, +}; + +static const struct phytium_can_devtype phytium_can_data = { + .cantype = PHYTIUM_CAN, + .bittiming_const = &phytium_bittiming_const_512, +}; + +static const struct phytium_can_devtype phytium_canfd_data = { + .cantype = PHYTIUM_CANFD, + .bittiming_const = &phytium_bittiming_const_8192, +}; + +#ifdef CONFIG_ACPI +static const struct acpi_device_id phytium_can_acpi_ids[] = { + { "PHYT000A", 0 }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(acpi, phytium_can_acpi_ids); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id phytium_can_of_ids[] = { + { .compatible = "phytium,can", .data = &phytium_can_data }, + { .compatible = "phytium,canfd", .data = &phytium_canfd_data }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, phytium_can_of_ids); +#endif + +static int phytium_can_plat_probe(struct platform_device *pdev) +{ + struct phytium_can_dev *cdev; + struct phytium_can_plat *priv; + struct resource *res; + const struct of_device_id *of_id; + const struct phytium_can_devtype *devtype = &phytium_can_data; + u32 tx_fifo_depth; + int ret; + const char *str = "canfd"; + + ret = fwnode_property_read_u32(dev_fwnode(&pdev->dev), "tx-fifo-depth", &tx_fifo_depth); + if (ret) + tx_fifo_depth = 64; + + cdev = phytium_can_allocate_dev(&pdev->dev, sizeof(struct phytium_can_plat), + tx_fifo_depth); + if (!cdev) + return -ENOMEM; + + priv = cdev2priv(cdev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->reg_base = devm_ioremap_resource(&pdev->dev, res); + priv->irq = platform_get_irq(pdev, 0); + if (IS_ERR(priv->reg_base) || cdev->net->irq < 0) { + ret = -EINVAL; + goto fail; + } + + if (pdev->dev.of_node) { + cdev->clk = devm_clk_get(&pdev->dev, "can_clk"); + if (IS_ERR(cdev->clk)) { + dev_err(&pdev->dev, "no clock found\n"); + ret = -ENODEV; + goto fail; + } + cdev->can.clock.freq = clk_get_rate(cdev->clk); + + of_id = of_match_device(phytium_can_of_ids, &pdev->dev); + if (of_id && of_id->data) + devtype = of_id->data; + } else if (has_acpi_companion(&pdev->dev)) { + ret = fwnode_property_read_u32(dev_fwnode(&pdev->dev), + "clock-frequency", + &cdev->can.clock.freq); + if (ret < 0) { + dev_err(&pdev->dev, "failed to get clock frequency.\n"); + goto fail; + } + + fwnode_property_read_string(dev_fwnode(&pdev->dev), + "mode-select", &str); + if (!(strcmp(str, "canfd"))) + devtype = &phytium_canfd_data; + else + devtype = &phytium_can_data; + } + + cdev->tx_fifo_depth = tx_fifo_depth; + cdev->tx_head = 0; + cdev->tx_tail = 0; + cdev->tx_max = tx_fifo_depth; + + if (devtype->cantype == PHYTIUM_CANFD) + cdev->fdmode = 1; + else + cdev->fdmode = 0; + + if (fwnode_property_present(dev_fwnode(&pdev->dev), "extend_brp")) + cdev->bit_timing = &phytium_bittiming_const_8192; + else + cdev->bit_timing = devtype->bittiming_const; + cdev->can.bittiming_const = devtype->bittiming_const; + cdev->base = priv->reg_base; + cdev->net->irq = priv->irq; + + platform_set_drvdata(pdev, cdev->net); + + pm_runtime_enable(cdev->dev); + ret = phytium_can_register(cdev); + if (ret) + goto out_runtime_disable; + + return ret; + +out_runtime_disable: + pm_runtime_disable(cdev->dev); +fail: + phytium_can_free_dev(cdev->net); + return ret; +} + +static __maybe_unused int phytium_can_plat_suspend(struct device *dev) +{ + return phytium_can_suspend(dev); +} + +static __maybe_unused int phytium_can_plat_resume(struct device *dev) +{ + return phytium_can_resume(dev); +} + +static int phytium_can_plat_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct phytium_can_dev *cdev = netdev_priv(dev); + + phytium_can_unregister(cdev); + + phytium_can_free_dev(cdev->net); + + return 0; +} + +static int __maybe_unused phytium_can_runtime_suspend(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct phytium_can_dev *cdev = netdev_priv(ndev); + + clk_disable_unprepare(cdev->clk); + + return 0; +} + +static int __maybe_unused phytium_can_runtime_resume(struct device *dev) +{ + struct net_device *ndev = dev_get_drvdata(dev); + struct phytium_can_dev *cdev = netdev_priv(ndev); + + return clk_prepare_enable(cdev->clk); +} + +static const struct dev_pm_ops phytium_can_plat_pm_ops = { + SET_RUNTIME_PM_OPS(phytium_can_runtime_suspend, + phytium_can_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(phytium_can_suspend, phytium_can_resume) +}; + +static struct platform_driver phytium_can_plat_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = of_match_ptr(phytium_can_of_ids), + .acpi_match_table = ACPI_PTR(phytium_can_acpi_ids), + .pm = &phytium_can_plat_pm_ops, + }, + .probe = phytium_can_plat_probe, + .remove = phytium_can_plat_remove, +}; + +module_platform_driver(phytium_can_plat_driver); + +MODULE_AUTHOR("Cheng Quan "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Phytium CAN driver for IO Mapped controllers"); From 6060198edfb43472827efe841f6a6e8a912fe6c8 Mon Sep 17 00:00:00 2001 From: yuanxia Date: Tue, 21 May 2024 16:42:17 +0800 Subject: [PATCH 3/5] arm64: can: phytium: Use CAN mode as the default mode under ACPI Phytium CANFD ususally is not supported without explicit specifying in ACPI. Therefore, the default mode under ACPI should be CAN rather than CANFD. Signed-off-by: yuanxia Signed-off-by: Chen Zhenhua Signed-off-by: Chen Baozi Signed-off-by: Wang Yinfeng --- drivers/net/can/phytium/phytium_can_platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/phytium/phytium_can_platform.c b/drivers/net/can/phytium/phytium_can_platform.c index a4203a454baa3..63f89d721784f 100644 --- a/drivers/net/can/phytium/phytium_can_platform.c +++ b/drivers/net/can/phytium/phytium_can_platform.c @@ -80,7 +80,7 @@ static int phytium_can_plat_probe(struct platform_device *pdev) const struct phytium_can_devtype *devtype = &phytium_can_data; u32 tx_fifo_depth; int ret; - const char *str = "canfd"; + const char *str = "can"; ret = fwnode_property_read_u32(dev_fwnode(&pdev->dev), "tx-fifo-depth", &tx_fifo_depth); if (ret) From c02974d89ac4f570d2204e10bf762826988cc869 Mon Sep 17 00:00:00 2001 From: yuanxia Date: Tue, 21 May 2024 16:43:08 +0800 Subject: [PATCH 4/5] arm64: can: phytium: Update clock frequency for px210 CAN Because the finally clock frequency of px210 has been decreased to 480M, update the struct member clock_freq of phytium_can_pci_data to match the updated design. Signed-off-by: yuanxia Signed-off-by: Li Zhengguang Signed-off-by: Chen Baozi Signed-off-by: Wang Yinfeng --- drivers/net/can/phytium/phytium_can_pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/can/phytium/phytium_can_pci.c b/drivers/net/can/phytium/phytium_can_pci.c index 73e88980f1723..ac84b3b15c448 100644 --- a/drivers/net/can/phytium/phytium_can_pci.c +++ b/drivers/net/can/phytium/phytium_can_pci.c @@ -41,7 +41,7 @@ static const struct phytium_can_devtype phytium_can_pci = { static const struct phytium_can_pci_config phytium_can_pci_data = { .devtype = &phytium_can_pci, - .clock_freq = 600000000, + .clock_freq = 480000000, .tx_fifo_depth = 64, }; From cdebe7ebf40ed20da84de78b5bda879c84d62f42 Mon Sep 17 00:00:00 2001 From: yuanxia Date: Tue, 21 May 2024 16:43:56 +0800 Subject: [PATCH 5/5] arm64: can: phytium: Optimize the low performance of can communication This patch is designed to optimize the issue of low performance in CAN communication at 1Mbps,as there is a waste of time in the interaction between software and hardware. Signed-off-by: yuanxia Signed-off-by: Wu Jinyong Signed-off-by: Wang Yinfeng --- drivers/net/can/phytium/phytium_can.c | 192 ++++++++++++------ drivers/net/can/phytium/phytium_can.h | 18 +- drivers/net/can/phytium/phytium_can_pci.c | 14 +- .../net/can/phytium/phytium_can_platform.c | 5 - 4 files changed, 160 insertions(+), 69 deletions(-) diff --git a/drivers/net/can/phytium/phytium_can.c b/drivers/net/can/phytium/phytium_can.c index 21bf74a1d66f2..0be8dd3876edf 100644 --- a/drivers/net/can/phytium/phytium_can.c +++ b/drivers/net/can/phytium/phytium_can.c @@ -23,7 +23,7 @@ enum phytium_can_reg { CAN_ACC_ID2_MASK = 0x28, /* Acceptance identifier2 mask register */ CAN_ACC_ID3_MASK = 0x2c, /* Acceptance identifier3 mask register */ CAN_XFER_STS = 0x30, /* Transfer status register */ - CAN_ERROR_CNT = 0x34, /* Error counter register */ + CAN_PHYTIUM_ERR_CNT = 0x34, /* Error counter register */ CAN_FIFO_CNT = 0x38, /* FIFO counter register */ CAN_DMA_CTRL = 0x3c, /* DMA request control register */ CAN_XFER_EN = 0x40, /* Transfer enable register */ @@ -85,8 +85,10 @@ enum phytium_can_reg { #define INTR_STATUS_MASK (INTR_BOIS | INTR_PWIS | INTR_PEIS | INTR_RFIS | \ INTR_TFIS | INTR_REIS | INTR_TEIS | INTR_EIS) -#define INTR_EN_MASK (INTR_BOIE | INTR_PWIE | INTR_PEIE | INTR_RFIE | \ - INTR_REIE | INTR_TEIE | INTR_EIE) + +#define INTR_EN_MASK (INTR_BOIE | INTR_PWIE | INTR_PEIE | INTR_RFIE | \ + INTR_TFIE | INTR_REIE | INTR_TEIE | INTR_EIE) + #define INTR_CLEAR_MASK (INTR_BOIC | INTR_PWIC | INTR_PEIC | INTR_RFIC | \ INTR_TFIC | INTR_REIC | INTR_TEIC | INTR_EIC) @@ -281,8 +283,8 @@ static inline u32 phytium_can_read(const struct phytium_can_dev *cdev, enum phyt return readl(cdev->base + reg); } -static inline void phytium_can_write(const struct phytium_can_dev *cdev, enum phytium_can_reg reg, - u32 val) +static inline void +phytium_can_write(const struct phytium_can_dev *cdev, enum phytium_can_reg reg, u32 val) { writel(val, cdev->base + reg); } @@ -302,8 +304,8 @@ static int phytium_can_get_berr_counter(const struct net_device *dev, { struct phytium_can_dev *cdev = netdev_priv(dev); - bec->rxerr = phytium_can_read(cdev, CAN_ERROR_CNT) & ERR_CNT_REC; - bec->txerr = (phytium_can_read(cdev, CAN_ERROR_CNT) & ERR_CNT_TEC) >> 16; + bec->rxerr = phytium_can_read(cdev, CAN_PHYTIUM_ERR_CNT) & ERR_CNT_REC; + bec->txerr = (phytium_can_read(cdev, CAN_PHYTIUM_ERR_CNT) & ERR_CNT_TEC) >> 16; return 0; } @@ -451,6 +453,7 @@ static int phytium_can_poll(struct napi_struct *napi, int quota) struct net_device *dev = napi->dev; struct phytium_can_dev *cdev = netdev_priv(dev); int work_done; + unsigned long flags; netdev_dbg(dev, "The receive processing is going on !\n"); @@ -461,7 +464,10 @@ static int phytium_can_poll(struct napi_struct *napi, int quota) */ if (work_done >= 0 && work_done < quota) { napi_complete_done(napi, work_done); - phytium_can_enable_all_interrupts(cdev); + + spin_lock_irqsave(&cdev->lock, flags); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_REIE); + spin_unlock_irqrestore(&cdev->lock, flags); } return work_done; @@ -471,9 +477,10 @@ static void phytium_can_write_frame(struct phytium_can_dev *cdev) { struct canfd_frame *cf = (struct canfd_frame *)cdev->tx_skb->data; struct net_device *dev = cdev->net; + struct net_device_stats *stats = &dev->stats; struct sk_buff *skb = cdev->tx_skb; u32 i, id, dlc = 0, frame_head[2] = {0, 0}; - u32 data_len; + u32 data_len, tmp_len; data_len = can_fd_len2dlc(cf->len); cdev->tx_skb = NULL; @@ -558,37 +565,43 @@ static void phytium_can_write_frame(struct phytium_can_dev *cdev) } } - can_put_echo_skb(skb, dev, cdev->tx_head % cdev->tx_max, 0); - cdev->tx_head++; + stats->tx_bytes += cf->len; + stats->tx_packets++; - netif_stop_queue(dev); - /* trigger transmission */ - phytium_can_clr_reg_bits(cdev, CAN_CTRL, CTRL_XFER); - phytium_can_set_reg_bits(cdev, CAN_CTRL, CTRL_TXREQ | CTRL_XFER); + cdev->is_tx_done = false; + cdev->is_need_stop_xmit = true; + mod_timer(&cdev->timer, jiffies + HZ / 10); netdev_dbg(dev, "Trigger send message!\n"); + can_put_echo_skb(skb, dev, 0, 0); + tmp_len = can_get_echo_skb(dev, 0, 0); } static netdev_tx_t phytium_can_tx_handler(struct phytium_can_dev *cdev) { struct net_device *dev = cdev->net; u32 tx_fifo_used; + unsigned long flags; + phytium_can_write_frame(cdev); /* Check if the TX buffer is full */ - tx_fifo_used = (phytium_can_read(cdev, CAN_FIFO_CNT) & FIFO_CNT_TFN) >> 16; - if (tx_fifo_used == cdev->tx_max) { - netif_stop_queue(dev); - netdev_err(dev, "BUG!, TX FIFO full when queue awake!\n"); - return NETDEV_TX_BUSY; - } - - if (cdev->tx_head == cdev->tx_tail) { - cdev->tx_head = 0; - cdev->tx_tail = 0; + tx_fifo_used = 4 * ((phytium_can_read(cdev, CAN_FIFO_CNT) & FIFO_CNT_TFN) >> 16); + if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) { + if (CAN_FIFO_BYTE_LEN - tx_fifo_used <= KEEP_CANFD_FIFO_MIN_LEN) { + netif_stop_queue(dev); + spin_lock_irqsave(&cdev->lock, flags); + cdev->is_stop_queue_flag = STOP_QUEUE_TRUE; + spin_unlock_irqrestore(&cdev->lock, flags); + } + } else { + if (CAN_FIFO_BYTE_LEN - tx_fifo_used <= KEEP_CAN_FIFO_MIN_LEN) { + netif_stop_queue(dev); + spin_lock_irqsave(&cdev->lock, flags); + cdev->is_stop_queue_flag = STOP_QUEUE_TRUE; + spin_unlock_irqrestore(&cdev->lock, flags); + } } - phytium_can_write_frame(cdev); - return NETDEV_TX_OK; } @@ -601,21 +614,59 @@ static void phytium_can_tx_interrupt(struct net_device *ndev, u32 isr) { struct phytium_can_dev *cdev = netdev_priv(ndev); struct net_device_stats *stats = &ndev->stats; + u32 tx_fifo_used = 0; + + if (isr & INTR_TEIS) + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_TEIC); - while ((cdev->tx_head - cdev->tx_tail > 0) && (isr & INTR_TEIS)) { - phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_TEIC | INTR_REIC); - stats->tx_bytes = can_get_echo_skb(ndev, cdev->tx_tail % cdev->tx_max, NULL); - cdev->tx_tail++; - stats->tx_packets++; - isr = (phytium_can_read(cdev, CAN_INTR) & INTR_STATUS_MASK); + /* Check if the TX buffer is full */ + if (cdev->is_stop_queue_flag) { + tx_fifo_used = 4 * ((phytium_can_read(cdev, CAN_FIFO_CNT) & FIFO_CNT_TFN) >> 16); + if (cdev->can.ctrlmode & CAN_CTRLMODE_FD) { + if (CAN_FIFO_BYTE_LEN - tx_fifo_used > KEEP_CANFD_FIFO_MIN_LEN) { + netif_wake_queue(ndev); + cdev->is_stop_queue_flag = STOP_QUEUE_FALSE; + } + } else { + if (CAN_FIFO_BYTE_LEN - tx_fifo_used > KEEP_CAN_FIFO_MIN_LEN) { + netif_wake_queue(ndev); + cdev->is_stop_queue_flag = STOP_QUEUE_FALSE; + } + } } - phytium_can_clr_reg_bits(cdev, CAN_CTRL, CTRL_XFER); - phytium_can_clr_reg_bits(cdev, CAN_CTRL, CTRL_TXREQ); - phytium_can_set_reg_bits(cdev, CAN_CTRL, CTRL_XFER); + cdev->is_tx_done = true; + cdev->is_need_stop_xmit = false; + del_timer(&cdev->timer); + netdev_dbg(ndev, "Finish transform packets %lu\n", stats->tx_packets); - netdev_dbg(ndev, "\n-------------------\n"); - netif_wake_queue(ndev); + + phytium_can_set_reg_bits(cdev, CAN_INTR, (INTR_BOIE | + INTR_PWIE | INTR_PEIE)); +} + +static void phytium_can_tx_done_timeout(struct timer_list *t) +{ + struct phytium_can_dev *priv = from_timer(priv, t, timer); + struct net_device *ndev = priv->net; + + if (!priv->is_tx_done) { + if (priv->is_need_stop_xmit) { + netdev_dbg(ndev, "%s stop xmit\n", __func__); + priv->is_need_stop_xmit = false; + phytium_can_clr_reg_bits(priv, CAN_CTRL, CTRL_XFER); + phytium_can_clr_reg_bits(priv, CAN_INTR, (INTR_BOIE | + INTR_PWIE | INTR_PEIE)); + /* stop xmit and restart after 500ms */ + mod_timer(&priv->timer, jiffies + HZ / 2); + } else { + netdev_dbg(ndev, "%s start xmit\n", __func__); + priv->is_need_stop_xmit = true; + phytium_can_set_reg_bits(priv, CAN_CTRL, CTRL_XFER); + /* start xmit and stop after 250ms */ + mod_timer(&priv->timer, jiffies + HZ / 4); + } + } } static void phytium_can_err_interrupt(struct net_device *ndev, u32 isr) @@ -628,11 +679,11 @@ static void phytium_can_err_interrupt(struct net_device *ndev, u32 isr) skb = alloc_can_err_skb(ndev, &cf); - rxerr = phytium_can_read(cdev, CAN_ERROR_CNT) & ERR_CNT_REC; - txerr = ((phytium_can_read(cdev, CAN_ERROR_CNT) & ERR_CNT_TEC) >> 16); + rxerr = phytium_can_read(cdev, CAN_PHYTIUM_ERR_CNT) & ERR_CNT_REC; + txerr = ((phytium_can_read(cdev, CAN_PHYTIUM_ERR_CNT) & ERR_CNT_TEC) >> 16); if (isr & INTR_BOIS) { - netdev_dbg(ndev, "%s: txerr :%u rxerr :%u\n", + netdev_dbg(ndev, "bus_off %s: txerr :%u rxerr :%u\n", __func__, txerr, rxerr); cdev->can.state = CAN_STATE_BUS_OFF; cdev->can.can_stats.bus_off++; @@ -642,10 +693,15 @@ static void phytium_can_err_interrupt(struct net_device *ndev, u32 isr) if (skb) cf->can_id |= CAN_ERR_BUSOFF; } else if ((isr & INTR_PEIS) == INTR_PEIS) { - netdev_dbg(ndev, "%s: txerr :%u rxerr :%u\n", + netdev_dbg(ndev, "error_passive %s: txerr :%u rxerr :%u\n", __func__, txerr, rxerr); cdev->can.state = CAN_STATE_ERROR_PASSIVE; cdev->can.can_stats.error_passive++; + /* Clear interrupt condition */ + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_PEIC); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_PWIC); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_TEIC); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_EIC); if (skb) { cf->can_id |= CAN_ERR_CRTL; cf->data[1] = (rxerr > 127) ? @@ -655,10 +711,13 @@ static void phytium_can_err_interrupt(struct net_device *ndev, u32 isr) cf->data[7] = rxerr; } } else if (isr & INTR_PWIS) { - netdev_dbg(ndev, "%s: txerr :%u rxerr :%u\n", + netdev_dbg(ndev, "error_warning %s: txerr :%u rxerr :%u\n", __func__, txerr, rxerr); cdev->can.state = CAN_STATE_ERROR_WARNING; cdev->can.can_stats.error_warning++; + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_PWIC); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_TEIC); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_EIC); if (skb) { cf->can_id |= CAN_ERR_CRTL; cf->data[1] |= (txerr > rxerr) ? @@ -709,6 +768,7 @@ static irqreturn_t phytium_can_isr(int irq, void *dev_id) if (!isr) return IRQ_NONE; + spin_lock(&cdev->lock); /* Check for FIFO full interrupt and alarm */ if ((isr & INTR_RFIS)) { netdev_dbg(dev, "rx_fifo is full!.\n"); @@ -718,14 +778,22 @@ static irqreturn_t phytium_can_isr(int irq, void *dev_id) napi_schedule(&cdev->napi); } + /* Check for FIFO empty interrupt and alarm */ + if ((isr & INTR_TFIS)) { + netdev_dbg(dev, "tx_fifo is empty!.\n"); + isr &= (~INTR_TFIS); + phytium_can_clr_reg_bits(cdev, CAN_INTR, INTR_TFIE); + phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_TFIC); + } + /* Check for the type of error interrupt and Processing it */ - if (isr & (INTR_EIS | INTR_RFIS | INTR_BOIS | INTR_PEIS)) { - phytium_can_clr_reg_bits(cdev, CAN_INTR, (INTR_EIE - | INTR_RFIE | INTR_BOIE | INTR_PEIE)); + if (isr & (INTR_EIS | INTR_RFIS | INTR_BOIS | INTR_PWIS | INTR_PEIS)) { + phytium_can_clr_reg_bits(cdev, CAN_INTR, (INTR_EIE | INTR_RFIE | + INTR_BOIE | INTR_PWIE | INTR_PEIE)); phytium_can_err_interrupt(dev, isr); - phytium_can_set_reg_bits(cdev, CAN_INTR, (INTR_EIC - | INTR_RFIC | INTR_BOIC | INTR_PEIC)); - phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_EN_MASK); + phytium_can_set_reg_bits(cdev, CAN_INTR, (INTR_EIC | INTR_RFIC | + INTR_BOIC | INTR_PWIC | INTR_PEIC)); + spin_unlock(&cdev->lock); return IRQ_HANDLED; } @@ -742,7 +810,7 @@ static irqreturn_t phytium_can_isr(int irq, void *dev_id) phytium_can_set_reg_bits(cdev, CAN_INTR, INTR_REIC); napi_schedule(&cdev->napi); } - + spin_unlock(&cdev->lock); return IRQ_HANDLED; } @@ -876,6 +944,8 @@ static void phytium_can_stop(struct net_device *dev) ctrl &= ~(CTRL_XFER | CTRL_TXREQ); phytium_can_write(cdev, CAN_CTRL, ctrl); + del_timer(&cdev->timer); + /* Set the state as STOPPED */ cdev->can.state = CAN_STATE_STOPPED; } @@ -917,10 +987,12 @@ static int phytium_can_open(struct net_device *dev) struct phytium_can_dev *cdev = netdev_priv(dev); int ret; - /* Start clock */ - ret = pm_runtime_resume(cdev->dev); - if (ret) + ret = pm_runtime_get_sync(cdev->dev); + if (ret < 0) { + netdev_err(dev, "%s: pm_runtime_get failed(%d)\n", + __func__, ret); return ret; + } /* Open the CAN device */ ret = open_candev(dev); @@ -940,12 +1012,16 @@ static int phytium_can_open(struct net_device *dev) /* Start the controller */ phytium_can_start(dev); + netdev_dbg(dev, "%s is going on\n", __func__); + napi_enable(&cdev->napi); + cdev->is_stop_queue_flag = STOP_QUEUE_FALSE; netif_start_queue(dev); return 0; fail: + pm_runtime_put(cdev->dev); close_candev(dev); disable_clk: pm_runtime_put_sync(cdev->dev); @@ -1025,7 +1101,7 @@ static int phytium_can_dev_setup(struct phytium_can_dev *cdev) cdev->can.ctrlmode = CAN_CTRLMODE_FD; cdev->can.data_bittiming_const = cdev->bit_timing; } - + spin_lock_init(&cdev->lock); return 0; } @@ -1062,10 +1138,6 @@ int phytium_can_register(struct phytium_can_dev *cdev) { int ret; - ret = pm_runtime_resume(cdev->dev); - if (ret) - return ret; - ret = phytium_can_dev_setup(cdev); if (ret) goto fail; @@ -1077,6 +1149,10 @@ int phytium_can_register(struct phytium_can_dev *cdev) goto fail; } + cdev->is_tx_done = true; + cdev->is_need_stop_xmit = false; + timer_setup(&cdev->timer, phytium_can_tx_done_timeout, 0); + dev_info(cdev->dev, "%s device registered (irq=%d)\n", KBUILD_MODNAME, cdev->net->irq); diff --git a/drivers/net/can/phytium/phytium_can.h b/drivers/net/can/phytium/phytium_can.h index edb74df010ca3..802bb36a13116 100644 --- a/drivers/net/can/phytium/phytium_can.h +++ b/drivers/net/can/phytium/phytium_can.h @@ -23,6 +23,12 @@ #include #include +#define KEEP_CAN_FIFO_MIN_LEN 16 +#define KEEP_CANFD_FIFO_MIN_LEN 128 +#define CAN_FIFO_BYTE_LEN 256 +#define STOP_QUEUE_TRUE 1 +#define STOP_QUEUE_FALSE 0 + enum phytium_can_ip_type { PHYTIUM_CAN = 0, PHYTIUM_CANFD, @@ -35,9 +41,7 @@ struct phytium_can_devtype { struct phytium_can_dev { struct can_priv can; - unsigned int tx_head; - unsigned int tx_tail; - unsigned int tx_max; + struct napi_struct napi; struct net_device *net; struct device *dev; @@ -46,12 +50,16 @@ struct phytium_can_dev { struct sk_buff *tx_skb; const struct can_bittiming_const *bit_timing; - + spinlock_t lock; /*spinlock*/ int fdmode; u32 isr; u32 tx_fifo_depth; - + unsigned int is_stop_queue_flag; void __iomem *base; + + struct timer_list timer; /* xmit done timer */ + u32 is_tx_done; + u32 is_need_stop_xmit; }; struct phytium_can_dev *phytium_can_allocate_dev(struct device *dev, int sizeof_priv, diff --git a/drivers/net/can/phytium/phytium_can_pci.c b/drivers/net/can/phytium/phytium_can_pci.c index ac84b3b15c448..af7f606f432a0 100644 --- a/drivers/net/can/phytium/phytium_can_pci.c +++ b/drivers/net/can/phytium/phytium_can_pci.c @@ -81,12 +81,22 @@ static int phytium_can_pci_probe(struct pci_dev *pdev, const struct pci_device_i pci_set_drvdata(pdev, cdev->net); - pm_runtime_enable(cdev->dev); + if (!pm_runtime_enabled(cdev->dev)) + pm_runtime_enable(cdev->dev); + ret = pm_runtime_get_sync(cdev->dev); + if (ret < 0) { + netdev_err(cdev->net, "%s: pm_runtime_get failed(%d)\n", + __func__, ret); + goto err_pmdisable; + } ret = phytium_can_register(cdev); if (ret) goto err; return 0; + +err_pmdisable: + pm_runtime_disable(&pdev->dev); err: return ret; } @@ -96,6 +106,8 @@ static void phytium_can_pci_remove(struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata(pdev); struct phytium_can_dev *cdev = netdev_priv(dev); + pm_runtime_disable(cdev->dev); + phytium_can_unregister(cdev); phytium_can_free_dev(cdev->net); } diff --git a/drivers/net/can/phytium/phytium_can_platform.c b/drivers/net/can/phytium/phytium_can_platform.c index 63f89d721784f..852da788f9f75 100644 --- a/drivers/net/can/phytium/phytium_can_platform.c +++ b/drivers/net/can/phytium/phytium_can_platform.c @@ -126,14 +126,9 @@ static int phytium_can_plat_probe(struct platform_device *pdev) "mode-select", &str); if (!(strcmp(str, "canfd"))) devtype = &phytium_canfd_data; - else - devtype = &phytium_can_data; } cdev->tx_fifo_depth = tx_fifo_depth; - cdev->tx_head = 0; - cdev->tx_tail = 0; - cdev->tx_max = tx_fifo_depth; if (devtype->cantype == PHYTIUM_CANFD) cdev->fdmode = 1;