Merge tag 'linux-can-next-for-6.4-20230404-2' of git://git.kernel.org/pub/scm/linux...
authorJakub Kicinski <kuba@kernel.org>
Thu, 6 Apr 2023 00:06:02 +0000 (17:06 -0700)
committerJakub Kicinski <kuba@kernel.org>
Thu, 6 Apr 2023 00:06:02 +0000 (17:06 -0700)
Marc Kleine-Budde says:

====================
pull-request: can-next 2023-04-04-2

The first patch is by Oliver Hartkopp and makes the maximum pdu size
of the CAN ISOTP protocol configurable.

The following 5 patches are by Dario Binacchi and add support for the
bxCAN controller by ST.

Geert Uytterhoeven's patch for the rcar_canfd driver fixes a sparse
warning.

Peng Fan's patch adds an optional power-domains property to the
flexcan device tree binding.

Frank Jungclaus adds support for CAN_CTRLMODE_BERR_REPORTING to the
esd_usb driver.

The last patch is by Oliver Hartkopp and converts the USB IDs of the
kvaser_usb driver to hexadecimal values.

* tag 'linux-can-next-for-6.4-20230404-2' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next:
  kvaser_usb: convert USB IDs to hexadecimal values
  can: esd_usb: Add support for CAN_CTRLMODE_BERR_REPORTING
  dt-bindings: can: fsl,flexcan: add optional power-domains property
  can: rcar_canfd: rcar_canfd_probe(): fix plain integer in transceivers[] init
  can: bxcan: add support for ST bxCAN controller
  ARM: dts: stm32: add pin map for CAN controller on stm32f4
  ARM: dts: stm32: add CAN support on stm32f429
  dt-bindings: net: can: add STM32 bxcan DT bindings
  dt-bindings: arm: stm32: add compatible for syscon gcan node
  can: isotp: add module parameter for maximum pdu size
====================

Link: https://lore.kernel.org/r/20230404145908.1714400-1-mkl@pengutronix.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
13 files changed:
Documentation/devicetree/bindings/arm/stm32/st,stm32-syscon.yaml
Documentation/devicetree/bindings/net/can/fsl,flexcan.yaml
Documentation/devicetree/bindings/net/can/st,stm32-bxcan.yaml [new file with mode: 0644]
MAINTAINERS
arch/arm/boot/dts/stm32f4-pinctrl.dtsi
arch/arm/boot/dts/stm32f429.dtsi
drivers/net/can/Kconfig
drivers/net/can/Makefile
drivers/net/can/bxcan.c [new file with mode: 0644]
drivers/net/can/rcar/rcar_canfd.c
drivers/net/can/usb/esd_usb.c
drivers/net/can/usb/kvaser_usb/kvaser_usb_core.c
net/can/isotp.c

index b2b156c..ad8e51a 100644 (file)
@@ -20,6 +20,7 @@ properties:
               - st,stm32-syscfg
               - st,stm32-power-config
               - st,stm32-tamp
+              - st,stm32f4-gcan
           - const: syscon
       - items:
           - const: st,stm32-tamp
@@ -42,6 +43,7 @@ if:
       contains:
         enum:
           - st,stm32mp157-syscfg
+          - st,stm32f4-gcan
 then:
   required:
     - clocks
index 6e59bd2..4162469 100644 (file)
@@ -63,6 +63,9 @@ properties:
       boot loader. This property should only be used the used operating system
       doesn't support the clocks and clock-names property.
 
+  power-domains:
+    maxItems: 1
+
   xceiver-supply:
     description: Regulator that powers the CAN transceiver.
 
diff --git a/Documentation/devicetree/bindings/net/can/st,stm32-bxcan.yaml b/Documentation/devicetree/bindings/net/can/st,stm32-bxcan.yaml
new file mode 100644 (file)
index 0000000..769fa5c
--- /dev/null
@@ -0,0 +1,85 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/can/st,stm32-bxcan.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: STMicroelectronics bxCAN controller
+
+description: STMicroelectronics BxCAN controller for CAN bus
+
+maintainers:
+  - Dario Binacchi <dario.binacchi@amarulasolutions.com>
+
+allOf:
+  - $ref: can-controller.yaml#
+
+properties:
+  compatible:
+    enum:
+      - st,stm32f4-bxcan
+
+  st,can-primary:
+    description:
+      Primary and secondary mode of the bxCAN peripheral is only relevant
+      if the chip has two CAN peripherals. In that case they share some
+      of the required logic.
+      To avoid misunderstandings, it should be noted that ST documentation
+      uses the terms master/slave instead of primary/secondary.
+    type: boolean
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    items:
+      - description: transmit interrupt
+      - description: FIFO 0 receive interrupt
+      - description: FIFO 1 receive interrupt
+      - description: status change error interrupt
+
+  interrupt-names:
+    items:
+      - const: tx
+      - const: rx0
+      - const: rx1
+      - const: sce
+
+  resets:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  st,gcan:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    description:
+      The phandle to the gcan node which allows to access the 512-bytes
+      SRAM memory shared by the two bxCAN cells (CAN1 primary and CAN2
+      secondary) in dual CAN peripheral configuration.
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - resets
+  - clocks
+  - st,gcan
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/stm32fx-clock.h>
+    #include <dt-bindings/mfd/stm32f4-rcc.h>
+
+    can1: can@40006400 {
+        compatible = "st,stm32f4-bxcan";
+        reg = <0x40006400 0x200>;
+        interrupts = <19>, <20>, <21>, <22>;
+        interrupt-names = "tx", "rx0", "rx1", "sce";
+        resets = <&rcc STM32F4_APB1_RESET(CAN1)>;
+        clocks = <&rcc 0 STM32F4_APB1_CLOCK(CAN1)>;
+        st,can-primary;
+        st,gcan = <&gcan>;
+    };
index 7812f0e..cb50c3b 100644 (file)
@@ -4431,6 +4431,13 @@ S:       Maintained
 F:     drivers/scsi/BusLogic.*
 F:     drivers/scsi/FlashPoint.*
 
+BXCAN CAN NETWORK DRIVER
+M:     Dario Binacchi <dario.binacchi@amarulasolutions.com>
+L:     linux-can@vger.kernel.org
+S:     Maintained
+F:     Documentation/devicetree/bindings/net/can/st,stm32-bxcan.yaml
+F:     drivers/net/can/bxcan.c
+
 C-MEDIA CMI8788 DRIVER
 M:     Clemens Ladisch <clemens@ladisch.de>
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
index 4523c63..3bb812d 100644 (file)
                                        slew-rate = <2>;
                                };
                        };
+
+                       can1_pins_a: can1-0 {
+                               pins1 {
+                                       pinmux = <STM32_PINMUX('B', 9, AF9)>; /* CAN1_TX */
+                               };
+                               pins2 {
+                                       pinmux = <STM32_PINMUX('B', 8, AF9)>; /* CAN1_RX */
+                                       bias-pull-up;
+                               };
+                       };
+
+                       can2_pins_a: can2-0 {
+                               pins1 {
+                                       pinmux = <STM32_PINMUX('B', 13, AF9)>; /* CAN2_TX */
+                               };
+                               pins2 {
+                                       pinmux = <STM32_PINMUX('B', 5, AF9)>; /* CAN2_RX */
+                                       bias-pull-up;
+                               };
+                       };
+
+                       can2_pins_b: can2-1 {
+                               pins1 {
+                                       pinmux = <STM32_PINMUX('B', 13, AF9)>; /* CAN2_TX */
+                               };
+                               pins2 {
+                                       pinmux = <STM32_PINMUX('B', 12, AF9)>; /* CAN2_RX */
+                                       bias-pull-up;
+                               };
+                       };
                };
        };
 };
index c31ceb8..c9e05e3 100644 (file)
                        status = "disabled";
                };
 
+               can1: can@40006400 {
+                       compatible = "st,stm32f4-bxcan";
+                       reg = <0x40006400 0x200>;
+                       interrupts = <19>, <20>, <21>, <22>;
+                       interrupt-names = "tx", "rx0", "rx1", "sce";
+                       resets = <&rcc STM32F4_APB1_RESET(CAN1)>;
+                       clocks = <&rcc 0 STM32F4_APB1_CLOCK(CAN1)>;
+                       st,can-primary;
+                       st,gcan = <&gcan>;
+                       status = "disabled";
+               };
+
+               gcan: gcan@40006600 {
+                       compatible = "st,stm32f4-gcan", "syscon";
+                       reg = <0x40006600 0x200>;
+                       clocks = <&rcc 0 STM32F4_APB1_CLOCK(CAN1)>;
+               };
+
+               can2: can@40006800 {
+                       compatible = "st,stm32f4-bxcan";
+                       reg = <0x40006800 0x200>;
+                       interrupts = <63>, <64>, <65>, <66>;
+                       interrupt-names = "tx", "rx0", "rx1", "sce";
+                       resets = <&rcc STM32F4_APB1_RESET(CAN2)>;
+                       clocks = <&rcc 0 STM32F4_APB1_CLOCK(CAN2)>;
+                       st,gcan = <&gcan>;
+                       status = "disabled";
+               };
+
                dac: dac@40007400 {
                        compatible = "st,stm32f4-dac-core";
                        reg = <0x40007400 0x400>;
index cd34e8d..3ceccaf 100644 (file)
@@ -93,6 +93,18 @@ config CAN_AT91
          This is a driver for the SoC CAN controller in Atmel's AT91SAM9263
          and AT91SAM9X5 processors.
 
+config CAN_BXCAN
+       tristate "STM32 Basic Extended CAN (bxCAN) devices"
+       depends on OF || ARCH_STM32 || COMPILE_TEST
+       depends on HAS_IOMEM
+       select CAN_RX_OFFLOAD
+       help
+         Say yes here to build support for the STMicroelectronics STM32 basic
+         extended CAN Controller (bxCAN).
+
+         This driver can also be built as a module. If so, the module
+         will be called bxcan.
+
 config CAN_CAN327
        tristate "Serial / USB serial ELM327 based OBD-II Interfaces (can327)"
        depends on TTY
index 52b0f6e..ff8f762 100644 (file)
@@ -14,6 +14,7 @@ obj-y                         += usb/
 obj-y                          += softing/
 
 obj-$(CONFIG_CAN_AT91)         += at91_can.o
+obj-$(CONFIG_CAN_BXCAN)                += bxcan.o
 obj-$(CONFIG_CAN_CAN327)       += can327.o
 obj-$(CONFIG_CAN_CC770)                += cc770/
 obj-$(CONFIG_CAN_C_CAN)                += c_can/
diff --git a/drivers/net/can/bxcan.c b/drivers/net/can/bxcan.c
new file mode 100644 (file)
index 0000000..e26ccd4
--- /dev/null
@@ -0,0 +1,1098 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// bxcan.c - STM32 Basic Extended CAN controller driver
+//
+// Copyright (c) 2022 Dario Binacchi <dario.binacchi@amarulasolutions.com>
+//
+// NOTE: The ST documentation uses the terms master/slave instead of
+// primary/secondary.
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitfield.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+#include <linux/can/rx-offload.h>
+#include <linux/clk.h>
+#include <linux/ethtool.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define BXCAN_NAPI_WEIGHT 3
+#define BXCAN_TIMEOUT_US 10000
+
+#define BXCAN_RX_MB_NUM 2
+#define BXCAN_TX_MB_NUM 3
+
+/* Primary control register (MCR) bits */
+#define BXCAN_MCR_RESET BIT(15)
+#define BXCAN_MCR_TTCM BIT(7)
+#define BXCAN_MCR_ABOM BIT(6)
+#define BXCAN_MCR_AWUM BIT(5)
+#define BXCAN_MCR_NART BIT(4)
+#define BXCAN_MCR_RFLM BIT(3)
+#define BXCAN_MCR_TXFP BIT(2)
+#define BXCAN_MCR_SLEEP BIT(1)
+#define BXCAN_MCR_INRQ BIT(0)
+
+/* Primary status register (MSR) bits */
+#define BXCAN_MSR_ERRI BIT(2)
+#define BXCAN_MSR_SLAK BIT(1)
+#define BXCAN_MSR_INAK BIT(0)
+
+/* Transmit status register (TSR) bits */
+#define BXCAN_TSR_RQCP2 BIT(16)
+#define BXCAN_TSR_RQCP1 BIT(8)
+#define BXCAN_TSR_RQCP0 BIT(0)
+
+/* Receive FIFO 0 register (RF0R) bits */
+#define BXCAN_RF0R_RFOM0 BIT(5)
+#define BXCAN_RF0R_FMP0_MASK GENMASK(1, 0)
+
+/* Interrupt enable register (IER) bits */
+#define BXCAN_IER_SLKIE BIT(17)
+#define BXCAN_IER_WKUIE BIT(16)
+#define BXCAN_IER_ERRIE BIT(15)
+#define BXCAN_IER_LECIE BIT(11)
+#define BXCAN_IER_BOFIE BIT(10)
+#define BXCAN_IER_EPVIE BIT(9)
+#define BXCAN_IER_EWGIE BIT(8)
+#define BXCAN_IER_FOVIE1 BIT(6)
+#define BXCAN_IER_FFIE1 BIT(5)
+#define BXCAN_IER_FMPIE1 BIT(4)
+#define BXCAN_IER_FOVIE0 BIT(3)
+#define BXCAN_IER_FFIE0 BIT(2)
+#define BXCAN_IER_FMPIE0 BIT(1)
+#define BXCAN_IER_TMEIE BIT(0)
+
+/* Error status register (ESR) bits */
+#define BXCAN_ESR_REC_MASK GENMASK(31, 24)
+#define BXCAN_ESR_TEC_MASK GENMASK(23, 16)
+#define BXCAN_ESR_LEC_MASK GENMASK(6, 4)
+#define BXCAN_ESR_BOFF BIT(2)
+#define BXCAN_ESR_EPVF BIT(1)
+#define BXCAN_ESR_EWGF BIT(0)
+
+/* Bit timing register (BTR) bits */
+#define BXCAN_BTR_SILM BIT(31)
+#define BXCAN_BTR_LBKM BIT(30)
+#define BXCAN_BTR_SJW_MASK GENMASK(25, 24)
+#define BXCAN_BTR_TS2_MASK GENMASK(22, 20)
+#define BXCAN_BTR_TS1_MASK GENMASK(19, 16)
+#define BXCAN_BTR_BRP_MASK GENMASK(9, 0)
+
+/* TX mailbox identifier register (TIxR, x = 0..2) bits */
+#define BXCAN_TIxR_STID_MASK GENMASK(31, 21)
+#define BXCAN_TIxR_EXID_MASK GENMASK(31, 3)
+#define BXCAN_TIxR_IDE BIT(2)
+#define BXCAN_TIxR_RTR BIT(1)
+#define BXCAN_TIxR_TXRQ BIT(0)
+
+/* TX mailbox data length and time stamp register (TDTxR, x = 0..2 bits */
+#define BXCAN_TDTxR_DLC_MASK GENMASK(3, 0)
+
+/* RX FIFO mailbox identifier register (RIxR, x = 0..1 */
+#define BXCAN_RIxR_STID_MASK GENMASK(31, 21)
+#define BXCAN_RIxR_EXID_MASK GENMASK(31, 3)
+#define BXCAN_RIxR_IDE BIT(2)
+#define BXCAN_RIxR_RTR BIT(1)
+
+/* RX FIFO mailbox data length and timestamp register (RDTxR, x = 0..1) bits */
+#define BXCAN_RDTxR_TIME_MASK GENMASK(31, 16)
+#define BXCAN_RDTxR_DLC_MASK GENMASK(3, 0)
+
+#define BXCAN_FMR_REG 0x00
+#define BXCAN_FM1R_REG 0x04
+#define BXCAN_FS1R_REG 0x0c
+#define BXCAN_FFA1R_REG 0x14
+#define BXCAN_FA1R_REG 0x1c
+#define BXCAN_FiR1_REG(b) (0x40 + (b) * 8)
+#define BXCAN_FiR2_REG(b) (0x44 + (b) * 8)
+
+#define BXCAN_FILTER_ID(primary) (primary ? 0 : 14)
+
+/* Filter primary register (FMR) bits */
+#define BXCAN_FMR_CANSB_MASK GENMASK(13, 8)
+#define BXCAN_FMR_FINIT BIT(0)
+
+enum bxcan_lec_code {
+       BXCAN_LEC_NO_ERROR = 0,
+       BXCAN_LEC_STUFF_ERROR,
+       BXCAN_LEC_FORM_ERROR,
+       BXCAN_LEC_ACK_ERROR,
+       BXCAN_LEC_BIT1_ERROR,
+       BXCAN_LEC_BIT0_ERROR,
+       BXCAN_LEC_CRC_ERROR,
+       BXCAN_LEC_UNUSED
+};
+
+/* Structure of the message buffer */
+struct bxcan_mb {
+       u32 id;                 /* can identifier */
+       u32 dlc;                /* data length control and timestamp */
+       u32 data[2];            /* data */
+};
+
+/* Structure of the hardware registers */
+struct bxcan_regs {
+       u32 mcr;                        /* 0x00 - primary control */
+       u32 msr;                        /* 0x04 - primary status */
+       u32 tsr;                        /* 0x08 - transmit status */
+       u32 rf0r;                       /* 0x0c - FIFO 0 */
+       u32 rf1r;                       /* 0x10 - FIFO 1 */
+       u32 ier;                        /* 0x14 - interrupt enable */
+       u32 esr;                        /* 0x18 - error status */
+       u32 btr;                        /* 0x1c - bit timing*/
+       u32 reserved0[88];              /* 0x20 */
+       struct bxcan_mb tx_mb[BXCAN_TX_MB_NUM]; /* 0x180 - tx mailbox */
+       struct bxcan_mb rx_mb[BXCAN_RX_MB_NUM]; /* 0x1b0 - rx mailbox */
+};
+
+struct bxcan_priv {
+       struct can_priv can;
+       struct can_rx_offload offload;
+       struct device *dev;
+       struct net_device *ndev;
+
+       struct bxcan_regs __iomem *regs;
+       struct regmap *gcan;
+       int tx_irq;
+       int sce_irq;
+       bool primary;
+       struct clk *clk;
+       spinlock_t rmw_lock;    /* lock for read-modify-write operations */
+       unsigned int tx_head;
+       unsigned int tx_tail;
+       u32 timestamp;
+};
+
+static const struct can_bittiming_const bxcan_bittiming_const = {
+       .name = KBUILD_MODNAME,
+       .tseg1_min = 1,
+       .tseg1_max = 16,
+       .tseg2_min = 1,
+       .tseg2_max = 8,
+       .sjw_max = 4,
+       .brp_min = 1,
+       .brp_max = 1024,
+       .brp_inc = 1,
+};
+
+static inline void bxcan_rmw(struct bxcan_priv *priv, void __iomem *addr,
+                            u32 clear, u32 set)
+{
+       unsigned long flags;
+       u32 old, val;
+
+       spin_lock_irqsave(&priv->rmw_lock, flags);
+       old = readl(addr);
+       val = (old & ~clear) | set;
+       if (val != old)
+               writel(val, addr);
+
+       spin_unlock_irqrestore(&priv->rmw_lock, flags);
+}
+
+static void bxcan_disable_filters(struct bxcan_priv *priv, bool primary)
+{
+       unsigned int fid = BXCAN_FILTER_ID(primary);
+       u32 fmask = BIT(fid);
+
+       regmap_update_bits(priv->gcan, BXCAN_FA1R_REG, fmask, 0);
+}
+
+static void bxcan_enable_filters(struct bxcan_priv *priv, bool primary)
+{
+       unsigned int fid = BXCAN_FILTER_ID(primary);
+       u32 fmask = BIT(fid);
+
+       /* Filter settings:
+        *
+        * Accept all messages.
+        * Assign filter 0 to CAN1 and filter 14 to CAN2 in identifier
+        * mask mode with 32 bits width.
+        */
+
+       /* Enter filter initialization mode and assing filters to CAN
+        * controllers.
+        */
+       regmap_update_bits(priv->gcan, BXCAN_FMR_REG,
+                          BXCAN_FMR_CANSB_MASK | BXCAN_FMR_FINIT,
+                          FIELD_PREP(BXCAN_FMR_CANSB_MASK, 14) |
+                          BXCAN_FMR_FINIT);
+
+       /* Deactivate filter */
+       regmap_update_bits(priv->gcan, BXCAN_FA1R_REG, fmask, 0);
+
+       /* Two 32-bit registers in identifier mask mode */
+       regmap_update_bits(priv->gcan, BXCAN_FM1R_REG, fmask, 0);
+
+       /* Single 32-bit scale configuration */
+       regmap_update_bits(priv->gcan, BXCAN_FS1R_REG, fmask, fmask);
+
+       /* Assign filter to FIFO 0 */
+       regmap_update_bits(priv->gcan, BXCAN_FFA1R_REG, fmask, 0);
+
+       /* Accept all messages */
+       regmap_write(priv->gcan, BXCAN_FiR1_REG(fid), 0);
+       regmap_write(priv->gcan, BXCAN_FiR2_REG(fid), 0);
+
+       /* Activate filter */
+       regmap_update_bits(priv->gcan, BXCAN_FA1R_REG, fmask, fmask);
+
+       /* Exit filter initialization mode */
+       regmap_update_bits(priv->gcan, BXCAN_FMR_REG, BXCAN_FMR_FINIT, 0);
+}
+
+static inline u8 bxcan_get_tx_head(const struct bxcan_priv *priv)
+{
+       return priv->tx_head % BXCAN_TX_MB_NUM;
+}
+
+static inline u8 bxcan_get_tx_tail(const struct bxcan_priv *priv)
+{
+       return priv->tx_tail % BXCAN_TX_MB_NUM;
+}
+
+static inline u8 bxcan_get_tx_free(const struct bxcan_priv *priv)
+{
+       return BXCAN_TX_MB_NUM - (priv->tx_head - priv->tx_tail);
+}
+
+static bool bxcan_tx_busy(const struct bxcan_priv *priv)
+{
+       if (bxcan_get_tx_free(priv) > 0)
+               return false;
+
+       netif_stop_queue(priv->ndev);
+
+       /* Memory barrier before checking tx_free (head and tail) */
+       smp_mb();
+
+       if (bxcan_get_tx_free(priv) == 0) {
+               netdev_dbg(priv->ndev,
+                          "Stopping tx-queue (tx_head=0x%08x, tx_tail=0x%08x, len=%d).\n",
+                          priv->tx_head, priv->tx_tail,
+                          priv->tx_head - priv->tx_tail);
+
+               return true;
+       }
+
+       netif_start_queue(priv->ndev);
+
+       return false;
+}
+
+static int bxcan_chip_softreset(struct bxcan_priv *priv)
+{
+       struct bxcan_regs __iomem *regs = priv->regs;
+       u32 value;
+
+       bxcan_rmw(priv, &regs->mcr, 0, BXCAN_MCR_RESET);
+       return readx_poll_timeout(readl, &regs->msr, value,
+                                 value & BXCAN_MSR_SLAK, BXCAN_TIMEOUT_US,
+                                 USEC_PER_SEC);
+}
+
+static int bxcan_enter_init_mode(struct bxcan_priv *priv)
+{
+       struct bxcan_regs __iomem *regs = priv->regs;
+       u32 value;
+
+       bxcan_rmw(priv, &regs->mcr, 0, BXCAN_MCR_INRQ);
+       return readx_poll_timeout(readl, &regs->msr, value,
+                                 value & BXCAN_MSR_INAK, BXCAN_TIMEOUT_US,
+                                 USEC_PER_SEC);
+}
+
+static int bxcan_leave_init_mode(struct bxcan_priv *priv)
+{
+       struct bxcan_regs __iomem *regs = priv->regs;
+       u32 value;
+
+       bxcan_rmw(priv, &regs->mcr, BXCAN_MCR_INRQ, 0);
+       return readx_poll_timeout(readl, &regs->msr, value,
+                                 !(value & BXCAN_MSR_INAK), BXCAN_TIMEOUT_US,
+                                 USEC_PER_SEC);
+}
+
+static int bxcan_enter_sleep_mode(struct bxcan_priv *priv)
+{
+       struct bxcan_regs __iomem *regs = priv->regs;
+       u32 value;
+
+       bxcan_rmw(priv, &regs->mcr, 0, BXCAN_MCR_SLEEP);
+       return readx_poll_timeout(readl, &regs->msr, value,
+                                 value & BXCAN_MSR_SLAK, BXCAN_TIMEOUT_US,
+                                 USEC_PER_SEC);
+}
+
+static int bxcan_leave_sleep_mode(struct bxcan_priv *priv)
+{
+       struct bxcan_regs __iomem *regs = priv->regs;
+       u32 value;
+
+       bxcan_rmw(priv, &regs->mcr, BXCAN_MCR_SLEEP, 0);
+       return readx_poll_timeout(readl, &regs->msr, value,
+                                 !(value & BXCAN_MSR_SLAK), BXCAN_TIMEOUT_US,
+                                 USEC_PER_SEC);
+}
+
+static inline
+struct bxcan_priv *rx_offload_to_priv(struct can_rx_offload *offload)
+{
+       return container_of(offload, struct bxcan_priv, offload);
+}
+
+static struct sk_buff *bxcan_mailbox_read(struct can_rx_offload *offload,
+                                         unsigned int mbxno, u32 *timestamp,
+                                         bool drop)
+{
+       struct bxcan_priv *priv = rx_offload_to_priv(offload);
+       struct bxcan_regs __iomem *regs = priv->regs;
+       struct bxcan_mb __iomem *mb_regs = &regs->rx_mb[0];
+       struct sk_buff *skb = NULL;
+       struct can_frame *cf;
+       u32 rf0r, id, dlc;
+
+       rf0r = readl(&regs->rf0r);
+       if (unlikely(drop)) {
+               skb = ERR_PTR(-ENOBUFS);
+               goto mark_as_read;
+       }
+
+       if (!(rf0r & BXCAN_RF0R_FMP0_MASK))
+               goto mark_as_read;
+
+       skb = alloc_can_skb(offload->dev, &cf);
+       if (unlikely(!skb)) {
+               skb = ERR_PTR(-ENOMEM);
+               goto mark_as_read;
+       }
+
+       id = readl(&mb_regs->id);
+       if (id & BXCAN_RIxR_IDE)
+               cf->can_id = FIELD_GET(BXCAN_RIxR_EXID_MASK, id) | CAN_EFF_FLAG;
+       else
+               cf->can_id = FIELD_GET(BXCAN_RIxR_STID_MASK, id) & CAN_SFF_MASK;
+
+       dlc = readl(&mb_regs->dlc);
+       priv->timestamp = FIELD_GET(BXCAN_RDTxR_TIME_MASK, dlc);
+       cf->len = can_cc_dlc2len(FIELD_GET(BXCAN_RDTxR_DLC_MASK, dlc));
+
+       if (id & BXCAN_RIxR_RTR) {
+               cf->can_id |= CAN_RTR_FLAG;
+       } else {
+               int i, j;
+
+               for (i = 0, j = 0; i < cf->len; i += 4, j++)
+                       *(u32 *)(cf->data + i) = readl(&mb_regs->data[j]);
+       }
+
+ mark_as_read:
+       rf0r |= BXCAN_RF0R_RFOM0;
+       writel(rf0r, &regs->rf0r);
+       return skb;
+}
+
+static irqreturn_t bxcan_rx_isr(int irq, void *dev_id)
+{
+       struct net_device *ndev = dev_id;
+       struct bxcan_priv *priv = netdev_priv(ndev);
+       struct bxcan_regs __iomem *regs = priv->regs;
+       u32 rf0r;
+
+       rf0r = readl(&regs->rf0r);
+       if (!(rf0r & BXCAN_RF0R_FMP0_MASK))
+               return IRQ_NONE;
+
+       can_rx_offload_irq_offload_fifo(&priv->offload);
+       can_rx_offload_irq_finish(&priv->offload);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t bxcan_tx_isr(int irq, void *dev_id)
+{
+       struct net_device *ndev = dev_id;
+       struct bxcan_priv *priv = netdev_priv(ndev);
+       struct bxcan_regs __iomem *regs = priv->regs;
+       struct net_device_stats *stats = &ndev->stats;
+       u32 tsr, rqcp_bit;
+       int idx;
+
+       tsr = readl(&regs->tsr);
+       if (!(tsr & (BXCAN_TSR_RQCP0 | BXCAN_TSR_RQCP1 | BXCAN_TSR_RQCP2)))
+               return IRQ_NONE;
+
+       while (priv->tx_head - priv->tx_tail > 0) {
+               idx = bxcan_get_tx_tail(priv);
+               rqcp_bit = BXCAN_TSR_RQCP0 << (idx << 3);
+               if (!(tsr & rqcp_bit))
+                       break;
+
+               stats->tx_packets++;
+               stats->tx_bytes += can_get_echo_skb(ndev, idx, NULL);
+               priv->tx_tail++;
+       }
+
+       writel(tsr, &regs->tsr);
+
+       if (bxcan_get_tx_free(priv)) {
+               /* Make sure that anybody stopping the queue after
+                * this sees the new tx_ring->tail.
+                */
+               smp_mb();
+               netif_wake_queue(ndev);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static void bxcan_handle_state_change(struct net_device *ndev, u32 esr)
+{
+       struct bxcan_priv *priv = netdev_priv(ndev);
+       enum can_state new_state = priv->can.state;
+       struct can_berr_counter bec;
+       enum can_state rx_state, tx_state;
+       struct sk_buff *skb;
+       struct can_frame *cf;
+
+       /* Early exit if no error flag is set */
+       if (!(esr & (BXCAN_ESR_EWGF | BXCAN_ESR_EPVF | BXCAN_ESR_BOFF)))
+               return;
+
+       bec.txerr = FIELD_GET(BXCAN_ESR_TEC_MASK, esr);
+       bec.rxerr = FIELD_GET(BXCAN_ESR_REC_MASK, esr);
+
+       if (esr & BXCAN_ESR_BOFF)
+               new_state = CAN_STATE_BUS_OFF;
+       else if (esr & BXCAN_ESR_EPVF)
+               new_state = CAN_STATE_ERROR_PASSIVE;
+       else if (esr & BXCAN_ESR_EWGF)
+               new_state = CAN_STATE_ERROR_WARNING;
+
+       /* state hasn't changed */
+       if (unlikely(new_state == priv->can.state))
+               return;
+
+       skb = alloc_can_err_skb(ndev, &cf);
+
+       tx_state = bec.txerr >= bec.rxerr ? new_state : 0;
+       rx_state = bec.txerr <= bec.rxerr ? new_state : 0;
+       can_change_state(ndev, cf, tx_state, rx_state);
+
+       if (new_state == CAN_STATE_BUS_OFF) {
+               can_bus_off(ndev);
+       } else if (skb) {
+               cf->can_id |= CAN_ERR_CNT;
+               cf->data[6] = bec.txerr;
+               cf->data[7] = bec.rxerr;
+       }
+
+       if (skb) {
+               int err;
+
+               err = can_rx_offload_queue_timestamp(&priv->offload, skb,
+                                                    priv->timestamp);
+               if (err)
+                       ndev->stats.rx_fifo_errors++;
+       }
+}
+
+static void bxcan_handle_bus_err(struct net_device *ndev, u32 esr)
+{
+       struct bxcan_priv *priv = netdev_priv(ndev);
+       enum bxcan_lec_code lec_code;
+       struct can_frame *cf;
+       struct sk_buff *skb;
+
+       lec_code = FIELD_GET(BXCAN_ESR_LEC_MASK, esr);
+
+       /* Early exit if no lec update or no error.
+        * No lec update means that no CAN bus event has been detected
+        * since CPU wrote BXCAN_LEC_UNUSED value to status reg.
+        */
+       if (lec_code == BXCAN_LEC_UNUSED || lec_code == BXCAN_LEC_NO_ERROR)
+               return;
+
+       /* Common for all type of bus errors */
+       priv->can.can_stats.bus_error++;
+
+       /* Propagate the error condition to the CAN stack */
+       skb = alloc_can_err_skb(ndev, &cf);
+       if (skb)
+               cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+       switch (lec_code) {
+       case BXCAN_LEC_STUFF_ERROR:
+               netdev_dbg(ndev, "Stuff error\n");
+               ndev->stats.rx_errors++;
+               if (skb)
+                       cf->data[2] |= CAN_ERR_PROT_STUFF;
+               break;
+
+       case BXCAN_LEC_FORM_ERROR:
+               netdev_dbg(ndev, "Form error\n");
+               ndev->stats.rx_errors++;
+               if (skb)
+                       cf->data[2] |= CAN_ERR_PROT_FORM;
+               break;
+
+       case BXCAN_LEC_ACK_ERROR:
+               netdev_dbg(ndev, "Ack error\n");
+               ndev->stats.tx_errors++;
+               if (skb) {
+                       cf->can_id |= CAN_ERR_ACK;
+                       cf->data[3] = CAN_ERR_PROT_LOC_ACK;
+               }
+               break;
+
+       case BXCAN_LEC_BIT1_ERROR:
+               netdev_dbg(ndev, "Bit error (recessive)\n");
+               ndev->stats.tx_errors++;
+               if (skb)
+                       cf->data[2] |= CAN_ERR_PROT_BIT1;
+               break;
+
+       case BXCAN_LEC_BIT0_ERROR:
+               netdev_dbg(ndev, "Bit error (dominant)\n");
+               ndev->stats.tx_errors++;
+               if (skb)
+                       cf->data[2] |= CAN_ERR_PROT_BIT0;
+               break;
+
+       case BXCAN_LEC_CRC_ERROR:
+               netdev_dbg(ndev, "CRC error\n");
+               ndev->stats.rx_errors++;
+               if (skb) {
+                       cf->data[2] |= CAN_ERR_PROT_BIT;
+                       cf->data[3] = CAN_ERR_PROT_LOC_CRC_SEQ;
+               }
+               break;
+
+       default:
+               break;
+       }
+
+       if (skb) {
+               int err;
+
+               err = can_rx_offload_queue_timestamp(&priv->offload, skb,
+                                                    priv->timestamp);
+               if (err)
+                       ndev->stats.rx_fifo_errors++;
+       }
+}
+
+static irqreturn_t bxcan_state_change_isr(int irq, void *dev_id)
+{
+       struct net_device *ndev = dev_id;
+       struct bxcan_priv *priv = netdev_priv(ndev);
+       struct bxcan_regs __iomem *regs = priv->regs;
+       u32 msr, esr;
+
+       msr = readl(&regs->msr);
+       if (!(msr & BXCAN_MSR_ERRI))
+               return IRQ_NONE;
+
+       esr = readl(&regs->esr);
+       bxcan_handle_state_change(ndev, esr);
+
+       if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
+               bxcan_handle_bus_err(ndev, esr);
+
+       msr |= BXCAN_MSR_ERRI;
+       writel(msr, &regs->msr);
+       can_rx_offload_irq_finish(&priv->offload);
+
+       return IRQ_HANDLED;
+}
+
+static int bxcan_chip_start(struct net_device *ndev)
+{
+       struct bxcan_priv *priv = netdev_priv(ndev);
+       struct bxcan_regs __iomem *regs = priv->regs;
+       struct can_bittiming *bt = &priv->can.bittiming;
+       u32 clr, set;
+       int err;
+
+       err = bxcan_chip_softreset(priv);
+       if (err) {
+               netdev_err(ndev, "failed to reset chip, error %pe\n",
+                          ERR_PTR(err));
+               return err;
+       }
+
+       err = bxcan_leave_sleep_mode(priv);
+       if (err) {
+               netdev_err(ndev, "failed to leave sleep mode, error %pe\n",
+                          ERR_PTR(err));
+               goto failed_leave_sleep;
+       }
+
+       err = bxcan_enter_init_mode(priv);
+       if (err) {
+               netdev_err(ndev, "failed to enter init mode, error %pe\n",
+                          ERR_PTR(err));
+               goto failed_enter_init;
+       }
+
+       /* MCR
+        *
+        * select request order priority
+        * enable time triggered mode
+        * bus-off state left on sw request
+        * sleep mode left on sw request
+        * retransmit automatically on error
+        * do not lock RX FIFO on overrun
+        */
+       bxcan_rmw(priv, &regs->mcr,
+                 BXCAN_MCR_ABOM | BXCAN_MCR_AWUM | BXCAN_MCR_NART |
+                 BXCAN_MCR_RFLM, BXCAN_MCR_TTCM | BXCAN_MCR_TXFP);
+
+       /* Bit timing register settings */
+       set = FIELD_PREP(BXCAN_BTR_BRP_MASK, bt->brp - 1) |
+               FIELD_PREP(BXCAN_BTR_TS1_MASK, bt->phase_seg1 +
+                          bt->prop_seg - 1) |
+               FIELD_PREP(BXCAN_BTR_TS2_MASK, bt->phase_seg2 - 1) |
+               FIELD_PREP(BXCAN_BTR_SJW_MASK, bt->sjw - 1);
+
+       /* loopback + silent mode put the controller in test mode,
+        * useful for hot self-test
+        */
+       if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
+               set |= BXCAN_BTR_LBKM;
+
+       if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+               set |= BXCAN_BTR_SILM;
+
+       bxcan_rmw(priv, &regs->btr, BXCAN_BTR_SILM | BXCAN_BTR_LBKM |
+                 BXCAN_BTR_BRP_MASK | BXCAN_BTR_TS1_MASK | BXCAN_BTR_TS2_MASK |
+                 BXCAN_BTR_SJW_MASK, set);
+
+       bxcan_enable_filters(priv, priv->primary);
+
+       /* Clear all internal status */
+       priv->tx_head = 0;
+       priv->tx_tail = 0;
+
+       err = bxcan_leave_init_mode(priv);
+       if (err) {
+               netdev_err(ndev, "failed to leave init mode, error %pe\n",
+                          ERR_PTR(err));
+               goto failed_leave_init;
+       }
+
+       /* Set a `lec` value so that we can check for updates later */
+       bxcan_rmw(priv, &regs->esr, BXCAN_ESR_LEC_MASK,
+                 FIELD_PREP(BXCAN_ESR_LEC_MASK, BXCAN_LEC_UNUSED));
+
+       /* IER
+        *
+        * Enable interrupt for:
+        * bus-off
+        * passive error
+        * warning error
+        * last error code
+        * RX FIFO pending message
+        * TX mailbox empty
+        */
+       clr = BXCAN_IER_WKUIE | BXCAN_IER_SLKIE |  BXCAN_IER_FOVIE1 |
+               BXCAN_IER_FFIE1 | BXCAN_IER_FMPIE1 | BXCAN_IER_FOVIE0 |
+               BXCAN_IER_FFIE0;
+       set = BXCAN_IER_ERRIE | BXCAN_IER_BOFIE | BXCAN_IER_EPVIE |
+               BXCAN_IER_EWGIE | BXCAN_IER_FMPIE0 | BXCAN_IER_TMEIE;
+
+       if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
+               set |= BXCAN_IER_LECIE;
+       else
+               clr |= BXCAN_IER_LECIE;
+
+       bxcan_rmw(priv, &regs->ier, clr, set);
+
+       priv->can.state = CAN_STATE_ERROR_ACTIVE;
+       return 0;
+
+failed_leave_init:
+failed_enter_init:
+failed_leave_sleep:
+       bxcan_chip_softreset(priv);
+       return err;
+}
+
+static int bxcan_open(struct net_device *ndev)
+{
+       struct bxcan_priv *priv = netdev_priv(ndev);
+       int err;
+
+       err = clk_prepare_enable(priv->clk);
+       if (err) {
+               netdev_err(ndev, "failed to enable clock, error %pe\n",
+                          ERR_PTR(err));
+               return err;
+       }
+
+       err = open_candev(ndev);
+       if (err) {
+               netdev_err(ndev, "open_candev() failed, error %pe\n",
+                          ERR_PTR(err));
+               goto out_disable_clock;
+       }
+
+       can_rx_offload_enable(&priv->offload);
+       err = request_irq(ndev->irq, bxcan_rx_isr, IRQF_SHARED, ndev->name,
+                         ndev);
+       if (err) {
+               netdev_err(ndev, "failed to register rx irq(%d), error %pe\n",
+                          ndev->irq, ERR_PTR(err));
+               goto out_close_candev;
+       }
+
+       err = request_irq(priv->tx_irq, bxcan_tx_isr, IRQF_SHARED, ndev->name,
+                         ndev);
+       if (err) {
+               netdev_err(ndev, "failed to register tx irq(%d), error %pe\n",
+                          priv->tx_irq, ERR_PTR(err));
+               goto out_free_rx_irq;
+       }
+
+       err = request_irq(priv->sce_irq, bxcan_state_change_isr, IRQF_SHARED,
+                         ndev->name, ndev);
+       if (err) {
+               netdev_err(ndev, "failed to register sce irq(%d), error %pe\n",
+                          priv->sce_irq, ERR_PTR(err));
+               goto out_free_tx_irq;
+       }
+
+       err = bxcan_chip_start(ndev);
+       if (err)
+               goto out_free_sce_irq;
+
+       netif_start_queue(ndev);
+       return 0;
+
+out_free_sce_irq:
+       free_irq(priv->sce_irq, ndev);
+out_free_tx_irq:
+       free_irq(priv->tx_irq, ndev);
+out_free_rx_irq:
+       free_irq(ndev->irq, ndev);
+out_close_candev:
+       can_rx_offload_disable(&priv->offload);
+       close_candev(ndev);
+out_disable_clock:
+       clk_disable_unprepare(priv->clk);
+       return err;
+}
+
+static void bxcan_chip_stop(struct net_device *ndev)
+{
+       struct bxcan_priv *priv = netdev_priv(ndev);
+       struct bxcan_regs __iomem *regs = priv->regs;
+
+       /* disable all interrupts */
+       bxcan_rmw(priv, &regs->ier, BXCAN_IER_SLKIE | BXCAN_IER_WKUIE |
+                 BXCAN_IER_ERRIE | BXCAN_IER_LECIE | BXCAN_IER_BOFIE |
+                 BXCAN_IER_EPVIE | BXCAN_IER_EWGIE | BXCAN_IER_FOVIE1 |
+                 BXCAN_IER_FFIE1 | BXCAN_IER_FMPIE1 | BXCAN_IER_FOVIE0 |
+                 BXCAN_IER_FFIE0 | BXCAN_IER_FMPIE0 | BXCAN_IER_TMEIE, 0);
+       bxcan_disable_filters(priv, priv->primary);
+       bxcan_enter_sleep_mode(priv);
+       priv->can.state = CAN_STATE_STOPPED;
+}
+
+static int bxcan_stop(struct net_device *ndev)
+{
+       struct bxcan_priv *priv = netdev_priv(ndev);
+
+       netif_stop_queue(ndev);
+       bxcan_chip_stop(ndev);
+       free_irq(ndev->irq, ndev);
+       free_irq(priv->tx_irq, ndev);
+       free_irq(priv->sce_irq, ndev);
+       can_rx_offload_disable(&priv->offload);
+       close_candev(ndev);
+       clk_disable_unprepare(priv->clk);
+       return 0;
+}
+
+static netdev_tx_t bxcan_start_xmit(struct sk_buff *skb,
+                                   struct net_device *ndev)
+{
+       struct bxcan_priv *priv = netdev_priv(ndev);
+       struct can_frame *cf = (struct can_frame *)skb->data;
+       struct bxcan_regs __iomem *regs = priv->regs;
+       struct bxcan_mb __iomem *mb_regs;
+       unsigned int idx;
+       u32 id;
+       int i, j;
+
+       if (can_dropped_invalid_skb(ndev, skb))
+               return NETDEV_TX_OK;
+
+       if (bxcan_tx_busy(priv))
+               return NETDEV_TX_BUSY;
+
+       idx = bxcan_get_tx_head(priv);
+       priv->tx_head++;
+       if (bxcan_get_tx_free(priv) == 0)
+               netif_stop_queue(ndev);
+
+       mb_regs = &regs->tx_mb[idx];
+       if (cf->can_id & CAN_EFF_FLAG)
+               id = FIELD_PREP(BXCAN_TIxR_EXID_MASK, cf->can_id) |
+                       BXCAN_TIxR_IDE;
+       else
+               id = FIELD_PREP(BXCAN_TIxR_STID_MASK, cf->can_id);
+
+       if (cf->can_id & CAN_RTR_FLAG) { /* Remote transmission request */
+               id |= BXCAN_TIxR_RTR;
+       } else {
+               for (i = 0, j = 0; i < cf->len; i += 4, j++)
+                       writel(*(u32 *)(cf->data + i), &mb_regs->data[j]);
+       }
+
+       writel(FIELD_PREP(BXCAN_TDTxR_DLC_MASK, cf->len), &mb_regs->dlc);
+
+       can_put_echo_skb(skb, ndev, idx, 0);
+
+       /* Start transmission */
+       writel(id | BXCAN_TIxR_TXRQ, &mb_regs->id);
+
+       return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops bxcan_netdev_ops = {
+       .ndo_open = bxcan_open,
+       .ndo_stop = bxcan_stop,
+       .ndo_start_xmit = bxcan_start_xmit,
+       .ndo_change_mtu = can_change_mtu,
+};
+
+static const struct ethtool_ops bxcan_ethtool_ops = {
+       .get_ts_info = ethtool_op_get_ts_info,
+};
+
+static int bxcan_do_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+       int err;
+
+       switch (mode) {
+       case CAN_MODE_START:
+               err = bxcan_chip_start(ndev);
+               if (err)
+                       return err;
+
+               netif_wake_queue(ndev);
+               break;
+
+       default:
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+static int bxcan_get_berr_counter(const struct net_device *ndev,
+                                 struct can_berr_counter *bec)
+{
+       struct bxcan_priv *priv = netdev_priv(ndev);
+       struct bxcan_regs __iomem *regs = priv->regs;
+       u32 esr;
+       int err;
+
+       err = clk_prepare_enable(priv->clk);
+       if (err)
+               return err;
+
+       esr = readl(&regs->esr);
+       bec->txerr = FIELD_GET(BXCAN_ESR_TEC_MASK, esr);
+       bec->rxerr = FIELD_GET(BXCAN_ESR_REC_MASK, esr);
+       clk_disable_unprepare(priv->clk);
+       return 0;
+}
+
+static int bxcan_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct device *dev = &pdev->dev;
+       struct net_device *ndev;
+       struct bxcan_priv *priv;
+       struct clk *clk = NULL;
+       void __iomem *regs;
+       struct regmap *gcan;
+       bool primary;
+       int err, rx_irq, tx_irq, sce_irq;
+
+       regs = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(regs)) {
+               dev_err(dev, "failed to get base address\n");
+               return PTR_ERR(regs);
+       }
+
+       gcan = syscon_regmap_lookup_by_phandle(np, "st,gcan");
+       if (IS_ERR(gcan)) {
+               dev_err(dev, "failed to get shared memory base address\n");
+               return PTR_ERR(gcan);
+       }
+
+       primary = of_property_read_bool(np, "st,can-primary");
+       clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(clk)) {
+               dev_err(dev, "failed to get clock\n");
+               return PTR_ERR(clk);
+       }
+
+       rx_irq = platform_get_irq_byname(pdev, "rx0");
+       if (rx_irq < 0) {
+               dev_err(dev, "failed to get rx0 irq\n");
+               return rx_irq;
+       }
+
+       tx_irq = platform_get_irq_byname(pdev, "tx");
+       if (tx_irq < 0) {
+               dev_err(dev, "failed to get tx irq\n");
+               return tx_irq;
+       }
+
+       sce_irq = platform_get_irq_byname(pdev, "sce");
+       if (sce_irq < 0) {
+               dev_err(dev, "failed to get sce irq\n");
+               return sce_irq;
+       }
+
+       ndev = alloc_candev(sizeof(struct bxcan_priv), BXCAN_TX_MB_NUM);
+       if (!ndev) {
+               dev_err(dev, "alloc_candev() failed\n");
+               return -ENOMEM;
+       }
+
+       priv = netdev_priv(ndev);
+       platform_set_drvdata(pdev, ndev);
+       SET_NETDEV_DEV(ndev, dev);
+       ndev->netdev_ops = &bxcan_netdev_ops;
+       ndev->ethtool_ops = &bxcan_ethtool_ops;
+       ndev->irq = rx_irq;
+       ndev->flags |= IFF_ECHO;
+
+       priv->dev = dev;
+       priv->ndev = ndev;
+       priv->regs = regs;
+       priv->gcan = gcan;
+       priv->clk = clk;
+       priv->tx_irq = tx_irq;
+       priv->sce_irq = sce_irq;
+       priv->primary = primary;
+       priv->can.clock.freq = clk_get_rate(clk);
+       spin_lock_init(&priv->rmw_lock);
+       priv->tx_head = 0;
+       priv->tx_tail = 0;
+       priv->can.bittiming_const = &bxcan_bittiming_const;
+       priv->can.do_set_mode = bxcan_do_set_mode;
+       priv->can.do_get_berr_counter = bxcan_get_berr_counter;
+       priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK |
+               CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_BERR_REPORTING;
+
+       priv->offload.mailbox_read = bxcan_mailbox_read;
+       err = can_rx_offload_add_fifo(ndev, &priv->offload, BXCAN_NAPI_WEIGHT);
+       if (err) {
+               dev_err(dev, "failed to add FIFO rx_offload\n");
+               goto out_free_candev;
+       }
+
+       err = register_candev(ndev);
+       if (err) {
+               dev_err(dev, "failed to register netdev\n");
+               goto out_can_rx_offload_del;
+       }
+
+       dev_info(dev, "clk: %d Hz, IRQs: %d, %d, %d\n", priv->can.clock.freq,
+                tx_irq, rx_irq, sce_irq);
+       return 0;
+
+out_can_rx_offload_del:
+       can_rx_offload_del(&priv->offload);
+out_free_candev:
+       free_candev(ndev);
+       return err;
+}
+
+static int bxcan_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct bxcan_priv *priv = netdev_priv(ndev);
+
+       unregister_candev(ndev);
+       clk_disable_unprepare(priv->clk);
+       can_rx_offload_del(&priv->offload);
+       free_candev(ndev);
+       return 0;
+}
+
+static int __maybe_unused bxcan_suspend(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct bxcan_priv *priv = netdev_priv(ndev);
+
+       if (!netif_running(ndev))
+               return 0;
+
+       netif_stop_queue(ndev);
+       netif_device_detach(ndev);
+
+       bxcan_enter_sleep_mode(priv);
+       priv->can.state = CAN_STATE_SLEEPING;
+       clk_disable_unprepare(priv->clk);
+       return 0;
+}
+
+static int __maybe_unused bxcan_resume(struct device *dev)
+{
+       struct net_device *ndev = dev_get_drvdata(dev);
+       struct bxcan_priv *priv = netdev_priv(ndev);
+
+       if (!netif_running(ndev))
+               return 0;
+
+       clk_prepare_enable(priv->clk);
+       bxcan_leave_sleep_mode(priv);
+       priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+       netif_device_attach(ndev);
+       netif_start_queue(ndev);
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(bxcan_pm_ops, bxcan_suspend, bxcan_resume);
+
+static const struct of_device_id bxcan_of_match[] = {
+       {.compatible = "st,stm32f4-bxcan"},
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, bxcan_of_match);
+
+static struct platform_driver bxcan_driver = {
+       .driver = {
+               .name = KBUILD_MODNAME,
+               .pm = &bxcan_pm_ops,
+               .of_match_table = bxcan_of_match,
+       },
+       .probe = bxcan_probe,
+       .remove = bxcan_remove,
+};
+
+module_platform_driver(bxcan_driver);
+
+MODULE_AUTHOR("Dario Binacchi <dario.binacchi@amarulasolutions.com>");
+MODULE_DESCRIPTION("STMicroelectronics Basic Extended CAN controller driver");
+MODULE_LICENSE("GPL");
index 701311d..963c42f 100644 (file)
@@ -1848,7 +1848,7 @@ static void rcar_canfd_channel_remove(struct rcar_canfd_global *gpriv, u32 ch)
 
 static int rcar_canfd_probe(struct platform_device *pdev)
 {
-       struct phy *transceivers[RCANFD_NUM_CHANNELS] = { 0, };
+       struct phy *transceivers[RCANFD_NUM_CHANNELS] = { NULL, };
        const struct rcar_canfd_hw_info *info;
        struct device *dev = &pdev->dev;
        void __iomem *addr;
index e78bb46..d33bac3 100644 (file)
@@ -237,14 +237,23 @@ static void esd_usb_rx_event(struct esd_usb_net_priv *priv,
        if (id == ESD_EV_CAN_ERROR_EXT) {
                u8 state = msg->rx.ev_can_err_ext.status;
                u8 ecc = msg->rx.ev_can_err_ext.ecc;
-               u8 rxerr = msg->rx.ev_can_err_ext.rec;
-               u8 txerr = msg->rx.ev_can_err_ext.tec;
+
+               priv->bec.rxerr = msg->rx.ev_can_err_ext.rec;
+               priv->bec.txerr = msg->rx.ev_can_err_ext.tec;
 
                netdev_dbg(priv->netdev,
                           "CAN_ERR_EV_EXT: dlc=%#02x state=%02x ecc=%02x rec=%02x tec=%02x\n",
-                          msg->rx.dlc, state, ecc, rxerr, txerr);
+                          msg->rx.dlc, state, ecc,
+                          priv->bec.rxerr, priv->bec.txerr);
+
+               /* if berr-reporting is off, only pass through on state change ... */
+               if (!(priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) &&
+                   state == priv->old_state)
+                       return;
 
                skb = alloc_can_err_skb(priv->netdev, &cf);
+               if (!skb)
+                       stats->rx_dropped++;
 
                if (state != priv->old_state) {
                        enum can_state tx_state, rx_state;
@@ -265,14 +274,14 @@ static void esd_usb_rx_event(struct esd_usb_net_priv *priv,
                                break;
                        default:
                                new_state = CAN_STATE_ERROR_ACTIVE;
-                               txerr = 0;
-                               rxerr = 0;
+                               priv->bec.txerr = 0;
+                               priv->bec.rxerr = 0;
                                break;
                        }
 
                        if (new_state != priv->can.state) {
-                               tx_state = (txerr >= rxerr) ? new_state : 0;
-                               rx_state = (txerr <= rxerr) ? new_state : 0;
+                               tx_state = (priv->bec.txerr >= priv->bec.rxerr) ? new_state : 0;
+                               rx_state = (priv->bec.txerr <= priv->bec.rxerr) ? new_state : 0;
                                can_change_state(priv->netdev, cf,
                                                 tx_state, rx_state);
                        }
@@ -304,17 +313,12 @@ static void esd_usb_rx_event(struct esd_usb_net_priv *priv,
                        cf->data[3] = ecc & SJA1000_ECC_SEG;
                }
 
-               priv->bec.txerr = txerr;
-               priv->bec.rxerr = rxerr;
-
                if (skb) {
                        cf->can_id |= CAN_ERR_CNT;
-                       cf->data[6] = txerr;
-                       cf->data[7] = rxerr;
+                       cf->data[6] = priv->bec.txerr;
+                       cf->data[7] = priv->bec.rxerr;
 
                        netif_rx(skb);
-               } else {
-                       stats->rx_dropped++;
                }
        }
 }
@@ -1016,7 +1020,8 @@ static int esd_usb_probe_one_net(struct usb_interface *intf, int index)
 
        priv->can.state = CAN_STATE_STOPPED;
        priv->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY |
-               CAN_CTRLMODE_CC_LEN8_DLC;
+               CAN_CTRLMODE_CC_LEN8_DLC |
+               CAN_CTRLMODE_BERR_REPORTING;
 
        if (le16_to_cpu(dev->udev->descriptor.idProduct) ==
            USB_CANUSBM_PRODUCT_ID)
index d4c5356..7135ec8 100644 (file)
 #include "kvaser_usb.h"
 
 /* Kvaser USB vendor id. */
-#define KVASER_VENDOR_ID                       0x0bfd
+#define KVASER_VENDOR_ID 0x0bfd
 
 /* Kvaser Leaf USB devices product ids */
-#define USB_LEAF_DEVEL_PRODUCT_ID              10
-#define USB_LEAF_LITE_PRODUCT_ID               11
-#define USB_LEAF_PRO_PRODUCT_ID                        12
-#define USB_LEAF_SPRO_PRODUCT_ID               14
-#define USB_LEAF_PRO_LS_PRODUCT_ID             15
-#define USB_LEAF_PRO_SWC_PRODUCT_ID            16
-#define USB_LEAF_PRO_LIN_PRODUCT_ID            17
-#define USB_LEAF_SPRO_LS_PRODUCT_ID            18
-#define USB_LEAF_SPRO_SWC_PRODUCT_ID           19
-#define USB_MEMO2_DEVEL_PRODUCT_ID             22
-#define USB_MEMO2_HSHS_PRODUCT_ID              23
-#define USB_UPRO_HSHS_PRODUCT_ID               24
-#define USB_LEAF_LITE_GI_PRODUCT_ID            25
-#define USB_LEAF_PRO_OBDII_PRODUCT_ID          26
-#define USB_MEMO2_HSLS_PRODUCT_ID              27
-#define USB_LEAF_LITE_CH_PRODUCT_ID            28
-#define USB_BLACKBIRD_SPRO_PRODUCT_ID          29
-#define USB_OEM_MERCURY_PRODUCT_ID             34
-#define USB_OEM_LEAF_PRODUCT_ID                        35
-#define USB_CAN_R_PRODUCT_ID                   39
-#define USB_LEAF_LITE_V2_PRODUCT_ID            288
-#define USB_MINI_PCIE_HS_PRODUCT_ID            289
-#define USB_LEAF_LIGHT_HS_V2_OEM_PRODUCT_ID    290
-#define USB_USBCAN_LIGHT_2HS_PRODUCT_ID                291
-#define USB_MINI_PCIE_2HS_PRODUCT_ID           292
-#define USB_USBCAN_R_V2_PRODUCT_ID             294
-#define USB_LEAF_LIGHT_R_V2_PRODUCT_ID         295
-#define USB_LEAF_LIGHT_HS_V2_OEM2_PRODUCT_ID   296
+#define USB_LEAF_DEVEL_PRODUCT_ID 0x000a
+#define USB_LEAF_LITE_PRODUCT_ID 0x000b
+#define USB_LEAF_PRO_PRODUCT_ID 0x000c
+#define USB_LEAF_SPRO_PRODUCT_ID 0x000e
+#define USB_LEAF_PRO_LS_PRODUCT_ID 0x000f
+#define USB_LEAF_PRO_SWC_PRODUCT_ID 0x0010
+#define USB_LEAF_PRO_LIN_PRODUCT_ID 0x0011
+#define USB_LEAF_SPRO_LS_PRODUCT_ID 0x0012
+#define USB_LEAF_SPRO_SWC_PRODUCT_ID 0x0013
+#define USB_MEMO2_DEVEL_PRODUCT_ID 0x0016
+#define USB_MEMO2_HSHS_PRODUCT_ID 0x0017
+#define USB_UPRO_HSHS_PRODUCT_ID 0x0018
+#define USB_LEAF_LITE_GI_PRODUCT_ID 0x0019
+#define USB_LEAF_PRO_OBDII_PRODUCT_ID 0x001a
+#define USB_MEMO2_HSLS_PRODUCT_ID 0x001b
+#define USB_LEAF_LITE_CH_PRODUCT_ID 0x001c
+#define USB_BLACKBIRD_SPRO_PRODUCT_ID 0x001d
+#define USB_OEM_MERCURY_PRODUCT_ID 0x0022
+#define USB_OEM_LEAF_PRODUCT_ID 0x0023
+#define USB_CAN_R_PRODUCT_ID 0x0027
+#define USB_LEAF_LITE_V2_PRODUCT_ID 0x0120
+#define USB_MINI_PCIE_HS_PRODUCT_ID 0x0121
+#define USB_LEAF_LIGHT_HS_V2_OEM_PRODUCT_ID 0x0122
+#define USB_USBCAN_LIGHT_2HS_PRODUCT_ID 0x0123
+#define USB_MINI_PCIE_2HS_PRODUCT_ID 0x0124
+#define USB_USBCAN_R_V2_PRODUCT_ID 0x0126
+#define USB_LEAF_LIGHT_R_V2_PRODUCT_ID 0x0127
+#define USB_LEAF_LIGHT_HS_V2_OEM2_PRODUCT_ID 0x0128
 
 /* Kvaser USBCan-II devices product ids */
-#define USB_USBCAN_REVB_PRODUCT_ID             2
-#define USB_VCI2_PRODUCT_ID                    3
-#define USB_USBCAN2_PRODUCT_ID                 4
-#define USB_MEMORATOR_PRODUCT_ID               5
+#define USB_USBCAN_REVB_PRODUCT_ID 0x0002
+#define USB_VCI2_PRODUCT_ID 0x0003
+#define USB_USBCAN2_PRODUCT_ID 0x0004
+#define USB_MEMORATOR_PRODUCT_ID 0x0005
 
 /* Kvaser Minihydra USB devices product ids */
-#define USB_BLACKBIRD_V2_PRODUCT_ID            258
-#define USB_MEMO_PRO_5HS_PRODUCT_ID            260
-#define USB_USBCAN_PRO_5HS_PRODUCT_ID          261
-#define USB_USBCAN_LIGHT_4HS_PRODUCT_ID                262
-#define USB_LEAF_PRO_HS_V2_PRODUCT_ID          263
-#define USB_USBCAN_PRO_2HS_V2_PRODUCT_ID       264
-#define USB_MEMO_2HS_PRODUCT_ID                        265
-#define USB_MEMO_PRO_2HS_V2_PRODUCT_ID         266
-#define USB_HYBRID_2CANLIN_PRODUCT_ID          267
-#define USB_ATI_USBCAN_PRO_2HS_V2_PRODUCT_ID   268
-#define USB_ATI_MEMO_PRO_2HS_V2_PRODUCT_ID     269
-#define USB_HYBRID_PRO_2CANLIN_PRODUCT_ID      270
-#define USB_U100_PRODUCT_ID                    273
-#define USB_U100P_PRODUCT_ID                   274
-#define USB_U100S_PRODUCT_ID                   275
-#define USB_USBCAN_PRO_4HS_PRODUCT_ID          276
-#define USB_HYBRID_CANLIN_PRODUCT_ID           277
-#define USB_HYBRID_PRO_CANLIN_PRODUCT_ID       278
+#define USB_BLACKBIRD_V2_PRODUCT_ID 0x0102
+#define USB_MEMO_PRO_5HS_PRODUCT_ID 0x0104
+#define USB_USBCAN_PRO_5HS_PRODUCT_ID 0x0105
+#define USB_USBCAN_LIGHT_4HS_PRODUCT_ID 0x0106
+#define USB_LEAF_PRO_HS_V2_PRODUCT_ID 0x0107
+#define USB_USBCAN_PRO_2HS_V2_PRODUCT_ID 0x0108
+#define USB_MEMO_2HS_PRODUCT_ID 0x0109
+#define USB_MEMO_PRO_2HS_V2_PRODUCT_ID 0x010a
+#define USB_HYBRID_2CANLIN_PRODUCT_ID 0x010b
+#define USB_ATI_USBCAN_PRO_2HS_V2_PRODUCT_ID 0x010c
+#define USB_ATI_MEMO_PRO_2HS_V2_PRODUCT_ID 0x010d
+#define USB_HYBRID_PRO_2CANLIN_PRODUCT_ID 0x010e
+#define USB_U100_PRODUCT_ID 0x0111
+#define USB_U100P_PRODUCT_ID 0x0112
+#define USB_U100S_PRODUCT_ID 0x0113
+#define USB_USBCAN_PRO_4HS_PRODUCT_ID 0x0114
+#define USB_HYBRID_CANLIN_PRODUCT_ID 0x0115
+#define USB_HYBRID_PRO_CANLIN_PRODUCT_ID 0x0116
 
 static const struct kvaser_usb_driver_info kvaser_usb_driver_info_hydra = {
        .quirks = KVASER_USB_QUIRK_HAS_HARDWARE_TIMESTAMP,
index 9bc3448..f1ea917 100644 (file)
@@ -85,10 +85,21 @@ MODULE_ALIAS("can-proto-6");
 
 /* ISO 15765-2:2016 supports more than 4095 byte per ISO PDU as the FF_DL can
  * take full 32 bit values (4 Gbyte). We would need some good concept to handle
- * this between user space and kernel space. For now increase the static buffer
- * to something about 64 kbyte to be able to test this new functionality.
+ * this between user space and kernel space. For now set the static buffer to
+ * something about 8 kbyte to be able to test this new functionality.
  */
-#define MAX_MSG_LENGTH 66000
+#define DEFAULT_MAX_PDU_SIZE 8300
+
+/* maximum PDU size before ISO 15765-2:2016 extension was 4095 */
+#define MAX_12BIT_PDU_SIZE 4095
+
+/* limit the isotp pdu size from the optional module parameter to 1MByte */
+#define MAX_PDU_SIZE (1025 * 1024U)
+
+static unsigned int max_pdu_size __read_mostly = DEFAULT_MAX_PDU_SIZE;
+module_param(max_pdu_size, uint, 0444);
+MODULE_PARM_DESC(max_pdu_size, "maximum isotp pdu size (default "
+                __stringify(DEFAULT_MAX_PDU_SIZE) ")");
 
 /* N_PCI type values in bits 7-4 of N_PCI bytes */
 #define N_PCI_SF 0x00  /* single frame */
@@ -123,13 +134,15 @@ enum {
 };
 
 struct tpcon {
-       unsigned int idx;
+       u8 *buf;
+       unsigned int buflen;
        unsigned int len;
+       unsigned int idx;
        u32 state;
        u8 bs;
        u8 sn;
        u8 ll_dl;
-       u8 buf[MAX_MSG_LENGTH + 1];
+       u8 sbuf[DEFAULT_MAX_PDU_SIZE];
 };
 
 struct isotp_sock {
@@ -503,7 +516,17 @@ static int isotp_rcv_ff(struct sock *sk, struct canfd_frame *cf, int ae)
        if (so->rx.len + ae + off + ff_pci_sz < so->rx.ll_dl)
                return 1;
 
-       if (so->rx.len > MAX_MSG_LENGTH) {
+       /* PDU size > default => try max_pdu_size */
+       if (so->rx.len > so->rx.buflen && so->rx.buflen < max_pdu_size) {
+               u8 *newbuf = kmalloc(max_pdu_size, GFP_ATOMIC);
+
+               if (newbuf) {
+                       so->rx.buf = newbuf;
+                       so->rx.buflen = max_pdu_size;
+               }
+       }
+
+       if (so->rx.len > so->rx.buflen) {
                /* send FC frame with overflow status */
                isotp_send_fc(sk, ae, ISOTP_FC_OVFLW);
                return 1;
@@ -807,7 +830,7 @@ static void isotp_create_fframe(struct canfd_frame *cf, struct isotp_sock *so,
                cf->data[0] = so->opt.ext_address;
 
        /* create N_PCI bytes with 12/32 bit FF_DL data length */
-       if (so->tx.len > 4095) {
+       if (so->tx.len > MAX_12BIT_PDU_SIZE) {
                /* use 32 bit FF_DL notation */
                cf->data[ae] = N_PCI_FF;
                cf->data[ae + 1] = 0;
@@ -947,7 +970,17 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
                so->tx.state = ISOTP_SENDING;
        }
 
-       if (!size || size > MAX_MSG_LENGTH) {
+       /* PDU size > default => try max_pdu_size */
+       if (size > so->tx.buflen && so->tx.buflen < max_pdu_size) {
+               u8 *newbuf = kmalloc(max_pdu_size, GFP_KERNEL);
+
+               if (newbuf) {
+                       so->tx.buf = newbuf;
+                       so->tx.buflen = max_pdu_size;
+               }
+       }
+
+       if (!size || size > so->tx.buflen) {
                err = -EINVAL;
                goto err_out_drop;
        }
@@ -1195,6 +1228,12 @@ static int isotp_release(struct socket *sock)
        so->ifindex = 0;
        so->bound = 0;
 
+       if (so->rx.buf != so->rx.sbuf)
+               kfree(so->rx.buf);
+
+       if (so->tx.buf != so->tx.sbuf)
+               kfree(so->tx.buf);
+
        sock_orphan(sk);
        sock->sk = NULL;
 
@@ -1591,6 +1630,11 @@ static int isotp_init(struct sock *sk)
        so->rx.state = ISOTP_IDLE;
        so->tx.state = ISOTP_IDLE;
 
+       so->rx.buf = so->rx.sbuf;
+       so->tx.buf = so->tx.sbuf;
+       so->rx.buflen = ARRAY_SIZE(so->rx.sbuf);
+       so->tx.buflen = ARRAY_SIZE(so->tx.sbuf);
+
        hrtimer_init(&so->rxtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
        so->rxtimer.function = isotp_rx_timer_handler;
        hrtimer_init(&so->txtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
@@ -1658,7 +1702,10 @@ static __init int isotp_module_init(void)
 {
        int err;
 
-       pr_info("can: isotp protocol\n");
+       max_pdu_size = max_t(unsigned int, max_pdu_size, MAX_12BIT_PDU_SIZE);
+       max_pdu_size = min_t(unsigned int, max_pdu_size, MAX_PDU_SIZE);
+
+       pr_info("can: isotp protocol (max_pdu_size %d)\n", max_pdu_size);
 
        err = can_proto_register(&isotp_can_proto);
        if (err < 0)