All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V5 0/4] Microchip mcp25xxfd can controller driver
@ 2018-12-21  9:29 kernel
  2018-12-21  9:29 ` [PATCH V5 1/4] dt-binding: can: mcp25xxfd: document device tree bindings kernel
                   ` (4 more replies)
  0 siblings, 5 replies; 12+ messages in thread
From: kernel @ 2018-12-21  9:29 UTC (permalink / raw)
  To: linux-can, devicetree, Wolfgang Grandegger, Mark Kleine-Budde,
	Rob Herring, Mark Rutland
  Cc: Martin Sperl, Sukkin Pang, Gerhard Bertelsmann,
	Wilhelm Leichtfried, Thomas Kopp, Enrico Scholz, Brad Hanson,
	Teemu Keskinarkaus

From: Martin Sperl <kernel@martin.sperl.org>

This patchset adds a driver for the mcp25xxfd CanFD controller.

Most of the features of the controller are supported by this driver
release.

The controller includes a few features that the current core Can(FD)
implementation does not support: * Transmit Bandwidth Sharing bits
  - this waits for a configurable number of syncronization bits before
    atempting to transmit the next frame - for the moment controllable
    via a module parameter
* SID11 with CanFD frames
  - at this moment not supported by driver
* 3 transmit attempts in addition to ONE_SHOT
* transmitter delay compensation configurations
* micro second exact transmission and reception timestamps
  - used internally by driver
* variable number of tx-fifos
  - exposed via module parameter at this moment

The driver has not been heavily optimized yet so there are a few
"low hanging fruit" that have been left out from earlier versions
of the patch set for sake of simplicity.

These optimizations shall be added as extra patches in a later cycle.

Still the driver is able to handle reception of 99.95% of all CAN frames
of a 100% saturated 1MHz Can2.0 Bus with Frames with standard IDs and
DLC=0 on a Raspberry Pi 3. Note that this statistics is without injection
into the network stack, which then drops about 60% of all frames.

At this time the SPI bus is utilized 75% (when counting CS asserted)
and 60% from a SPI-Clock perspective.

On the Rasberry pi 3 in such a situation we are using 100% of one
CPU for the interrupt thread (as the spi driver is using polling for
short transfers) and this has exposed a performance inefficiency in the
spi framework that shows as the the spi-worker thread also consuming
CPU cycles (30% of one CPU in this test case) unnecessarily.

The driver also allows to use the INT pins as GPIOs.
It also exposes lots of internal data/statistics via debugfs.

On the HW side some errata related to SRAM access have been found
during the development of the patchsets. Since then an errata for
the controller has been shared by microchip.
All of mitigations that we could think (including the use of SPI-CRC
transfers) or special ordering of transfers are not effective in avoiding
the SERR issue. That is why we are only able to reach 99.95% rate, because
when this issue occurres then there is the need to reconfigure the
controller and that takes some time which can not get used to process
frames. Fortunately this only happens on highly congested systems, but it
is more likely to occur on low end systems.

Version 5 of the patchset is a big reorganization of the patchset
into different components (basic controller management, GPIO and CAN)
and has split out each of those compüonents/functions into separate
source files for the sake of readability.

So as an example almost everything related to can-transmission can get
found in mcp25xxfd_can_tx.c. mcp25xxfd_can_rx.c is responsible for
can-reception and mcp25xxfd_can_int.c for interrupt handling.

This also reduces the size of the individual patches to below 128k
so that the linux-can mailing-list should accepts the patch.

Changelog:
  V1 -> V2: new more generic name based on feedback from microchip
            cleanup of code (checkpatch)
            address all feedback on code
            handle (HW) systemerrors in a much better/reliable way
            cleanup of dt custom properties removing (most) gpio
              related properties
            implemented GPIOLIB support as per feedback
  V2 -> V3: added vendor-prefix for gpio-opendrain to dt-binding
            added gpio-controller to dt-binding
	    added feedback by Rob Herring
	    waited for other feedback regarding the code
  V3 -> V4: resend
            Patch-1 (dt) added: Reviewed-by: Rob Herring <robh@kernel.org>
  V4 -> V5: reorganisation of the patchset into smaller patches
            review of the whole driver code for better modularization

Martin Sperl (4):
  dt-binding: can: mcp25xxfd: document device tree bindings
  can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver basics
  can: mcp25xxfd: add gpiolib support for GPIO0/1 (aka. INT0/INT1)
  can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver

 .../bindings/net/can/microchip,mcp25xxfd.txt       |   32 +
 drivers/net/can/spi/Kconfig                        |    2 +
 drivers/net/can/spi/Makefile                       |    2 +
 drivers/net/can/spi/mcp25xxfd/Kconfig              |    5 +
 drivers/net/can/spi/mcp25xxfd/Makefile             |   11 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h          |  279 +++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c     | 1204 ++++++++++++++++++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c      |  803 +++++++++++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h      |  760 ++++++++++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c |  479 ++++++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c  |  723 ++++++++++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c   |  208 ++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c   |  824 ++++++++++++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_gpio.c     |  199 ++++
 14 files changed, 5531 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.txt
 create mode 100644 drivers/net/can/spi/mcp25xxfd/Kconfig
 create mode 100644 drivers/net/can/spi/mcp25xxfd/Makefile
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_gpio.c

--
2.11.0

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH V5 1/4] dt-binding: can: mcp25xxfd: document device tree bindings
  2018-12-21  9:29 [PATCH V5 0/4] Microchip mcp25xxfd can controller driver kernel
@ 2018-12-21  9:29 ` kernel
  2018-12-21  9:29 ` [PATCH V5 2/4] can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver basics kernel
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 12+ messages in thread
From: kernel @ 2018-12-21  9:29 UTC (permalink / raw)
  To: linux-can, devicetree, Wolfgang Grandegger, Mark Kleine-Budde,
	Rob Herring, Mark Rutland
  Cc: Martin Sperl, Sukkin Pang, Gerhard Bertelsmann,
	Wilhelm Leichtfried, Thomas Kopp, Enrico Scholz, Brad Hanson,
	Teemu Keskinarkaus

From: Martin Sperl <kernel@martin.sperl.org>

Add device-tree bindings for Microcip CanFD Controller mcp2517fd

Signed-off-by: Martin Sperl <kernel@martin.sperl.org>
Reviewed-by: Rob Herring <robh@kernel.org>
--
Changelog:
  V1 -> V2: new more generic name based on feedback from microchip
            cleanup of dt custom properties removing (most) gpio functions
  V2 -> V3: added vendor-prefix for gpio-opendrain
            s/_/-/
            added gpio-controller
  V3 -> V4: resend
            added: Reviewed-by: Rob Herring <robh@kernel.org>
  V4 -> V5: resend
            driver refactored, but no DTS changes
---
 .../bindings/net/can/microchip,mcp25xxfd.txt       | 32 ++++++++++++++++++++++
 1 file changed, 32 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.txt

diff --git a/Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.txt b/Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.txt
new file mode 100644
index 000000000000..b388b3eb3905
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.txt
@@ -0,0 +1,32 @@
+* Microchip MCP2517 stand-alone CAN controller device tree bindings
+
+Required properties:
+ - compatible: Should be one of the following:
+   - "microchip,mcp2517fd" for MCP2517fd.
+ - reg: SPI chip select.
+ - clocks: The clock feeding the CAN controller.
+ - interrupt-parent: The parent interrupt controller.
+ - interrupts: Should contain IRQ line for the CAN controller.
+ - gpio-controller: Marks the device node as a GPIO controller
+
+Optional properties:
+ - vdd-supply: Regulator that powers the CAN controller.
+ - xceiver-supply: Regulator that powers the CAN transceiver.
+ - microchip,clock-out-div = <0|1|2|4|10>: Clock output pin divider
+					   0 = Start of Frame output
+					   default: 10
+ - microchip,clock-div2: bool: divide the internal clock by 2
+ - microchip,gpio-open-drain: bool: enable open-drain for all pins
+                                    (except cantx)
+
+Example:
+	can0: can@1 {
+		compatible = "microchip,mcp2517fd";
+		reg = <1>;
+		clocks = <&clk24m>;
+		interrupt-parent = <&gpio4>;
+		interrupts = <13 0x8>;
+		vdd-supply = <&reg5v0>;
+		xceiver-supply = <&reg5v0>;
+		gpio-controller;
+	};
--
2.11.0

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V5 2/4] can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver basics
  2018-12-21  9:29 [PATCH V5 0/4] Microchip mcp25xxfd can controller driver kernel
  2018-12-21  9:29 ` [PATCH V5 1/4] dt-binding: can: mcp25xxfd: document device tree bindings kernel
@ 2018-12-21  9:29 ` kernel
  2019-01-21 12:31   ` Wolfgang Grandegger
  2018-12-21  9:29 ` [PATCH V5 3/4] can: mcp25xxfd: add gpiolib support for GPIO0/1 (aka. INT0/INT1) kernel
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 12+ messages in thread
From: kernel @ 2018-12-21  9:29 UTC (permalink / raw)
  To: linux-can, devicetree, Wolfgang Grandegger, Mark Kleine-Budde,
	Rob Herring, Mark Rutland
  Cc: Martin Sperl, Sukkin Pang, Gerhard Bertelsmann,
	Wilhelm Leichtfried, Thomas Kopp, Enrico Scholz, Brad Hanson,
	Teemu Keskinarkaus

From: Martin Sperl <kernel@martin.sperl.org>

This patch adds basic driver support for the Microchip mcp2517fd
CAN-FD controller.

The mcp2517fd is capable of transmitting and receiving standard data
frames, extended data frames, remote frames and Can-FD frames.
The mcp2517fd interfaces with the host over SPI.

This patch iprovides basic driver functionality:
setting up clocks and the infrastructure for the can and gpio portions
of the driver.

Datasheet:
* http://ww1.microchip.com/downloads/en/DeviceDoc/20005688A.pdf
Reference manual:
* http://ww1.microchip.com/downloads/en/DeviceDoc/20005678A.pdf
Errata:
* http://ww1.microchip.com/downloads/en/DeviceDoc/MCP2517FD-Silicon-Errata-and-Data-Sheet-Clarification-DS80000792A.pdf

Signed-off-by: Martin Sperl <kernel@martin.sperl.org>

---
Changelog:
  V1 -> V2: implemented support as per feedback
  V2 -> V3: moved dt-binding changes to patch 1
  V3 -> V4: resend
  V4 -> V5: reorganized driver into separate files
            added more HW detection test

---
 drivers/net/can/spi/Kconfig                    |    2 +
 drivers/net/can/spi/Makefile                   |    2 +
 drivers/net/can/spi/mcp25xxfd/Kconfig          |    5 +
 drivers/net/can/spi/mcp25xxfd/Makefile         |    6 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h      |  238 +++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c | 1190 ++++++++++++++++++++++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c  |  228 +++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h  |  487 ++++++++++
 8 files changed, 2158 insertions(+)
 create mode 100644 drivers/net/can/spi/mcp25xxfd/Kconfig
 create mode 100644 drivers/net/can/spi/mcp25xxfd/Makefile
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h

diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig
index 8f2e0dd7b756..7a5b1436492e 100644
--- a/drivers/net/can/spi/Kconfig
+++ b/drivers/net/can/spi/Kconfig
@@ -13,4 +13,6 @@ config CAN_MCP251X
 	---help---
 	  Driver for the Microchip MCP251x SPI CAN controllers.

+source "drivers/net/can/spi/mcp25xxfd/Kconfig"
+
 endmenu
diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile
index f59fa3731073..67d3ad21730b 100644
--- a/drivers/net/can/spi/Makefile
+++ b/drivers/net/can/spi/Makefile
@@ -5,3 +5,5 @@

 obj-$(CONFIG_CAN_HI311X)	+= hi311x.o
 obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
+
+obj-y                           += mcp25xxfd/
diff --git a/drivers/net/can/spi/mcp25xxfd/Kconfig b/drivers/net/can/spi/mcp25xxfd/Kconfig
new file mode 100644
index 000000000000..f720f1377612
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/Kconfig
@@ -0,0 +1,5 @@
+config CAN_MCP25XXFD
+	tristate "Microchip MCP25xxFD SPI CAN controllers"
+	depends on HAS_DMA
+	help
+	  Driver for the Microchip MCP25XXFD SPI FD-CAN controller family.
diff --git a/drivers/net/can/spi/mcp25xxfd/Makefile b/drivers/net/can/spi/mcp25xxfd/Makefile
new file mode 100644
index 000000000000..83de2d40cf9a
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/Makefile
@@ -0,0 +1,6 @@
+#
+#  Makefile for the Linux Controller Area Network SPI drivers.
+#
+obj-$(CONFIG_CAN_MCP25XXFD)	+= mcp25xxfd.o
+mcp25xxfd-objs                  := mcp25xxfd_base.o
+mcp25xxfd-objs                  += mcp25xxfd_can.o
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
new file mode 100644
index 000000000000..0f02c78a9a56
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
@@ -0,0 +1,238 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+/* some constants derived from the datasheets */
+#define MCP25XXFD_OST_DELAY_MS		3
+#define MCP25XXFD_MIN_CLOCK_FREQUENCY	1000000
+#define MCP25XXFD_MAX_CLOCK_FREQUENCY	40000000
+#define MCP25XXFD_PLL_MULTIPLIER	10
+#define MCP25XXFD_AUTO_PLL_MAX_CLOCK_FREQUENCY				\
+	(MCP25XXFD_MAX_CLOCK_FREQUENCY / MCP25XXFD_PLL_MULTIPLIER)
+#define MCP25XXFD_SCLK_DIVIDER		2
+
+/* address defines for SRAM */
+#define MCP25XXFD_SRAM_SIZE 2048
+#define MCP25XXFD_SRAM_ADDR(x) (0x400 + (x))
+
+/* some defines for the driver */
+#define MCP25XXFD_BUFFER_TXRX_SIZE (MCP25XXFD_SRAM_SIZE)
+#define MCP25XXFD_OSC_POLLING_JIFFIES	(HZ / 2)
+
+/* SPI commands */
+#define INSTRUCTION_RESET		0x0000
+#define INSTRUCTION_READ		0x3000
+#define INSTRUCTION_WRITE		0x2000
+#define INSTRUCTION_READ_CRC		0xB000
+#define INSTRUCTION_WRITE_CRC		0xA000
+#define INSTRUCTION_WRITE_SAVE		0xC000
+
+#define ADDRESS_MASK			0x0fff
+
+/* Register definitions */
+#define MCP25XXFD_SFR_BASE(x)		(0xE00 + (x))
+#define MCP25XXFD_OSC			MCP25XXFD_SFR_BASE(0x00)
+#  define MCP25XXFD_OSC_PLLEN		BIT(0)
+#  define MCP25XXFD_OSC_OSCDIS		BIT(2)
+#  define MCP25XXFD_OSC_SCLKDIV		BIT(4)
+#  define MCP25XXFD_OSC_CLKODIV_BITS	2
+#  define MCP25XXFD_OSC_CLKODIV_SHIFT	5
+#  define MCP25XXFD_OSC_CLKODIV_MASK			\
+	GENMASK(MCP25XXFD_OSC_CLKODIV_SHIFT		\
+		+ MCP25XXFD_OSC_CLKODIV_BITS - 1,	\
+		MCP25XXFD_OSC_CLKODIV_SHIFT)
+#  define MCP25XXFD_OSC_CLKODIV_10	3
+#  define MCP25XXFD_OSC_CLKODIV_4	2
+#  define MCP25XXFD_OSC_CLKODIV_2	1
+#  define MCP25XXFD_OSC_CLKODIV_1	0
+#  define MCP25XXFD_OSC_PLLRDY		BIT(8)
+#  define MCP25XXFD_OSC_OSCRDY		BIT(10)
+#  define MCP25XXFD_OSC_SCLKRDY		BIT(12)
+#define MCP25XXFD_IOCON			MCP25XXFD_SFR_BASE(0x04)
+#  define MCP25XXFD_IOCON_TRIS0		BIT(0)
+#  define MCP25XXFD_IOCON_TRIS1		BIT(1)
+#  define MCP25XXFD_IOCON_XSTBYEN	BIT(6)
+#  define MCP25XXFD_IOCON_LAT0		BIT(8)
+#  define MCP25XXFD_IOCON_LAT1		BIT(9)
+#  define MCP25XXFD_IOCON_GPIO0		BIT(16)
+#  define MCP25XXFD_IOCON_GPIO1		BIT(17)
+#  define MCP25XXFD_IOCON_PM0		BIT(24)
+#  define MCP25XXFD_IOCON_PM1		BIT(25)
+#  define MCP25XXFD_IOCON_TXCANOD	BIT(28)
+#  define MCP25XXFD_IOCON_SOF		BIT(29)
+#  define MCP25XXFD_IOCON_INTOD		BIT(30)
+#define MCP25XXFD_CRC			MCP25XXFD_SFR_BASE(0x08)
+#  define MCP25XXFD_CRC_MASK		GENMASK(15, 0)
+#  define MCP25XXFD_CRC_CRCERRIE	BIT(16)
+#  define MCP25XXFD_CRC_FERRIE		BIT(17)
+#  define MCP25XXFD_CRC_CRCERRIF	BIT(24)
+#  define MCP25XXFD_CRC_FERRIF		BIT(25)
+#define MCP25XXFD_ECCCON		MCP25XXFD_SFR_BASE(0x0C)
+#  define MCP25XXFD_ECCCON_ECCEN	BIT(0)
+#  define MCP25XXFD_ECCCON_SECIE	BIT(1)
+#  define MCP25XXFD_ECCCON_DEDIE	BIT(2)
+#  define MCP25XXFD_ECCCON_PARITY_BITS 6
+#  define MCP25XXFD_ECCCON_PARITY_SHIFT 8
+#  define MCP25XXFD_ECCCON_PARITY_MASK			\
+	GENMASK(MCP25XXFD_ECCCON_PARITY_SHIFT		\
+		+ MCP25XXFD_ECCCON_PARITY_BITS - 1,	\
+		MCP25XXFD_ECCCON_PARITY_SHIFT)
+#define MCP25XXFD_ECCSTAT		MCP25XXFD_SFR_BASE(0x10)
+#  define MCP25XXFD_ECCSTAT_SECIF	BIT(1)
+#  define MCP25XXFD_ECCSTAT_DEDIF	BIT(2)
+#  define MCP25XXFD_ECCSTAT_ERRADDR_SHIFT 8
+#  define MCP25XXFD_ECCSTAT_ERRADDR_MASK	      \
+	GENMASK(MCP25XXFD_ECCSTAT_ERRADDR_SHIFT + 11, \
+		MCP25XXFD_ECCSTAT_ERRADDR_SHIFT)
+
+#define DEVICE_NAME "mcp25xxfd"
+
+enum mcp25xxfd_model {
+	CAN_MCP2517FD	= 0x2517,
+};
+
+struct mcp25xxfd_priv {
+	struct spi_device *spi;
+	struct clk *clk;
+
+	/* the actual model of the mcp25xxfd */
+	enum mcp25xxfd_model model;
+
+	/* everything clock related */
+	int clock_freq;
+	struct {
+		/* clock configuration */
+		int clock_pll;
+		int clock_div2;
+		int  clock_odiv;
+	} config;
+
+	/* lock for enabling/disabling the clock */
+	struct mutex clk_user_lock;
+	u32 clk_user_mask;
+	u32 clk_sleep_mask;
+#define MCP25XXFD_CLK_USER_CAN BIT(0)
+#define MCP25XXFD_CLK_USER_GPIO0 BIT(1)
+#define MCP25XXFD_CLK_USER_GPIO1 BIT(2)
+#define MCP25XXFD_CLK_USER_CLKOUT BIT(3)
+
+	/* power related */
+	struct regulator *power;
+
+	/* the distinct spi_speeds to use for spi communication */
+	u32 spi_setup_speed_hz;
+	u32 spi_normal_speed_hz;
+	u32 spi_use_speed_hz;
+
+	/* spi-tx/rx buffers for efficient transfers
+	 * used during setup and irq
+	 */
+	struct mutex spi_rxtx_lock;
+	u8 spi_tx[MCP25XXFD_BUFFER_TXRX_SIZE];
+	u8 spi_rx[MCP25XXFD_BUFFER_TXRX_SIZE];
+
+	/* configuration registers */
+	struct {
+		u32 osc;
+		u32 iocon;
+		u32 crc;
+		u32 ecccon;
+	} regs;
+
+	/* debugfs related */
+#if defined(CONFIG_DEBUG_FS)
+	struct dentry *debugfs_dir;
+	struct dentry *debugfs_regs_dir;
+#endif
+};
+
+static inline int mcp25xxfd_power_enable(struct regulator *reg, int enable)
+{
+	if (IS_ERR_OR_NULL(reg))
+		return 0;
+
+	if (enable)
+		return regulator_enable(reg);
+	else
+		return regulator_disable(reg);
+}
+
+static inline void mcp25xxfd_convert_to_cpu(u32 *data, int n)
+{
+	int i;
+
+	for (i = 0; i < n; i++)
+		data[i] = le32_to_cpu(data[i]);
+}
+
+static inline void mcp25xxfd_convert_from_cpu(u32 *data, int n)
+{
+	int i;
+
+	for (i = 0; i < n; i++)
+		data[i] = cpu_to_le32(data[i]);
+}
+
+int mcp25xxfd_cmd_readn(struct spi_device *spi, u32 reg,
+			void *data, int n);
+int mcp25xxfd_cmd_read_mask(struct spi_device *spi, u32 reg,
+			    u32 *data, u32 mask);
+static inline int mcp25xxfd_cmd_read(struct spi_device *spi, u32 reg,
+				     u32 *data)
+{
+	return mcp25xxfd_cmd_read_mask(spi, reg, data, -1);
+}
+
+int mcp25xxfd_cmd_read_regs(struct spi_device *spi, u32 reg,
+			    u32 *data, u32 bytes);
+
+int mcp25xxfd_cmd_writen(struct spi_device *spi, u32 reg,
+			 void *data, int n);
+int mcp25xxfd_cmd_write_mask(struct spi_device *spi, u32 reg,
+			     u32 data, u32 mask);
+static inline int mcp25xxfd_cmd_write(struct spi_device *spi, u32 reg,
+				      u32 data)
+{
+	return mcp25xxfd_cmd_write_mask(spi, reg, data, -1);
+}
+
+int mcp25xxfd_cmd_write_regs(struct spi_device *spi, u32 reg,
+			     u32 *data, u32 bytes);
+
+int mcp25xxfd_dump_regs_range(struct seq_file *file, u32 start, u32 end);
+
+int mcp25xxfd_cmd_write_regs(struct spi_device *spi, u32 reg,
+			     u32 *data, u32 bytes);
+
+int mcp25xxfd_dump_regs_range(struct seq_file *file, u32 start, u32 end);
+
+/* shared (internal) clock control */
+int mcp25xxfd_stop_clock(struct spi_device *spi, int requestor_mask);
+int mcp25xxfd_start_clock(struct spi_device *spi, int requestor_mask);
+
+/* to put us to sleep fully we need the CAN controller to enter sleep mode */
+int mcp25xxfd_can_sleep_mode(struct spi_device *spi);
+/* probe the can controller */
+int mcp25xxfd_can_hw_probe(struct spi_device *spi);
+
+/* clearing interrupt flags */
+int mcp25xxfd_clear_crc_interrupts(struct spi_device *spi);
+int mcp25xxfd_clear_ecc_interrupts(struct spi_device *spi);
+int mcp25xxfd_clear_interrupts(struct spi_device *spi);
+
+/* enabling interrupts */
+int mcp25xxfd_enable_interrupts(struct spi_device *spi, bool enable);
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
new file mode 100644
index 000000000000..9c34893243d1
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
@@ -0,0 +1,1190 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
+ *
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "mcp25xxfd.h"
+
+/* device description and rational:
+ *
+ * the mcp25xxfd is a CanFD controller that also supports can2.0 only
+ * modes.
+ * It is connected via spi to the host and requires at minimum a single
+ * irq line in addition to the SPI lines - it is not mentioned explicitly
+ * in the documentation but in principle SPI 3-wire should be possible.
+ *
+ * The clock connected is typically 4MHz, 20MHz or 40MHz.
+ * When using a 4MHz clock the controller can use an integrated PLL to
+ * get 40MHz.
+ *
+ * The controller itself has 2KB of SRAM for CAN-data.
+ * ECC can get enabled for SRAM.
+ * CRC-16 checksumming of SPI transfers can get implemented
+ *   - some optimization options may not be efficient in such a situation.
+ *   - more SPI bus bandwidth is used for transfer of CRCs and
+ *     transfer length information
+ *
+ * It also contains 2 GPIO pins that can get used either as interrupt lines
+ * or GPIO IN or Out or STANDBY flags.
+ * In addition there is a PIN that allows output of a (divided) clock out
+ * or as a SOF (Start of Can FRAME) interrupt line - e.g for wakeup.
+ */
+
+/* known hardware issues and workarrounds in this driver:
+ *
+ * * There is one situation where the controller will require a full POR
+ *   (total power off) to recover from a bad Clock configuration.
+ *   This happens when the wrong clock is configured in the device tree
+ *   (say 4MHz are configured, while 40MHz is the actual clock frequency
+ *   of the HW).
+ *   In such a situation the driver tries to enable the PLL, which will
+ *   never synchronize and the controller becomes unresponsive to further
+ *   spi requests until a full POR.
+ *
+ *   Mitigation:
+ *     none as of now
+ *
+ *   Possible implementation of a mitigation/sanity check:
+ *     during initialization:
+ *       * try to identify the HW at 1MHz:
+ *         on success:
+ *           * controller is identified
+ *         on failure:
+ *           * controller is absent - fail
+ *       * force controller clock to run with disabled PLL
+ *       * try to identify the HW at 2MHz:
+ *         on success:
+ *           * controller clock is >= 4 MHz
+ *           * this may be 4MHz
+ *         on failure:
+ *           * controller clock is < 4 MHz
+ *       * try to identify the HW at 2.5MHz:
+ *         on success:
+ *           * controller clock is >= 5 MHz
+ *           * this may not be 4MHz
+ *         on failure:
+ *           * controller clock is 4 MHz
+ *           * enable PLL
+ *           * exit successfully (or run last test for verification purposes)
+ *       * try to identify the HW at <dt-clock/2> MHz:
+ *         on success:
+ *           * controller clock is >= <dt-clock/2> MHz
+ *              (it could be higher though)
+ *         on failure:
+ *           * the controller is not running at the
+ *             clock rate configured in the DT
+ *           * if PLL is enabled warn about requirements of POR
+ *           * fail
+ *
+ *   Side-effects:
+ *     * longer initialization time
+ *
+ *   Possible issues with mitigation:
+ *     * possibly miss-identification because the SPI block may work
+ *       "somewhat" at frequencies > < clock / 2 + delta f>
+ *       this may be especially true for the situation where we test if
+ *       2.5MHz SPI-Clock works.
+ *     * also SPI HW-clock dividers may do a round down to fixed frequencies
+ *       which is not propperly reported and may result in false positives
+ *       because a frequency lower than expected is used.
+ *
+ *   This is the reason why only simple testing is enabled at the risk of
+ *   the need for a POR.
+ */
+
+/* SPI helper */
+
+/* wrapper arround spi_sync, that sets speed_hz */
+static int mcp25xxfd_sync_transfer(struct spi_device *spi,
+				   struct spi_transfer *xfer,
+				   unsigned int xfers)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	int i;
+
+	for (i = 0; i < xfers; i++)
+		xfer[i].speed_hz = priv->spi_use_speed_hz;
+
+	return spi_sync_transfer(spi, xfer, xfers);
+}
+
+/* simple spi_write wrapper with speed_hz */
+static int mcp25xxfd_write(struct spi_device *spi,
+			   const void *tx_buf,
+			   unsigned int tx_len)
+{
+	struct spi_transfer xfer;
+
+	memset(&xfer, 0, sizeof(xfer));
+	xfer.tx_buf = tx_buf;
+	xfer.len = tx_len;
+
+	return mcp25xxfd_sync_transfer(spi, &xfer, 1);
+}
+
+/* an optimization of spi_write_then_read that merges the transfers */
+static int mcp25xxfd_write_then_read(struct spi_device *spi,
+				     const void *tx_buf,
+				     unsigned int tx_len,
+				     void *rx_buf,
+				     unsigned int rx_len)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct spi_transfer xfer[2];
+	u8 single_reg_data_tx[6];
+	u8 single_reg_data_rx[6];
+	int ret;
+
+	memset(xfer, 0, sizeof(xfer));
+
+	/* when using a halfduplex controller or to big for buffer */
+	if ((spi->master->flags & SPI_MASTER_HALF_DUPLEX) ||
+	    (tx_len + rx_len > sizeof(priv->spi_tx))) {
+		xfer[0].tx_buf = tx_buf;
+		xfer[0].len = tx_len;
+
+		xfer[1].rx_buf = rx_buf;
+		xfer[1].len = rx_len;
+
+		return mcp25xxfd_sync_transfer(spi, xfer, 2);
+	}
+
+	/* full duplex optimization */
+	xfer[0].len = tx_len + rx_len;
+	if (xfer[0].len > sizeof(single_reg_data_tx)) {
+		mutex_lock(&priv->spi_rxtx_lock);
+		xfer[0].tx_buf = priv->spi_tx;
+		xfer[0].rx_buf = priv->spi_rx;
+	} else {
+		xfer[0].tx_buf = single_reg_data_tx;
+		xfer[0].rx_buf = single_reg_data_rx;
+	}
+
+	/* copy and clean */
+	memcpy((u8 *)xfer[0].tx_buf, tx_buf, tx_len);
+	memset((u8 *)xfer[0].tx_buf + tx_len, 0, rx_len);
+
+	ret = mcp25xxfd_sync_transfer(spi, xfer, 1);
+	if (!ret)
+		memcpy(rx_buf, xfer[0].rx_buf + tx_len, rx_len);
+
+	if (xfer[0].len > sizeof(single_reg_data_tx))
+		mutex_unlock(&priv->spi_rxtx_lock);
+
+	return ret;
+}
+
+static int mcp25xxfd_write_then_write(struct spi_device *spi,
+				      const void *tx_buf,
+				      unsigned int tx_len,
+				      const void *tx2_buf,
+				      unsigned int tx2_len)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct spi_transfer xfer;
+	u8 single_reg_data[6];
+	int ret;
+
+	if (tx_len + tx2_len > MCP25XXFD_BUFFER_TXRX_SIZE)
+		return -EINVAL;
+
+	memset(&xfer, 0, sizeof(xfer));
+
+	xfer.len = tx_len + tx2_len;
+	if (xfer.len > sizeof(single_reg_data)) {
+		mutex_lock(&priv->spi_rxtx_lock);
+		xfer.tx_buf = priv->spi_tx;
+	} else {
+		xfer.tx_buf = single_reg_data;
+	}
+
+	memcpy((u8 *)xfer.tx_buf, tx_buf, tx_len);
+	memcpy((u8 *)xfer.tx_buf + tx_len, tx2_buf, tx2_len);
+
+	ret = mcp25xxfd_sync_transfer(spi, &xfer, 1);
+
+	if (xfer.len > sizeof(single_reg_data))
+		mutex_unlock(&priv->spi_rxtx_lock);
+
+	return ret;
+}
+
+/* mcp25xxfd spi command/protocol helper */
+
+static void mcp25xxfd_calc_cmd_addr(u16 cmd, u16 addr, u8 *data)
+{
+	cmd = cmd | (addr & ADDRESS_MASK);
+
+	data[0] = (cmd >> 8) & 0xff;
+	data[1] = (cmd >> 0) & 0xff;
+}
+
+static int mcp25xxfd_first_byte(u32 mask)
+{
+	return (mask & 0x0000ffff) ?
+		((mask & 0x000000ff) ? 0 : 1) :
+		((mask & 0x00ff0000) ? 2 : 3);
+}
+
+static int mcp25xxfd_last_byte(u32 mask)
+{
+	return (mask & 0xffff0000) ?
+		((mask & 0xff000000) ? 3 : 2) :
+		((mask & 0x0000ff00) ? 1 : 0);
+}
+
+/* read multiple bytes, transform some registers */
+int mcp25xxfd_cmd_readn(struct spi_device *spi, u32 reg,
+			void *data, int n)
+{
+	u8 cmd[2];
+	int ret;
+
+	mcp25xxfd_calc_cmd_addr(INSTRUCTION_READ, reg, cmd);
+
+	ret = mcp25xxfd_write_then_read(spi, &cmd, 2, data, n);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/* read a register, but we are only interrested in a few bytes */
+int mcp25xxfd_cmd_read_mask(struct spi_device *spi, u32 reg,
+			    u32 *data, u32 mask)
+{
+	int first_byte, last_byte, len_byte;
+	int ret;
+
+	/* check that at least one bit is set */
+	if (!mask)
+		return -EINVAL;
+
+	/* calculate first and last byte used */
+	first_byte = mcp25xxfd_first_byte(mask);
+	last_byte = mcp25xxfd_last_byte(mask);
+	len_byte = last_byte - first_byte + 1;
+
+	/* do a partial read */
+	*data = 0;
+	ret = mcp25xxfd_cmd_readn(spi, reg + first_byte,
+				  ((void *)data + first_byte), len_byte);
+	if (ret)
+		return ret;
+
+	mcp25xxfd_convert_to_cpu(data, 1);
+
+	return 0;
+}
+
+static int mcp25xxfd_cmd_reset(struct spi_device *spi)
+{
+	u8 cmd[2];
+
+	mcp25xxfd_calc_cmd_addr(INSTRUCTION_RESET, 0, cmd);
+
+	/* write the reset command */
+	return mcp25xxfd_write(spi, cmd, 2);
+}
+
+int mcp25xxfd_cmd_writen(struct spi_device *spi, u32 reg,
+			 void *data, int n)
+{
+	u8 cmd[2];
+	int ret;
+
+	mcp25xxfd_calc_cmd_addr(INSTRUCTION_WRITE, reg, cmd);
+
+	ret = mcp25xxfd_write_then_write(spi, &cmd, 2, data, n);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+/* read a register, but we are only interrested in a few bytes */
+int mcp25xxfd_cmd_write_mask(struct spi_device *spi, u32 reg,
+			     u32 data, u32 mask)
+{
+	int first_byte, last_byte, len_byte;
+	u8 cmd[2];
+
+	/* check that at least one bit is set */
+	if (!mask)
+		return -EINVAL;
+
+	/* calculate first and last byte used */
+	first_byte = mcp25xxfd_first_byte(mask);
+	last_byte = mcp25xxfd_last_byte(mask);
+	len_byte = last_byte - first_byte + 1;
+
+	/* prepare buffer */
+	mcp25xxfd_calc_cmd_addr(INSTRUCTION_WRITE, reg + first_byte, cmd);
+	data = cpu_to_le32(data);
+
+	return mcp25xxfd_write_then_write(spi,
+					  cmd, sizeof(cmd),
+					  ((void *)&data + first_byte),
+					  len_byte);
+}
+
+int mcp25xxfd_cmd_write_regs(struct spi_device *spi, u32 reg,
+			     u32 *data, u32 bytes)
+{
+	int ret;
+
+	/* first transpose to controller format */
+	mcp25xxfd_convert_from_cpu(data, bytes / sizeof(u32));
+
+	/* now write it */
+	ret = mcp25xxfd_cmd_writen(spi, reg, data, bytes);
+
+	/* and convert it back to cpu format even if it fails */
+	mcp25xxfd_convert_to_cpu(data, bytes / sizeof(u32));
+
+	return ret;
+}
+
+int mcp25xxfd_cmd_read_regs(struct spi_device *spi, u32 reg,
+			    u32 *data, u32 bytes)
+{
+	int ret;
+
+	/* read it */
+	ret = mcp25xxfd_cmd_readn(spi, reg, data, bytes);
+
+	/* and convert it to cpu format */
+	mcp25xxfd_convert_to_cpu((u32 *)data, bytes / sizeof(u32));
+
+	return ret;
+}
+
+/* debugfs related */
+#if defined(CONFIG_DEBUG_FS)
+
+int mcp25xxfd_dump_regs_range(struct seq_file *file, u32 start, u32 end)
+{
+	struct spi_device *spi = file->private;
+	u32 data[32];
+	int bytes = end - start + sizeof(u32);
+	int i, l, count, ret;
+
+	for (count =  bytes / sizeof(u32); count > 0; count -= 32) {
+		/* read up to 32 registers in one go */
+		l = min(count, 32);
+		ret = mcp25xxfd_cmd_read_regs(spi, start,
+					      data, l * sizeof(u32));
+		if (ret)
+			return ret;
+		/* dump those read registers */
+		for (i = 0; i < l; i++, start += sizeof(u32))
+			seq_printf(file, "Reg 0x%03x = 0x%08x\n",
+				   start, data[i]);
+	}
+
+	return 0;
+}
+
+static int mcp25xxfd_dump_regs(struct seq_file *file, void *offset)
+{
+	return mcp25xxfd_dump_regs_range(file,
+					 MCP25XXFD_OSC,
+					 MCP25XXFD_ECCSTAT);
+}
+
+static int mcp25xxfd_debugfs_setup(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct dentry *root, *regs;
+	char name[64];
+
+	/* the base directory */
+	snprintf(name, sizeof(name),
+		 DEVICE_NAME "-%s",
+		 dev_name(&priv->spi->dev));
+	priv->debugfs_dir = debugfs_create_dir(name, NULL);
+	root = priv->debugfs_dir;
+
+	/* expose some parameters related to clocks */
+	debugfs_create_u32("spi_setup_speed_hz", 0444, root,
+			   &priv->spi_setup_speed_hz);
+	debugfs_create_u32("spi_normal_speed_hz", 0444, root,
+			   &priv->spi_normal_speed_hz);
+	debugfs_create_u32("spi_use_speed_hz", 0444, root,
+			   &priv->spi_use_speed_hz);
+	debugfs_create_u32("clk_user_mask", 0444, root, &priv->clk_user_mask);
+
+	/* expose the system registers */
+	priv->debugfs_regs_dir = debugfs_create_dir("regs", root);
+	regs = priv->debugfs_regs_dir;
+	debugfs_create_x32("osc", 0444, regs, &priv->regs.osc);
+	debugfs_create_x32("iocon", 0444, regs, &priv->regs.iocon);
+	debugfs_create_x32("crc", 0444, regs, &priv->regs.crc);
+	debugfs_create_x32("ecccon", 0444, regs, &priv->regs.ecccon);
+
+	/* dump the controller registers themselves */
+	debugfs_create_devm_seqfile(&priv->spi->dev, "regs_live_dump",
+				    root, mcp25xxfd_dump_regs);
+
+	return 0;
+}
+
+static void mcp25xxfd_debugfs_remove(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+	debugfs_remove_recursive(priv->debugfs_dir);
+	priv->debugfs_dir = NULL;
+}
+#else
+int mcp25xxfd_dump_regs_range(struct seq_file *file, u32 start, u32 end)
+{
+	return 0;
+}
+
+static int mcp25xxfd_debugfs_setup(struct mcp25xxfd_priv *priv)
+{
+	return 0;
+}
+
+static void mcp25xxfd_debugfs_remove(struct mcp25xxfd_priv *priv)
+{
+}
+#endif
+
+/* HW Related */
+int mcp25xxfd_clear_ecc_interrupts(struct spi_device *spi)
+{
+	u32 val, addr;
+	int ret;
+
+	/* first report the error address */
+	ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_ECCSTAT, &val);
+	if (ret)
+		return ret;
+
+	/* if no flags are set then nothing to do */
+	if (!(val & (MCP25XXFD_ECCSTAT_SECIF | MCP25XXFD_ECCSTAT_DEDIF)))
+		return 0;
+
+	addr = (val & MCP25XXFD_ECCSTAT_ERRADDR_MASK) >>
+		MCP25XXFD_ECCSTAT_ERRADDR_SHIFT;
+
+	dev_err_ratelimited(&spi->dev,
+			    "ECC %s bit error at %03x\n",
+			    (val & MCP25XXFD_ECCSTAT_DEDIF) ?
+			    "double" : "single",
+			    addr);
+
+	/* and clear the error */
+	return mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_ECCSTAT, 0,
+					MCP25XXFD_ECCSTAT_SECIF |
+					MCP25XXFD_ECCSTAT_DEDIF);
+}
+
+int mcp25xxfd_clear_crc_interrupts(struct spi_device *spi)
+{
+	return mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_CRC, 0,
+					MCP25XXFD_CRC_CRCERRIF |
+					MCP25XXFD_CRC_FERRIF);
+}
+
+int mcp25xxfd_clear_interrupts(struct spi_device *spi)
+{
+	mcp25xxfd_clear_ecc_interrupts(spi);
+	mcp25xxfd_clear_crc_interrupts(spi);
+
+	return 0;
+}
+
+static int mcp25xxfd_enable_ecc_interrupts(struct spi_device *spi,
+					   bool enable)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	u32 mask = MCP25XXFD_ECCCON_SECIE | MCP25XXFD_ECCCON_DEDIE;
+
+	priv->regs.ecccon &= ~mask;
+	priv->regs.ecccon |= MCP25XXFD_ECCCON_ECCEN | (enable ? mask : 0);
+
+	return mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_ECCCON,
+					priv->regs.ecccon,
+					MCP25XXFD_ECCCON_ECCEN | mask);
+}
+
+static int mcp25xxfd_enable_crc_interrupts(struct spi_device *spi,
+					   bool enable)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	u32 mask = MCP25XXFD_CRC_CRCERRIE | MCP25XXFD_CRC_FERRIE;
+
+	priv->regs.crc &= ~mask;
+	priv->regs.crc |= enable ? mask : 0;
+
+	return mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_CRC,
+					priv->regs.crc, mask);
+}
+
+int mcp25xxfd_enable_interrupts(struct spi_device *spi,
+				bool enable)
+{
+	/* error handling only on enable for this function */
+	int ret = 0;
+
+	/* if we enable clear interrupt flags first */
+	if (enable)
+		ret = mcp25xxfd_clear_interrupts(spi);
+	if (enable && ret)
+		goto out;
+
+	ret = mcp25xxfd_enable_ecc_interrupts(spi, enable);
+	if (enable && ret)
+		goto out;
+
+	ret = mcp25xxfd_enable_crc_interrupts(spi, enable);
+	if (enable && ret)
+		goto out_ecc;
+
+	/* if we disable interrupts clear interrupt flags last */
+	if (!enable)
+		mcp25xxfd_clear_interrupts(spi);
+
+	return 0;
+
+out_ecc:
+	mcp25xxfd_enable_ecc_interrupts(spi, false);
+out:
+	return ret;
+}
+
+static int _mcp25xxfd_waitfor_osc(struct spi_device *spi,
+				  u32 waitfor,
+				  u32 mask)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	unsigned long timeout;
+	int ret;
+
+	/* wait for synced pll/osc/sclk */
+	timeout = jiffies + MCP25XXFD_OSC_POLLING_JIFFIES;
+	while (time_before_eq(jiffies, timeout)) {
+		ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_OSC,
+					 &priv->regs.osc);
+		if (ret)
+			return ret;
+		/* check for expected bits to be set/unset */
+		if ((priv->regs.osc & mask) == waitfor)
+			return 0;
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int mcp25xxfd_configure_osc(struct spi_device *spi,
+				   u32 value,
+				   u32 waitfor,
+				   u32 mask)
+{
+	int ret;
+
+	/* write the osc value to the controller - waking it if necessary */
+	ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_OSC, value);
+	if (ret)
+		return ret;
+
+	/* wait for the clock to stabelize */
+	ret = _mcp25xxfd_waitfor_osc(spi, waitfor, mask);
+
+	/* on timeout try again setting the register */
+	if (ret == -ETIMEDOUT) {
+		/* write the clock to the controller */
+		ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_OSC, value);
+		if (ret)
+			return ret;
+
+		/* wait for the clock to stabelize */
+		ret = _mcp25xxfd_waitfor_osc(spi, waitfor, mask);
+	}
+
+	/* handle timeout special - report the fact */
+	if (ret == -ETIMEDOUT)
+		dev_err(&spi->dev,
+			"Clock did not switch within the timeout period\n");
+
+	return ret;
+}
+
+static u32 _mcp25xxfd_clkout_mask(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	u32 value = 0;
+
+	if (priv->config.clock_div2)
+		value |= MCP25XXFD_OSC_SCLKDIV;
+
+	switch (priv->config.clock_odiv) {
+	case 0:
+		break;
+	case 1:
+		value |= MCP25XXFD_OSC_CLKODIV_1 <<
+			MCP25XXFD_OSC_CLKODIV_SHIFT;
+		break;
+	case 2:
+		value |= MCP25XXFD_OSC_CLKODIV_2 <<
+			MCP25XXFD_OSC_CLKODIV_SHIFT;
+		break;
+	case 4:
+		value |= MCP25XXFD_OSC_CLKODIV_4 <<
+			MCP25XXFD_OSC_CLKODIV_SHIFT;
+		break;
+	case 10:
+		value |= MCP25XXFD_OSC_CLKODIV_10 <<
+			MCP25XXFD_OSC_CLKODIV_SHIFT;
+		break;
+	default:
+		/* this should never happen but is error-handled
+		 * by the dt-parsing
+		 */
+		break;
+	}
+
+	return value;
+}
+
+static int _mcp25xxfd_start_clock(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	u32 value   = _mcp25xxfd_clkout_mask(spi);
+	u32 waitfor = MCP25XXFD_OSC_OSCRDY;
+	u32 mask    = waitfor |
+		MCP25XXFD_OSC_OSCDIS |
+		MCP25XXFD_OSC_PLLRDY |
+		MCP25XXFD_OSC_PLLEN;
+
+	/* enable PLL as well - set expectations */
+	if (priv->config.clock_pll) {
+		value   |= MCP25XXFD_OSC_PLLEN;
+		waitfor |= MCP25XXFD_OSC_PLLRDY | MCP25XXFD_OSC_PLLEN;
+	}
+
+	/* set the oscilator now */
+	return mcp25xxfd_configure_osc(spi, value, waitfor, mask);
+}
+
+static int _mcp25xxfd_stop_clock(struct spi_device *spi)
+{
+	u32 value   = _mcp25xxfd_clkout_mask(spi);
+	u32 waitfor = 0;
+	u32 mask    = MCP25XXFD_OSC_OSCDIS |
+		MCP25XXFD_OSC_PLLRDY |
+		MCP25XXFD_OSC_PLLEN;
+	int ret;
+
+	ret = mcp25xxfd_configure_osc(spi, value, waitfor, mask);
+	if (ret)
+		return ret;
+
+	/* finally switch the controller mode to sleep
+	 * by this time the controller should be in config mode already
+	 * this way we wake to config mode again
+	 */
+	return mcp25xxfd_can_sleep_mode(spi);
+}
+
+int mcp25xxfd_start_clock(struct spi_device *spi, int requestor_mask)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	int ret = 0;
+
+	/* without a clock there is nothing we can do... */
+	if (IS_ERR(priv->clk))
+		return IS_ERR(priv->clk);
+
+	mutex_lock(&priv->clk_user_lock);
+
+	/* if clock is already started, then skip */
+	if (priv->clk_user_mask & requestor_mask)
+		goto out;
+
+	/* enable the clock on the host side*/
+	ret = clk_prepare_enable(priv->clk);
+	if (ret)
+		goto out;
+
+	/* enable the clock on the controller side */
+	ret = _mcp25xxfd_start_clock(spi);
+	if (ret)
+		goto out;
+
+	/* mark the clock for the specific component as started */
+	priv->clk_user_mask |= requestor_mask;
+
+	/* and now we use the normal spi speed */
+	priv->spi_use_speed_hz = priv->spi_normal_speed_hz;
+
+out:
+	mutex_unlock(&priv->clk_user_lock);
+
+	return ret;
+}
+
+int mcp25xxfd_stop_clock(struct spi_device *spi, int requestor_mask)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	int ret;
+
+	/* without a clock there is nothing we can do... */
+	if (IS_ERR(priv->clk))
+		return IS_ERR(priv->clk);
+
+	mutex_lock(&priv->clk_user_lock);
+
+	/* if the mask is empty then skip, as the clock is stopped */
+	if (!priv->clk_user_mask)
+		goto out;
+
+	/* clear the clock mask */
+	priv->clk_user_mask &= ~requestor_mask;
+
+	/* if the mask is not empty then skip, as the clock is needed */
+	if (priv->clk_user_mask)
+		goto out;
+
+	/* and now we use the setup spi speed */
+	priv->spi_use_speed_hz = priv->spi_setup_speed_hz;
+
+	/* stop the clock on the controller */
+	ret = _mcp25xxfd_stop_clock(spi);
+
+	/* and we stop the clock on the host*/
+	if (!IS_ERR(priv->clk))
+		clk_disable_unprepare(priv->clk);
+out:
+	mutex_unlock(&priv->clk_user_lock);
+
+	return 0;
+}
+
+static int mcp25xxfd_enable_ecc(struct spi_device *spi)
+{
+	u8 buffer[256];
+	int i;
+	int ret;
+
+	/* set up RAM ECC - enable interrupts sets it as well */
+	ret = mcp25xxfd_enable_ecc_interrupts(spi, false);
+	if (ret)
+		return ret;
+
+	/* and clear SRAM so that no read fails from now on */
+	memset(buffer, 0, sizeof(buffer));
+	for (i = 0; i < MCP25XXFD_SRAM_SIZE; i += sizeof(buffer)) {
+		ret = mcp25xxfd_cmd_writen(spi,
+					   MCP25XXFD_SRAM_ADDR(i),
+					   buffer, sizeof(buffer));
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int mcp25xxfd_hw_wake(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	int ret = 0;
+
+	/* enable power to controller */
+	mcp25xxfd_power_enable(priv->power, 1);
+
+	/* if there is no sleep mask, then there is nothing to wake */
+	if (!priv->clk_sleep_mask)
+		return 0;
+
+	/* start the clocks */
+	ret = mcp25xxfd_start_clock(spi, priv->clk_sleep_mask);
+	if (ret)
+		return 0;
+
+	/* clear the sleep mask */
+	mutex_lock(&priv->clk_user_lock);
+	priv->clk_sleep_mask = 0;
+	mutex_unlock(&priv->clk_user_lock);
+
+	/* enable the interrupts again */
+	return mcp25xxfd_enable_interrupts(spi, true);
+}
+
+static int mcp25xxfd_hw_sleep(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+	mutex_lock(&priv->clk_user_lock);
+	priv->clk_sleep_mask = priv->clk_user_mask;
+	mutex_unlock(&priv->clk_user_lock);
+
+	/* disable interrupts */
+	mcp25xxfd_enable_interrupts(spi, false);
+
+	/* stop the clocks */
+	mcp25xxfd_stop_clock(spi, priv->clk_sleep_mask);
+
+	/* disable power to controller */
+	return mcp25xxfd_power_enable(priv->power, 0);
+}
+
+static int mcp25xxfd_hw_probe(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	int ret;
+
+	/* Wait for oscillator startup timer after power up */
+	mdelay(MCP25XXFD_OST_DELAY_MS);
+
+	/* send a "blind" reset, hoping we are in Config mode */
+	mcp25xxfd_cmd_reset(spi);
+
+	/* Wait for oscillator startup again */
+	mdelay(MCP25XXFD_OST_DELAY_MS);
+
+	/* check clock register that the clock is ready or disabled */
+	ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_OSC,
+				 &priv->regs.osc);
+	if (ret)
+		return ret;
+
+	/* there can only be one... */
+	switch (priv->regs.osc &
+		(MCP25XXFD_OSC_OSCRDY | MCP25XXFD_OSC_OSCDIS)) {
+	case MCP25XXFD_OSC_OSCRDY: /* either the clock is ready */
+		break;
+	case MCP25XXFD_OSC_OSCDIS: /* or the clock is disabled */
+		break;
+	default:
+		/* otherwise there is no valid device (or in strange state)
+		 *
+		 * if PLL is enabled but not ready, then there may be
+		 * something "fishy"
+		 * this happened during driver development
+		 * (enabling pll, when when on wrong clock), so best warn
+		 * about such a possibility
+		 */
+		if ((priv->regs.osc &
+		     (MCP25XXFD_OSC_PLLEN | MCP25XXFD_OSC_PLLRDY))
+		    == MCP25XXFD_OSC_PLLEN)
+			dev_err(&spi->dev,
+				"mcp25xxfd may be in a strange state - a power disconnect may be required\n");
+
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_OF_DYNAMIC
+int mcp25xxfd_of_parse(struct mcp25xxfd_priv *priv)
+{
+	struct spi_device *spi = priv->spi;
+	const struct device_node *np = spi->dev.of_node;
+	u32 val;
+	int ret;
+
+	priv->config.clock_div2 =
+		of_property_read_bool(np, "microchip,clock-div2");
+
+	ret = of_property_read_u32_index(np, "microchip,clock-out-div",
+					 0, &val);
+	if (!ret) {
+		switch (val) {
+		case 0:
+		case 1:
+		case 2:
+		case 4:
+		case 10:
+			priv->config.clock_odiv = val;
+			break;
+		default:
+			dev_err(&spi->dev,
+				"Invalid value in device tree for microchip,clock_out_div: %u - valid values: 0, 1, 2, 4, 10\n",
+				val);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+#else
+int mcp25xxfd_of_parse(struct mcp25xxfd_priv *priv)
+{
+	return 0;
+}
+#endif
+
+static const struct of_device_id mcp25xxfd_of_match[] = {
+	{
+		.compatible	= "microchip,mcp2517fd",
+		.data		= (void *)CAN_MCP2517FD,
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, mcp25xxfd_of_match);
+
+static int mcp25xxfd_probe(struct spi_device *spi)
+{
+	const struct of_device_id *of_id =
+		of_match_device(mcp25xxfd_of_match, &spi->dev);
+	struct mcp25xxfd_priv *priv;
+	struct clk *clk;
+	int ret, freq;
+
+	/* as irq_create_fwspec_mapping() can return 0, check for it */
+	if (spi->irq <= 0) {
+		dev_err(&spi->dev, "no valid irq line defined: irq = %i\n",
+			spi->irq);
+		return -EINVAL;
+	}
+
+	clk = devm_clk_get(&spi->dev, NULL);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	freq = clk_get_rate(clk);
+	if (freq < MCP25XXFD_MIN_CLOCK_FREQUENCY ||
+	    freq > MCP25XXFD_MAX_CLOCK_FREQUENCY) {
+		dev_err(&spi->dev,
+			"Clock frequency %i is not in range [%i:%i]\n",
+			freq,
+			MCP25XXFD_MIN_CLOCK_FREQUENCY,
+			MCP25XXFD_MAX_CLOCK_FREQUENCY);
+		return -ERANGE;
+	}
+
+	priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	/* assign some data */
+	if (of_id)
+		priv->model = (enum mcp25xxfd_model)of_id->data;
+	else
+		priv->model = spi_get_device_id(spi)->driver_data;
+
+	spi_set_drvdata(spi, priv);
+	priv->spi = spi;
+
+	mutex_init(&priv->clk_user_lock);
+
+	/* enable the clock and mark as enabled */
+	ret = clk_prepare_enable(clk);
+	if (ret)
+		goto out_free;
+
+	/* do not use the SCK clock divider of 2 */
+	priv->config.clock_div2 = false;
+
+	/* clock output is divided by 10 */
+	priv->config.clock_odiv = 10;
+
+	/* if we have a clock that is <= 4MHz, enable the pll */
+	priv->config.clock_pll =
+		(freq <= MCP25XXFD_AUTO_PLL_MAX_CLOCK_FREQUENCY);
+
+	/* check in device tree for overrrides */
+	ret = mcp25xxfd_of_parse(priv);
+	if (ret)
+		return ret;
+
+	/* decide on the effective clock rate */
+	priv->clock_freq = freq;
+	if (priv->config.clock_pll)
+		priv->clock_freq *= MCP25XXFD_PLL_MULTIPLIER;
+	if (priv->config.clock_div2)
+		priv->clock_freq /= MCP25XXFD_SCLK_DIVIDER;
+
+	/* calculate the clock frequencies to use
+	 *
+	 * setup clock speed is at most 1/4 the input clock speed
+	 * the reason for the factor of 4 is that there is
+	 * a clock divider in the controller that MAY be enabled in some
+	 * circumstances so we may find a controller with that enabled
+	 * during probe phase
+	 */
+	priv->spi_setup_speed_hz = freq / 4;
+
+	/* normal operation clock speeds */
+	priv->spi_normal_speed_hz = priv->clock_freq / 2;
+	if (priv->config.clock_div2) {
+		priv->spi_setup_speed_hz /= MCP25XXFD_SCLK_DIVIDER;
+		priv->spi_normal_speed_hz /= MCP25XXFD_SCLK_DIVIDER;
+	}
+	/* set limit on speed */
+	if (spi->max_speed_hz) {
+		priv->spi_setup_speed_hz = min_t(int,
+						 priv->spi_setup_speed_hz,
+						 spi->max_speed_hz);
+		priv->spi_normal_speed_hz = min_t(int,
+						  priv->spi_normal_speed_hz,
+						  spi->max_speed_hz);
+	}
+
+	/* use setup speed by default
+	 * - this is switched when clock is enabled/disabled
+	 */
+	priv->spi_use_speed_hz = priv->spi_setup_speed_hz;
+
+	/* Configure the SPI bus */
+	spi->bits_per_word = 8;
+	ret = spi_setup(spi);
+	if (ret)
+		goto out_clk;
+
+	priv->power = devm_regulator_get_optional(&spi->dev, "vdd");
+	if (PTR_ERR(priv->power) == -EPROBE_DEFER) {
+		ret = -EPROBE_DEFER;
+		goto out_clk;
+	}
+
+	ret = mcp25xxfd_power_enable(priv->power, 1);
+	if (ret)
+		goto out_clk;
+
+	/* this will also enable the MCP25XXFD_CLK_USER_CAN clock */
+	ret = mcp25xxfd_hw_probe(spi);
+
+	/* on error retry a second time */
+	if (ret == -ENODEV) {
+		ret = mcp25xxfd_hw_probe(spi);
+		if (!ret)
+			dev_info(&spi->dev,
+				 "found device only during retry\n");
+	}
+	if (ret) {
+		if (ret == -ENODEV)
+			dev_err(&spi->dev,
+				"Cannot initialize MCP%x. Wrong wiring? (oscilator register reads as %08x)\n",
+				priv->model, priv->regs.osc);
+		goto out_probe;
+	}
+
+	/* enable the can controller clock */
+	ret = mcp25xxfd_start_clock(spi, MCP25XXFD_CLK_USER_CAN);
+	if (ret)
+		goto out_probe;
+
+	/* try to identify the can-controller - we need the clock here */
+	ret = mcp25xxfd_can_hw_probe(spi);
+	if (ret)
+		goto out_ctlclk;
+
+	/* add debugfs */
+	ret = mcp25xxfd_debugfs_setup(spi);
+	if (ret)
+		goto out_ctlclk;
+
+	/* disable interrupts */
+	ret = mcp25xxfd_enable_interrupts(spi, false);
+	if (ret)
+		goto out_debugfs;
+
+	/* setup ECC for SRAM */
+	ret = mcp25xxfd_enable_ecc(spi);
+	if (ret)
+		goto out_debugfs;
+
+	/* and put controller to sleep by stopping the can clock */
+	ret = mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
+	if (ret)
+		goto out_debugfs;
+
+	dev_info(&spi->dev,
+		 "MCP%x successfully initialized.\n", priv->model);
+	return 0;
+
+out_debugfs:
+	mcp25xxfd_debugfs_remove(spi);
+out_ctlclk:
+	mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
+out_probe:
+	mcp25xxfd_power_enable(priv->power, 0);
+out_clk:
+	mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
+out_free:
+	mcp25xxfd_debugfs_remove(spi);
+	dev_err(&spi->dev, "Probe failed, err=%d\n", -ret);
+	return ret;
+}
+
+static int mcp25xxfd_remove(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+	/* clear all running clocks */
+	mcp25xxfd_stop_clock(spi, priv->clk_user_mask);
+
+	mcp25xxfd_debugfs_remove(spi);
+
+	mcp25xxfd_power_enable(priv->power, 0);
+
+	if (!IS_ERR(priv->clk))
+		clk_disable_unprepare(priv->clk);
+
+	return 0;
+}
+
+static int __maybe_unused mcp25xxfd_suspend(struct device *dev)
+{
+	struct spi_device *spi = to_spi_device(dev);
+
+	return mcp25xxfd_hw_sleep(spi);
+
+	return 0;
+}
+
+static int __maybe_unused mcp25xxfd_resume(struct device *dev)
+{
+	struct spi_device *spi = to_spi_device(dev);
+
+	return mcp25xxfd_hw_wake(spi);
+}
+
+static SIMPLE_DEV_PM_OPS(mcp25xxfd_pm_ops, mcp25xxfd_suspend,
+	mcp25xxfd_resume);
+
+static const struct spi_device_id mcp25xxfd_id_table[] = {
+	{
+		.name		= "mcp2517fd",
+		.driver_data	= (kernel_ulong_t)CAN_MCP2517FD,
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(spi, mcp25xxfd_id_table);
+
+static struct spi_driver mcp25xxfd_can_driver = {
+	.driver = {
+		.name = DEVICE_NAME,
+		.of_match_table = mcp25xxfd_of_match,
+		.pm = &mcp25xxfd_pm_ops,
+	},
+	.id_table = mcp25xxfd_id_table,
+	.probe = mcp25xxfd_probe,
+	.remove = mcp25xxfd_remove,
+};
+module_spi_driver(mcp25xxfd_can_driver);
+
+MODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>");
+MODULE_DESCRIPTION("Microchip 25XXFD CAN driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
new file mode 100644
index 000000000000..e5c7098a09c2
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ */
+
+/* controller details
+ *
+ *  It has 32 FIFOs (of up to 32 CAN-frames).
+ *
+ * There are 4 Fifo types which can get configured:
+ * * TEF - Transmission Event Fifo - which consumes FIFO 0
+ *   even if it is not configured
+ * * Tansmission Queue - for up to 32 Frames.
+ *   this queue reorders CAN frames to get transmitted following the
+ *   typical CAN dominant/recessive rules on the can bus itself.
+ *   This FIFO is optional.
+ * * TX FIFO: generic TX fifos that can contain arbitrary data
+ *   and which come with a configurable priority for transmission
+ *   It is also possible to have the Controller automatically trigger
+ *   a transfer when a Filter Rule for a RTR frame matches.
+ *   Each of these fifos in principle can get configured for distinct
+ *   dlc sizes (8 thru 64 bytes)
+ * * RX FIFO: generic RX fifo which is filled via filter-rules.
+ *   Each of these fifos in principle can get configured for distinct
+ *   dlc sizes (8 thru 64 bytes)
+ *   Unfortunately there is no filter rule that would allow triggering
+ *   on different frame sizes, so for all practical purposes the
+ *   RX fifos have to be of the same size (unless one wants to experience
+ *   lost data).
+ * When a Can Frame is transmitted from the TX Queue or an individual
+ * TX FIFO then a small TEF Frame can get added to the TEF FIFO queue
+ * to log the Transmission of the frame - this includes ID, Flags
+ * (including a custom identifier/index) and the timestamp (see below).
+ *
+ * The controller provides an optional free running counter with a divider
+ * for timestamping of RX frames as well as for TEF entries.
+ */
+
+/* Implementation notes:
+ *
+ * Right now we only use the CAN controller block to put us into deep sleep
+ * this means that the oscillator clock is turned off.
+ * So this is the only thing that we implement here right now
+ */
+
+#include "mcp25xxfd_can.h"
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+
+static int mcp25xxfd_can_get_mode(struct spi_device *spi,
+				  u32 *mode_data)
+{
+	int ret;
+
+	ret = mcp25xxfd_cmd_read(spi, CAN_CON, mode_data);
+	if (ret)
+		return ret;
+
+	return (*mode_data & CAN_CON_OPMOD_MASK) >> CAN_CON_OPMOD_SHIFT;
+}
+
+int mcp25xxfd_can_switch_mode(struct spi_device *spi,
+			      u32 *mode_data, int mode)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	int ret, i;
+
+	/* get the current mode/register - if mode_data is -1
+	 * this only happens during initialization phase
+	 * when the can controller is not setup yet
+	 * typically by calling mcp25xxfd_can_sleep_mode
+	 */
+	if (*mode_data == -1) {
+		ret = mcp25xxfd_can_get_mode(spi, mode_data);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* compute the effective mode in osc*/
+	*mode_data &= ~(CAN_CON_REQOP_MASK | CAN_CON_OPMOD_MASK);
+	*mode_data |= (mode << CAN_CON_REQOP_SHIFT) |
+		(mode << CAN_CON_OPMOD_SHIFT);
+
+	/* if the opmode is sleep then the oscilator will be disabled
+	 * and also not ready, so fake this change
+	 */
+	if (mode == CAN_CON_MODE_SLEEP) {
+		priv->regs.osc &= ~(MCP25XXFD_OSC_OSCRDY |
+				    MCP25XXFD_OSC_PLLRDY |
+				    MCP25XXFD_OSC_SCLKRDY);
+		priv->regs.osc |= MCP25XXFD_OSC_OSCDIS;
+	}
+
+	/* request the mode switch */
+	ret = mcp25xxfd_cmd_write(spi, CAN_CON, *mode_data);
+	if (ret)
+		return ret;
+
+	/* if we are in sleep mode then return immediately
+	 * the controller does not respond back!
+	 */
+	if (mode == CAN_CON_MODE_SLEEP)
+		return 0;
+
+	/* wait for it to stabilize/switch mode
+	 * we assume 256 rounds should be enough as this is > 12ms
+	 * at 1MHz Can Bus speed without any extra overhead
+	 *
+	 * The assumption here is that it depends on bus activity
+	 * how long it takes the controller to switch modes
+	 */
+	for (i = 0; i < 256; i++) {
+		/* get the mode */
+		ret = mcp25xxfd_can_get_mode(spi, mode_data);
+		if (ret < 0)
+			return ret;
+		/* check that we have reached our mode */
+		if (ret == mode)
+			return 0;
+	}
+
+	dev_err(&spi->dev, "Failed to switch to mode %u in time\n", mode);
+	return -ETIMEDOUT;
+}
+
+int mcp25xxfd_can_sleep_mode(struct spi_device *spi)
+{
+	u32 value = -1;
+
+	return mcp25xxfd_can_switch_mode(spi, &value, CAN_CON_MODE_SLEEP);
+}
+
+int mcp25xxfd_can_hw_probe_modeswitch(struct spi_device *spi)
+{
+	u32 mode_data;
+	int ret;
+
+	/* so we should be in config mode now, so move to INTERNAL_LOOPBACK */
+	ret = mcp25xxfd_can_switch_mode(spi, &mode_data,
+					CAN_CON_MODE_INTERNAL_LOOPBACK);
+	if (ret) {
+		dev_err(&spi->dev,
+			"Failed to switch into loopback mode\n");
+		return ret;
+	}
+
+	/* and back into config mode */
+	ret = mcp25xxfd_can_switch_mode(spi, &mode_data,
+					CAN_CON_MODE_CONFIG);
+	if (ret) {
+		dev_err(&spi->dev,
+			"Failed to switch back to config mode\n");
+		return ret;
+	}
+
+	/* so we have checked basic functionality successfully */
+	return 0;
+}
+
+int mcp25xxfd_can_hw_probe(struct spi_device *spi)
+{
+	u32 mode_data;
+	int mode, ret;
+
+	/* read TXQCON - the TXEN bit should always read as 1 */
+	ret = mcp25xxfd_cmd_read(spi, CAN_TXQCON, &mode_data);
+	if (ret)
+		return ret;
+	if ((mode_data & CAN_TXQCON_TXEN) == 0) {
+		dev_err(&spi->dev,
+			"Register TXQCON does not have bit TXEN set - reads as %08x",
+			mode_data);
+		return -EINVAL;
+	}
+
+	/* try to get the current mode */
+	mode = mcp25xxfd_can_get_mode(spi, &mode_data);
+	if (mode < 0)
+		return mode;
+
+	/* we would expect to be in config mode, as a SPI-reset should
+	 * have moved us into config mode.
+	 * But then the documentation says that SPI-reset may only work
+	 * reliably when already in config mode
+	 */
+
+	/* so if we are in config mode then everything is fine
+	 * and we check that a mode switch works propperly
+	 */
+	if (mode == CAN_CON_MODE_CONFIG)
+		return mcp25xxfd_can_hw_probe_modeswitch(spi);
+
+	/* if the bitfield is 0 then there is something is wrong */
+	if (!mode_data) {
+		dev_err(&spi->dev,
+			"got controller config register reading as 0\n");
+		return -EINVAL;
+	}
+
+	/* any other mode is unexpected */
+	dev_err(&spi->dev,
+		"Found controller in unexpected mode %i - register reads as %08x\n",
+		mode, mode_data);
+
+	/* so try to move to config mode
+	 * if this fails, then everything is lost and the controller
+	 * is not identified
+	 * This action MAY be destructive if a different device is connected
+	 * but note that the first hurdle (oscillator) was already
+	 * successful - so we should be safe...
+	 */
+	ret = mcp25xxfd_can_switch_mode(spi, &mode_data,
+					CAN_CON_MODE_CONFIG);
+	if (ret) {
+		dev_err(&spi->dev,
+			"Mode did not switch to config as expected - could not identify controller - register reads as %08x\n",
+			mode_data);
+		return -EINVAL;
+	}
+	/* check that modeswitch is really working */
+	return mcp25xxfd_can_hw_probe_modeswitch(spi);
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
new file mode 100644
index 000000000000..fe0e93e28b62
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
@@ -0,0 +1,487 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ */
+
+#include "mcp25xxfd.h"
+#include <linux/bitops.h>
+
+#define CAN_SFR_BASE(x)			(0x000 + (x))
+#define CAN_CON				CAN_SFR_BASE(0x00)
+#  define CAN_CON_DNCNT_BITS		5
+#  define CAN_CON_DNCNT_SHIFT		0
+#  define CAN_CON_DNCNT_MASK					\
+	GENMASK(CAN_CON_DNCNT_SHIFT + CAN_CON_DNCNT_BITS - 1,	\
+		CAN_CON_DNCNT_SHIFT)
+#  define CAN_CON_ISOCRCEN		BIT(5)
+#  define CAN_CON_PXEDIS		BIT(6)
+#  define CAN_CON_WAKFIL		BIT(8)
+#  define CAN_CON_WFT_BITS		2
+#  define CAN_CON_WFT_SHIFT		9
+#  define CAN_CON_WFT_MASK					\
+	GENMASK(CAN_CON_WFT_SHIFT + CAN_CON_WFT_BITS - 1,	\
+		CAN_CON_WFT_SHIFT)
+#  define CAN_CON_BUSY			BIT(11)
+#  define CAN_CON_BRSDIS		BIT(12)
+#  define CAN_CON_RTXAT			BIT(16)
+#  define CAN_CON_ESIGM			BIT(17)
+#  define CAN_CON_SERR2LOM		BIT(18)
+#  define CAN_CON_STEF			BIT(19)
+#  define CAN_CON_TXQEN			BIT(20)
+#  define CAN_CON_OPMODE_BITS		3
+#  define CAN_CON_OPMOD_SHIFT		21
+#  define CAN_CON_OPMOD_MASK					\
+	GENMASK(CAN_CON_OPMOD_SHIFT + CAN_CON_OPMODE_BITS - 1,	\
+		CAN_CON_OPMOD_SHIFT)
+#  define CAN_CON_REQOP_BITS		3
+#  define CAN_CON_REQOP_SHIFT		24
+#  define CAN_CON_REQOP_MASK					\
+	GENMASK(CAN_CON_REQOP_SHIFT + CAN_CON_REQOP_BITS - 1,	\
+		CAN_CON_REQOP_SHIFT)
+#    define CAN_CON_MODE_MIXED			0
+#    define CAN_CON_MODE_SLEEP			1
+#    define CAN_CON_MODE_INTERNAL_LOOPBACK	2
+#    define CAN_CON_MODE_LISTENONLY		3
+#    define CAN_CON_MODE_CONFIG			4
+#    define CAN_CON_MODE_EXTERNAL_LOOPBACK	5
+#    define CAN_CON_MODE_CAN2_0			6
+#    define CAN_CON_MODE_RESTRICTED		7
+#  define CAN_CON_ABAT			BIT(27)
+#  define CAN_CON_TXBWS_BITS		3
+#  define CAN_CON_TXBWS_SHIFT		28
+#  define CAN_CON_TXBWS_MASK					\
+	GENMASK(CAN_CON_TXBWS_SHIFT + CAN_CON_TXBWS_BITS - 1,	\
+		CAN_CON_TXBWS_SHIFT)
+#  define CAN_CON_DEFAULT				\
+	(CAN_CON_ISOCRCEN |				\
+	 CAN_CON_PXEDIS |				\
+	 CAN_CON_WAKFIL |				\
+	 (3 << CAN_CON_WFT_SHIFT) |			\
+	 CAN_CON_STEF |					\
+	 CAN_CON_TXQEN |				\
+	 (CAN_CON_MODE_CONFIG << CAN_CON_OPMOD_SHIFT) |	\
+	 (CAN_CON_MODE_CONFIG << CAN_CON_REQOP_SHIFT))
+#  define CAN_CON_DEFAULT_MASK	\
+	(CAN_CON_DNCNT_MASK |	\
+	 CAN_CON_ISOCRCEN |	\
+	 CAN_CON_PXEDIS |	\
+	 CAN_CON_WAKFIL |	\
+	 CAN_CON_WFT_MASK |	\
+	 CAN_CON_BRSDIS |	\
+	 CAN_CON_RTXAT |	\
+	 CAN_CON_ESIGM |	\
+	 CAN_CON_SERR2LOM |	\
+	 CAN_CON_STEF |		\
+	 CAN_CON_TXQEN |	\
+	 CAN_CON_OPMOD_MASK |	\
+	 CAN_CON_REQOP_MASK |	\
+	 CAN_CON_ABAT |		\
+	 CAN_CON_TXBWS_MASK)
+#define CAN_NBTCFG			CAN_SFR_BASE(0x04)
+#  define CAN_NBTCFG_SJW_BITS		7
+#  define CAN_NBTCFG_SJW_SHIFT		0
+#  define CAN_NBTCFG_SJW_MASK					\
+	GENMASK(CAN_NBTCFG_SJW_SHIFT + CAN_NBTCFG_SJW_BITS - 1, \
+		CAN_NBTCFG_SJW_SHIFT)
+#  define CAN_NBTCFG_TSEG2_BITS		7
+#  define CAN_NBTCFG_TSEG2_SHIFT	8
+#  define CAN_NBTCFG_TSEG2_MASK					    \
+	GENMASK(CAN_NBTCFG_TSEG2_SHIFT + CAN_NBTCFG_TSEG2_BITS - 1, \
+		CAN_NBTCFG_TSEG2_SHIFT)
+#  define CAN_NBTCFG_TSEG1_BITS		8
+#  define CAN_NBTCFG_TSEG1_SHIFT	16
+#  define CAN_NBTCFG_TSEG1_MASK					    \
+	GENMASK(CAN_NBTCFG_TSEG1_SHIFT + CAN_NBTCFG_TSEG1_BITS - 1, \
+		CAN_NBTCFG_TSEG1_SHIFT)
+#  define CAN_NBTCFG_BRP_BITS		8
+#  define CAN_NBTCFG_BRP_SHIFT		24
+#  define CAN_NBTCFG_BRP_MASK					\
+	GENMASK(CAN_NBTCFG_BRP_SHIFT + CAN_NBTCFG_BRP_BITS - 1, \
+		CAN_NBTCFG_BRP_SHIFT)
+#define CAN_DBTCFG			CAN_SFR_BASE(0x08)
+#  define CAN_DBTCFG_SJW_BITS		4
+#  define CAN_DBTCFG_SJW_SHIFT		0
+#  define CAN_DBTCFG_SJW_MASK					\
+	GENMASK(CAN_DBTCFG_SJW_SHIFT + CAN_DBTCFG_SJW_BITS - 1, \
+		CAN_DBTCFG_SJW_SHIFT)
+#  define CAN_DBTCFG_TSEG2_BITS		4
+#  define CAN_DBTCFG_TSEG2_SHIFT	8
+#  define CAN_DBTCFG_TSEG2_MASK					    \
+	GENMASK(CAN_DBTCFG_TSEG2_SHIFT + CAN_DBTCFG_TSEG2_BITS - 1, \
+		CAN_DBTCFG_TSEG2_SHIFT)
+#  define CAN_DBTCFG_TSEG1_BITS		5
+#  define CAN_DBTCFG_TSEG1_SHIFT	16
+#  define CAN_DBTCFG_TSEG1_MASK					    \
+	GENMASK(CAN_DBTCFG_TSEG1_SHIFT + CAN_DBTCFG_TSEG1_BITS - 1, \
+		CAN_DBTCFG_TSEG1_SHIFT)
+#  define CAN_DBTCFG_BRP_BITS		8
+#  define CAN_DBTCFG_BRP_SHIFT		24
+#  define CAN_DBTCFG_BRP_MASK					\
+	GENMASK(CAN_DBTCFG_BRP_SHIFT + CAN_DBTCFG_BRP_BITS - 1, \
+		CAN_DBTCFG_BRP_SHIFT)
+#define CAN_TDC				CAN_SFR_BASE(0x0C)
+#  define CAN_TDC_TDCV_BITS		5
+#  define CAN_TDC_TDCV_SHIFT		0
+#  define CAN_TDC_TDCV_MASK					\
+	GENMASK(CAN_TDC_TDCV_SHIFT + CAN_TDC_TDCV_BITS - 1,	\
+		CAN_TDC_TDCV_SHIFT)
+#  define CAN_TDC_TDCO_BITS		5
+#  define CAN_TDC_TDCO_SHIFT		8
+#  define CAN_TDC_TDCO_MASK					\
+	GENMASK(CAN_TDC_TDCO_SHIFT + CAN_TDC_TDCO_BITS - 1,	\
+		CAN_TDC_TDCO_SHIFT)
+#  define CAN_TDC_TDCMOD_BITS		2
+#  define CAN_TDC_TDCMOD_SHIFT		16
+#  define CAN_TDC_TDCMOD_MASK					\
+	GENMASK(CAN_TDC_TDCMOD_SHIFT + CAN_TDC_TDCMOD_BITS - 1, \
+		CAN_TDC_TDCMOD_SHIFT)
+#  define CAN_TDC_TDCMOD_DISABLED	0
+#  define CAN_TDC_TDCMOD_MANUAL		1
+#  define CAN_TDC_TDCMOD_AUTO		2
+#  define CAN_TDC_SID11EN		BIT(24)
+#  define CAN_TDC_EDGFLTEN		BIT(25)
+#define CAN_TBC				CAN_SFR_BASE(0x10)
+#define CAN_TSCON			CAN_SFR_BASE(0x14)
+#  define CAN_TSCON_TBCPRE_BITS		10
+#  define CAN_TSCON_TBCPRE_SHIFT	0
+#  define CAN_TSCON_TBCPRE_MASK					    \
+	GENMASK(CAN_TSCON_TBCPRE_SHIFT + CAN_TSCON_TBCPRE_BITS - 1, \
+		CAN_TSCON_TBCPRE_SHIFT)
+#  define CAN_TSCON_TBCEN		BIT(16)
+#  define CAN_TSCON_TSEOF		BIT(17)
+#  define CAN_TSCON_TSRES		BIT(18)
+#define CAN_VEC				CAN_SFR_BASE(0x18)
+#  define CAN_VEC_ICODE_BITS		7
+#  define CAN_VEC_ICODE_SHIFT		0
+#  define CAN_VEC_ICODE_MASK					    \
+	GENMASK(CAN_VEC_ICODE_SHIFT + CAN_VEC_ICODE_BITS - 1,	    \
+		CAN_VEC_ICODE_SHIFT)
+#  define CAN_VEC_FILHIT_BITS		5
+#  define CAN_VEC_FILHIT_SHIFT		8
+#  define CAN_VEC_FILHIT_MASK					\
+	GENMASK(CAN_VEC_FILHIT_SHIFT + CAN_VEC_FILHIT_BITS - 1, \
+		CAN_VEC_FILHIT_SHIFT)
+#  define CAN_VEC_TXCODE_BITS		7
+#  define CAN_VEC_TXCODE_SHIFT		16
+#  define CAN_VEC_TXCODE_MASK					\
+	GENMASK(CAN_VEC_TXCODE_SHIFT + CAN_VEC_TXCODE_BITS - 1, \
+		CAN_VEC_TXCODE_SHIFT)
+#  define CAN_VEC_RXCODE_BITS		7
+#  define CAN_VEC_RXCODE_SHIFT		24
+#  define CAN_VEC_RXCODE_MASK					\
+	GENMASK(CAN_VEC_RXCODE_SHIFT + CAN_VEC_RXCODE_BITS - 1, \
+		CAN_VEC_RXCODE_SHIFT)
+#define CAN_INT				CAN_SFR_BASE(0x1C)
+#  define CAN_INT_IF_SHIFT		0
+#  define CAN_INT_TXIF			BIT(0)
+#  define CAN_INT_RXIF			BIT(1)
+#  define CAN_INT_TBCIF			BIT(2)
+#  define CAN_INT_MODIF			BIT(3)
+#  define CAN_INT_TEFIF			BIT(4)
+#  define CAN_INT_ECCIF			BIT(8)
+#  define CAN_INT_SPICRCIF		BIT(9)
+#  define CAN_INT_TXATIF		BIT(10)
+#  define CAN_INT_RXOVIF		BIT(11)
+#  define CAN_INT_SERRIF		BIT(12)
+#  define CAN_INT_CERRIF		BIT(13)
+#  define CAN_INT_WAKIF			BIT(14)
+#  define CAN_INT_IVMIF			BIT(15)
+#  define CAN_INT_IF_MASK		\
+	(CAN_INT_TXIF |			\
+	 CAN_INT_RXIF |			\
+	 CAN_INT_TBCIF	|		\
+	 CAN_INT_MODIF	|		\
+	 CAN_INT_TEFIF	|		\
+	 CAN_INT_ECCIF	|		\
+	 CAN_INT_SPICRCIF |		\
+	 CAN_INT_TXATIF |		\
+	 CAN_INT_RXOVIF |		\
+	 CAN_INT_CERRIF |		\
+	 CAN_INT_SERRIF |		\
+	 CAN_INT_WAKIF |		\
+	 CAN_INT_IVMIF)
+#  define CAN_INT_IE_SHIFT		16
+#  define CAN_INT_TXIE			(CAN_INT_TXIF << CAN_INT_IE_SHIFT)
+#  define CAN_INT_RXIE			(CAN_INT_RXIF << CAN_INT_IE_SHIFT)
+#  define CAN_INT_TBCIE			(CAN_INT_TBCIF << CAN_INT_IE_SHIFT)
+#  define CAN_INT_MODIE			(CAN_INT_MODIF << CAN_INT_IE_SHIFT)
+#  define CAN_INT_TEFIE			(CAN_INT_TEFIF << CAN_INT_IE_SHIFT)
+#  define CAN_INT_ECCIE			(CAN_INT_ECCIF << CAN_INT_IE_SHIFT)
+#  define CAN_INT_SPICRCIE		\
+	(CAN_INT_SPICRCIF << CAN_INT_IE_SHIFT)
+#  define CAN_INT_TXATIE		(CAN_INT_TXATIF << CAN_INT_IE_SHIFT)
+#  define CAN_INT_RXOVIE		(CAN_INT_RXOVIF << CAN_INT_IE_SHIFT)
+#  define CAN_INT_CERRIE		(CAN_INT_CERRIF << CAN_INT_IE_SHIFT)
+#  define CAN_INT_SERRIE		(CAN_INT_SERRIF << CAN_INT_IE_SHIFT)
+#  define CAN_INT_WAKIE			(CAN_INT_WAKIF << CAN_INT_IE_SHIFT)
+#  define CAN_INT_IVMIE			(CAN_INT_IVMIF << CAN_INT_IE_SHIFT)
+#  define CAN_INT_IE_MASK		\
+	(CAN_INT_TXIE |			\
+	 CAN_INT_RXIE |			\
+	 CAN_INT_TBCIE	|		\
+	 CAN_INT_MODIE	|		\
+	 CAN_INT_TEFIE	|		\
+	 CAN_INT_ECCIE	|		\
+	 CAN_INT_SPICRCIE |		\
+	 CAN_INT_TXATIE |		\
+	 CAN_INT_RXOVIE |		\
+	 CAN_INT_CERRIE |		\
+	 CAN_INT_SERRIE |		\
+	 CAN_INT_WAKIE |		\
+	 CAN_INT_IVMIE)
+#define CAN_RXIF			CAN_SFR_BASE(0x20)
+#define CAN_TXIF			CAN_SFR_BASE(0x24)
+#define CAN_RXOVIF			CAN_SFR_BASE(0x28)
+#define CAN_TXATIF			CAN_SFR_BASE(0x2C)
+#define CAN_TXREQ			CAN_SFR_BASE(0x30)
+#define CAN_TREC			CAN_SFR_BASE(0x34)
+#  define CAN_TREC_REC_BITS		8
+#  define CAN_TREC_REC_SHIFT		0
+#  define CAN_TREC_REC_MASK				    \
+	GENMASK(CAN_TREC_REC_SHIFT + CAN_TREC_REC_BITS - 1, \
+		CAN_TREC_REC_SHIFT)
+#  define CAN_TREC_TEC_BITS		8
+#  define CAN_TREC_TEC_SHIFT		8
+#  define CAN_TREC_TEC_MASK				    \
+	GENMASK(CAN_TREC_TEC_SHIFT + CAN_TREC_TEC_BITS - 1, \
+		CAN_TREC_TEC_SHIFT)
+#  define CAN_TREC_EWARN		BIT(16)
+#  define CAN_TREC_RXWARN		BIT(17)
+#  define CAN_TREC_TXWARN		BIT(18)
+#  define CAN_TREC_RXBP			BIT(19)
+#  define CAN_TREC_TXBP			BIT(20)
+#  define CAN_TREC_TXBO			BIT(21)
+#define CAN_BDIAG0			CAN_SFR_BASE(0x38)
+#  define CAN_BDIAG0_NRERRCNT_BITS	8
+#  define CAN_BDIAG0_NRERRCNT_SHIFT	0
+#  define CAN_BDIAG0_NRERRCNT_MASK					\
+	GENMASK(CAN_BDIAG0_NRERRCNT_SHIFT + CAN_BDIAG0_NRERRCNT_BITS - 1, \
+		CAN_BDIAG0_NRERRCNT_SHIFT)
+#  define CAN_BDIAG0_NTERRCNT_BITS	8
+#  define CAN_BDIAG0_NTERRCNT_SHIFT	8
+#  define CAN_BDIAG0_NTERRCNT_MASK					\
+	GENMASK(CAN_BDIAG0_NTERRCNT_SHIFT + CAN_BDIAG0_NTERRCNT_BITS - 1, \
+		CAN_BDIAG0_NTERRCNT_SHIFT)
+#  define CAN_BDIAG0_DRERRCNT_BITS	8
+#  define CAN_BDIAG0_DRERRCNT_SHIFT	16
+#  define CAN_BDIAG0_DRERRCNT_MASK					\
+	GENMASK(CAN_BDIAG0_DRERRCNT_SHIFT + CAN_BDIAG0_DRERRCNT_BITS - 1, \
+		CAN_BDIAG0_DRERRCNT_SHIFT)
+#  define CAN_BDIAG0_DTERRCNT_BITS	8
+#  define CAN_BDIAG0_DTERRCNT_SHIFT	24
+#  define CAN_BDIAG0_DTERRCNT_MASK					\
+	GENMASK(CAN_BDIAG0_DTERRCNT_SHIFT + CAN_BDIAG0_DTERRCNT_BITS - 1, \
+		CAN_BDIAG0_DTERRCNT_SHIFT)
+#define CAN_BDIAG1			CAN_SFR_BASE(0x3C)
+#  define CAN_BDIAG1_EFMSGCNT_BITS	16
+#  define CAN_BDIAG1_EFMSGCNT_SHIFT	0
+#  define CAN_BDIAG1_EFMSGCNT_MASK					\
+	GENMASK(CAN_BDIAG1_EFMSGCNT_SHIFT + CAN_BDIAG1_EFMSGCNT_BITS - 1, \
+		CAN_BDIAG1_EFMSGCNT_SHIFT)
+#  define CAN_BDIAG1_NBIT0ERR		BIT(16)
+#  define CAN_BDIAG1_NBIT1ERR		BIT(17)
+#  define CAN_BDIAG1_NACKERR		BIT(18)
+#  define CAN_BDIAG1_NSTUFERR		BIT(19)
+#  define CAN_BDIAG1_NFORMERR		BIT(20)
+#  define CAN_BDIAG1_NCRCERR		BIT(21)
+#  define CAN_BDIAG1_TXBOERR		BIT(23)
+#  define CAN_BDIAG1_DBIT0ERR		BIT(24)
+#  define CAN_BDIAG1_DBIT1ERR		BIT(25)
+#  define CAN_BDIAG1_DFORMERR		BIT(27)
+#  define CAN_BDIAG1_DSTUFERR		BIT(28)
+#  define CAN_BDIAG1_DCRCERR		BIT(29)
+#  define CAN_BDIAG1_ESI		BIT(30)
+#  define CAN_BDIAG1_DLCMM		BIT(31)
+#define CAN_TEFCON			CAN_SFR_BASE(0x40)
+#  define CAN_TEFCON_TEFNEIE		BIT(0)
+#  define CAN_TEFCON_TEFHIE		BIT(1)
+#  define CAN_TEFCON_TEFFIE		BIT(2)
+#  define CAN_TEFCON_TEFOVIE		BIT(3)
+#  define CAN_TEFCON_TEFTSEN		BIT(5)
+#  define CAN_TEFCON_UINC		BIT(8)
+#  define CAN_TEFCON_FRESET		BIT(10)
+#  define CAN_TEFCON_FSIZE_BITS		5
+#  define CAN_TEFCON_FSIZE_SHIFT	24
+#  define CAN_TEFCON_FSIZE_MASK					    \
+	GENMASK(CAN_TEFCON_FSIZE_SHIFT + CAN_TEFCON_FSIZE_BITS - 1, \
+		CAN_TEFCON_FSIZE_SHIFT)
+#define CAN_TEFSTA			CAN_SFR_BASE(0x44)
+#  define CAN_TEFSTA_TEFNEIF		BIT(0)
+#  define CAN_TEFSTA_TEFHIF		BIT(1)
+#  define CAN_TEFSTA_TEFFIF		BIT(2)
+#  define CAN_TEFSTA_TEVOVIF		BIT(3)
+#define CAN_TEFUA			CAN_SFR_BASE(0x48)
+#define CAN_RESERVED			CAN_SFR_BASE(0x4C)
+#define CAN_TXQCON			CAN_SFR_BASE(0x50)
+#  define CAN_TXQCON_TXQNIE		BIT(0)
+#  define CAN_TXQCON_TXQEIE		BIT(2)
+#  define CAN_TXQCON_TXATIE		BIT(4)
+#  define CAN_TXQCON_TXEN		BIT(7)
+#  define CAN_TXQCON_UINC		BIT(8)
+#  define CAN_TXQCON_TXREQ		BIT(9)
+#  define CAN_TXQCON_FRESET		BIT(10)
+#  define CAN_TXQCON_TXPRI_BITS		5
+#  define CAN_TXQCON_TXPRI_SHIFT	16
+#  define CAN_TXQCON_TXPRI_MASK					    \
+	GENMASK(CAN_TXQCON_TXPRI_SHIFT + CAN_TXQCON_TXPRI_BITS - 1, \
+		CAN_TXQCON_TXPRI_SHIFT)
+#  define CAN_TXQCON_TXAT_BITS		2
+#  define CAN_TXQCON_TXAT_SHIFT		21
+#  define CAN_TXQCON_TXAT_MASK					    \
+	GENMASK(CAN_TXQCON_TXAT_SHIFT + CAN_TXQCON_TXAT_BITS - 1,   \
+		CAN_TXQCON_TXAT_SHIFT)
+#  define CAN_TXQCON_FSIZE_BITS		5
+#  define CAN_TXQCON_FSIZE_SHIFT	24
+#  define CAN_TXQCON_FSIZE_MASK					    \
+	GENMASK(CAN_TXQCON_FSIZE_SHIFT + CAN_TXQCON_FSIZE_BITS - 1, \
+		CAN_TXQCON_FSIZE_SHIFT)
+#  define CAN_TXQCON_PLSIZE_BITS	3
+#  define CAN_TXQCON_PLSIZE_SHIFT	29
+#  define CAN_TXQCON_PLSIZE_MASK				      \
+	GENMASK(CAN_TXQCON_PLSIZE_SHIFT + CAN_TXQCON_PLSIZE_BITS - 1, \
+		CAN_TXQCON_PLSIZE_SHIFT)
+#    define CAN_TXQCON_PLSIZE_8		0
+#    define CAN_TXQCON_PLSIZE_12	1
+#    define CAN_TXQCON_PLSIZE_16	2
+#    define CAN_TXQCON_PLSIZE_20	3
+#    define CAN_TXQCON_PLSIZE_24	4
+#    define CAN_TXQCON_PLSIZE_32	5
+#    define CAN_TXQCON_PLSIZE_48	6
+#    define CAN_TXQCON_PLSIZE_64	7
+
+#define CAN_TXQSTA			CAN_SFR_BASE(0x54)
+#  define CAN_TXQSTA_TXQNIF		BIT(0)
+#  define CAN_TXQSTA_TXQEIF		BIT(2)
+#  define CAN_TXQSTA_TXATIF		BIT(4)
+#  define CAN_TXQSTA_TXERR		BIT(5)
+#  define CAN_TXQSTA_TXLARB		BIT(6)
+#  define CAN_TXQSTA_TXABT		BIT(7)
+#  define CAN_TXQSTA_TXQCI_BITS		5
+#  define CAN_TXQSTA_TXQCI_SHIFT	8
+#  define CAN_TXQSTA_TXQCI_MASK					    \
+	GENMASK(CAN_TXQSTA_TXQCI_SHIFT + CAN_TXQSTA_TXQCI_BITS - 1, \
+		CAN_TXQSTA_TXQCI_SHIFT)
+
+#define CAN_TXQUA			CAN_SFR_BASE(0x58)
+#define CAN_FIFOCON(x)			CAN_SFR_BASE(0x5C + 12 * ((x) - 1))
+#define CAN_FIFOCON_TFNRFNIE		BIT(0)
+#define CAN_FIFOCON_TFHRFHIE		BIT(1)
+#define CAN_FIFOCON_TFERFFIE		BIT(2)
+#define CAN_FIFOCON_RXOVIE		BIT(3)
+#define CAN_FIFOCON_TXATIE		BIT(4)
+#define CAN_FIFOCON_RXTSEN		BIT(5)
+#define CAN_FIFOCON_RTREN		BIT(6)
+#define CAN_FIFOCON_TXEN		BIT(7)
+#define CAN_FIFOCON_UINC		BIT(8)
+#define CAN_FIFOCON_TXREQ		BIT(9)
+#define CAN_FIFOCON_FRESET		BIT(10)
+#  define CAN_FIFOCON_TXPRI_BITS	5
+#  define CAN_FIFOCON_TXPRI_SHIFT	16
+#  define CAN_FIFOCON_TXPRI_MASK					\
+	GENMASK(CAN_FIFOCON_TXPRI_SHIFT + CAN_FIFOCON_TXPRI_BITS - 1,	\
+		CAN_FIFOCON_TXPRI_SHIFT)
+#  define CAN_FIFOCON_TXAT_BITS		2
+#  define CAN_FIFOCON_TXAT_SHIFT	21
+#  define CAN_FIFOCON_TXAT_MASK					    \
+	GENMASK(CAN_FIFOCON_TXAT_SHIFT + CAN_FIFOCON_TXAT_BITS - 1, \
+		CAN_FIFOCON_TXAT_SHIFT)
+#  define CAN_FIFOCON_TXAT_ONE_SHOT	0
+#  define CAN_FIFOCON_TXAT_THREE_SHOT	1
+#  define CAN_FIFOCON_TXAT_UNLIMITED	2
+#  define CAN_FIFOCON_FSIZE_BITS	5
+#  define CAN_FIFOCON_FSIZE_SHIFT	24
+#  define CAN_FIFOCON_FSIZE_MASK					\
+	GENMASK(CAN_FIFOCON_FSIZE_SHIFT + CAN_FIFOCON_FSIZE_BITS - 1,	\
+		CAN_FIFOCON_FSIZE_SHIFT)
+#  define CAN_FIFOCON_PLSIZE_BITS	3
+#  define CAN_FIFOCON_PLSIZE_SHIFT	29
+#  define CAN_FIFOCON_PLSIZE_MASK					\
+	GENMASK(CAN_FIFOCON_PLSIZE_SHIFT + CAN_FIFOCON_PLSIZE_BITS - 1, \
+		CAN_FIFOCON_PLSIZE_SHIFT)
+#define CAN_FIFOSTA(x)			CAN_SFR_BASE(0x60 + 12 * ((x) - 1))
+#  define CAN_FIFOSTA_TFNRFNIF		BIT(0)
+#  define CAN_FIFOSTA_TFHRFHIF		BIT(1)
+#  define CAN_FIFOSTA_TFERFFIF		BIT(2)
+#  define CAN_FIFOSTA_RXOVIF		BIT(3)
+#  define CAN_FIFOSTA_TXATIF		BIT(4)
+#  define CAN_FIFOSTA_TXERR		BIT(5)
+#  define CAN_FIFOSTA_TXLARB		BIT(6)
+#  define CAN_FIFOSTA_TXABT		BIT(7)
+#  define CAN_FIFOSTA_FIFOCI_BITS	5
+#  define CAN_FIFOSTA_FIFOCI_SHIFT	8
+#  define CAN_FIFOSTA_FIFOCI_MASK					\
+	GENMASK(CAN_FIFOSTA_FIFOCI_SHIFT + CAN_FIFOSTA_FIFOCI_BITS - 1, \
+		CAN_FIFOSTA_FIFOCI_SHIFT)
+#define CAN_FIFOUA(x)			CAN_SFR_BASE(0x64 + 12 * ((x) - 1))
+#define CAN_FLTCON(x)			CAN_SFR_BASE(0x1D0 + ((x) & 0x1c))
+#  define CAN_FILCON_SHIFT(x)		(((x) & 3) * 8)
+#  define CAN_FILCON_BITS(x)		CAN_FILCON_BITS_
+#  define CAN_FILCON_BITS_		4
+	/* avoid macro reuse warning, so do not use GENMASK as above */
+#  define CAN_FILCON_MASK(x)					\
+	(GENMASK(CAN_FILCON_BITS_ - 1, 0) << CAN_FILCON_SHIFT(x))
+#  define CAN_FIFOCON_FLTEN(x)		BIT(7 + CAN_FILCON_SHIFT(x))
+#define CAN_FLTOBJ(x)			CAN_SFR_BASE(0x1F0 + 8 * (x))
+#  define CAN_FILOBJ_SID_BITS		11
+#  define CAN_FILOBJ_SID_SHIFT		0
+#  define CAN_FILOBJ_SID_MASK					\
+	GENMASK(CAN_FILOBJ_SID_SHIFT + CAN_FILOBJ_SID_BITS - 1, \
+		CAN_FILOBJ_SID_SHIFT)
+#  define CAN_FILOBJ_EID_BITS		18
+#  define CAN_FILOBJ_EID_SHIFT		12
+#  define CAN_FILOBJ_EID_MASK					\
+	GENMASK(CAN_FILOBJ_EID_SHIFT + CAN_FILOBJ_EID_BITS - 1, \
+		CAN_FILOBJ_EID_SHIFT)
+#  define CAN_FILOBJ_SID11		BIT(29)
+#  define CAN_FILOBJ_EXIDE		BIT(30)
+#define CAN_FLTMASK(x)			CAN_SFR_BASE(0x1F4 + 8 * (x))
+#  define CAN_FILMASK_MSID_BITS		11
+#  define CAN_FILMASK_MSID_SHIFT	0
+#  define CAN_FILMASK_MSID_MASK					    \
+	GENMASK(CAN_FILMASK_MSID_SHIFT + CAN_FILMASK_MSID_BITS - 1, \
+		CAN_FILMASK_MSID_SHIFT)
+#  define CAN_FILMASK_MEID_BITS		18
+#  define CAN_FILMASK_MEID_SHIFT	12
+#  define CAN_FILMASK_MEID_MASK					    \
+	GENMASK(CAN_FILMASK_MEID_SHIFT + CAN_FILMASK_MEID_BITS - 1, \
+		CAN_FILMASK_MEID_SHIFT)
+#  define CAN_FILMASK_MSID11		BIT(29)
+#  define CAN_FILMASK_MIDE		BIT(30)
+
+#define CAN_OBJ_ID_SID_BITS		11
+#define CAN_OBJ_ID_SID_SHIFT		0
+#define CAN_OBJ_ID_SID_MASK					\
+	GENMASK(CAN_OBJ_ID_SID_SHIFT + CAN_OBJ_ID_SID_BITS - 1, \
+		CAN_OBJ_ID_SID_SHIFT)
+#define CAN_OBJ_ID_EID_BITS		18
+#define CAN_OBJ_ID_EID_SHIFT		11
+#define CAN_OBJ_ID_EID_MASK					\
+	GENMASK(CAN_OBJ_ID_EID_SHIFT + CAN_OBJ_ID_EID_BITS - 1, \
+		CAN_OBJ_ID_EID_SHIFT)
+#define CAN_OBJ_ID_SID_BIT11		BIT(29)
+
+#define CAN_OBJ_FLAGS_DLC_BITS		4
+#define CAN_OBJ_FLAGS_DLC_SHIFT		0
+#define CAN_OBJ_FLAGS_DLC_MASK					      \
+	GENMASK(CAN_OBJ_FLAGS_DLC_SHIFT + CAN_OBJ_FLAGS_DLC_BITS - 1, \
+		CAN_OBJ_FLAGS_DLC_SHIFT)
+#define CAN_OBJ_FLAGS_IDE		BIT(4)
+#define CAN_OBJ_FLAGS_RTR		BIT(5)
+#define CAN_OBJ_FLAGS_BRS		BIT(6)
+#define CAN_OBJ_FLAGS_FDF		BIT(7)
+#define CAN_OBJ_FLAGS_ESI		BIT(8)
+#define CAN_OBJ_FLAGS_SEQ_BITS		7
+#define CAN_OBJ_FLAGS_SEQ_SHIFT		9
+#define CAN_OBJ_FLAGS_SEQ_MASK					      \
+	GENMASK(CAN_OBJ_FLAGS_SEQ_SHIFT + CAN_OBJ_FLAGS_SEQ_BITS - 1, \
+		CAN_OBJ_FLAGS_SEQ_SHIFT)
+#define CAN_OBJ_FLAGS_FILHIT_BITS	11
+#define CAN_OBJ_FLAGS_FILHIT_SHIFT	5
+#define CAN_OBJ_FLAGS_FILHIT_MASK				      \
+	GENMASK(CAN_FLAGS_FILHIT_SHIFT + CAN_FLAGS_FILHIT_BITS - 1,   \
+		CAN_FLAGS_FILHIT_SHIFT)
--
2.11.0

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V5 3/4] can: mcp25xxfd: add gpiolib support for GPIO0/1 (aka. INT0/INT1)
  2018-12-21  9:29 [PATCH V5 0/4] Microchip mcp25xxfd can controller driver kernel
  2018-12-21  9:29 ` [PATCH V5 1/4] dt-binding: can: mcp25xxfd: document device tree bindings kernel
  2018-12-21  9:29 ` [PATCH V5 2/4] can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver basics kernel
@ 2018-12-21  9:29 ` kernel
  2018-12-21  9:29 ` [PATCH V5 4/4] can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver kernel
  2019-01-10  8:30 ` [PATCH V5 0/4] Microchip mcp25xxfd can controller driver Wolfgang Grandegger
  4 siblings, 0 replies; 12+ messages in thread
From: kernel @ 2018-12-21  9:29 UTC (permalink / raw)
  To: linux-can, devicetree, Wolfgang Grandegger, Mark Kleine-Budde,
	Rob Herring, Mark Rutland
  Cc: Martin Sperl, Sukkin Pang, Gerhard Bertelsmann,
	Wilhelm Leichtfried, Thomas Kopp, Enrico Scholz, Brad Hanson,
	Teemu Keskinarkaus

From: Martin Sperl <kernel@martin.sperl.org>

Add GPIOLIB support to mcp25xxfd.

Signed-off-by: Martin Sperl <kernel@martin.sperl.org>

---
Changelog:
  V1 -> V2: implemented support as per feedback
  V2 -> V3: moved dt-binding changes to patch 1
  V3 -> V4: resend
  V4 -> V5: reorganized driver into separate files

---
 drivers/net/can/spi/mcp25xxfd/Makefile         |   1 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h      |   5 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c |  12 +-
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_gpio.c | 199 +++++++++++++++++++++++++
 4 files changed, 216 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_gpio.c

diff --git a/drivers/net/can/spi/mcp25xxfd/Makefile b/drivers/net/can/spi/mcp25xxfd/Makefile
index 83de2d40cf9a..5dd7f13674ec 100644
--- a/drivers/net/can/spi/mcp25xxfd/Makefile
+++ b/drivers/net/can/spi/mcp25xxfd/Makefile
@@ -4,3 +4,4 @@
 obj-$(CONFIG_CAN_MCP25XXFD)	+= mcp25xxfd.o
 mcp25xxfd-objs                  := mcp25xxfd_base.o
 mcp25xxfd-objs                  += mcp25xxfd_can.o
+mcp25xxfd-objs                  += mcp25xxfd_gpio.o
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
index 0f02c78a9a56..73817a567219 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
@@ -107,6 +107,7 @@ enum mcp25xxfd_model {

 struct mcp25xxfd_priv {
 	struct spi_device *spi;
+	struct gpio_chip *gpio;
 	struct clk *clk;

 	/* the actual model of the mcp25xxfd */
@@ -236,3 +237,7 @@ int mcp25xxfd_clear_interrupts(struct spi_device *spi);

 /* enabling interrupts */
 int mcp25xxfd_enable_interrupts(struct spi_device *spi, bool enable);
+
+/* gpiolib support */
+int mcp25xxfd_gpio_setup(struct spi_device *spi);
+void mcp25xxfd_gpio_remove(struct spi_device *spi);
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
index 9c34893243d1..b0851d97bcbb 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
@@ -1105,15 +1105,22 @@ static int mcp25xxfd_probe(struct spi_device *spi)
 	if (ret)
 		goto out_debugfs;

+	/* setting up GPIO */
+	ret = mcp25xxfd_gpio_setup(spi);
+	if (ret)
+		goto out_debugfs;
+
 	/* and put controller to sleep by stopping the can clock */
 	ret = mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
 	if (ret)
-		goto out_debugfs;
+		goto out_gpio;

 	dev_info(&spi->dev,
 		 "MCP%x successfully initialized.\n", priv->model);
 	return 0;

+out_gpio:
+	mcp25xxfd_gpio_remove(spi);
 out_debugfs:
 	mcp25xxfd_debugfs_remove(spi);
 out_ctlclk:
@@ -1132,6 +1139,9 @@ static int mcp25xxfd_remove(struct spi_device *spi)
 {
 	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);

+	/* remove gpio */
+	mcp25xxfd_gpio_remove(spi);
+
 	/* clear all running clocks */
 	mcp25xxfd_stop_clock(spi, priv->clk_user_mask);

diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_gpio.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_gpio.c
new file mode 100644
index 000000000000..06c5db937d81
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_gpio.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
+ *
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "mcp25xxfd.h"
+
+/* GPIO component */
+#ifdef CONFIG_GPIOLIB
+
+enum mcp25xxfd_gpio_pins {
+	MCP25XXFD_GPIO_GPIO0 = 0,
+	MCP25XXFD_GPIO_GPIO1 = 1,
+};
+
+static int mcp25xxfd_gpio_request(struct gpio_chip *chip,
+				  unsigned int offset)
+{
+	struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
+	int clock_requestor = offset ?
+		MCP25XXFD_CLK_USER_GPIO1 : MCP25XXFD_CLK_USER_GPIO0;
+
+	/* only handle gpio 0/1 */
+	if (offset > 1)
+		return -EINVAL;
+
+	mcp25xxfd_start_clock(priv->spi, clock_requestor);
+
+	return 0;
+}
+
+static void mcp25xxfd_gpio_free(struct gpio_chip *chip,
+				unsigned int offset)
+{
+	struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
+	int clock_requestor = offset ?
+		MCP25XXFD_CLK_USER_GPIO1 : MCP25XXFD_CLK_USER_GPIO0;
+
+	/* only handle gpio 0/1 */
+	if (offset > 1)
+		return;
+
+	mcp25xxfd_stop_clock(priv->spi, clock_requestor);
+}
+
+static int mcp25xxfd_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+	struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
+	u32 mask = (offset) ? MCP25XXFD_IOCON_GPIO1 : MCP25XXFD_IOCON_GPIO0;
+	int ret;
+
+	/* only handle gpio 0/1 */
+	if (offset > 1)
+		return -EINVAL;
+
+	/* read the relevant gpio Latch */
+	ret = mcp25xxfd_cmd_read_mask(priv->spi, MCP25XXFD_IOCON,
+				      &priv->regs.iocon, mask);
+	if (ret)
+		return ret;
+
+	/* return the match */
+	return priv->regs.iocon & mask;
+}
+
+static void mcp25xxfd_gpio_set(struct gpio_chip *chip, unsigned int offset,
+			       int value)
+{
+	struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
+	u32 mask = (offset) ? MCP25XXFD_IOCON_LAT1 : MCP25XXFD_IOCON_LAT0;
+
+	/* only handle gpio 0/1 */
+	if (offset > 1)
+		return;
+
+	/* update in memory representation with the corresponding value */
+	if (value)
+		priv->regs.iocon |= mask;
+	else
+		priv->regs.iocon &= ~mask;
+
+	mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_IOCON,
+				 priv->regs.iocon, mask);
+}
+
+static int mcp25xxfd_gpio_direction_input(struct gpio_chip *chip,
+					  unsigned int offset)
+{
+	struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
+	u32 mask_tri = (offset) ?
+		MCP25XXFD_IOCON_TRIS1 : MCP25XXFD_IOCON_TRIS0;
+	u32 mask_stby = (offset) ?
+		0 : MCP25XXFD_IOCON_XSTBYEN;
+	u32 mask_pm = (offset) ?
+		MCP25XXFD_IOCON_PM1 : MCP25XXFD_IOCON_PM0;
+
+	/* only handle gpio 0/1 */
+	if (offset > 1)
+		return -EINVAL;
+
+	/* set the mask */
+	priv->regs.iocon |= mask_tri | mask_pm;
+
+	/* clear stby */
+	priv->regs.iocon &= ~mask_stby;
+
+	return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_IOCON,
+					priv->regs.iocon,
+					mask_tri | mask_stby | mask_pm);
+}
+
+static int mcp25xxfd_gpio_direction_output(struct gpio_chip *chip,
+					   unsigned int offset, int value)
+{
+	struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
+	u32 mask_tri = (offset) ?
+		MCP25XXFD_IOCON_TRIS1 : MCP25XXFD_IOCON_TRIS0;
+	u32 mask_lat = (offset) ?
+		MCP25XXFD_IOCON_LAT1 : MCP25XXFD_IOCON_LAT0;
+	u32 mask_pm = (offset) ?
+		MCP25XXFD_IOCON_PM1 : MCP25XXFD_IOCON_PM0;
+	u32 mask_stby = (offset) ?
+		0 : MCP25XXFD_IOCON_XSTBYEN;
+
+	/* only handle gpio 0/1 */
+	if (offset > 1)
+		return -EINVAL;
+
+	/* clear the tristate bit and also clear stby */
+	priv->regs.iocon &= ~(mask_tri | mask_stby);
+
+	/* set GPIO mode */
+	priv->regs.iocon |= mask_pm;
+
+	/* set the value */
+	if (value)
+		priv->regs.iocon |= mask_lat;
+	else
+		priv->regs.iocon &= ~mask_lat;
+
+	return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_IOCON,
+					priv->regs.iocon,
+					mask_tri | mask_lat |
+					mask_pm | mask_stby);
+}
+
+int mcp25xxfd_gpio_setup(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct gpio_chip *gpio = devm_kzalloc(&spi->dev, sizeof(*gpio),
+					     GFP_KERNEL);
+	priv->gpio = gpio;
+
+	/* gpiochip only handles GPIO0 and GPIO1 */
+	gpio->owner                = THIS_MODULE;
+	gpio->parent               = &spi->dev;
+	gpio->label                = dev_name(&spi->dev);
+	gpio->direction_input      = mcp25xxfd_gpio_direction_input;
+	gpio->get                  = mcp25xxfd_gpio_get;
+	gpio->direction_output     = mcp25xxfd_gpio_direction_output;
+	gpio->set                  = mcp25xxfd_gpio_set;
+	gpio->request              = mcp25xxfd_gpio_request;
+	gpio->free                 = mcp25xxfd_gpio_free;
+	gpio->base                 = -1;
+	gpio->ngpio                = 2;
+	gpio->can_sleep            = 1;
+
+	return gpiochip_add_data(gpio, priv);
+}
+
+void mcp25xxfd_gpio_remove(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+	gpiochip_remove(priv->gpio);
+	priv->gpio = NULL;
+}
+
+#else
+int mcp25xxfd_gpio_setup(struct spi_device *spi)
+{
+	return 0;
+}
+
+void mcp25xxfd_gpio_remove(struct spi_device *spi)
+{
+}
+#endif
--
2.11.0

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V5 4/4] can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver
  2018-12-21  9:29 [PATCH V5 0/4] Microchip mcp25xxfd can controller driver kernel
                   ` (2 preceding siblings ...)
  2018-12-21  9:29 ` [PATCH V5 3/4] can: mcp25xxfd: add gpiolib support for GPIO0/1 (aka. INT0/INT1) kernel
@ 2018-12-21  9:29 ` kernel
  2019-01-21 18:32   ` Wolfgang Grandegger
  2019-01-10  8:30 ` [PATCH V5 0/4] Microchip mcp25xxfd can controller driver Wolfgang Grandegger
  4 siblings, 1 reply; 12+ messages in thread
From: kernel @ 2018-12-21  9:29 UTC (permalink / raw)
  To: linux-can, devicetree, Wolfgang Grandegger, Mark Kleine-Budde,
	Rob Herring, Mark Rutland
  Cc: Martin Sperl, Sukkin Pang, Gerhard Bertelsmann,
	Wilhelm Leichtfried, Thomas Kopp, Enrico Scholz, Brad Hanson,
	Teemu Keskinarkaus

From: Martin Sperl <kernel@martin.sperl.org>

Add un-optimized Can2.0 and CanFD support.

Optimizations and HW-bug-workarounds are separate patches

On a Rasperry pi 3 it is already able to process Can2.0 Frames
with DLC=0 on a CAN bus with 1MHz. without losing any packets
on the SPI side. Packets still get lost inside the network stack.

As for transmission on a Rpi3 we can saturate the CAN bus at 1MHz
with Can2.0 frames with DLC=0 for the number of configured tx-fifos.
Afterwards we need some time to recover the state before we can
fill in the fifos again.

With 7 tx fifos we can: send those 7 frames in 0.33ms and then we
wait for 0.26ms so that is a 56% duty cycle.

With 24 tx fifos this changes to: 1.19ms for 24 frames and then we
wait for 0.52ms so that is a 70% duty cycle.

Signed-off-by: Martin Sperl <kernel@martin.sperl.org>

---

Changelog:
  V1 -> V2: new more generic name based on feedback from microchip
            cleanup of code (checkpatch)
            address all feedback on code
            prepare code for GPIO handling (separate patches)
            handle systemerrors in a much better/reliable way
  V2 -> V3: added vendor-prefix for gpio-opendrain
  V3 -> V4: resend
  V4 -> V5: split can driver into a separate patch
            code cleanup/rewrite

---
 drivers/net/can/spi/mcp25xxfd/Makefile             |   4 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h          |  36 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c     |  52 +-
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c      | 593 ++++++++++++++-
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h      | 275 ++++++-
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c | 479 ++++++++++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c  | 723 ++++++++++++++++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c   | 208 ++++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c   | 824 +++++++++++++++++++++
 9 files changed, 3160 insertions(+), 34 deletions(-)
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c

diff --git a/drivers/net/can/spi/mcp25xxfd/Makefile b/drivers/net/can/spi/mcp25xxfd/Makefile
index 5dd7f13674ec..6a27cec424de 100644
--- a/drivers/net/can/spi/mcp25xxfd/Makefile
+++ b/drivers/net/can/spi/mcp25xxfd/Makefile
@@ -4,4 +4,8 @@
 obj-$(CONFIG_CAN_MCP25XXFD)	+= mcp25xxfd.o
 mcp25xxfd-objs                  := mcp25xxfd_base.o
 mcp25xxfd-objs                  += mcp25xxfd_can.o
+mcp25xxfd-objs                  += mcp25xxfd_can_fifo.o
+mcp25xxfd-objs                  += mcp25xxfd_can_int.o
+mcp25xxfd-objs                  += mcp25xxfd_can_rx.o
+mcp25xxfd-objs                  += mcp25xxfd_can_tx.o
 mcp25xxfd-objs                  += mcp25xxfd_gpio.o
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
index 73817a567219..624602ca898f 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
@@ -107,6 +107,7 @@ enum mcp25xxfd_model {

 struct mcp25xxfd_priv {
 	struct spi_device *spi;
+	struct net_device *net;
 	struct gpio_chip *gpio;
 	struct clk *clk;

@@ -154,6 +155,11 @@ struct mcp25xxfd_priv {
 		u32 ecccon;
 	} regs;

+	/* flag to see if the irq is enabled */
+	struct {
+		int enabled;
+	} irq;
+
 	/* debugfs related */
 #if defined(CONFIG_DEBUG_FS)
 	struct dentry *debugfs_dir;
@@ -188,6 +194,28 @@ static inline void mcp25xxfd_convert_from_cpu(u32 *data, int n)
 		data[i] = cpu_to_le32(data[i]);
 }

+static inline void mcp25xxfd_calc_cmd_addr(u16 cmd, u16 addr, u8 *data)
+{
+	cmd = cmd | (addr & ADDRESS_MASK);
+
+	data[0] = (cmd >> 8) & 0xff;
+	data[1] = (cmd >> 0) & 0xff;
+}
+
+static inline int mcp25xxfd_first_byte(u32 mask)
+{
+	return (mask & 0x0000ffff) ?
+		((mask & 0x000000ff) ? 0 : 1) :
+		((mask & 0x00ff0000) ? 2 : 3);
+}
+
+static inline int mcp25xxfd_last_byte(u32 mask)
+{
+	return (mask & 0xffff0000) ?
+		((mask & 0xff000000) ? 3 : 2) :
+		((mask & 0x0000ff00) ? 1 : 0);
+}
+
 int mcp25xxfd_cmd_readn(struct spi_device *spi, u32 reg,
 			void *data, int n);
 int mcp25xxfd_cmd_read_mask(struct spi_device *spi, u32 reg,
@@ -233,11 +261,19 @@ int mcp25xxfd_can_hw_probe(struct spi_device *spi);
 /* clearing interrupt flags */
 int mcp25xxfd_clear_crc_interrupts(struct spi_device *spi);
 int mcp25xxfd_clear_ecc_interrupts(struct spi_device *spi);
+int mcp25xxfd_clear_can_interrupts(struct spi_device *spi);
 int mcp25xxfd_clear_interrupts(struct spi_device *spi);

 /* enabling interrupts */
 int mcp25xxfd_enable_interrupts(struct spi_device *spi, bool enable);
+int mcp25xxfd_enable_can_interrupts(struct spi_device *spi, bool enable);

 /* gpiolib support */
 int mcp25xxfd_gpio_setup(struct spi_device *spi);
 void mcp25xxfd_gpio_remove(struct spi_device *spi);
+
+/* can support */
+int mcp25xxfd_can_setup(struct spi_device *spi);
+void mcp25xxfd_can_remove(struct spi_device *spi);
+int mcp25xxfd_can_suspend(struct spi_device *spi);
+int mcp25xxfd_can_resume(struct spi_device *spi);
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
index b0851d97bcbb..63c69139cfdf 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
@@ -226,28 +226,6 @@ static int mcp25xxfd_write_then_write(struct spi_device *spi,

 /* mcp25xxfd spi command/protocol helper */

-static void mcp25xxfd_calc_cmd_addr(u16 cmd, u16 addr, u8 *data)
-{
-	cmd = cmd | (addr & ADDRESS_MASK);
-
-	data[0] = (cmd >> 8) & 0xff;
-	data[1] = (cmd >> 0) & 0xff;
-}
-
-static int mcp25xxfd_first_byte(u32 mask)
-{
-	return (mask & 0x0000ffff) ?
-		((mask & 0x000000ff) ? 0 : 1) :
-		((mask & 0x00ff0000) ? 2 : 3);
-}
-
-static int mcp25xxfd_last_byte(u32 mask)
-{
-	return (mask & 0xffff0000) ?
-		((mask & 0xff000000) ? 3 : 2) :
-		((mask & 0x0000ff00) ? 1 : 0);
-}
-
 /* read multiple bytes, transform some registers */
 int mcp25xxfd_cmd_readn(struct spi_device *spi, u32 reg,
 			void *data, int n)
@@ -508,6 +486,7 @@ int mcp25xxfd_clear_interrupts(struct spi_device *spi)
 {
 	mcp25xxfd_clear_ecc_interrupts(spi);
 	mcp25xxfd_clear_crc_interrupts(spi);
+	mcp25xxfd_clear_can_interrupts(spi);

 	return 0;
 }
@@ -542,7 +521,7 @@ static int mcp25xxfd_enable_crc_interrupts(struct spi_device *spi,
 int mcp25xxfd_enable_interrupts(struct spi_device *spi,
 				bool enable)
 {
-	/* error handling only on enable for this function */
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
 	int ret = 0;

 	/* if we enable clear interrupt flags first */
@@ -559,6 +538,21 @@ int mcp25xxfd_enable_interrupts(struct spi_device *spi,
 	if (enable && ret)
 		goto out_ecc;

+	ret = mcp25xxfd_enable_can_interrupts(spi, enable);
+	if (enable && ret)
+		goto out_ecc;
+
+	/* enable/disable interrupt as required */
+	if (enable) {
+		if (!priv->irq.enabled)
+			enable_irq(spi->irq);
+	} else {
+		if (priv->irq.enabled)
+			disable_irq(spi->irq);
+	}
+	/* mark enabled/disabled */
+	priv->irq.enabled = enable;
+
 	/* if we disable interrupts clear interrupt flags last */
 	if (!enable)
 		mcp25xxfd_clear_interrupts(spi);
@@ -1110,15 +1104,22 @@ static int mcp25xxfd_probe(struct spi_device *spi)
 	if (ret)
 		goto out_debugfs;

+	/* add can (depends on debugfs) */
+	ret = mcp25xxfd_can_setup(spi);
+	if (ret)
+		goto out_gpio;
+
 	/* and put controller to sleep by stopping the can clock */
 	ret = mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
 	if (ret)
-		goto out_gpio;
+		goto out_can;

 	dev_info(&spi->dev,
 		 "MCP%x successfully initialized.\n", priv->model);
 	return 0;

+out_can:
+	mcp25xxfd_can_remove(spi);
 out_gpio:
 	mcp25xxfd_gpio_remove(spi);
 out_debugfs:
@@ -1139,6 +1140,9 @@ static int mcp25xxfd_remove(struct spi_device *spi)
 {
 	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);

+	/* remove can */
+	mcp25xxfd_can_remove(spi);
+
 	/* remove gpio */
 	mcp25xxfd_gpio_remove(spi);

diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
index e5c7098a09c2..e827edb37da8 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
@@ -43,18 +43,160 @@

 /* Implementation notes:
  *
- * Right now we only use the CAN controller block to put us into deep sleep
- * this means that the oscillator clock is turned off.
- * So this is the only thing that we implement here right now
+ * * the can related portion of the driver is split out into
+ *   * basic configuration (mcp25xxfd_can.c)
+ *   * can fifo configuration (mcp25xxfd_can_fifo.c)
+ *   * can interrupt handling (mcp25xxfd_can_int.c)
+ *   * can reception including interrupt handling (mcp25xxfd_can_rx.c)
+ *   * can transmission including interrupt handling (mcp25xxfd_can_tx.c)
+ * * the interrupt handler trys to issue the smallest amount of
+ *   spi messages to avoid spi stack overhead. for that it is more agressive
+ *   when reading registers that are close to each other - e.g:
+ *   * if there is one register that is not really needed then it will
+ *     still read it to avoid the startup and teardown costs of the
+ *     spi stack which results in latencies.
+ *   * when in can2.0 mode it will read all 8 data bytes even if DLC = 0
+ *     the header on its own is 12 bytes and then there are 2 bytes for
+ *     the spi overhead so the effecitve worsted case overhead introduced
+ *     is +57%
+ * * due to the reordering inside the controller the Transmission queue
+ *   feature is not used (it also makes the "next" frame freed less
+ *   predictable which would complicate the code as well.
+ *   the only advantage seen is that one could have more transmission
+ *   slots in the case of CAN2.0 only.
+ * * transmissions are instead handled by fixed priority fifos that can
+ *   get filled and triggered asyncronously.
+ *   The biggest drawback is that we are unable to sustain 100% CAN bus
+ *   usage for more than the <number of can fifos allocated> frames.
+ *   This means that we can have a duty cycle of about 7/8th of the CAN bus
+ *   (unless more fifos are reserved for transmission)
+ *   The only situation where this would be observable in real live
+ *   would be big transfers (for firmware updates or similar)
+ *   * this could have been better supported with a better
+ *     layout of FIFOCON, so that TXREQ and TXPRIO can be written
+ *     in a single transfer without a race condition
+ * * can transmissions are handled by using spi_async for submission and
+ *   scheduling. This somewhat impacts interrupt handling as some spi_sync
+ *   calls will defer to the spi kthread resulting in some context switches.
+ *   which introduces some latencies.
+ * * some special features of the can controller can only get configured
+ *   by the use of module parameters (some of which are changeable at
+ *   runtime via sysfs) due to the fact that netlink does not support:
+ *   * 3-shot submission
+ *   * selection of number of fifos
+ *   * configuarble delay between transmission of two can frames so that
+ *     the can bus is not monopolized by this device.
+ *   also some debug settings are controlable via modules.
  */

 #include "mcp25xxfd_can.h"
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/debugfs.h>
 #include <linux/device.h>
 #include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
 #include <linux/spi/spi.h>

-static int mcp25xxfd_can_get_mode(struct spi_device *spi,
-				  u32 *mode_data)
+/* module parameters */
+unsigned int bw_sharing_log2bits;
+module_param(bw_sharing_log2bits, uint, 0664);
+MODULE_PARM_DESC(bw_sharing_log2bits,
+		 "Delay between 2 transmissions in number of arbitration bit times\n");
+/* everything related to bit timing */
+static
+const struct can_bittiming_const mcp25xxfd_can_nominal_bittiming_const = {
+	.name           = DEVICE_NAME,
+	.tseg1_min      = 2,
+	.tseg1_max      = BIT(CAN_NBTCFG_TSEG1_BITS),
+	.tseg2_min      = 1,
+	.tseg2_max      = BIT(CAN_NBTCFG_TSEG2_BITS),
+	.sjw_max        = BIT(CAN_NBTCFG_SJW_BITS),
+	.brp_min        = 1,
+	.brp_max        = BIT(CAN_NBTCFG_BRP_BITS),
+	.brp_inc        = 1,
+};
+
+static
+const struct can_bittiming_const mcp25xxfd_can_data_bittiming_const = {
+	.name           = DEVICE_NAME,
+	.tseg1_min      = 1,
+	.tseg1_max      = BIT(CAN_DBTCFG_TSEG1_BITS),
+	.tseg2_min      = 1,
+	.tseg2_max      = BIT(CAN_DBTCFG_TSEG2_BITS),
+	.sjw_max        = BIT(CAN_DBTCFG_SJW_BITS),
+	.brp_min        = 1,
+	.brp_max        = BIT(CAN_DBTCFG_BRP_BITS),
+	.brp_inc        = 1,
+};
+
+static int mcp25xxfd_can_do_set_nominal_bittiming(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp25xxfd_priv *priv = cpriv->priv;
+	struct can_bittiming *bt = &cpriv->can.bittiming;
+	struct spi_device *spi = priv->spi;
+
+	int sjw = bt->sjw;
+	int pseg2 = bt->phase_seg2;
+	int pseg1 = bt->phase_seg1;
+	int propseg = bt->prop_seg;
+	int brp = bt->brp;
+
+	int tseg1 = propseg + pseg1;
+	int tseg2 = pseg2;
+
+	/* calculate nominal bit timing */
+	cpriv->regs.nbtcfg = ((sjw - 1) << CAN_NBTCFG_SJW_SHIFT) |
+		((tseg2 - 1) << CAN_NBTCFG_TSEG2_SHIFT) |
+		((tseg1 - 1) << CAN_NBTCFG_TSEG1_SHIFT) |
+		((brp - 1) << CAN_NBTCFG_BRP_SHIFT);
+
+	return mcp25xxfd_cmd_write(spi, CAN_NBTCFG,
+				   cpriv->regs.nbtcfg);
+}
+
+static int mcp25xxfd_can_do_set_data_bittiming(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp25xxfd_priv *priv = cpriv->priv;
+	struct can_bittiming *bt = &cpriv->can.data_bittiming;
+	struct spi_device *spi = priv->spi;
+
+	int sjw = bt->sjw;
+	int pseg2 = bt->phase_seg2;
+	int pseg1 = bt->phase_seg1;
+	int propseg = bt->prop_seg;
+	int brp = bt->brp;
+
+	int tseg1 = propseg + pseg1;
+	int tseg2 = pseg2;
+
+	int ret;
+
+	/* set up Transmitter delay compensation */
+	if (!cpriv->regs.tdc)
+		cpriv->regs.tdc = CAN_TDC_EDGFLTEN |
+			(CAN_TDC_TDCMOD_AUTO << CAN_TDC_TDCMOD_SHIFT);
+	ret = mcp25xxfd_cmd_write(spi, CAN_TDC, cpriv->regs.tdc);
+	if (ret)
+		return ret;
+
+	/* calculate nominal bit timing */
+	cpriv->regs.dbtcfg = ((sjw - 1) << CAN_DBTCFG_SJW_SHIFT) |
+		((tseg2 - 1) << CAN_DBTCFG_TSEG2_SHIFT) |
+		((tseg1 - 1) << CAN_DBTCFG_TSEG1_SHIFT) |
+		((brp - 1) << CAN_DBTCFG_BRP_SHIFT);
+
+	return mcp25xxfd_cmd_write(spi, CAN_DBTCFG,
+				   cpriv->regs.dbtcfg);
+}
+
+/* configuration functions that are used by the base during initialization */
+int mcp25xxfd_can_get_mode(struct spi_device *spi, u32 *mode_data)
 {
 	int ret;

@@ -65,11 +207,11 @@ static int mcp25xxfd_can_get_mode(struct spi_device *spi,
 	return (*mode_data & CAN_CON_OPMOD_MASK) >> CAN_CON_OPMOD_SHIFT;
 }

-int mcp25xxfd_can_switch_mode(struct spi_device *spi,
-			      u32 *mode_data, int mode)
+int mcp25xxfd_can_switch_mode_nowait(struct spi_device *spi,
+				     u32 *mode_data, int mode)
 {
 	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
-	int ret, i;
+	int ret;

 	/* get the current mode/register - if mode_data is -1
 	 * this only happens during initialization phase
@@ -98,7 +240,16 @@ int mcp25xxfd_can_switch_mode(struct spi_device *spi,
 	}

 	/* request the mode switch */
-	ret = mcp25xxfd_cmd_write(spi, CAN_CON, *mode_data);
+	return mcp25xxfd_cmd_write(spi, CAN_CON, *mode_data);
+}
+
+int mcp25xxfd_can_switch_mode(struct spi_device *spi,
+			      u32 *mode_data, int mode)
+{
+	int ret, i;
+
+	/* trigger the mode switch itself */
+	ret = mcp25xxfd_can_switch_mode_nowait(spi, mode_data, mode);
 	if (ret)
 		return ret;

@@ -226,3 +377,427 @@ int mcp25xxfd_can_hw_probe(struct spi_device *spi)
 	/* check that modeswitch is really working */
 	return mcp25xxfd_can_hw_probe_modeswitch(spi);
 }
+
+/* debugfs related */
+#if defined(CONFIG_DEBUG_FS)
+
+static int mcp25xxfd_can_dump_all_regs(struct seq_file *file, void *offset)
+{
+	return mcp25xxfd_dump_regs_range(file, CAN_CON, CAN_FLTMASK(31));
+}
+
+static int mcp25xxfd_can_dump_regs(struct seq_file *file, void *offset)
+{
+	return mcp25xxfd_dump_regs_range(file, CAN_CON, CAN_TXQUA);
+}
+
+static void mcp25xxfd_can_debugfs_add(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(priv->net);
+	struct dentry *root = priv->debugfs_dir;
+	struct dentry *cregs, *cstatus, *cfifotef, *cstats;
+
+	/* export config registers */
+	cregs = debugfs_create_dir("can_regs", root);
+	debugfs_create_x32("con",    0444, cregs, &cpriv->regs.con);
+	debugfs_create_x32("tdc",    0774, cregs, &cpriv->regs.tdc);
+	debugfs_create_x32("tscon",  0444, cregs, &cpriv->regs.tscon);
+	debugfs_create_x32("tefcon", 0444, cregs, &cpriv->regs.tscon);
+	debugfs_create_x32("nbtcfg", 0444, cregs, &cpriv->regs.nbtcfg);
+	debugfs_create_x32("dbtcfg", 0444, cregs, &cpriv->regs.dbtcfg);
+
+	/* export status registers */
+	cstatus = debugfs_create_dir("can_status", root);
+	debugfs_create_x32("intf",    0444, cstatus,
+			   &cpriv->status.intf);
+	debugfs_create_x32("rx_if",   0444, cstatus,
+			   &cpriv->status.rxif);
+	debugfs_create_x32("tx_if",   0444, cstatus,
+			   &cpriv->status.txif);
+	debugfs_create_x32("rx_ovif", 0444, cstatus,
+			   &cpriv->status.rxovif);
+	debugfs_create_x32("tx_atif", 0444, cstatus,
+			   &cpriv->status.txatif);
+	debugfs_create_x32("tx_req",  0444, cstatus,
+			   &cpriv->status.txreq);
+	debugfs_create_x32("trec",    0444, cstatus,
+			   &cpriv->status.trec);
+
+	cfifotef = debugfs_create_dir("can_tef", root);
+	debugfs_create_u32("count",  0444, cfifotef,
+			   &cpriv->fifos.tef.count);
+	debugfs_create_u32("size",  0444, cfifotef,
+			   &cpriv->fifos.tef.size);
+
+	/* dump the controller registers themselves */
+	debugfs_create_devm_seqfile(&priv->spi->dev, "can_regs_live_dump",
+				    root, mcp25xxfd_can_dump_regs);
+	debugfs_create_devm_seqfile(&priv->spi->dev, "can_regs_full_live_dump",
+				    root, mcp25xxfd_can_dump_all_regs);
+
+	/* and stats */
+	cstats = debugfs_create_dir("can_stats", root);
+# define DEBUGFS_CREATE(name, var) \
+	debugfs_create_u64(name,  0444, cstats, &cpriv->stats.var)
+	DEBUGFS_CREATE("int_calls",		 irq_calls);
+	DEBUGFS_CREATE("int_loops",		 irq_loops);
+	DEBUGFS_CREATE("int_system_error",	 int_serr_count);
+	DEBUGFS_CREATE("int_system_error_tx",	 int_serr_tx_count);
+	DEBUGFS_CREATE("int_system_error_rx",	 int_serr_rx_count);
+	DEBUGFS_CREATE("int_mode_switch",	 int_mod_count);
+	DEBUGFS_CREATE("int_rx",		 int_rx_count);
+	DEBUGFS_CREATE("int_tx_attempt",	 int_txat_count);
+	DEBUGFS_CREATE("int_tef",		 int_tef_count);
+	DEBUGFS_CREATE("int_rx_overflow",	 int_rxov_count);
+	DEBUGFS_CREATE("int_ecc_error",		 int_ecc_count);
+	DEBUGFS_CREATE("int_rx_invalid_message", int_ivm_count);
+	DEBUGFS_CREATE("int_crcerror",		 int_cerr_count);
+
+	DEBUGFS_CREATE("tx_frames_fd",		 tx_fd_count);
+	DEBUGFS_CREATE("tx_frames_brs",		 tx_brs_count);
+#undef DEBUGFS_CREATE
+}
+#else
+static void mcp25xxfd_can_debugfs_add(struct spi_device *spi)
+{
+}
+#endif
+
+int mcp25xxfd_clear_can_interrupts(struct spi_device *spi)
+{
+	return mcp25xxfd_cmd_write_mask(spi, CAN_INT, 0, CAN_INT_IF_MASK);
+}
+
+int mcp25xxfd_enable_can_interrupts(struct spi_device *spi, bool enable)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct mcp25xxfd_can_priv *cpriv = priv->net ?
+		netdev_priv(priv->net) : NULL;
+	const u32 mask = CAN_INT_TEFIE |
+		CAN_INT_RXIE |
+		CAN_INT_MODIE |
+		CAN_INT_SERRIE |
+		CAN_INT_IVMIE |
+		CAN_INT_CERRIE |
+		CAN_INT_RXOVIE |
+		CAN_INT_ECCIE;
+	u32 value = cpriv ? cpriv->status.intf : 0;
+
+	/* apply mask and */
+	value &= ~(CAN_INT_IE_MASK);
+	if (enable)
+		value |= mask;
+	/* write back if net is set up */
+	if (cpriv)
+		cpriv->status.intf = value;
+
+	/* and write to int register */
+	return mcp25xxfd_cmd_write_mask(spi, CAN_INT, value, mask);
+}
+
+static int mcp25xxfd_can_config(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp25xxfd_priv *priv = cpriv->priv;
+	struct spi_device *spi = priv->spi;
+	int ret;
+
+	/* setup value of con_register */
+	cpriv->regs.con = CAN_CON_STEF; /* enable TEF, disable TXQUEUE */
+
+	/* transmission bandwidth sharing bits */
+	if (bw_sharing_log2bits > 12)
+		bw_sharing_log2bits = 12;
+	cpriv->regs.con |= bw_sharing_log2bits << CAN_CON_TXBWS_SHIFT;
+
+	/* non iso FD mode */
+	if (!(cpriv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO))
+		cpriv->regs.con |= CAN_CON_ISOCRCEN;
+
+	/* one shot */
+	if (cpriv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+		cpriv->regs.con |= CAN_CON_RTXAT;
+
+	/* apply it now together with a mode switch */
+	ret = mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
+					CAN_CON_MODE_CONFIG);
+	if (ret)
+		return 0;
+
+	/* time stamp control register - 1ns resolution */
+	cpriv->regs.tscon = 0;
+	ret = mcp25xxfd_cmd_write(spi, CAN_TBC, 0);
+	if (ret)
+		return ret;
+
+	cpriv->regs.tscon = CAN_TSCON_TBCEN |
+		((cpriv->can.clock.freq / 1000000)
+		 << CAN_TSCON_TBCPRE_SHIFT);
+	ret = mcp25xxfd_cmd_write(spi, CAN_TSCON, cpriv->regs.tscon);
+	if (ret)
+		return ret;
+
+	/* setup fifos */
+	ret = mcp25xxfd_can_setup_fifos(net);
+	if (ret)
+		return ret;
+
+	/* setup can bittiming now - the do_set_bittiming methods
+	 * are not used as they get callled before open
+	 */
+	ret = mcp25xxfd_can_do_set_nominal_bittiming(net);
+	if (ret)
+		return ret;
+	ret = mcp25xxfd_can_do_set_data_bittiming(net);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+static void mcp25xxfd_can_shutdown(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp25xxfd_priv *priv = cpriv->priv;
+	struct spi_device *spi = priv->spi;
+
+	/* switch us to CONFIG mode - this disables the controller */
+	mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
+				  CAN_CON_MODE_CONFIG);
+}
+
+/* all ops */
+
+/* open and stop */
+static int mcp25xxfd_can_open(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp25xxfd_priv *priv = cpriv->priv;
+	struct spi_device *spi = priv->spi;
+	int ret;
+
+	netdev_err(net, "OPEN\n");
+
+	ret = open_candev(net);
+	if (ret) {
+		netdev_err(net, "unable to set initial baudrate!\n");
+		return ret;
+	}
+
+	/* clear those statistics */
+	memset(&cpriv->stats, 0, sizeof(cpriv->stats));
+
+	/* request an IRQ but keep disabled for now */
+	ret = request_threaded_irq(spi->irq, NULL,
+				   mcp25xxfd_can_int,
+				   IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+				   DEVICE_NAME, priv);
+	if (ret) {
+		dev_err(&spi->dev, "failed to acquire irq %d - %i\n",
+			spi->irq, ret);
+		goto out_candev;
+	}
+	disable_irq(spi->irq);
+	priv->irq.enabled = false;
+
+	/* enable power to the transceiver */
+	ret = mcp25xxfd_power_enable(cpriv->transceiver, 1);
+	if (ret)
+		goto out_irq;
+
+	/* enable clock (so that spi works) */
+	ret = mcp25xxfd_start_clock(spi, MCP25XXFD_CLK_USER_CAN);
+	if (ret)
+		goto out_transceiver;
+
+	/* configure controller for reception */
+	ret = mcp25xxfd_can_config(net);
+	if (ret)
+		goto out_canclock;
+
+	/* setting up state */
+	cpriv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+	/* enable interrupts */
+	ret = mcp25xxfd_enable_interrupts(spi, true);
+	if (ret)
+		goto out_canconfig;
+
+	/* switch to active mode */
+	ret = mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
+					(net->mtu == CAN_MTU) ?
+					CAN_CON_MODE_CAN2_0 :
+					CAN_CON_MODE_MIXED);
+	if (ret)
+		goto out_int;
+
+	/* start the tx_queue */
+	mcp25xxfd_can_tx_queue_manage(net, TX_QUEUE_STATE_STARTED);
+
+	return 0;
+
+out_int:
+	mcp25xxfd_enable_interrupts(spi, false);
+out_canconfig:
+	mcp25xxfd_can_release_fifos(net);
+out_canclock:
+	mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
+out_transceiver:
+	mcp25xxfd_power_enable(cpriv->transceiver, 0);
+out_irq:
+	free_irq(spi->irq, priv);
+out_candev:
+	close_candev(net);
+	return ret;
+}
+
+static int mcp25xxfd_can_stop(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp25xxfd_priv *priv = cpriv->priv;
+	struct spi_device *spi = priv->spi;
+
+	netdev_err(net, "STOP\n");
+
+	/* stop transmit queue */
+	mcp25xxfd_can_tx_queue_manage(net, TX_QUEUE_STATE_STOPPED);
+
+	/* release debugfs */
+	mcp25xxfd_can_release_fifos(net);
+
+	/* shutdown the can controller */
+	mcp25xxfd_can_shutdown(net);
+
+	/* disable inerrupts on controller */
+	mcp25xxfd_enable_interrupts(spi, false);
+
+	/* stop the clock */
+	mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
+
+	/* and disable the transceiver */
+	mcp25xxfd_power_enable(cpriv->transceiver, 0);
+
+	/* disable interrupt on host */
+	free_irq(spi->irq, priv);
+	priv->irq.enabled = false;
+
+	/* close the can_decice */
+	close_candev(net);
+
+	return 0;
+}
+
+/* mode setting */
+static int mcp25xxfd_can_do_set_mode(struct net_device *net,
+				     enum can_mode mode)
+{
+	switch (mode) {
+	case CAN_MODE_START:
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+/* binary error counters */
+static int mcp25xxfd_can_get_berr_counter(const struct net_device *net,
+					  struct can_berr_counter *bec)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+	bec->txerr = (cpriv->status.trec & CAN_TREC_TEC_MASK) >>
+		CAN_TREC_TEC_SHIFT;
+	bec->rxerr = (cpriv->status.trec & CAN_TREC_REC_MASK) >>
+		CAN_TREC_REC_SHIFT;
+
+	return 0;
+}
+
+static const struct net_device_ops mcp25xxfd_netdev_ops = {
+	.ndo_open = mcp25xxfd_can_open,
+	.ndo_stop = mcp25xxfd_can_stop,
+	.ndo_start_xmit = mcp25xxfd_can_start_xmit,
+	.ndo_change_mtu = can_change_mtu,
+};
+
+/* probe and remove */
+int mcp25xxfd_can_setup(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct mcp25xxfd_can_priv *cpriv;
+	struct net_device *net;
+	struct regulator *transceiver;
+	int ret;
+
+	/* get transceiver power regulator*/
+	transceiver = devm_regulator_get_optional(&spi->dev,
+						  "xceiver");
+	if (PTR_ERR(transceiver) == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+
+	/* allocate can device */
+	net = alloc_candev(sizeof(*cpriv), TX_ECHO_SKB_MAX);
+	if (!net)
+		return -ENOMEM;
+
+	/* and do some cross-asignments */
+	cpriv = netdev_priv(net);
+	cpriv->priv = priv;
+	priv->net = net;
+	SET_NETDEV_DEV(net, &spi->dev);
+
+	net->netdev_ops = &mcp25xxfd_netdev_ops;
+	net->flags |= IFF_ECHO;
+
+	cpriv->transceiver = transceiver;
+
+	cpriv->can.clock.freq = priv->clock_freq;
+
+	cpriv->can.bittiming_const =
+		&mcp25xxfd_can_nominal_bittiming_const;
+	cpriv->can.data_bittiming_const =
+		&mcp25xxfd_can_data_bittiming_const;
+	/* we are not setting bit-timing methods here as they get
+	 * called by the framework before open so the controller is
+	 * still in sleep mode, which does not help
+	 */
+	cpriv->can.do_set_mode =
+		mcp25xxfd_can_do_set_mode;
+	cpriv->can.do_get_berr_counter =
+		mcp25xxfd_can_get_berr_counter;
+	cpriv->can.ctrlmode_supported =
+		CAN_CTRLMODE_FD |
+		CAN_CTRLMODE_FD_NON_ISO |
+		CAN_CTRLMODE_LOOPBACK |
+		CAN_CTRLMODE_LISTENONLY |
+		CAN_CTRLMODE_BERR_REPORTING |
+		CAN_CTRLMODE_ONE_SHOT;
+
+	ret = register_candev(net);
+	if (ret) {
+		dev_err(&spi->dev, "Failed to register can device\n");
+		goto out;
+	}
+
+	mcp25xxfd_can_debugfs_add(spi);
+
+	return 0;
+out:
+	free_candev(net);
+
+	return ret;
+}
+
+void mcp25xxfd_can_remove(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+	if (priv->net) {
+		unregister_candev(priv->net);
+		free_candev(priv->net);
+		priv->net = NULL;
+	}
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
index fe0e93e28b62..ce35518fdb28 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
@@ -2,7 +2,7 @@

 /* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
  *
- * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
+ * Copyright 2018 Martin Sperl <kernel@martin.sperl.org>
  *
  * Based on Microchip MCP251x CAN controller driver written by
  * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
@@ -10,6 +10,12 @@

 #include "mcp25xxfd.h"
 #include <linux/bitops.h>
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>

 #define CAN_SFR_BASE(x)			(0x000 + (x))
 #define CAN_CON				CAN_SFR_BASE(0x00)
@@ -205,6 +211,13 @@
 	 CAN_INT_SERRIF |		\
 	 CAN_INT_WAKIF |		\
 	 CAN_INT_IVMIF)
+#  define CAN_INT_IF_CLEAR_MASK		\
+	(CAN_INT_TBCIF	|		\
+	 CAN_INT_MODIF	|		\
+	 CAN_INT_CERRIF |		\
+	 CAN_INT_SERRIF |		\
+	 CAN_INT_WAKIF |		\
+	 CAN_INT_IVMIF)
 #  define CAN_INT_IE_SHIFT		16
 #  define CAN_INT_TXIE			(CAN_INT_TXIF << CAN_INT_IE_SHIFT)
 #  define CAN_INT_RXIE			(CAN_INT_RXIF << CAN_INT_IE_SHIFT)
@@ -485,3 +498,263 @@
 #define CAN_OBJ_FLAGS_FILHIT_MASK				      \
 	GENMASK(CAN_FLAGS_FILHIT_SHIFT + CAN_FLAGS_FILHIT_BITS - 1,   \
 		CAN_FLAGS_FILHIT_SHIFT)
+
+#define TX_ECHO_SKB_MAX	32
+
+struct mcp25xxfd_fifo {
+	u32 count;
+	u32 start;
+	u32 increment;
+	u32 size;
+	u32 priority_start;
+	u32 priority_increment;
+	u64 dlc_usage[16];
+	u64 fd_count;
+
+	struct dentry *debugfs_dir;
+};
+
+struct mcp25xxfd_obj_ts {
+	/* using signed here to handle rollover correctly */
+	s32 ts;
+	/* positive fifos are rx, negative tx, 0 is not a valid fifo */
+	s32 fifo;
+};
+
+struct mcp25xxfd_can_priv {
+	/* can_priv has to be the first one to be usable with alloc_candev
+	 * which expects struct can_priv to be right at the start of the
+	 * priv structure
+	 */
+	struct can_priv can;
+	struct mcp25xxfd_priv *priv;
+	struct regulator *transceiver;
+
+	/* the can mode currently active */
+	int mode;
+
+	/* can config registers */
+	struct {
+		u32 con;
+		u32 tdc;
+		u32 tscon;
+		u32 tefcon;
+		u32 nbtcfg;
+		u32 dbtcfg;
+	} regs;
+
+	/* can status registers (mostly) - read in one go
+	 * bdiag0 and bdiag1 are optional, but when
+	 * berr counters are requested on a regular basis
+	 * during high CAN-bus load this would trigger the fact
+	 * that spi_sync would get queued for execution in the
+	 * spi thread and the spi handler would not get
+	 * called inline in the interrupt thread without any
+	 * context switches or wakeups...
+	 */
+	struct {
+		u32 intf;
+		/* ASSERT(CAN_INT + 4 == CAN_RXIF) */
+		u32 rxif;
+		/* ASSERT(CAN_RXIF + 4 == CAN_TXIF) */
+		u32 txif;
+		/* ASSERT(CAN_TXIF + 4 == CAN_RXOVIF) */
+		u32 rxovif;
+		/* ASSERT(CAN_RXOVIF + 4 == CAN_TXATIF) */
+		u32 txatif;
+		/* ASSERT(CAN_TXATIF + 4 == CAN_TXREQ) */
+		u32 txreq;
+		/* ASSERT(CAN_TXREQ + 4 == CAN_TREC) */
+		u32 trec;
+	} status;
+
+	/* information of fifo setup */
+	struct {
+		/* define payload size and mode */
+		u32 payload_size;
+		u32 payload_mode;
+
+		/* infos on fifo layout */
+		struct {
+			u32 count;
+			u32 size;
+			u32 index;
+		} tef;
+		struct mcp25xxfd_fifo tx;
+		struct mcp25xxfd_fifo rx;
+
+		/* the address and priority of all fifos */
+		struct {
+			u32 control;
+			u32 status;
+			u32 offset;
+		} fifo_reg[32];
+		struct {
+			u32 is_tx;
+			u32 priority;
+			u64 use_count;
+		} fifo_info[32];
+
+		/* queue of can frames that need to get submitted
+		 * to the network stack during an interrupt loop in one go
+		 * (this gets sorted by timestamp before submission
+		 * and contains both rx frames as well tx frames that have
+		 * gone over the CAN bus successfully
+		 */
+		struct mcp25xxfd_obj_ts submit_queue[32];
+		int  submit_queue_count;
+
+		/* the tx queue of spi messages */
+		struct mcp2517fd_tx_spi_message_queue *tx_queue;
+
+		/* the directory entry of the can_fifo debugfs directory */
+		struct dentry *debugfs_dir;
+	} fifos;
+
+	/* statistics */
+	struct {
+		u64 irq_calls;
+		u64 irq_loops;
+
+		u64 int_serr_count;
+		u64 int_serr_rx_count;
+		u64 int_serr_tx_count;
+		u64 int_mod_count;
+		u64 int_rx_count;
+		u64 int_txat_count;
+		u64 int_tef_count;
+		u64 int_rxov_count;
+		u64 int_ecc_count;
+		u64 int_ivm_count;
+		u64 int_cerr_count;
+
+		u64 tx_fd_count;
+		u64 tx_brs_count;
+	} stats;
+
+	/* bus state */
+	struct {
+		u32 state;
+		u32 new_state;
+
+		u32 bdiag[2];
+	} bus;
+
+	/* can merror messages */
+	struct {
+		u32 id;
+		u8  data[8];
+	} error_frame;
+
+	/* a sram equivalent */
+	u8 sram[MCP25XXFD_SRAM_SIZE];
+};
+
+struct mcp25xxfd_obj_tef {
+	u32 id;
+	u32 flags;
+	u32 ts;
+};
+
+struct mcp25xxfd_obj_tx {
+	u32 id;
+	u32 flags;
+	u8 data[];
+};
+
+struct mcp25xxfd_obj_rx {
+	u32 id;
+	u32 flags;
+	u32 ts;
+	u8 data[];
+};
+
+int mcp25xxfd_can_get_mode(struct spi_device *spi, u32 *mode_data);
+int mcp25xxfd_can_switch_mode(struct spi_device *spi, u32 *mode_data,
+			      int mode);
+irqreturn_t mcp25xxfd_can_int(int irq, void *dev_id);
+netdev_tx_t mcp25xxfd_can_start_xmit(struct sk_buff *skb,
+				     struct net_device *net);
+int mcp25xxfd_can_setup_fifos(struct net_device *net);
+void mcp25xxfd_can_release_fifos(struct net_device *net);
+
+/* ideally these would be defined in uapi/linux/can.h */
+#define CAN_EFF_SID_SHIFT		(CAN_EFF_ID_BITS - CAN_SFF_ID_BITS)
+#define CAN_EFF_SID_BITS		CAN_SFF_ID_BITS
+#define CAN_EFF_SID_MASK				      \
+	GENMASK(CAN_EFF_SID_SHIFT + CAN_EFF_SID_BITS - 1,     \
+		CAN_EFF_SID_SHIFT)
+#define CAN_EFF_EID_SHIFT		0
+#define CAN_EFF_EID_BITS		CAN_EFF_SID_SHIFT
+#define CAN_EFF_EID_MASK				      \
+	GENMASK(CAN_EFF_EID_SHIFT + CAN_EFF_EID_BITS - 1,     \
+		CAN_EFF_EID_SHIFT)
+
+static inline
+void mcp25xxfd_mcpid_to_canid(u32 mcp_id, u32 mcp_flags, u32 *can_id)
+{
+	u32 sid = (mcp_id & CAN_OBJ_ID_SID_MASK) >> CAN_OBJ_ID_SID_SHIFT;
+	u32 eid = (mcp_id & CAN_OBJ_ID_EID_MASK) >> CAN_OBJ_ID_EID_SHIFT;
+
+	/* select normal or extended ids */
+	if (mcp_flags & CAN_OBJ_FLAGS_IDE) {
+		*can_id = (eid << CAN_EFF_EID_SHIFT) |
+			(sid << CAN_EFF_SID_SHIFT) |
+			CAN_EFF_FLAG;
+	} else {
+		*can_id = sid;
+	}
+	/* handle rtr */
+	*can_id |= (mcp_flags & CAN_OBJ_FLAGS_RTR) ? CAN_RTR_FLAG : 0;
+}
+
+static inline
+void mcp25xxfd_canid_to_mcpid(u32 can_id, u32 *id, u32 *flags)
+{
+	/* depending on can_id flag compute extended or standard ids */
+	if (can_id & CAN_EFF_FLAG) {
+		int sid = (can_id & CAN_EFF_SID_MASK) >> CAN_EFF_SID_SHIFT;
+		int eid = (can_id & CAN_EFF_EID_MASK) >> CAN_EFF_EID_SHIFT;
+		*id = (eid << CAN_OBJ_ID_EID_SHIFT) |
+			(sid << CAN_OBJ_ID_SID_SHIFT);
+		*flags = CAN_OBJ_FLAGS_IDE;
+	} else {
+		*id = can_id & CAN_SFF_MASK;
+		*flags = 0;
+	}
+
+	/* Handle RTR */
+	*flags |= (can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
+}
+
+static inline
+void mcp25xxfd_can_queue_frame(struct mcp25xxfd_can_priv *cpriv,
+			       s32 fifo, s32 ts)
+{
+	int idx = cpriv->fifos.submit_queue_count;
+
+	cpriv->fifos.submit_queue[idx].fifo = fifo;
+	cpriv->fifos.submit_queue[idx].ts = ts;
+
+	cpriv->fifos.submit_queue_count++;
+}
+
+int mcp25xxfd_can_read_rx_frames(struct spi_device *spi);
+int mcp25xxfd_can_submit_rx_frame(struct spi_device *spi, int fifo);
+int mcp25xxfd_can_submit_tx_frame(struct spi_device *spi, int fifo);
+
+int mcp25xxfd_can_int_handle_txatif(struct spi_device *spi);
+int mcp25xxfd_can_int_handle_tefif(struct spi_device *spi);
+
+int mcp25xxfd_can_tx_queue_alloc(struct net_device *net);
+void mcp25xxfd_can_tx_queue_free(struct net_device *net);
+void mcp25xxfd_can_tx_queue_restart(struct net_device *net);
+void mcp25xxfd_can_tx_queue_manage(struct net_device *net, int state);
+void mcp25xxfd_can_tx_queue_manage_nolock(struct net_device *net, int state);
+# define TX_QUEUE_STATE_STOPPED  0
+# define TX_QUEUE_STATE_STARTED  1
+# define TX_QUEUE_STATE_RUNABLE 2
+# define TX_QUEUE_STATE_RESTART  3
+
+int mcp25xxfd_can_switch_mode_nowait(struct spi_device *spi,
+				     u32 *mode_data, int mode);
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c
new file mode 100644
index 000000000000..1a85fc57a955
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c
@@ -0,0 +1,479 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ */
+
+/* here we define and configure the fifo layout */
+
+#include "mcp25xxfd_can.h"
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/spi/spi.h>
+
+/* some module parameters that are currently not configurable via netlink */
+unsigned int tx_fifos;
+module_param(tx_fifos, uint, 0664);
+MODULE_PARM_DESC(tx_fifos, "Number of tx-fifos to configure\n");
+
+bool three_shot;
+module_param(three_shot, bool, 0664);
+MODULE_PARM_DESC(three_shot, "Use 3 shots when one-shot is requested");
+
+#if defined(CONFIG_DEBUG_FS)
+static
+void mcp25xxfd_can_setup_fifo_debugfs_rxtx(struct net_device *net,
+					   const char *name,
+					   struct mcp25xxfd_fifo *desc)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp25xxfd_priv *priv = cpriv->priv;
+
+	/* remove old debug directory */
+	debugfs_remove_recursive(desc->debugfs_dir);
+
+	/* create new one */
+	desc->debugfs_dir = debugfs_create_dir(name, priv->debugfs_dir);
+
+	/* and fill it */
+	debugfs_create_u32("count",  0444, desc->debugfs_dir,
+			   &desc->count);
+	debugfs_create_u32("size",  0444, desc->debugfs_dir,
+			   &desc->size);
+	debugfs_create_u32("start",  0444, desc->debugfs_dir,
+			   &desc->start);
+	debugfs_create_u32("increment",  0444, desc->debugfs_dir,
+			   &desc->increment);
+	debugfs_create_u32("priority_start",  0444, desc->debugfs_dir,
+			   &desc->priority_start);
+	debugfs_create_u32("priority_increment",  0444, desc->debugfs_dir,
+			   &desc->priority_increment);
+}
+
+static
+void mcp25xxfd_can_setup_fifo_debugfs_link_rxtx(struct mcp25xxfd_fifo *desc,
+						int index, int fifo)
+{
+	char name[4];
+	char link[32];
+
+	snprintf(name, sizeof(name), "%02i", index);
+	snprintf(link, sizeof(link), "../can_fifos/%02i", fifo);
+
+	debugfs_create_symlink(name, desc->debugfs_dir, link);
+}
+
+static
+void mcp25xxfd_can_setup_fifo_debugfs_present_fifo(struct net_device *net,
+						   int index)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct dentry *debugfs_dir;
+	char name[4];
+
+	snprintf(name, sizeof(name), "%02i", index);
+	debugfs_dir = debugfs_create_dir(name, cpriv->fifos.debugfs_dir);
+
+	/* and the entries */
+	debugfs_create_u32("is_tx", 0444, debugfs_dir,
+			   &cpriv->fifos.fifo_info[index].is_tx);
+	debugfs_create_x32("offset", 0444, debugfs_dir,
+			   &cpriv->fifos.fifo_reg[index].offset);
+	debugfs_create_u32("priority", 0444, debugfs_dir,
+			   &cpriv->fifos.fifo_info[index].priority);
+	debugfs_create_u64("use_count", 0444, debugfs_dir,
+			   &cpriv->fifos.fifo_info[index].use_count);
+}
+
+static
+void mcp25xxfd_can_setup_fifo_debugfs_present_fifos(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp25xxfd_priv *priv = cpriv->priv;
+	int i;
+
+	/* create the directory if necessary */
+	if (!cpriv->fifos.debugfs_dir)
+		cpriv->fifos.debugfs_dir =
+			debugfs_create_dir("can_fifos", priv->debugfs_dir);
+
+	/* now present all fifos
+	 * - note that there is no fifo 0 - that is the TX-queue!
+	 */
+	for (i = 1; i < 32; i++)
+		mcp25xxfd_can_setup_fifo_debugfs_present_fifo(net, i);
+}
+
+static void mcp25xxfd_can_remove_fifo_debugfs(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+	debugfs_remove_recursive(cpriv->fifos.debugfs_dir);
+	cpriv->fifos.debugfs_dir = NULL;
+	debugfs_remove_recursive(cpriv->fifos.rx.debugfs_dir);
+	cpriv->fifos.rx.debugfs_dir = NULL;
+	debugfs_remove_recursive(cpriv->fifos.tx.debugfs_dir);
+	cpriv->fifos.tx.debugfs_dir = NULL;
+}
+
+#else
+
+static
+void mcp25xxfd_can_setup_fifo_debugfs_rxtx(struct net_device *net,
+					   const char *name,
+					   struct mcp25xxfd_fifo *desc)
+{
+}
+
+static
+void mcp25xxfd_can_setup_fifo_debugfs_link_rxtx(struct mcp25xxfd_fifo *desc,
+						int index, int fifo)
+{
+}
+
+static
+void mcp25xxfd_can_setup_fifo_debugfs_present_fifos(struct net_device *net)
+{
+}
+
+static void mcp25xxfd_can_remove_fifo_debugfs(struct net_device *net)
+{
+}
+#endif
+
+static int mcp25xxfd_can_get_fifo_address(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp25xxfd_priv *priv = cpriv->priv;
+	struct spi_device *spi = priv->spi;
+	int ret;
+
+	/* write the config to the controller in one go */
+	ret = mcp25xxfd_cmd_write_regs(spi, CAN_FIFOCON(0),
+				       &cpriv->fifos.fifo_reg[0].control,
+				       sizeof(cpriv->fifos.fifo_reg));
+	if (ret)
+		return ret;
+
+	/* we need to move out of config mode to force address computation */
+	ret = mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
+					CAN_CON_MODE_INTERNAL_LOOPBACK);
+	if (ret)
+		return ret;
+
+	/* and get back into config mode */
+	ret = mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
+					CAN_CON_MODE_CONFIG);
+	if (ret)
+		return ret;
+
+	/* read address and config back in */
+	return mcp25xxfd_cmd_read_regs(spi, CAN_FIFOCON(0),
+				       &cpriv->fifos.fifo_reg[0].control,
+				       sizeof(cpriv->fifos.fifo_reg));
+}
+
+static int mcp25xxfd_can_setup_fifo_config(struct net_device *net,
+					   const char *name,
+					   struct mcp25xxfd_fifo *desc,
+					   u32 flags,
+					   u32 flags_last)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	bool is_tx;
+	u32 val;
+	int index, prio, fifo, count;
+
+	/* present fifo type info */
+	mcp25xxfd_can_setup_fifo_debugfs_rxtx(net, name, desc);
+
+	/* now setup the fifos themselves */
+	for (index = 0,
+	     fifo = desc->start,
+	     count = desc->count,
+	     prio = desc->priority_start;
+	     count > 0;
+	     index++,
+	     fifo += desc->increment,
+	     prio += desc->priority_increment,
+	     count--) {
+		/* select the effective value */
+		val = (count > 1) ? flags : flags_last;
+
+		/* are we in tx mode */
+		is_tx = flags & CAN_FIFOCON_TXEN;
+		cpriv->fifos.fifo_info[fifo].is_tx = is_tx;
+
+		/* set priority if we are tx */
+		if (is_tx) {
+			cpriv->fifos.fifo_info[fifo].priority = prio;
+			val |= (prio << CAN_FIFOCON_TXPRI_SHIFT);
+		}
+
+		/* setup interface */
+		cpriv->fifos.fifo_reg[fifo].control = val;
+
+		/* and link debugfs fifo */
+		mcp25xxfd_can_setup_fifo_debugfs_link_rxtx(desc, index,
+							   fifo);
+	}
+
+	return 0;
+}
+
+static int mcp25xxfd_can_setup_tx_fifo_config(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	u32 tx_flags;
+
+	/* TX Fifo configuration */
+	tx_flags = CAN_FIFOCON_FRESET | /* reset FIFO */
+		CAN_FIFOCON_TXEN | /* this is a TX_FIFO */
+		CAN_FIFOCON_TXATIE | /* show up txatie flags in txatif reg */
+		(cpriv->fifos.payload_mode << CAN_FIFOCON_PLSIZE_SHIFT) |
+		(0 << CAN_FIFOCON_FSIZE_SHIFT); /* 1 FIFO only */
+	if (cpriv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+		if (three_shot)
+			tx_flags |= CAN_FIFOCON_TXAT_THREE_SHOT <<
+				CAN_FIFOCON_TXAT_SHIFT;
+		else
+			tx_flags |= CAN_FIFOCON_TXAT_ONE_SHOT <<
+				CAN_FIFOCON_TXAT_SHIFT;
+	else
+		tx_flags |= CAN_FIFOCON_TXAT_UNLIMITED <<
+			CAN_FIFOCON_TXAT_SHIFT;
+
+	return mcp25xxfd_can_setup_fifo_config(net, "can_fifo_tx",
+					       &cpriv->fifos.tx,
+					       tx_flags, tx_flags);
+}
+
+static int mcp25xxfd_can_setup_rx_fifo_config(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	u32 rx_flags, rx_flags_last;
+
+	/* RX Fifo configuration */
+	rx_flags = CAN_FIFOCON_FRESET | /* reset FIFO */
+		CAN_FIFOCON_RXTSEN | /* RX timestamps */
+		CAN_FIFOCON_TFERFFIE | /* FIFO Full */
+		CAN_FIFOCON_TFHRFHIE | /* FIFO Half Full*/
+		CAN_FIFOCON_TFNRFNIE | /* FIFO not empty */
+		(cpriv->fifos.payload_mode << CAN_FIFOCON_PLSIZE_SHIFT) |
+		(0 << CAN_FIFOCON_FSIZE_SHIFT); /* 1 FIFO only */
+	rx_flags_last = rx_flags | CAN_FIFOCON_RXOVIE;
+
+	/* configure the fifos */
+	return mcp25xxfd_can_setup_fifo_config(net, "can_fifo_rx",
+					       &cpriv->fifos.rx,
+					       rx_flags, rx_flags_last);
+}
+
+static int mcp25xxfd_can_clear_rx_filter_masks(struct spi_device *spi)
+{
+	u32 data[2 * 32]; /* 2 registers (match and mask) per filter */
+
+	memset(data, 0, sizeof(data));
+
+	return mcp25xxfd_cmd_write_regs(spi, CAN_FLTOBJ(0),
+					data, sizeof(data));
+}
+
+static int mcp25xxfd_can_setup_rx_filter_config(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp25xxfd_priv *priv = cpriv->priv;
+	struct spi_device *spi = priv->spi;
+	struct mcp25xxfd_fifo *desc = &cpriv->fifos.rx;
+	u8 filter_con[32];
+	int filter, fifo, ret;
+
+	/* present all fifos */
+	mcp25xxfd_can_setup_fifo_debugfs_present_fifos(net);
+
+	/* clear the filter mask - match any frame with every filter */
+	ret = mcp25xxfd_can_clear_rx_filter_masks(spi);
+	if (ret)
+		return ret;
+
+	/* clear the filters and filter mappings for all filters */
+	memset(filter_con, 0, sizeof(filter_con));
+
+	/* and now set up the rx filters */
+	for (filter = 0, fifo = desc->start;
+	     filter < desc->count;
+	     filter++, fifo += desc->increment) {
+		/* set up filter config - we can use the mask of filter 0 */
+		filter_con[filter] = CAN_FIFOCON_FLTEN(0) |
+			(fifo << CAN_FILCON_SHIFT(0));
+	}
+
+	/* and set up filter control */
+	return mcp25xxfd_cmd_write_regs(spi, CAN_FLTCON(0),
+					(u32 *)filter_con,
+					sizeof(filter_con));
+}
+
+static int mcp25xxfd_can_compute_fifos(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	int tef_memory_used, tx_memory_used, rx_memory_available;
+
+	/* default settings as per MTU/CANFD */
+	switch (net->mtu) {
+	case CAN_MTU:
+		/* mtu is 8 */
+		cpriv->fifos.payload_size = 8;
+		cpriv->fifos.payload_mode = CAN_TXQCON_PLSIZE_8;
+
+		/* 7 tx fifos */
+		cpriv->fifos.tx.count = 7;
+
+		break;
+	case CANFD_MTU:
+		/* wish there was a way to have hw filters
+		 * that can separate based on length ...
+		 */
+		/* MTU is 64 */
+		cpriv->fifos.payload_size = 64;
+		cpriv->fifos.payload_mode = CAN_TXQCON_PLSIZE_64;
+
+		/* 7 tx fifos */
+		cpriv->fifos.tx.count = 7;
+
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* compute effective sizes */
+	cpriv->fifos.tef.size = sizeof(struct mcp25xxfd_obj_tef);
+	cpriv->fifos.tx.size = sizeof(struct mcp25xxfd_obj_tx) +
+		cpriv->fifos.payload_size;
+	cpriv->fifos.rx.size = sizeof(struct mcp25xxfd_obj_rx) +
+		cpriv->fifos.payload_size;
+
+	/* if defined as a module parameter modify the number of tx_fifos */
+	if (tx_fifos) {
+		netdev_info(net,
+			    "Using %i tx-fifos as per module parameter\n",
+			    tx_fifos);
+		cpriv->fifos.tx.count = tx_fifos;
+	}
+
+	/* there can be at the most 30 tx fifos (TEF and at least 1 RX fifo */
+	if (cpriv->fifos.tx.count > 30) {
+		netdev_err(net,
+			   "There is an absolute maximum of 30 tx-fifos\n");
+		return -EINVAL;
+	}
+
+	/* set tef fifos to the number of tx fifos */
+	cpriv->fifos.tef.count = cpriv->fifos.tx.count;
+
+	/* compute size of the tx fifos and TEF */
+	tx_memory_used = cpriv->fifos.tx.count * cpriv->fifos.tx.size;
+	tef_memory_used = cpriv->fifos.tef.count * cpriv->fifos.tef.size;
+
+	/* calculate evailable memory for RX_fifos */
+	rx_memory_available = MCP25XXFD_BUFFER_TXRX_SIZE -
+		tx_memory_used -
+		tef_memory_used;
+
+	/* we need at least one RX Frame */
+	if (rx_memory_available < cpriv->fifos.rx.size) {
+		netdev_err(net,
+			   "Configured %i tx-fifos exceeds available memory already\n",
+			   cpriv->fifos.tx.count);
+		return -EINVAL;
+	}
+
+	/* calculate possible amount of RX fifos */
+	cpriv->fifos.rx.count = rx_memory_available / cpriv->fifos.rx.size;
+
+	/* so now calculate effective number of rx-fifos
+	 * there are only 31 fifos available in total,
+	 * so we need to limit ourselves
+	 */
+	if (cpriv->fifos.rx.count + cpriv->fifos.tx.count > 31)
+		cpriv->fifos.rx.count = 31 - cpriv->fifos.tx.count;
+
+	/* define the layout now that we have gotten everything */
+	cpriv->fifos.tx.start = 1;
+	cpriv->fifos.tx.increment = 1;
+	/* highest priority is 31 */
+	cpriv->fifos.tx.priority_start = 31;
+	cpriv->fifos.tx.priority_increment = -1;
+
+	cpriv->fifos.rx.start = 1 + cpriv->fifos.tx.count;
+	cpriv->fifos.rx.increment = 1;
+	/* rx fifos do not have a priority */
+	cpriv->fifos.rx.priority_start = 0;
+	cpriv->fifos.rx.priority_increment = 0;
+
+	return 0;
+}
+
+int mcp25xxfd_can_setup_fifos(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp25xxfd_priv *priv = cpriv->priv;
+	struct spi_device *spi = priv->spi;
+	int ret;
+
+	/* compute fifos counts */
+	ret = mcp25xxfd_can_compute_fifos(net);
+	if (ret)
+		return ret;
+
+	/* configure TEF */
+	if (cpriv->fifos.tef.count)
+		cpriv->regs.tefcon =
+			CAN_TEFCON_FRESET |
+			CAN_TEFCON_TEFNEIE |
+			CAN_TEFCON_TEFTSEN |
+			((cpriv->fifos.tef.count - 1) <<
+			 CAN_TEFCON_FSIZE_SHIFT);
+	else
+		cpriv->regs.tefcon = 0;
+	ret = mcp25xxfd_cmd_write(spi, CAN_TEFCON, cpriv->regs.tefcon);
+	if (ret)
+		return ret;
+
+	/* clear fifo_reg and fifo_info */
+	memset(cpriv->fifos.fifo_reg, 0, sizeof(cpriv->fifos.fifo_reg));
+	memset(cpriv->fifos.fifo_info, 0, sizeof(cpriv->fifos.fifo_info));
+
+	/* configure FIFOS themselves */
+	ret = mcp25xxfd_can_setup_tx_fifo_config(net);
+	if (ret)
+		return ret;
+	ret = mcp25xxfd_can_setup_rx_fifo_config(net);
+	if (ret)
+		return ret;
+
+	/* get fifo addresses */
+	ret = mcp25xxfd_can_get_fifo_address(net);
+	if (ret)
+		return ret;
+
+	/* finally configure RX filters */
+	ret = mcp25xxfd_can_setup_rx_filter_config(net);
+	if (ret)
+		return ret;
+
+	/* setup tx_fifo_queue */
+	return mcp25xxfd_can_tx_queue_alloc(net);
+}
+
+void mcp25xxfd_can_release_fifos(struct net_device *net)
+{
+	mcp25xxfd_can_tx_queue_free(net);
+	mcp25xxfd_can_remove_fifo_debugfs(net);
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c
new file mode 100644
index 000000000000..842c714fbbfe
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c
@@ -0,0 +1,723 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ * implementation of can interrupt handling
+ *
+ * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ */
+
+#include "mcp25xxfd_can.h"
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+
+static void mcp25xxfd_can_error_skb(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct sk_buff *skb;
+	struct can_frame *frame;
+
+	/* allocate error frame */
+	skb = alloc_can_err_skb(net, &frame);
+	if (!skb) {
+		netdev_err(net, "cannot allocate error skb\n");
+		return;
+	}
+
+	/* setup can error frame data */
+	frame->can_id |= cpriv->error_frame.id;
+	memcpy(frame->data, cpriv->error_frame.data, sizeof(frame->data));
+
+	/* and submit it */
+	netif_receive_skb(skb);
+}
+
+static int mcp25xxfd_can_int_clear_int_flags(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	u32 clearable_irq_active = cpriv->status.intf & CAN_INT_IF_CLEAR_MASK;
+	u32 clear_irq = cpriv->status.intf & (~CAN_INT_IF_CLEAR_MASK);
+
+	/* if no clearable flags are set then skip the whole transfer */
+	if (!clearable_irq_active)
+		return 0;
+
+	return mcp25xxfd_cmd_write_mask(spi, CAN_INT,
+					clear_irq, clearable_irq_active);
+}
+
+static int mcp25xxfd_can_ist_handle_serrif_txmab(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+	net->stats.tx_fifo_errors++;
+	net->stats.tx_errors++;
+	cpriv->stats.int_serr_tx_count++;
+
+	/* and switch back into the correct mode */
+	return mcp25xxfd_can_switch_mode_nowait(spi, &cpriv->regs.con,
+						(net->mtu == CAN_MTU) ?
+						CAN_CON_MODE_CAN2_0 :
+						CAN_CON_MODE_MIXED);
+}
+
+static int mcp25xxfd_can_ist_handle_serrif_rxmab(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+	net->stats.rx_dropped++;
+	net->stats.rx_errors++;
+	cpriv->stats.int_serr_rx_count++;
+
+	return 0;
+}
+
+static int mcp25xxfd_can_int_handle_serrif(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+	if (!(cpriv->status.intf & CAN_INT_SERRIF))
+		return 0;
+
+	/* increment statistics counter now */
+	cpriv->stats.int_serr_count++;
+
+	/* interrupt flags have been cleared already */
+
+	/* Errors here are:
+	 * * Bus Bandwidth Error: when a RX Message Assembly Buffer
+	 *   is still full when the next message has already arrived
+	 *   the recived message shall be ignored
+	 * * TX MAB Underflow: when a TX Message is invalid
+	 *   due to ECC errors or TXMAB underflow
+	 *   in this situatioon the system will transition to
+	 *   Restricted or Listen Only mode
+	 */
+
+	cpriv->error_frame.id |= CAN_ERR_CRTL;
+	cpriv->error_frame.data[1] |= CAN_ERR_CRTL_UNSPEC;
+
+	/* a mode change + invalid message would indicate
+	 * TX MAB Underflow
+	 */
+	if ((cpriv->status.intf & CAN_INT_MODIF) &&
+	    (cpriv->status.intf & CAN_INT_IVMIF)) {
+		return mcp25xxfd_can_ist_handle_serrif_txmab(spi);
+	}
+
+	/* for RX there is only the RXIF an indicator
+	 * - surprizingly RX-MAB does not change mode or anything
+	 */
+	if (cpriv->status.intf & CAN_INT_RXIF)
+		return mcp25xxfd_can_ist_handle_serrif_rxmab(spi);
+
+	/* the final case */
+	dev_warn_ratelimited(&spi->dev,
+			     "unidentified system error - intf =  %08x\n",
+			     cpriv->status.intf);
+
+	return 0;
+}
+
+static int mcp25xxfd_can_int_handle_modif(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	int mode;
+	int ret;
+
+	/* Note that this irq does not get triggered in all situations
+	 * for example SERRIF will move to RESTICTED or LISTENONLY
+	 * but MODIF will not be raised!
+	 */
+
+	if (!(cpriv->status.intf & CAN_INT_MODIF))
+		return 0;
+	cpriv->stats.int_mod_count++;
+
+	/* get the current mode */
+	ret = mcp25xxfd_can_get_mode(spi, &mode);
+	if (ret)
+		return ret;
+	mode = ret;
+
+	/* switches to the same mode as before are ignored
+	 * - this typically happens if the driver is shortly
+	 *   switching to a different mode and then returning to the
+	 *   original mode
+	 */
+	if (mode == cpriv->mode)
+		return 0;
+
+	/* if we are restricted, then return to "normal" mode */
+	if (mode == CAN_CON_MODE_RESTRICTED) {
+		cpriv->mode = mode;
+		return mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
+						 (net->mtu == CAN_MTU) ?
+						 CAN_CON_MODE_CAN2_0 :
+						 CAN_CON_MODE_MIXED);
+	}
+
+	/* the controller itself will transition to sleep, so we ignore it */
+	if (mode == CAN_CON_MODE_SLEEP) {
+		cpriv->mode = mode;
+		return 0;
+	}
+
+	/* these we need to handle correctly, so warn and give context */
+	dev_warn(&spi->dev,
+		 "Controller unexpectedly switched from mode %u to %u\n",
+		 cpriv->mode, mode);
+
+	/* assign the mode as current */
+	cpriv->mode = mode;
+
+	return 0;
+}
+
+static int mcp25xxfd_compare_obj_ts(const void *a, const void *b)
+{
+	s32 ats = ((struct mcp25xxfd_obj_ts *)a)->ts;
+	s32 bts = ((struct mcp25xxfd_obj_ts *)b)->ts;
+
+	if (ats < bts)
+		return -1;
+	if (ats > bts)
+		return 1;
+	return 0;
+}
+
+static int mcp25xxfd_can_submit_frames(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp25xxfd_obj_ts *queue = cpriv->fifos.submit_queue;
+	int count = cpriv->fifos.submit_queue_count;
+	int i, fifo;
+	int ret;
+
+	/* skip processing if the queue count is 0 */
+	if (count == 0)
+		goto out;
+
+	/* sort the fifos (rx and TEF) by receive timestamp */
+	sort(queue, count, sizeof(*queue), mcp25xxfd_compare_obj_ts, NULL);
+
+	/* now submit the fifos  */
+	for (i = 0; i < count; i++) {
+		fifo = queue[i].fifo;
+		if (fifo > 0)
+			ret = mcp25xxfd_can_submit_rx_frame(spi, fifo);
+		else
+			ret = mcp25xxfd_can_submit_tx_frame(spi, -fifo);
+		if (ret)
+			return ret;
+	}
+
+	/* if we have received or transmitted something
+	 * and the IVMIE is disabled, then enable it
+	 * this is mostly to avoid unnecessary interrupts during a
+	 * disconnected CAN BUS
+	 */
+	if (!(cpriv->status.intf | CAN_INT_IVMIE)) {
+		cpriv->status.intf |= CAN_INT_IVMIE;
+		ret = mcp25xxfd_cmd_write_mask(spi, CAN_INT,
+					       cpriv->status.intf,
+					       CAN_INT_IVMIE);
+		if (ret)
+			return ret;
+	}
+
+out:
+	/* enable tx_queue if necessary */
+	mcp25xxfd_can_tx_queue_restart(net);
+
+	return 0;
+}
+
+static int mcp25xxfd_can_int_handle_rxif(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+	if (!cpriv->status.rxif)
+		return 0;
+
+	cpriv->stats.int_rx_count++;
+
+	/* read all the fifos */
+	return mcp25xxfd_can_read_rx_frames(spi);
+}
+
+static int mcp25xxfd_can_int_handle_rxovif(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	int ret, i;
+
+	if (!cpriv->status.rxovif)
+		return 0;
+	cpriv->stats.int_rxov_count++;
+
+	/* clear all fifos that have an overflow bit set */
+	for (i = 0; i < 32; i++) {
+		if (cpriv->status.rxovif & BIT(i)) {
+			/* clear fifo status */
+			ret = mcp25xxfd_cmd_write_mask(spi,
+						       CAN_FIFOSTA(i),
+						       0,
+						       CAN_FIFOSTA_RXOVIF);
+			if (ret)
+				return ret;
+
+			/* update statistics */
+			net->stats.rx_over_errors++;
+			net->stats.rx_errors++;
+
+			/* and prepare ERROR FRAME */
+			cpriv->error_frame.id |= CAN_ERR_CRTL;
+			cpriv->error_frame.data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+		}
+	}
+
+	return 0;
+}
+
+static int mcp25xxfd_can_int_handle_eccif(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+	if (!(cpriv->status.intf & CAN_INT_ECCIF))
+		return 0;
+
+	cpriv->stats.int_ecc_count++;
+
+	/* and prepare ERROR FRAME */
+	cpriv->error_frame.id |= CAN_ERR_CRTL;
+	cpriv->error_frame.data[1] |= CAN_ERR_CRTL_UNSPEC;
+
+	/* delegate to interrupt cleaning */
+	return mcp25xxfd_clear_ecc_interrupts(spi);
+}
+
+static void mcp25xxfd_can_int_handle_ivmif_tx(struct spi_device *spi,
+					      u32 *mask)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+	/* check if it is really a known tx error */
+	if ((cpriv->bus.bdiag[1] &
+	     (CAN_BDIAG1_DBIT1ERR |
+	      CAN_BDIAG1_DBIT0ERR |
+	      CAN_BDIAG1_NACKERR |
+	      CAN_BDIAG1_NBIT1ERR |
+	      CAN_BDIAG1_NBIT0ERR
+		     )) == 0)
+		return;
+
+	/* mark it as a protocol error */
+	cpriv->error_frame.id |= CAN_ERR_PROT;
+
+	/* and update statistics */
+	net->stats.tx_errors++;
+
+	/* and handle all the known cases */
+	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NACKERR) {
+		/* TX-Frame not acknowledged - connected to CAN-bus? */
+		*mask |= CAN_BDIAG1_NACKERR;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_TX;
+		net->stats.tx_aborted_errors++;
+	}
+	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NBIT1ERR) {
+		/* TX-Frame CAN-BUS Level is unexpectedly dominant */
+		*mask |= CAN_BDIAG1_NBIT1ERR;
+		net->stats.tx_carrier_errors++;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT1;
+	}
+	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NBIT0ERR) {
+		/* TX-Frame CAN-BUS Level is unexpectedly recessive */
+		*mask |= CAN_BDIAG1_NBIT0ERR;
+		net->stats.tx_carrier_errors++;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT0;
+	}
+	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_DBIT1ERR) {
+		/* TX-Frame CAN-BUS Level is unexpectedly dominant
+		 * during data phase
+		 */
+		*mask |= CAN_BDIAG1_DBIT1ERR;
+		net->stats.tx_carrier_errors++;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT1;
+	}
+	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_DBIT0ERR) {
+		/* TX-Frame CAN-BUS Level is unexpectedly recessive
+		 * during data phase
+		 */
+		*mask |= CAN_BDIAG1_DBIT0ERR;
+		net->stats.tx_carrier_errors++;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT0;
+	}
+}
+
+static void mcp25xxfd_can_int_handle_ivmif_rx(struct spi_device *spi,
+					      u32 *mask)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+	/* check if it is really a known tx error */
+	if ((cpriv->bus.bdiag[1] &
+	     (CAN_BDIAG1_DCRCERR |
+	      CAN_BDIAG1_DSTUFERR |
+	      CAN_BDIAG1_DFORMERR |
+	      CAN_BDIAG1_NCRCERR |
+	      CAN_BDIAG1_NSTUFERR |
+	      CAN_BDIAG1_NFORMERR
+		     )) == 0)
+		return;
+
+	/* mark it as a protocol error */
+	cpriv->error_frame.id |= CAN_ERR_PROT;
+
+	/* and update statistics */
+	net->stats.rx_errors++;
+
+	/* handle the cases */
+	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_DCRCERR) {
+		/* RX-Frame with bad CRC during data phase */
+		*mask |= CAN_BDIAG1_DCRCERR;
+		net->stats.rx_crc_errors++;
+		cpriv->error_frame.data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
+	}
+	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_DSTUFERR) {
+		/* RX-Frame with bad stuffing during data phase */
+		*mask |= CAN_BDIAG1_DSTUFERR;
+		net->stats.rx_frame_errors++;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_STUFF;
+	}
+	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_DFORMERR) {
+		/* RX-Frame with bad format during data phase */
+		*mask |= CAN_BDIAG1_DFORMERR;
+		net->stats.rx_frame_errors++;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_FORM;
+	}
+	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NCRCERR) {
+		/* RX-Frame with bad CRC during data phase */
+		*mask |= CAN_BDIAG1_NCRCERR;
+		net->stats.rx_crc_errors++;
+		cpriv->error_frame.data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
+	}
+	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NSTUFERR) {
+		/* RX-Frame with bad stuffing during data phase */
+		*mask |= CAN_BDIAG1_NSTUFERR;
+		net->stats.rx_frame_errors++;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_STUFF;
+	}
+	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NFORMERR) {
+		/* RX-Frame with bad format during data phase */
+		*mask |= CAN_BDIAG1_NFORMERR;
+		net->stats.rx_frame_errors++;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_FORM;
+	}
+}
+
+static int mcp25xxfd_can_int_handle_ivmif(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	u32 mask, bdiag1;
+	int ret;
+
+	if (!(cpriv->status.intf & CAN_INT_IVMIF))
+		return 0;
+
+	cpriv->stats.int_ivm_count++;
+
+	/* if we have a systemerror as well,
+	 * then ignore it as they correlate
+	 */
+	if (cpriv->status.intf & CAN_INT_SERRIF)
+		return 0;
+
+	/* read bus diagnostics */
+	ret = mcp25xxfd_cmd_read_regs(spi, CAN_BDIAG0,
+				      cpriv->bus.bdiag,
+				      sizeof(cpriv->bus.bdiag));
+	if (ret)
+		return ret;
+
+	/* clear the masks of bits to clear */
+	mask = 0;
+
+	/* check rx and tx errors */
+	mcp25xxfd_can_int_handle_ivmif_tx(spi, &mask);
+	mcp25xxfd_can_int_handle_ivmif_rx(spi, &mask);
+
+	/* clear flags if we have bits masked */
+	if (!mask) {
+		/* the unsupported case, where we are not
+		 * clearing any registers
+		 */
+		dev_warn_once(&spi->dev,
+			      "found IVMIF situation not supported by driver - bdiag = [0x%08x, 0x%08x]",
+			      cpriv->bus.bdiag[0], cpriv->bus.bdiag[1]);
+		return -EINVAL;
+	}
+
+	/* clear the bits in bdiag1 */
+	bdiag1 = cpriv->bus.bdiag[1] & (~mask);
+	/* and write it */
+	ret = mcp25xxfd_cmd_write_mask(spi, CAN_BDIAG1, bdiag1, mask);
+	if (ret)
+		return ret;
+
+	/* and clear the interrupt flag until we have received or transmited */
+	cpriv->status.intf &= ~(CAN_INT_IVMIE);
+	return mcp25xxfd_cmd_write_mask(spi, CAN_INT, cpriv->status.intf,
+					CAN_INT_IVMIE);
+}
+
+static int mcp25xxfd_can_int_handle_cerrif(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+	if (!(cpriv->status.intf & CAN_INT_CERRIF))
+		return 0;
+
+	/* this interrupt exists primarilly to counter possible
+	 * bus off situations more detailed information
+	 * can be found and controlled in the TREC register
+	 */
+
+	cpriv->stats.int_cerr_count++;
+
+	netdev_warn(net, "CAN Bus error experienced");
+
+	return 0;
+}
+
+static int mcp25xxfd_can_int_error_counters(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+	if (cpriv->status.trec & CAN_TREC_TXWARN) {
+		cpriv->bus.new_state = CAN_STATE_ERROR_WARNING;
+		cpriv->error_frame.id |= CAN_ERR_CRTL;
+		cpriv->error_frame.data[1] |= CAN_ERR_CRTL_TX_WARNING;
+	}
+	if (cpriv->status.trec & CAN_TREC_RXWARN) {
+		cpriv->bus.new_state = CAN_STATE_ERROR_WARNING;
+		cpriv->error_frame.id |= CAN_ERR_CRTL;
+		cpriv->error_frame.data[1] |= CAN_ERR_CRTL_RX_WARNING;
+	}
+	if (cpriv->status.trec & CAN_TREC_TXBP) {
+		cpriv->bus.new_state = CAN_STATE_ERROR_PASSIVE;
+		cpriv->error_frame.id |= CAN_ERR_CRTL;
+		cpriv->error_frame.data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+	}
+	if (cpriv->status.trec & CAN_TREC_RXBP) {
+		cpriv->bus.new_state = CAN_STATE_ERROR_PASSIVE;
+		cpriv->error_frame.id |= CAN_ERR_CRTL;
+		cpriv->error_frame.data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+	}
+	if (cpriv->status.trec & CAN_TREC_TXBO) {
+		cpriv->bus.new_state = CAN_STATE_BUS_OFF;
+		cpriv->error_frame.id |= CAN_ERR_BUSOFF;
+	}
+
+	return 0;
+}
+
+static int mcp25xxfd_can_int_error_handling(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+	/* based on the last state state check the new state */
+	switch (cpriv->can.state) {
+	case CAN_STATE_ERROR_ACTIVE:
+		if (cpriv->bus.new_state >= CAN_STATE_ERROR_WARNING &&
+		    cpriv->bus.new_state <= CAN_STATE_BUS_OFF)
+			cpriv->can.can_stats.error_warning++;
+		/* fallthrough */
+	case CAN_STATE_ERROR_WARNING:
+		if (cpriv->bus.new_state >= CAN_STATE_ERROR_PASSIVE &&
+		    cpriv->bus.new_state <= CAN_STATE_BUS_OFF)
+			cpriv->can.can_stats.error_passive++;
+		break;
+	default:
+		break;
+	}
+	cpriv->can.state = cpriv->bus.new_state;
+
+	/* and send error packet */
+	if (cpriv->error_frame.id)
+		mcp25xxfd_can_error_skb(spi);
+
+	/* handle BUS OFF */
+	if (cpriv->can.state == CAN_STATE_BUS_OFF) {
+		if (cpriv->can.restart_ms == 0) {
+			cpriv->can.can_stats.bus_off++;
+			can_bus_off(net);
+		}
+	} else {
+		/* restart the tx queue if needed */
+	}
+
+	return 0;
+}
+
+static int mcp25xxfd_can_int_handle_status(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	int ret;
+
+	/* clear all the interrupts asap - we have them on file allready */
+	ret = mcp25xxfd_can_int_clear_int_flags(spi);
+	if (ret)
+		return ret;
+
+	/* set up new state and error frame for this loop */
+	cpriv->bus.new_state = cpriv->bus.state;
+	memset(&cpriv->error_frame, 0, sizeof(cpriv->error_frame));
+
+	/* setup the process queue by clearing the counter */
+	cpriv->fifos.submit_queue_count = 0;
+
+	/* handle interrupts */
+
+	/* system error interrupt needs to get handled first
+	 * to get us out of restricted mode
+	 */
+	ret = mcp25xxfd_can_int_handle_serrif(spi);
+	if (ret)
+		return ret;
+
+	/* mode change interrupt */
+	ret = mcp25xxfd_can_int_handle_modif(spi);
+	if (ret)
+		return ret;
+
+	/* handle the rx */
+	ret = mcp25xxfd_can_int_handle_rxif(spi);
+	if (ret)
+		return ret;
+	/* handle aborted TX FIFOs */
+	ret = mcp25xxfd_can_int_handle_txatif(spi);
+	if (ret)
+		return ret;
+
+	/* handle the TEF */
+	ret = mcp25xxfd_can_int_handle_tefif(spi);
+	if (ret)
+		return ret;
+
+	/* handle error interrupt flags */
+	ret = mcp25xxfd_can_int_handle_rxovif(spi);
+	if (ret)
+		return ret;
+
+	/* sram ECC error interrupt */
+	ret = mcp25xxfd_can_int_handle_eccif(spi);
+	if (ret)
+		return ret;
+
+	/* message format interrupt */
+	ret = mcp25xxfd_can_int_handle_ivmif(spi);
+	if (ret)
+		return ret;
+
+	/* handle bus errors in more detail */
+	ret = mcp25xxfd_can_int_handle_cerrif(spi);
+	if (ret)
+		return ret;
+
+	/* error counter handling */
+	ret = mcp25xxfd_can_int_error_counters(spi);
+	if (ret)
+		return ret;
+
+	/* error counter handling */
+	ret = mcp25xxfd_can_int_error_handling(spi);
+	if (ret)
+		return ret;
+
+	/* and submit can frames to network stack */
+	ret = mcp25xxfd_can_submit_frames(spi);
+
+	return ret;
+}
+
+irqreturn_t mcp25xxfd_can_int(int irq, void *dev_id)
+{
+	struct mcp25xxfd_priv *priv = dev_id;
+	struct spi_device *spi = priv->spi;
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	int ret;
+
+	/* count interrupt calls */
+	cpriv->stats.irq_calls++;
+
+	/* as long as we should be running */
+	while (1) {
+		/* count irq loops */
+		cpriv->stats.irq_loops++;
+
+		/* read interrupt status flags in bulk */
+		ret = mcp25xxfd_cmd_read_regs(spi, CAN_INT,
+					      &cpriv->status.intf,
+					      sizeof(cpriv->status));
+		if (ret)
+			return ret;
+
+		/* only act if the IE mask configured has active IF bits
+		 * otherwise the Interrupt line should be deasserted already
+		 * so we can exit the loop
+		 */
+		if (((cpriv->status.intf >> CAN_INT_IE_SHIFT) &
+		       cpriv->status.intf) == 0)
+			break;
+
+		/* handle the status */
+		ret = mcp25xxfd_can_int_handle_status(spi);
+		if (ret)
+			return ret;
+	}
+
+	return IRQ_HANDLED;
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c
new file mode 100644
index 000000000000..d5e4e2a18bb7
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ * implementation of can transmission
+ *
+ * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ */
+
+#include "mcp25xxfd_can.h"
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+/* module parameters */
+bool do_not_submit_rx;
+module_param(do_not_submit_rx, bool, 0664);
+MODULE_PARM_DESC(do_not_submit_rx,
+		 "do not submit rx frames to can stack - used to test performance of the spi layer");
+
+static
+struct sk_buff *mcp25xxfd_can_submit_rx_normal_frame(struct net_device *net,
+						     u32 id,
+						     u32 dlc, u8 **data)
+{
+	struct can_frame *frame;
+	struct sk_buff *skb;
+
+	/* allocate frame */
+	skb = alloc_can_skb(net, &frame);
+	if (!skb)
+		return NULL;
+
+	/* set id, dlc and flags */
+	frame->can_id = id;
+	frame->can_dlc = dlc;
+
+	/* and set the pointer to data */
+	*data = frame->data;
+
+	return skb;
+}
+
+/* it is almost identical except for the type of the frame... */
+static
+struct sk_buff *mcp25xxfd_can_submit_rx_fd_frame(struct net_device *net,
+						 u32 id, u32 flags,
+						 u32 len, u8 **data)
+{
+	struct canfd_frame *frame;
+	struct sk_buff *skb;
+
+	/* allocate frame */
+	skb = alloc_canfd_skb(net, &frame);
+	if (!skb)
+		return NULL;
+
+	/* set id, dlc and flags */
+	frame->can_id = id;
+	frame->len = len;
+	frame->flags |= flags;
+
+	/* and set the pointer to data */
+	*data = frame->data;
+
+	return skb;
+}
+
+int mcp25xxfd_can_submit_rx_frame(struct spi_device *spi, int fifo)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	int addr = cpriv->fifos.fifo_reg[fifo].offset;
+	struct mcp25xxfd_obj_rx *rx =
+		(struct mcp25xxfd_obj_rx *)(cpriv->sram + addr);
+	u8 *data = NULL;
+	struct sk_buff *skb;
+	u32 id, dlc, len, flags;
+
+	/* compute the can_id */
+	mcp25xxfd_mcpid_to_canid(rx->id, rx->flags, &id);
+
+	/* and dlc */
+	dlc = (rx->flags & CAN_OBJ_FLAGS_DLC_MASK) >>
+		CAN_OBJ_FLAGS_DLC_SHIFT;
+	len = can_dlc2len(dlc);
+
+	/* update stats */
+	priv->net->stats.rx_packets++;
+	priv->net->stats.rx_bytes += len;
+	cpriv->fifos.rx.dlc_usage[dlc]++;
+	if (rx->flags & CAN_OBJ_FLAGS_FDF)
+		cpriv->fifos.rx.fd_count++;
+
+	/* allocate the skb buffer */
+	if (rx->flags & CAN_OBJ_FLAGS_FDF) {
+		flags = 0;
+
+		flags |= (flags & CAN_OBJ_FLAGS_BRS) ? CANFD_BRS : 0;
+		flags |= (flags & CAN_OBJ_FLAGS_ESI) ? CANFD_ESI : 0;
+		skb = mcp25xxfd_can_submit_rx_fd_frame(net, id, flags,
+						       dlc, &data);
+	} else {
+		skb = mcp25xxfd_can_submit_rx_normal_frame(net, id,
+							   len, &data);
+	}
+	if (!skb) {
+		dev_err(&spi->dev, "cannot allocate RX skb\n");
+		priv->net->stats.rx_dropped++;
+		return -ENOMEM;
+	}
+
+	/* copy the payload data */
+	memcpy(data, rx->data, len);
+
+	/* and submit the frame */
+	if (do_not_submit_rx)
+		consume_skb(skb);
+	else
+		netif_rx_ni(skb);
+
+	return 0;
+}
+
+static int mcp25xxfd_can_read_rx_frame(struct spi_device *spi,
+				       int fifo)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	int addr = cpriv->fifos.fifo_reg[fifo].offset;
+	struct mcp25xxfd_obj_rx *rx =
+		(struct mcp25xxfd_obj_rx *)(cpriv->sram + addr);
+	int len, ret;
+
+	/* we read the header plus 8 data bytes for "standard frames" */
+	ret = mcp25xxfd_cmd_readn(spi, MCP25XXFD_SRAM_ADDR(addr),
+				  rx, sizeof(*rx) + 8);
+	if (ret)
+		return ret;
+
+	/* transpose the headers to CPU format*/
+	rx->id = le32_to_cpu(rx->id);
+	rx->flags = le32_to_cpu(rx->flags);
+	rx->ts = le32_to_cpu(rx->ts);
+
+	/* compute len */
+	len = can_dlc2len((rx->flags & CAN_OBJ_FLAGS_DLC_MASK) >>
+			  CAN_OBJ_FLAGS_DLC_SHIFT);
+
+	/* read the remaining data for canfd frames */
+	if (net->mtu == CANFD_MTU && len > 8) {
+		/* here the extra portion reading CanFD frames */
+		ret = mcp25xxfd_cmd_readn(spi,
+					  MCP25XXFD_SRAM_ADDR(addr) +
+					  sizeof(*rx) + 8,
+					  &rx->data[8], len - 8);
+		if (ret)
+			return ret;
+	}
+
+	/* clear the rest of the buffer - just to be safe */
+	memset(rx->data + len, 0,
+	       ((net->mtu == CANFD_MTU) ? 64 : 8) - len);
+
+	/* increment the statistics counter */
+	cpriv->fifos.fifo_info[fifo].use_count++;
+
+	/* add the fifo to the process queues */
+	mcp25xxfd_can_queue_frame(cpriv, fifo, rx->ts);
+
+	/* and clear the interrupt flag for that fifo */
+	return mcp25xxfd_cmd_write_mask(spi, CAN_FIFOCON(fifo),
+					CAN_FIFOCON_FRESET,
+					CAN_FIFOCON_FRESET);
+}
+
+int mcp25xxfd_can_read_rx_frames(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	int i, fifo;
+	int ret;
+
+	/* we can optimize here */
+	for (i = 0, fifo = cpriv->fifos.rx.start;
+	     i < cpriv->fifos.rx.count;
+	     i++, fifo += cpriv->fifos.rx.increment) {
+		if (cpriv->status.rxif & BIT(fifo)) {
+			/* read the frame */
+			ret = mcp25xxfd_can_read_rx_frame(spi, fifo);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c
new file mode 100644
index 000000000000..9d53ac04ae10
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c
@@ -0,0 +1,824 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ * implementation of can transmission
+ *
+ * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ */
+
+#include "mcp25xxfd_can.h"
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+/* structure of a spi message that is prepared and can get deployed quickly */
+struct mcp2517fd_tx_spi_message {
+	/* the network device this is related to */
+	struct net_device *net;
+	/* the fifo this fills */
+	u32 fifo;
+	/* the xfer to fill in the fifo data */
+	struct {
+		struct spi_message msg;
+		struct spi_transfer xfer;
+		struct {
+			u8 cmd[2];
+			u8 header[sizeof(struct mcp25xxfd_obj_tx)];
+			u8 data[64];
+		} data;
+	} fill_fifo;
+	/* the xfer to enable transmission on the can bus */
+	struct {
+		struct spi_message msg;
+		struct spi_transfer xfer;
+		struct {
+			u8 cmd[2];
+			u8 data;
+		} data;
+	} trigger_fifo;
+};
+
+struct mcp2517fd_tx_spi_message_queue {
+	/* spinlock protecting the bitmaps
+	 * as well as state and the skb_echo_* functions
+	 */
+	spinlock_t lock;
+	/* bitmap of which fifo is in which stage */
+	u32 idle;
+	u32 in_fill_fifo_transfer;
+	u32 in_trigger_fifo_transfer;
+	u32 in_can_transfer;
+	u32 transferred;
+
+	/* the queue state as seen per controller */
+	int state;
+
+	/* spinlock protecting spi submission order */
+	spinlock_t spi_lock;
+
+	/* map each fifo to a mcp2517fd_tx_spi_message */
+	struct mcp2517fd_tx_spi_message *fifo2message[32];
+
+	/* the individual messages */
+	struct mcp2517fd_tx_spi_message message[];
+};
+
+/* mostly bit manipulations to move between stages */
+static
+struct mcp2517fd_tx_spi_message *mcp25xxfd_can_tx_queue_first_spi_message(
+	struct mcp2517fd_tx_spi_message_queue *queue, u32 *bitmap)
+{
+	u32 first = ffs(*bitmap);
+
+	if (!first)
+		return NULL;
+
+	return queue->fifo2message[first - 1];
+}
+
+static
+struct mcp2517fd_tx_spi_message *mcp25xxfd_can_tx_queue_last_spi_message(
+	struct mcp2517fd_tx_spi_message_queue *queue, u32 *bitmap)
+{
+	u32 last = ffs(*bitmap);
+
+	if (!last)
+		return NULL;
+
+	return queue->fifo2message[last - 1];
+}
+
+static void mcp25xxfd_can_tx_queue_remove_spi_message(u32 *bitmap, int fifo)
+{
+	*bitmap &= ~BIT(fifo);
+}
+
+static void mcp25xxfd_can_tx_queue_add_spi_message(u32 *bitmap, int fifo)
+{
+	*bitmap |= BIT(fifo);
+}
+
+static void mcp25xxfd_can_tx_queue_move_spi_message(u32 *src, u32 *dest,
+						    int fifo)
+{
+	mcp25xxfd_can_tx_queue_remove_spi_message(src, fifo);
+	mcp25xxfd_can_tx_queue_add_spi_message(dest, fifo);
+}
+
+static void mcp25xxfd_can_tx_spi_message_fill_fifo_complete(void *context)
+{
+	struct mcp2517fd_tx_spi_message *msg = context;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(msg->net);
+	unsigned long flags;
+
+	/* reset transfer length to without data (DLC = 0) */
+	msg->fill_fifo.xfer.len = sizeof(msg->fill_fifo.data.cmd) +
+		sizeof(msg->fill_fifo.data.header);
+
+	/* we need to hold this lock to protect us from
+	 * concurrent access by start_xmit
+	 */
+	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+	/* move to in_trigger_fifo_transfer */
+	mcp25xxfd_can_tx_queue_move_spi_message(
+		&cpriv->fifos.tx_queue->in_fill_fifo_transfer,
+		&cpriv->fifos.tx_queue->in_trigger_fifo_transfer,
+		msg->fifo);
+
+	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+}
+
+static void mcp25xxfd_can_tx_spi_message_trigger_fifo_complete(void *context)
+{
+	struct mcp2517fd_tx_spi_message *msg = context;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(msg->net);
+	unsigned long flags;
+
+	/* we need to hold this lock to protect us from
+	 * concurrent access by the interrupt thread
+	 */
+	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+	/* move to can_transfer */
+	mcp25xxfd_can_tx_queue_move_spi_message(
+		&cpriv->fifos.tx_queue->in_trigger_fifo_transfer,
+		&cpriv->fifos.tx_queue->in_can_transfer,
+		msg->fifo);
+
+	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static void mcp25xxfd_can_tx_queue_debugfs(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct dentry *dir = cpriv->fifos.tx.debugfs_dir;
+	struct mcp2517fd_tx_spi_message_queue *queue = cpriv->fifos.tx_queue;
+
+	debugfs_create_u32("netif_queue_state",  0444, dir,
+			   &cpriv->fifos.tx_queue->state);
+
+	/* and for each queue stage the states */
+	debugfs_create_x32("fifos_idle", 0444, dir,
+			   &queue->idle);
+	debugfs_create_x32("fifos_in_fill_fifo_transfer", 0444, dir,
+			   &queue->in_fill_fifo_transfer);
+	debugfs_create_x32("fifos_in_trigger_fifo_transfer", 0444, dir,
+			   &queue->in_trigger_fifo_transfer);
+	debugfs_create_x32("fifos_in_can_transfer", 0444, dir,
+			   &queue->in_can_transfer);
+	debugfs_create_x32("fifos_transferred", 0444, dir,
+			   &queue->transferred);
+}
+#else
+static void mcp25xxfd_can_tx_queue_debugfs(struct net_device *net)
+{
+}
+#endif
+
+static
+void mcp25xxfd_can_tx_message_init(struct net_device *net,
+				   struct mcp2517fd_tx_spi_message *msg,
+				   int fifo)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp25xxfd_priv *priv = cpriv->priv;
+	const u32 trigger = CAN_FIFOCON_TXREQ | CAN_FIFOCON_UINC;
+	const int first_byte = mcp25xxfd_first_byte(trigger);
+	u32 addr;
+
+	/* and initialize the structure */
+	msg->net = net;
+	msg->fifo = fifo;
+
+	/* init fill_fifo */
+	spi_message_init(&msg->fill_fifo.msg);
+	msg->fill_fifo.msg.complete =
+		mcp25xxfd_can_tx_spi_message_fill_fifo_complete;
+	msg->fill_fifo.msg.context = msg;
+
+	msg->fill_fifo.xfer.speed_hz = priv->spi_use_speed_hz;
+	msg->fill_fifo.xfer.tx_buf = msg->fill_fifo.data.cmd;
+	msg->fill_fifo.xfer.len = sizeof(msg->fill_fifo.data.cmd) +
+		sizeof(msg->fill_fifo.data.header);
+	spi_message_add_tail(&msg->fill_fifo.xfer, &msg->fill_fifo.msg);
+
+	addr = MCP25XXFD_SRAM_ADDR(cpriv->fifos.fifo_reg[fifo].offset);
+	mcp25xxfd_calc_cmd_addr(INSTRUCTION_WRITE,
+				addr, msg->fill_fifo.data.cmd);
+
+	/* init trigger_fifo */
+	spi_message_init(&msg->trigger_fifo.msg);
+	msg->trigger_fifo.msg.complete =
+		mcp25xxfd_can_tx_spi_message_trigger_fifo_complete;
+	msg->trigger_fifo.msg.context = msg;
+
+	msg->trigger_fifo.xfer.speed_hz = priv->spi_use_speed_hz;
+	msg->trigger_fifo.xfer.tx_buf = msg->trigger_fifo.data.cmd;
+	msg->trigger_fifo.xfer.len = sizeof(msg->trigger_fifo.data.cmd) +
+		sizeof(msg->trigger_fifo.data.data);
+	spi_message_add_tail(&msg->trigger_fifo.xfer, &msg->trigger_fifo.msg);
+
+	mcp25xxfd_calc_cmd_addr(INSTRUCTION_WRITE,
+				CAN_FIFOCON(fifo) + first_byte,
+				msg->trigger_fifo.data.cmd);
+	msg->trigger_fifo.data.data = trigger >> (8 * first_byte);
+
+	/* and add to idle tx transfers */
+	mcp25xxfd_can_tx_queue_add_spi_message(&cpriv->fifos.tx_queue->idle,
+					       fifo);
+}
+
+int mcp25xxfd_can_tx_queue_alloc(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	size_t size = sizeof(struct mcp2517fd_tx_spi_message_queue) +
+		cpriv->fifos.tx.count *
+		sizeof(struct mcp2517fd_tx_spi_message);
+	struct mcp2517fd_tx_spi_message *msg;
+	int i, fifo;
+
+	/* allocate the fifos as an array */
+	cpriv->fifos.tx_queue = kzalloc(size, GFP_KERNEL);
+	if (!cpriv->fifos.tx_queue)
+		return -ENOMEM;
+
+	/* initialize the tx_queue structure */
+	spin_lock_init(&cpriv->fifos.tx_queue->lock);
+	spin_lock_init(&cpriv->fifos.tx_queue->spi_lock);
+
+	/* initialize the individual spi_message structures */
+	for (i = 0, fifo = cpriv->fifos.tx.start;
+	     i < cpriv->fifos.tx.count;
+	     i++, fifo += cpriv->fifos.tx.increment) {
+		msg = &cpriv->fifos.tx_queue->message[i];
+		cpriv->fifos.tx_queue->fifo2message[fifo] = msg;
+		mcp25xxfd_can_tx_message_init(net, msg, fifo);
+	}
+
+	mcp25xxfd_can_tx_queue_debugfs(net);
+
+	return 0;
+}
+
+void mcp25xxfd_can_tx_queue_free(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+	/* eventually we may need to wait here
+	 * for all transfers to have finished
+	 */
+
+	kfree(cpriv->fifos.tx_queue);
+	cpriv->fifos.tx_queue = NULL;
+}
+
+void mcp25xxfd_can_tx_queue_manage_nolock(struct net_device *net, int state)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+	/* skip early */
+	if (state == cpriv->fifos.tx_queue->state)
+		return;
+
+	/* start/stop netif_queue if necessary */
+	switch (cpriv->fifos.tx_queue->state) {
+	case TX_QUEUE_STATE_RUNABLE:
+		switch (state) {
+		case TX_QUEUE_STATE_RESTART:
+		case TX_QUEUE_STATE_STARTED:
+			netif_wake_queue(net);
+			cpriv->fifos.tx_queue->state =
+				TX_QUEUE_STATE_STARTED;
+			break;
+		}
+		break;
+	case TX_QUEUE_STATE_STOPPED:
+		switch (state) {
+		case TX_QUEUE_STATE_STARTED:
+			netif_wake_queue(net);
+			cpriv->fifos.tx_queue->state = state;
+			break;
+		}
+		break;
+	case TX_QUEUE_STATE_STARTED:
+		switch (state) {
+		case TX_QUEUE_STATE_RUNABLE:
+		case TX_QUEUE_STATE_STOPPED:
+			netif_stop_queue(net);
+			cpriv->fifos.tx_queue->state = state;
+			break;
+		}
+		break;
+	default:
+		WARN(true, "Unsupported tx_queue state: %i\n",
+		     cpriv->fifos.tx_queue->state);
+		break;
+	}
+}
+
+void mcp25xxfd_can_tx_queue_manage(struct net_device *net, int state)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	unsigned long flags;
+
+	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+	mcp25xxfd_can_tx_queue_manage_nolock(net, state);
+
+	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+}
+
+void mcp25xxfd_can_tx_queue_restart(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	unsigned long flags;
+	u32 mask;
+
+	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+	/* only move if there is nothing pending or idle */
+	mask = cpriv->fifos.tx_queue->idle |
+		cpriv->fifos.tx_queue->in_fill_fifo_transfer |
+		cpriv->fifos.tx_queue->in_trigger_fifo_transfer |
+		cpriv->fifos.tx_queue->in_can_transfer;
+	if (mask)
+		goto out;
+
+	/* move all items from transferred to idle */
+	cpriv->fifos.tx_queue->idle |= cpriv->fifos.tx_queue->transferred;
+	cpriv->fifos.tx_queue->transferred = 0;
+
+	/* and enable queue */
+	mcp25xxfd_can_tx_queue_manage_nolock(net, TX_QUEUE_STATE_RESTART);
+out:
+	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+}
+
+static int mcp25xxfd_can_int_handle_txatif_fifo(struct spi_device *spi,
+						int fifo)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	u32 val;
+	unsigned long flags;
+	int ret;
+
+	/* read fifo status */
+	ret = mcp25xxfd_cmd_read(spi, CAN_FIFOSTA(fifo), &val);
+	if (ret)
+		return ret;
+
+	/* clear the relevant interrupt flags */
+	ret = mcp25xxfd_cmd_write_mask(spi,
+				       CAN_FIFOSTA(fifo),
+				       0,
+				       CAN_FIFOSTA_TXABT |
+				       CAN_FIFOSTA_TXLARB |
+				       CAN_FIFOSTA_TXERR |
+				       CAN_FIFOSTA_TXATIF);
+	if (ret)
+		return ret;
+
+	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+	/* for specific cases we probably could trigger a retransmit
+	 * instead of an abort.
+	 */
+
+	/* and we release it from the echo_skb buffer
+	 * NOTE: this is one place where packet delivery will not
+	 * be ordered, as we do not have any timing information
+	 * when this occurred
+	 */
+	can_get_echo_skb(net, fifo);
+
+	mcp25xxfd_can_tx_queue_move_spi_message(
+		&cpriv->fifos.tx_queue->in_can_transfer,
+		&cpriv->fifos.tx_queue->transferred,
+		fifo);
+
+	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+
+	/* but we need to run a bit of cleanup */
+	cpriv->status.txif &= ~BIT(fifo);
+	net->stats.tx_aborted_errors++;
+
+	/* handle all the known cases accordingly - ignoring FIFO full */
+	val &= CAN_FIFOSTA_TXABT |
+		CAN_FIFOSTA_TXLARB |
+		CAN_FIFOSTA_TXERR;
+	switch (val) {
+	case CAN_FIFOSTA_TXERR:
+		/* this indicates a possible bus error */
+		break;
+	default:
+		dev_warn_ratelimited(&spi->dev,
+				     "Unknown TX-Fifo abort condition: %08x - stopping tx-queue\n",
+				     val);
+		break;
+	}
+
+	return 0;
+}
+
+int mcp25xxfd_can_int_handle_txatif(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	int i, fifo;
+	int ret;
+
+	/* if txatif is unset, then there are no
+	 * can frames that have been transmitted
+	 * and need to get reingested into the network stack
+	 */
+	if (!cpriv->status.txatif)
+		return 0;
+	cpriv->stats.int_txat_count++;
+
+	/* process all the fifos with that flag set */
+	for (i = 0, fifo = cpriv->fifos.tx.start;
+	     i < cpriv->fifos.tx.count;
+	     i++, fifo += cpriv->fifos.tx.increment) {
+		if (cpriv->status.txatif & BIT(fifo)) {
+			ret = mcp25xxfd_can_int_handle_txatif_fifo(spi,
+								   fifo);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+/* submit the fifo back to the network stack */
+int mcp25xxfd_can_submit_tx_frame(struct spi_device *spi, int fifo)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp25xxfd_obj_tx *tx = (struct mcp25xxfd_obj_tx *)
+		(cpriv->sram + cpriv->fifos.fifo_reg[fifo].offset);
+	int dlc = (tx->flags & CAN_OBJ_FLAGS_DLC_MASK) >>
+		CAN_OBJ_FLAGS_DLC_SHIFT;
+	unsigned long flags;
+
+	/* update counters */
+	net->stats.tx_packets++;
+	cpriv->fifos.tx.dlc_usage[dlc]++;
+	priv->net->stats.tx_bytes += can_dlc2len(dlc);
+	if (tx->flags & CAN_OBJ_FLAGS_FDF)
+		cpriv->stats.tx_fd_count++;
+	if (tx->flags & CAN_OBJ_FLAGS_BRS)
+		cpriv->stats.tx_brs_count++;
+
+	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+	/* release the echo buffer */
+	can_get_echo_skb(priv->net, fifo);
+
+	/* move from in_can_transfer to transferred */
+	mcp25xxfd_can_tx_queue_move_spi_message(
+		&cpriv->fifos.tx_queue->in_can_transfer,
+		&cpriv->fifos.tx_queue->transferred,
+		fifo);
+
+	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+
+	return 0;
+}
+
+static int mcp25xxfd_can_int_handle_tefif_fifo(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp25xxfd_obj_tef *tef;
+	u32 tef_offset = cpriv->fifos.tef.index * cpriv->fifos.tef.size;
+	int fifo, ret;
+
+	/* read the next TEF entry to get the transmit timestamp and fifo */
+	tef = (struct mcp25xxfd_obj_tef *)(cpriv->sram + tef_offset);
+	ret = mcp25xxfd_cmd_read_regs(spi, MCP25XXFD_SRAM_ADDR(tef_offset),
+				      &tef->id, sizeof(*tef));
+	if (ret)
+		return ret;
+
+	/* now we can schedule the fifo for echo submission */
+	fifo = (tef->flags & CAN_OBJ_FLAGS_SEQ_MASK) >>
+		CAN_OBJ_FLAGS_SEQ_SHIFT;
+	mcp25xxfd_can_queue_frame(cpriv, -fifo, tef->ts);
+
+	/* increment the tef index with wraparround */
+	cpriv->fifos.tef.index++;
+	if (cpriv->fifos.tef.index >= cpriv->fifos.tef.count)
+		cpriv->fifos.tef.index = 0;
+
+	/* finally just increment the TEF pointer */
+	return mcp25xxfd_cmd_write_mask(spi, CAN_TEFCON,
+				 CAN_TEFCON_UINC,
+				 CAN_TEFCON_UINC);
+}
+
+static
+int mcp25xxfd_can_int_handle_tefif_conservative(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	u32 tefsta;
+	int ret;
+
+	netdev_warn(net,
+		    "Something is wrong - we got a TEF interrupt but we were not able to detect a finished fifo\n");
+
+	/* we can assume that there is at least one,
+	 * so fake the first read of TEFSTA
+	 */
+	tefsta = CAN_TEFSTA_TEFNEIF;
+
+	/* read the tef in an inefficient loop */
+	while (tefsta & CAN_TEFSTA_TEFNEIF) {
+		/* read one tef */
+		ret = mcp25xxfd_can_int_handle_tefif_fifo(spi);
+		if (ret)
+			return ret;
+
+		/* read the TEF status again*/
+		ret = mcp25xxfd_cmd_read_mask(spi, CAN_TEFSTA,
+					      &tefsta, CAN_TEFSTA_TEFNEIF);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static
+int mcp25xxfd_can_int_handle_tefif_oportunistic(struct spi_device *spi,
+						u32 finished)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	int i, fifo, ret;
+
+	/* now iterate those */
+	for (i = 0, fifo = cpriv->fifos.tx.start;
+	     i < cpriv->fifos.tx.count;
+	     i++, fifo += cpriv->fifos.tx.increment) {
+		if (finished & BIT(fifo)) {
+			ret = mcp25xxfd_can_int_handle_tefif_fifo(spi);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+int mcp25xxfd_can_int_handle_tefif(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+	struct net_device *net = priv->net;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	unsigned long flags;
+	u32 finished;
+
+	if (!(cpriv->status.intf & CAN_INT_TEFIF))
+		return 0;
+	cpriv->stats.int_tef_count++;
+
+	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+	/* compute finished fifos and clear them immediately */
+	finished = (cpriv->fifos.tx_queue->in_can_transfer ^
+		    cpriv->status.txreq) &
+		cpriv->fifos.tx_queue->in_can_transfer;
+
+	cpriv->fifos.tx_queue->in_can_transfer &= ~finished;
+	cpriv->fifos.tx_queue->transferred |= finished;
+
+	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+
+	/* in case of a strange situation run in safe mode */
+	if (!finished)
+		return mcp25xxfd_can_int_handle_tefif_conservative(spi);
+
+	/* otherwise run in oportunistic mode */
+	return mcp25xxfd_can_int_handle_tefif_oportunistic(spi, finished);
+}
+
+static
+void mcp25xxfd_can_tx_fill_fifo_common(struct net_device *net,
+				       struct mcp2517fd_tx_spi_message *smsg,
+				       struct mcp25xxfd_obj_tx *tx,
+				       int dlc, u8 *data)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	int len = can_dlc2len(dlc);
+
+	/* update statistics */
+	cpriv->fifos.tx.dlc_usage[dlc]++;
+	cpriv->fifos.fifo_info[smsg->fifo].use_count++;
+
+	/* add fifo number as seq */
+	tx->flags |= smsg->fifo << CAN_OBJ_FLAGS_SEQ_SHIFT;
+
+	/* copy data to tx->data for future reference */
+	memcpy(tx->data, data, len);
+
+	/* transform header to controller format */
+	mcp25xxfd_convert_from_cpu(&tx->id, sizeof(*tx) / sizeof(u32));
+
+	/* copy header + data to final location - we are not aligned */
+	memcpy(smsg->fill_fifo.data.header, &tx->id, sizeof(*tx) + len);
+
+	/* convert it back to CPU format */
+	mcp25xxfd_convert_to_cpu(&tx->id, sizeof(*tx) / sizeof(u32));
+
+	/* set up size of transfer */
+	smsg->fill_fifo.xfer.len = sizeof(smsg->fill_fifo.data.cmd) +
+		sizeof(smsg->fill_fifo.data.header) + len;
+}
+
+static
+void mcp25xxfd_can_tx_fill_fifo_fd(struct net_device *net,
+				   struct canfd_frame *frame,
+				   struct mcp2517fd_tx_spi_message *smsg,
+				   struct mcp25xxfd_obj_tx *tx)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	int dlc = can_len2dlc(frame->len);
+
+	/* update some statistics */
+	cpriv->stats.tx_fd_count++;
+
+	/* compute can id */
+	mcp25xxfd_canid_to_mcpid(frame->can_id, &tx->id, &tx->flags);
+
+	/* setup flags */
+	tx->flags |= dlc << CAN_OBJ_FLAGS_DLC_SHIFT;
+	tx->flags |= (frame->can_id & CAN_EFF_FLAG) ? CAN_OBJ_FLAGS_IDE : 0;
+	tx->flags |= (frame->can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
+	if (frame->flags & CANFD_BRS) {
+		tx->flags |= CAN_OBJ_FLAGS_BRS;
+		cpriv->stats.tx_brs_count++;
+	}
+	tx->flags |= (frame->flags & CANFD_ESI) ? CAN_OBJ_FLAGS_ESI : 0;
+	tx->flags |= CAN_OBJ_FLAGS_FDF;
+
+	/* and do common processing */
+	mcp25xxfd_can_tx_fill_fifo_common(net, smsg, tx,
+					  dlc, frame->data);
+}
+
+static
+void mcp25xxfd_can_tx_fill_fifo(struct net_device *net,
+				struct can_frame *frame,
+				struct mcp2517fd_tx_spi_message *smsg,
+				struct mcp25xxfd_obj_tx *tx)
+{
+	/* set frame to valid dlc */
+	if (frame->can_dlc > 8)
+		frame->can_dlc = 8;
+
+	/* compute can id */
+	mcp25xxfd_canid_to_mcpid(frame->can_id, &tx->id, &tx->flags);
+
+	/* setup flags */
+	tx->flags |= frame->can_dlc << CAN_OBJ_FLAGS_DLC_SHIFT;
+	tx->flags |= (frame->can_id & CAN_EFF_FLAG) ? CAN_OBJ_FLAGS_IDE : 0;
+	tx->flags |= (frame->can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
+
+	/* and do common processing */
+	mcp25xxfd_can_tx_fill_fifo_common(net, smsg, tx,
+					  frame->can_dlc, frame->data);
+}
+
+static struct mcp2517fd_tx_spi_message
+*mcp25xxfd_can_tx_queue_get_next_fifo(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp2517fd_tx_spi_message *smsg;
+	unsigned long flags;
+
+	/* we need to hold this lock to protect us against
+	 * concurrent modifications of cpriv->fifos.tx_queue->idle
+	 * in the interrupt thread
+	 */
+	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+	/* get the first entry from idle */
+	if (cpriv->fifos.tx.increment > 0)
+		smsg = mcp25xxfd_can_tx_queue_first_spi_message(
+			cpriv->fifos.tx_queue, &cpriv->fifos.tx_queue->idle);
+	else
+		smsg = mcp25xxfd_can_tx_queue_last_spi_message(
+			cpriv->fifos.tx_queue, &cpriv->fifos.tx_queue->idle);
+	if (!smsg)
+		goto out_busy;
+
+	/* and move the fifo to next stage */
+	mcp25xxfd_can_tx_queue_move_spi_message(
+		&cpriv->fifos.tx_queue->idle,
+		&cpriv->fifos.tx_queue->in_fill_fifo_transfer,
+		smsg->fifo);
+
+	/* if queue is empty then stop the network queue immediately */
+	if (!cpriv->fifos.tx_queue->idle)
+		mcp25xxfd_can_tx_queue_manage_nolock(net,
+						     TX_QUEUE_STATE_RUNABLE);
+out_busy:
+	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+
+	return smsg;
+}
+
+/* submit the can message to the can-bus */
+netdev_tx_t mcp25xxfd_can_start_xmit(struct sk_buff *skb,
+				     struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp25xxfd_priv *priv = cpriv->priv;
+	struct spi_device *spi = priv->spi;
+	struct mcp2517fd_tx_spi_message *smsg;
+	struct mcp25xxfd_obj_tx *tx;
+	int addr;
+	unsigned long flags;
+	int ret;
+
+	/* invalid skb we can ignore */
+	if (can_dropped_invalid_skb(net, skb))
+		return NETDEV_TX_OK;
+
+	/* acquire lock on spi so that we are are not risking
+	 * some reordering of spi messages when we are running
+	 * start_xmit in multiple threads (on multiple cores)
+	 */
+	spin_lock_irqsave(&cpriv->fifos.tx_queue->spi_lock, flags);
+
+	/* get the fifo message structure to process now */
+	smsg = mcp25xxfd_can_tx_queue_get_next_fifo(net);
+	if (!smsg)
+		goto out_busy;
+
+	/* compute the fifo in sram */
+	addr = cpriv->fifos.fifo_reg[smsg->fifo].offset;
+	tx = (struct mcp25xxfd_obj_tx *)(cpriv->sram + addr);
+
+	/* fill in message from skb->data depending on can2.0 or canfd */
+	if (can_is_canfd_skb(skb))
+		mcp25xxfd_can_tx_fill_fifo_fd(net,
+					      (struct canfd_frame *)skb->data,
+					      smsg, tx);
+	else
+		mcp25xxfd_can_tx_fill_fifo(net,
+					   (struct can_frame *)skb->data,
+					   smsg, tx);
+
+	/* submit the two messages asyncronously
+	 * the reason why we separate transfers into two spi_messages is:
+	 *  * because the spi framework (currently) does add a 10us delay
+	 *    between 2 spi_transfers in a single spi_message when
+	 *    change_cs is set - 2 consecutive spi messages show a shorter
+	 *     cs disable phase increasing bus utilization
+	 *  * this allows the interrupt handler to start spi messages earlier
+	 *    so reducing latencies a bit and to allow for better concurrency
+	 */
+	ret = spi_async(spi, &smsg->fill_fifo.msg);
+	if (ret)
+		goto out_async_failed;
+	ret = spi_async(spi, &smsg->trigger_fifo.msg);
+	if (ret)
+		goto out_async_failed;
+
+	/* unlock the spi bus */
+	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->spi_lock, flags);
+
+	/* keep it for reference until the message really got transmitted */
+	can_put_echo_skb(skb, net, smsg->fifo);
+
+	return NETDEV_TX_OK;
+out_async_failed:
+	netdev_err(net, "spi_async submission of fifo %i failed - %i\n",
+		   smsg->fifo, ret);
+
+out_busy:
+	/* stop the queue */
+	mcp25xxfd_can_tx_queue_manage_nolock(net, TX_QUEUE_STATE_STOPPED);
+
+	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->spi_lock, flags);
+
+	return NETDEV_TX_BUSY;
+}
--
2.11.0

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH V5 0/4] Microchip mcp25xxfd can controller driver
  2018-12-21  9:29 [PATCH V5 0/4] Microchip mcp25xxfd can controller driver kernel
                   ` (3 preceding siblings ...)
  2018-12-21  9:29 ` [PATCH V5 4/4] can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver kernel
@ 2019-01-10  8:30 ` Wolfgang Grandegger
  2019-01-10 16:37   ` kernel
  4 siblings, 1 reply; 12+ messages in thread
From: Wolfgang Grandegger @ 2019-01-10  8:30 UTC (permalink / raw)
  To: kernel, linux-can, devicetree, Mark Kleine-Budde, Rob Herring,
	Mark Rutland
  Cc: Sukkin Pang, Gerhard Bertelsmann, Wilhelm Leichtfried,
	Thomas Kopp, Enrico Scholz, Brad Hanson, Teemu Keskinarkaus

Hello Martin,

Am 21.12.18 um 10:29 schrieb kernel@martin.sperl.org:
> From: Martin Sperl <kernel@martin.sperl.org>
> 
> This patchset adds a driver for the mcp25xxfd CanFD controller.
> 
> Most of the features of the controller are supported by this driver
> release.
> 
> The controller includes a few features that the current core Can(FD)
> implementation does not support: * Transmit Bandwidth Sharing bits
>   - this waits for a configurable number of syncronization bits before
>     atempting to transmit the next frame - for the moment controllable
>     via a module parameter
> * SID11 with CanFD frames
>   - at this moment not supported by driver
> * 3 transmit attempts in addition to ONE_SHOT
> * transmitter delay compensation configurations
> * micro second exact transmission and reception timestamps
>   - used internally by driver
> * variable number of tx-fifos
>   - exposed via module parameter at this moment
> 
> The driver has not been heavily optimized yet so there are a few
> "low hanging fruit" that have been left out from earlier versions
> of the patch set for sake of simplicity.
> 
> These optimizations shall be added as extra patches in a later cycle.

I applied all four patches adding:

  $ wc -l drivers/net/can/spi/mcp25xxfd/*
       5 drivers/net/can/spi/mcp25xxfd/Kconfig
      11 drivers/net/can/spi/mcp25xxfd/Makefile
    1204 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
     803 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
     479 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c
     760 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
     723 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c
     208 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c
     824 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c
     199 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_gpio.c
     279 drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
    5495 insgesamt

Do I understand you correctly, this is the basic driver and the
optimizations are not yet included!? Well, I expected less code than
last time but now it's spit up in smaller files, at least. Will try to
do a review this week...

Wolfgang.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH V5 0/4] Microchip mcp25xxfd can controller driver
  2019-01-10  8:30 ` [PATCH V5 0/4] Microchip mcp25xxfd can controller driver Wolfgang Grandegger
@ 2019-01-10 16:37   ` kernel
  2019-01-11  8:21     ` Wolfgang Grandegger
  0 siblings, 1 reply; 12+ messages in thread
From: kernel @ 2019-01-10 16:37 UTC (permalink / raw)
  To: Wolfgang Grandegger
  Cc: linux-can, devicetree, Mark Kleine-Budde, Rob Herring,
	Mark Rutland, Sukkin Pang, Gerhard Bertelsmann,
	Wilhelm Leichtfried, Thomas Kopp, Enrico Scholz, Brad Hanson,
	Teemu Keskinarkaus

Hi Wolfgang,

> On 10.01.2019, at 09:30, Wolfgang Grandegger <wg@grandegger.com> wrote:
> 
> Hello Martin,
> 
> Do I understand you correctly, this is the basic driver and the
> optimizations are not yet included!? Well, I expected less code than
> last time but now it's spit up in smaller files, at least. Will try to
> do a review this week…
> 
This is the basic driver - split into the 3 main components:
* core (essentially basic module management, identification and clock/ram)
* GPIO handling of the GPIO/INT pins
* CAN

where CAN is obviously the biggest, but it is also split out into
* basic
* interrupt handling
* fifo setup
* CAN Frame reception
* CAN Frame transmission

The place that we could split out more is reducing the fifo-setup
to a single TX and RX fifo, but the cost of ripping out that portion 
(and itsstructural dependencies) is quite high just to produce code 
that is never actually run…

So I have made the driver all minimal removing all the big performance
optimizations while leaving the basic multi-fifo setup in the config 
- mostly in mcp25xxfd_can_fifo and some in mcp25xxfd_can_rx and 
mcp25xxfd_can_tx.

I have to admit that there is one optimization that is in can_tx that
could get separated out into a separate patch.

That is the function mcp25xxfd_can_int_handle_tefif_oportunistic
the removal of which would simplify mcp25xxfd_can_int_handle_tefif
slightly, but that code should be fairly simple to read.

If you want, then I can move this to a separate patch in the next
version.

Basic setup, interrupt handling, SERR error handling and others
would still need to happen in the initial driver, so that is a 
big chunk of the overall driver.

There is already a new patchset in the pipeline that:
* addresses some concerns in mcp_write_then_* with regards to the
  use of stack variables for transmit/receive buffers 
  - rewrite of function.
  (reported by Enrico Scholz)
* initialization of 2 variables in priv (reported by Enrico Scholz).
* adds 4 optimization patches:
  * can: mcp25xxfd: optimize tef reads reading multiple TEFs in one go
  * can: mcp25xxfd: optimize SPI reads of FIFOs in can2.0 mode
  * can: mcp25xxfd: add prediction of CanFD frames sizes based on history
  * can: mcp25xxfd: optimize reception of big CanFD frame reception with BRS

There is a bug with regards to transmission of CanFD with BRS that
still needs to get fixed before I send out the next version - what
is the root cause here is unclear (this may be a regression in the
calculation of data bitrate that gives now different SJW values than
before that impacts the controllers ability to send BRS frames,
but it could also be something else like TDC (= Transmitter Delay
Compensation).

But I guess I shall also wait for further feedback before sending
out this next version.

Thanks,
	Martin

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH V5 0/4] Microchip mcp25xxfd can controller driver
  2019-01-10 16:37   ` kernel
@ 2019-01-11  8:21     ` Wolfgang Grandegger
  0 siblings, 0 replies; 12+ messages in thread
From: Wolfgang Grandegger @ 2019-01-11  8:21 UTC (permalink / raw)
  To: kernel
  Cc: linux-can, devicetree, Mark Kleine-Budde, Rob Herring,
	Mark Rutland, Sukkin Pang, Gerhard Bertelsmann,
	Wilhelm Leichtfried, Thomas Kopp, Enrico Scholz, Brad Hanson,
	Teemu Keskinarkaus

Hello Martin,

Am 10.01.19 um 17:37 schrieb kernel@martin.sperl.org:
> Hi Wolfgang,
> 
>> On 10.01.2019, at 09:30, Wolfgang Grandegger <wg@grandegger.com> wrote:
>>
>> Hello Martin,
>>
>> Do I understand you correctly, this is the basic driver and the
>> optimizations are not yet included!? Well, I expected less code than
>> last time but now it's spit up in smaller files, at least. Will try to
>> do a review this week…
>>
> This is the basic driver - split into the 3 main components:
> * core (essentially basic module management, identification and clock/ram)
> * GPIO handling of the GPIO/INT pins
> * CAN
> 
> where CAN is obviously the biggest, but it is also split out into
> * basic
> * interrupt handling
> * fifo setup
> * CAN Frame reception
> * CAN Frame transmission
> 
> The place that we could split out more is reducing the fifo-setup
> to a single TX and RX fifo, but the cost of ripping out that portion 
> (and itsstructural dependencies) is quite high just to produce code 
> that is never actually run…
> 
> So I have made the driver all minimal removing all the big performance
> optimizations while leaving the basic multi-fifo setup in the config 
> - mostly in mcp25xxfd_can_fifo and some in mcp25xxfd_can_rx and 
> mcp25xxfd_can_tx.
> 
> I have to admit that there is one optimization that is in can_tx that
> could get separated out into a separate patch.
> 
> That is the function mcp25xxfd_can_int_handle_tefif_oportunistic
> the removal of which would simplify mcp25xxfd_can_int_handle_tefif
> slightly, but that code should be fairly simple to read.
> 
> If you want, then I can move this to a separate patch in the next
> version.
> 
> Basic setup, interrupt handling, SERR error handling and others
> would still need to happen in the initial driver, so that is a 
> big chunk of the overall driver.
> 
> There is already a new patchset in the pipeline that:
> * addresses some concerns in mcp_write_then_* with regards to the
>   use of stack variables for transmit/receive buffers 
>   - rewrite of function.
>   (reported by Enrico Scholz)
> * initialization of 2 variables in priv (reported by Enrico Scholz).
> * adds 4 optimization patches:
>   * can: mcp25xxfd: optimize tef reads reading multiple TEFs in one go
>   * can: mcp25xxfd: optimize SPI reads of FIFOs in can2.0 mode
>   * can: mcp25xxfd: add prediction of CanFD frames sizes based on history
>   * can: mcp25xxfd: optimize reception of big CanFD frame reception with BRS
> 
> There is a bug with regards to transmission of CanFD with BRS that
> still needs to get fixed before I send out the next version - what
> is the root cause here is unclear (this may be a regression in the
> calculation of data bitrate that gives now different SJW values than
> before that impacts the controllers ability to send BRS frames,
> but it could also be something else like TDC (= Transmitter Delay
> Compensation).
> 
> But I guess I shall also wait for further feedback before sending
> out this next version.

Yes, please wait for my review!

And there is the DEBUG_FS stuff increasing code size as well. We should
start with *one* robust and efficient solution working for most CAN
use-cases.

Wolfgang.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH V5 2/4] can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver basics
  2018-12-21  9:29 ` [PATCH V5 2/4] can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver basics kernel
@ 2019-01-21 12:31   ` Wolfgang Grandegger
  2019-01-21 18:29     ` Martin Sperl
  0 siblings, 1 reply; 12+ messages in thread
From: Wolfgang Grandegger @ 2019-01-21 12:31 UTC (permalink / raw)
  To: kernel, linux-can, devicetree, Mark Kleine-Budde, Rob Herring,
	Mark Rutland
  Cc: Sukkin Pang, Gerhard Bertelsmann, Wilhelm Leichtfried,
	Thomas Kopp, Enrico Scholz, Brad Hanson, Teemu Keskinarkaus

Hello Martin,

this is the first part of my review...

I'm still not happy about the big amount of code lines, but I also do
not see a way to reduce it significantly/easily.

Am 21.12.18 um 10:29 schrieb kernel@martin.sperl.org:
> From: Martin Sperl <kernel@martin.sperl.org>
> 
> This patch adds basic driver support for the Microchip mcp2517fd
> CAN-FD controller.
> 
> The mcp2517fd is capable of transmitting and receiving standard data
> frames, extended data frames, remote frames and Can-FD frames.
> The mcp2517fd interfaces with the host over SPI.
> 
> This patch iprovides basic driver functionality:

Typo!

> setting up clocks and the infrastructure for the can and gpio portions
> of the driver.
> 
> Datasheet:
> * http://ww1.microchip.com/downloads/en/DeviceDoc/20005688A.pdf
> Reference manual:
> * http://ww1.microchip.com/downloads/en/DeviceDoc/20005678A.pdf
> Errata:
> * http://ww1.microchip.com/downloads/en/DeviceDoc/MCP2517FD-Silicon-Errata-and-Data-Sheet-Clarification-DS80000792A.pdf
> 
> Signed-off-by: Martin Sperl <kernel@martin.sperl.org>
> 
> ---
> Changelog:
>   V1 -> V2: implemented support as per feedback
>   V2 -> V3: moved dt-binding changes to patch 1
>   V3 -> V4: resend
>   V4 -> V5: reorganized driver into separate files
>             added more HW detection test
> 
> ---
>  drivers/net/can/spi/Kconfig                    |    2 +
>  drivers/net/can/spi/Makefile                   |    2 +
>  drivers/net/can/spi/mcp25xxfd/Kconfig          |    5 +
>  drivers/net/can/spi/mcp25xxfd/Makefile         |    6 +
>  drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h      |  238 +++++
>  drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c | 1190 ++++++++++++++++++++++++
>  drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c  |  228 +++++
>  drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h  |  487 ++++++++++

About file names and modularization: A "mcp25xxfd_xxx.c" should also have
a header file "mcp25xxfd_xxx.h" defining the interface and use the
prefix "mcp25xxfd_xxx". Therefore, "s/mcp25xxfd_base/mcp25xxfd" would
make more sense.

Also, could you please put just the register definitions in
"mcp25xxfd_reg.h".

>  8 files changed, 2158 insertions(+)
>  create mode 100644 drivers/net/can/spi/mcp25xxfd/Kconfig
>  create mode 100644 drivers/net/can/spi/mcp25xxfd/Makefile
>  create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
>  create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
>  create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
>  create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
> 
> diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig
> index 8f2e0dd7b756..7a5b1436492e 100644
> --- a/drivers/net/can/spi/Kconfig
> +++ b/drivers/net/can/spi/Kconfig
> @@ -13,4 +13,6 @@ config CAN_MCP251X
>  	---help---
>  	  Driver for the Microchip MCP251x SPI CAN controllers.
> 
> +source "drivers/net/can/spi/mcp25xxfd/Kconfig"
> +
>  endmenu
> diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile
> index f59fa3731073..67d3ad21730b 100644
> --- a/drivers/net/can/spi/Makefile
> +++ b/drivers/net/can/spi/Makefile
> @@ -5,3 +5,5 @@
> 
>  obj-$(CONFIG_CAN_HI311X)	+= hi311x.o
>  obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
> +
> +obj-y                           += mcp25xxfd/
> diff --git a/drivers/net/can/spi/mcp25xxfd/Kconfig b/drivers/net/can/spi/mcp25xxfd/Kconfig
> new file mode 100644
> index 000000000000..f720f1377612
> --- /dev/null
> +++ b/drivers/net/can/spi/mcp25xxfd/Kconfig
> @@ -0,0 +1,5 @@
> +config CAN_MCP25XXFD
> +	tristate "Microchip MCP25xxFD SPI CAN controllers"
> +	depends on HAS_DMA
> +	help
> +	  Driver for the Microchip MCP25XXFD SPI FD-CAN controller family.
> diff --git a/drivers/net/can/spi/mcp25xxfd/Makefile b/drivers/net/can/spi/mcp25xxfd/Makefile
> new file mode 100644
> index 000000000000..83de2d40cf9a
> --- /dev/null
> +++ b/drivers/net/can/spi/mcp25xxfd/Makefile
> @@ -0,0 +1,6 @@
> +#
> +#  Makefile for the Linux Controller Area Network SPI drivers.
> +#
> +obj-$(CONFIG_CAN_MCP25XXFD)	+= mcp25xxfd.o
> +mcp25xxfd-objs                  := mcp25xxfd_base.o
> +mcp25xxfd-objs                  += mcp25xxfd_can.o
> diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
> new file mode 100644
> index 000000000000..0f02c78a9a56
> --- /dev/null
> +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
> @@ -0,0 +1,238 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
> + *
> + * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
> + *
> + * Based on Microchip MCP251x CAN controller driver written by
> + * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/debugfs.h>
> +#include <linux/device.h>
> +#include <linux/jiffies.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/spi/spi.h>
> +
> +/* some constants derived from the datasheets */
> +#define MCP25XXFD_OST_DELAY_MS		3
> +#define MCP25XXFD_MIN_CLOCK_FREQUENCY	1000000
> +#define MCP25XXFD_MAX_CLOCK_FREQUENCY	40000000
> +#define MCP25XXFD_PLL_MULTIPLIER	10
> +#define MCP25XXFD_AUTO_PLL_MAX_CLOCK_FREQUENCY				\
> +	(MCP25XXFD_MAX_CLOCK_FREQUENCY / MCP25XXFD_PLL_MULTIPLIER)
> +#define MCP25XXFD_SCLK_DIVIDER		2
> +
> +/* address defines for SRAM */
> +#define MCP25XXFD_SRAM_SIZE 2048
> +#define MCP25XXFD_SRAM_ADDR(x) (0x400 + (x))
> +
> +/* some defines for the driver */
> +#define MCP25XXFD_BUFFER_TXRX_SIZE (MCP25XXFD_SRAM_SIZE)
> +#define MCP25XXFD_OSC_POLLING_JIFFIES	(HZ / 2)
> +
> +/* SPI commands */
> +#define INSTRUCTION_RESET		0x0000
> +#define INSTRUCTION_READ		0x3000
> +#define INSTRUCTION_WRITE		0x2000
> +#define INSTRUCTION_READ_CRC		0xB000
> +#define INSTRUCTION_WRITE_CRC		0xA000
> +#define INSTRUCTION_WRITE_SAVE		0xC000
> +
> +#define ADDRESS_MASK			0x0fff

These definitions are not specific enough. Please add a prefix "MCP25XXFD_".

> +/* Register definitions */
> +#define MCP25XXFD_SFR_BASE(x)		(0xE00 + (x))
> +#define MCP25XXFD_OSC			MCP25XXFD_SFR_BASE(0x00)
> +#  define MCP25XXFD_OSC_PLLEN		BIT(0)
> +#  define MCP25XXFD_OSC_OSCDIS		BIT(2)
> +#  define MCP25XXFD_OSC_SCLKDIV		BIT(4)
> +#  define MCP25XXFD_OSC_CLKODIV_BITS	2
> +#  define MCP25XXFD_OSC_CLKODIV_SHIFT	5
> +#  define MCP25XXFD_OSC_CLKODIV_MASK			\
> +	GENMASK(MCP25XXFD_OSC_CLKODIV_SHIFT		\
> +		+ MCP25XXFD_OSC_CLKODIV_BITS - 1,	\
> +		MCP25XXFD_OSC_CLKODIV_SHIFT)
> +#  define MCP25XXFD_OSC_CLKODIV_10	3
> +#  define MCP25XXFD_OSC_CLKODIV_4	2
> +#  define MCP25XXFD_OSC_CLKODIV_2	1
> +#  define MCP25XXFD_OSC_CLKODIV_1	0
> +#  define MCP25XXFD_OSC_PLLRDY		BIT(8)
> +#  define MCP25XXFD_OSC_OSCRDY		BIT(10)
> +#  define MCP25XXFD_OSC_SCLKRDY		BIT(12)
> +#define MCP25XXFD_IOCON			MCP25XXFD_SFR_BASE(0x04)
> +#  define MCP25XXFD_IOCON_TRIS0		BIT(0)
> +#  define MCP25XXFD_IOCON_TRIS1		BIT(1)
> +#  define MCP25XXFD_IOCON_XSTBYEN	BIT(6)
> +#  define MCP25XXFD_IOCON_LAT0		BIT(8)
> +#  define MCP25XXFD_IOCON_LAT1		BIT(9)
> +#  define MCP25XXFD_IOCON_GPIO0		BIT(16)
> +#  define MCP25XXFD_IOCON_GPIO1		BIT(17)
> +#  define MCP25XXFD_IOCON_PM0		BIT(24)
> +#  define MCP25XXFD_IOCON_PM1		BIT(25)
> +#  define MCP25XXFD_IOCON_TXCANOD	BIT(28)
> +#  define MCP25XXFD_IOCON_SOF		BIT(29)
> +#  define MCP25XXFD_IOCON_INTOD		BIT(30)
> +#define MCP25XXFD_CRC			MCP25XXFD_SFR_BASE(0x08)
> +#  define MCP25XXFD_CRC_MASK		GENMASK(15, 0)
> +#  define MCP25XXFD_CRC_CRCERRIE	BIT(16)
> +#  define MCP25XXFD_CRC_FERRIE		BIT(17)
> +#  define MCP25XXFD_CRC_CRCERRIF	BIT(24)
> +#  define MCP25XXFD_CRC_FERRIF		BIT(25)
> +#define MCP25XXFD_ECCCON		MCP25XXFD_SFR_BASE(0x0C)
> +#  define MCP25XXFD_ECCCON_ECCEN	BIT(0)
> +#  define MCP25XXFD_ECCCON_SECIE	BIT(1)
> +#  define MCP25XXFD_ECCCON_DEDIE	BIT(2)
> +#  define MCP25XXFD_ECCCON_PARITY_BITS 6
> +#  define MCP25XXFD_ECCCON_PARITY_SHIFT 8
> +#  define MCP25XXFD_ECCCON_PARITY_MASK			\
> +	GENMASK(MCP25XXFD_ECCCON_PARITY_SHIFT		\
> +		+ MCP25XXFD_ECCCON_PARITY_BITS - 1,	\
> +		MCP25XXFD_ECCCON_PARITY_SHIFT)
> +#define MCP25XXFD_ECCSTAT		MCP25XXFD_SFR_BASE(0x10)
> +#  define MCP25XXFD_ECCSTAT_SECIF	BIT(1)
> +#  define MCP25XXFD_ECCSTAT_DEDIF	BIT(2)
> +#  define MCP25XXFD_ECCSTAT_ERRADDR_SHIFT 8
> +#  define MCP25XXFD_ECCSTAT_ERRADDR_MASK	      \
> +	GENMASK(MCP25XXFD_ECCSTAT_ERRADDR_SHIFT + 11, \
> +		MCP25XXFD_ECCSTAT_ERRADDR_SHIFT)
> +
> +#define DEVICE_NAME "mcp25xxfd"
> +
> +enum mcp25xxfd_model {
> +	CAN_MCP2517FD	= 0x2517,
> +};
> +
> +struct mcp25xxfd_priv {
> +	struct spi_device *spi;
> +	struct clk *clk;
> +
> +	/* the actual model of the mcp25xxfd */
> +	enum mcp25xxfd_model model;
> +
> +	/* everything clock related */
> +	int clock_freq;
> +	struct {
> +		/* clock configuration */
> +		int clock_pll;
> +		int clock_div2;
> +		int  clock_odiv;

Just one space please!

> +	} config;
> +
> +	/* lock for enabling/disabling the clock */
> +	struct mutex clk_user_lock;
> +	u32 clk_user_mask;
> +	u32 clk_sleep_mask;
> +#define MCP25XXFD_CLK_USER_CAN BIT(0)
> +#define MCP25XXFD_CLK_USER_GPIO0 BIT(1)
> +#define MCP25XXFD_CLK_USER_GPIO1 BIT(2)
> +#define MCP25XXFD_CLK_USER_CLKOUT BIT(3)
> +
> +	/* power related */
> +	struct regulator *power;
> +
> +	/* the distinct spi_speeds to use for spi communication */
> +	u32 spi_setup_speed_hz;
> +	u32 spi_normal_speed_hz;
> +	u32 spi_use_speed_hz;
> +
> +	/* spi-tx/rx buffers for efficient transfers
> +	 * used during setup and irq
> +	 */
> +	struct mutex spi_rxtx_lock;
> +	u8 spi_tx[MCP25XXFD_BUFFER_TXRX_SIZE];
> +	u8 spi_rx[MCP25XXFD_BUFFER_TXRX_SIZE];
> +
> +	/* configuration registers */
> +	struct {
> +		u32 osc;
> +		u32 iocon;
> +		u32 crc;
> +		u32 ecccon;
> +	} regs;
> +
> +	/* debugfs related */
> +#if defined(CONFIG_DEBUG_FS)
> +	struct dentry *debugfs_dir;
> +	struct dentry *debugfs_regs_dir;
> +#endif
> +};
> +
> +static inline int mcp25xxfd_power_enable(struct regulator *reg, int enable)
> +{
> +	if (IS_ERR_OR_NULL(reg))
> +		return 0;
> +
> +	if (enable)
> +		return regulator_enable(reg);
> +	else
> +		return regulator_disable(reg);
> +}
> +
> +static inline void mcp25xxfd_convert_to_cpu(u32 *data, int n)
> +{
> +	int i;
> +
> +	for (i = 0; i < n; i++)
> +		data[i] = le32_to_cpu(data[i]);

Is this optimized away if the endianess is the same?

> +}
> +
> +static inline void mcp25xxfd_convert_from_cpu(u32 *data, int n)
> +{
> +	int i;
> +
> +	for (i = 0; i < n; i++)
> +		data[i] = cpu_to_le32(data[i]);
> +}
> +
> +int mcp25xxfd_cmd_readn(struct spi_device *spi, u32 reg,
> +			void *data, int n);
> +int mcp25xxfd_cmd_read_mask(struct spi_device *spi, u32 reg,
> +			    u32 *data, u32 mask);
> +static inline int mcp25xxfd_cmd_read(struct spi_device *spi, u32 reg,
> +				     u32 *data)
> +{
> +	return mcp25xxfd_cmd_read_mask(spi, reg, data, -1);
> +}
> +
> +int mcp25xxfd_cmd_read_regs(struct spi_device *spi, u32 reg,
> +			    u32 *data, u32 bytes);
> +
> +int mcp25xxfd_cmd_writen(struct spi_device *spi, u32 reg,
> +			 void *data, int n);
> +int mcp25xxfd_cmd_write_mask(struct spi_device *spi, u32 reg,
> +			     u32 data, u32 mask);
> +static inline int mcp25xxfd_cmd_write(struct spi_device *spi, u32 reg,
> +				      u32 data)
> +{
> +	return mcp25xxfd_cmd_write_mask(spi, reg, data, -1);
> +}
> +
> +int mcp25xxfd_cmd_write_regs(struct spi_device *spi, u32 reg,
> +			     u32 *data, u32 bytes);
> +
> +int mcp25xxfd_dump_regs_range(struct seq_file *file, u32 start, u32 end);
> +
> +int mcp25xxfd_cmd_write_regs(struct spi_device *spi, u32 reg,
> +			     u32 *data, u32 bytes);
> +
> +int mcp25xxfd_dump_regs_range(struct seq_file *file, u32 start, u32 end);
> +
> +/* shared (internal) clock control */
> +int mcp25xxfd_stop_clock(struct spi_device *spi, int requestor_mask);
> +int mcp25xxfd_start_clock(struct spi_device *spi, int requestor_mask);
> +
> +/* to put us to sleep fully we need the CAN controller to enter sleep mode */
> +int mcp25xxfd_can_sleep_mode(struct spi_device *spi);
> +/* probe the can controller */
> +int mcp25xxfd_can_hw_probe(struct spi_device *spi);
> +
> +/* clearing interrupt flags */
> +int mcp25xxfd_clear_crc_interrupts(struct spi_device *spi);
> +int mcp25xxfd_clear_ecc_interrupts(struct spi_device *spi);
> +int mcp25xxfd_clear_interrupts(struct spi_device *spi);
> +
> +/* enabling interrupts */
> +int mcp25xxfd_enable_interrupts(struct spi_device *spi, bool enable);
> diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
> new file mode 100644
> index 000000000000..9c34893243d1
> --- /dev/null
> +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
> @@ -0,0 +1,1190 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
> + *
> + * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
> + *
> + *
> + * Based on Microchip MCP251x CAN controller driver written by
> + * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include "mcp25xxfd.h"
> +
> +/* device description and rational:
> + *
> + * the mcp25xxfd is a CanFD controller that also supports can2.0 only
> + * modes.
> + * It is connected via spi to the host and requires at minimum a single
> + * irq line in addition to the SPI lines - it is not mentioned explicitly
> + * in the documentation but in principle SPI 3-wire should be possible.
> + *
> + * The clock connected is typically 4MHz, 20MHz or 40MHz.
> + * When using a 4MHz clock the controller can use an integrated PLL to
> + * get 40MHz.
> + *
> + * The controller itself has 2KB of SRAM for CAN-data.
> + * ECC can get enabled for SRAM.
> + * CRC-16 checksumming of SPI transfers can get implemented
> + *   - some optimization options may not be efficient in such a situation.
> + *   - more SPI bus bandwidth is used for transfer of CRCs and
> + *     transfer length information
> + *
> + * It also contains 2 GPIO pins that can get used either as interrupt lines
> + * or GPIO IN or Out or STANDBY flags.
> + * In addition there is a PIN that allows output of a (divided) clock out
> + * or as a SOF (Start of Can FRAME) interrupt line - e.g for wakeup.
> + */
> +
> +/* known hardware issues and workarrounds in this driver:

"Known" and "workarounds"

> + *
> + * * There is one situation where the controller will require a full POR
> + *   (total power off) to recover from a bad Clock configuration.
> + *   This happens when the wrong clock is configured in the device tree
> + *   (say 4MHz are configured, while 40MHz is the actual clock frequency
> + *   of the HW).
> + *   In such a situation the driver tries to enable the PLL, which will
> + *   never synchronize and the controller becomes unresponsive to further
> + *   spi requests until a full POR.
> + *
> + *   Mitigation:
> + *     none as of now
> + *
> + *   Possible implementation of a mitigation/sanity check:
> + *     during initialization:
> + *       * try to identify the HW at 1MHz:
> + *         on success:
> + *           * controller is identified
> + *         on failure:
> + *           * controller is absent - fail
> + *       * force controller clock to run with disabled PLL
> + *       * try to identify the HW at 2MHz:
> + *         on success:
> + *           * controller clock is >= 4 MHz
> + *           * this may be 4MHz
> + *         on failure:
> + *           * controller clock is < 4 MHz
> + *       * try to identify the HW at 2.5MHz:
> + *         on success:
> + *           * controller clock is >= 5 MHz
> + *           * this may not be 4MHz
> + *         on failure:
> + *           * controller clock is 4 MHz
> + *           * enable PLL
> + *           * exit successfully (or run last test for verification purposes)
> + *       * try to identify the HW at <dt-clock/2> MHz:
> + *         on success:
> + *           * controller clock is >= <dt-clock/2> MHz
> + *              (it could be higher though)
> + *         on failure:
> + *           * the controller is not running at the
> + *             clock rate configured in the DT
> + *           * if PLL is enabled warn about requirements of POR
> + *           * fail
> + *
> + *   Side-effects:
> + *     * longer initialization time
> + *
> + *   Possible issues with mitigation:
> + *     * possibly miss-identification because the SPI block may work
> + *       "somewhat" at frequencies > < clock / 2 + delta f>
> + *       this may be especially true for the situation where we test if
> + *       2.5MHz SPI-Clock works.
> + *     * also SPI HW-clock dividers may do a round down to fixed frequencies
> + *       which is not propperly reported and may result in false positives
Typo

> + *       because a frequency lower than expected is used.
> + *
> + *   This is the reason why only simple testing is enabled at the risk of
> + *   the need for a POR.
> + */
> +
> +/* SPI helper */
> +
> +/* wrapper arround spi_sync, that sets speed_hz */
> +static int mcp25xxfd_sync_transfer(struct spi_device *spi,
> +				   struct spi_transfer *xfer,
> +				   unsigned int xfers)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	int i;
> +
> +	for (i = 0; i < xfers; i++)
> +		xfer[i].speed_hz = priv->spi_use_speed_hz;
> +
> +	return spi_sync_transfer(spi, xfer, xfers);
> +}
> +
> +/* simple spi_write wrapper with speed_hz */
> +static int mcp25xxfd_write(struct spi_device *spi,
> +			   const void *tx_buf,
> +			   unsigned int tx_len)
> +{
> +	struct spi_transfer xfer;
> +
> +	memset(&xfer, 0, sizeof(xfer));
> +	xfer.tx_buf = tx_buf;
> +	xfer.len = tx_len;
> +
> +	return mcp25xxfd_sync_transfer(spi, &xfer, 1);
> +}
> +
> +/* an optimization of spi_write_then_read that merges the transfers */
> +static int mcp25xxfd_write_then_read(struct spi_device *spi,
> +				     const void *tx_buf,
> +				     unsigned int tx_len,
> +				     void *rx_buf,
> +				     unsigned int rx_len)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct spi_transfer xfer[2];
> +	u8 single_reg_data_tx[6];
> +	u8 single_reg_data_rx[6];
> +	int ret;
> +
> +	memset(xfer, 0, sizeof(xfer));
> +
> +	/* when using a halfduplex controller or to big for buffer */
> +	if ((spi->master->flags & SPI_MASTER_HALF_DUPLEX) ||
> +	    (tx_len + rx_len > sizeof(priv->spi_tx))) {
> +		xfer[0].tx_buf = tx_buf;
> +		xfer[0].len = tx_len;
> +
> +		xfer[1].rx_buf = rx_buf;
> +		xfer[1].len = rx_len;
> +
> +		return mcp25xxfd_sync_transfer(spi, xfer, 2);
> +	}
> +
> +	/* full duplex optimization */
> +	xfer[0].len = tx_len + rx_len;
> +	if (xfer[0].len > sizeof(single_reg_data_tx)) {
> +		mutex_lock(&priv->spi_rxtx_lock);
> +		xfer[0].tx_buf = priv->spi_tx;
> +		xfer[0].rx_buf = priv->spi_rx;
> +	} else {
> +		xfer[0].tx_buf = single_reg_data_tx;
> +		xfer[0].rx_buf = single_reg_data_rx;
> +	}
> +
> +	/* copy and clean */
> +	memcpy((u8 *)xfer[0].tx_buf, tx_buf, tx_len);
> +	memset((u8 *)xfer[0].tx_buf + tx_len, 0, rx_len);

Casts needed?

> +
> +	ret = mcp25xxfd_sync_transfer(spi, xfer, 1);
> +	if (!ret)
> +		memcpy(rx_buf, xfer[0].rx_buf + tx_len, rx_len);
> +
> +	if (xfer[0].len > sizeof(single_reg_data_tx))
> +		mutex_unlock(&priv->spi_rxtx_lock);
> +
> +	return ret;
> +}
> +
> +static int mcp25xxfd_write_then_write(struct spi_device *spi,
> +				      const void *tx_buf,
> +				      unsigned int tx_len,
> +				      const void *tx2_buf,
> +				      unsigned int tx2_len)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct spi_transfer xfer;
> +	u8 single_reg_data[6];
> +	int ret;
> +
> +	if (tx_len + tx2_len > MCP25XXFD_BUFFER_TXRX_SIZE)
> +		return -EINVAL;
> +
> +	memset(&xfer, 0, sizeof(xfer));
> +
> +	xfer.len = tx_len + tx2_len;
> +	if (xfer.len > sizeof(single_reg_data)) {
> +		mutex_lock(&priv->spi_rxtx_lock);
> +		xfer.tx_buf = priv->spi_tx;
> +	} else {
> +		xfer.tx_buf = single_reg_data;
> +	}
> +
> +	memcpy((u8 *)xfer.tx_buf, tx_buf, tx_len);
> +	memcpy((u8 *)xfer.tx_buf + tx_len, tx2_buf, tx2_len);

Casts?

> +
> +	ret = mcp25xxfd_sync_transfer(spi, &xfer, 1);
> +
> +	if (xfer.len > sizeof(single_reg_data))
> +		mutex_unlock(&priv->spi_rxtx_lock);
> +
> +	return ret;
> +}
> +
> +/* mcp25xxfd spi command/protocol helper */
> +
> +static void mcp25xxfd_calc_cmd_addr(u16 cmd, u16 addr, u8 *data)
> +{
> +	cmd = cmd | (addr & ADDRESS_MASK);
> +
> +	data[0] = (cmd >> 8) & 0xff;
> +	data[1] = (cmd >> 0) & 0xff;
> +}
> +
> +static int mcp25xxfd_first_byte(u32 mask)
> +{
> +	return (mask & 0x0000ffff) ?
> +		((mask & 0x000000ff) ? 0 : 1) :
> +		((mask & 0x00ff0000) ? 2 : 3);
> +}

inline?

> +static int mcp25xxfd_last_byte(u32 mask)
> +{
> +	return (mask & 0xffff0000) ?
> +		((mask & 0xff000000) ? 3 : 2) :
> +		((mask & 0x0000ff00) ? 1 : 0);
> +}

Ditto.

> +/* read multiple bytes, transform some registers */
> +int mcp25xxfd_cmd_readn(struct spi_device *spi, u32 reg,
> +			void *data, int n)
> +{
> +	u8 cmd[2];
> +	int ret;
> +
> +	mcp25xxfd_calc_cmd_addr(INSTRUCTION_READ, reg, cmd);
> +
> +	ret = mcp25xxfd_write_then_read(spi, &cmd, 2, data, n);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +/* read a register, but we are only interrested in a few bytes */
> +int mcp25xxfd_cmd_read_mask(struct spi_device *spi, u32 reg,
> +			    u32 *data, u32 mask)
> +{
> +	int first_byte, last_byte, len_byte;
> +	int ret;
> +
> +	/* check that at least one bit is set */
> +	if (!mask)
> +		return -EINVAL;
> +
> +	/* calculate first and last byte used */
> +	first_byte = mcp25xxfd_first_byte(mask);
> +	last_byte = mcp25xxfd_last_byte(mask);
> +	len_byte = last_byte - first_byte + 1;
> +
> +	/* do a partial read */
> +	*data = 0;
> +	ret = mcp25xxfd_cmd_readn(spi, reg + first_byte,
> +				  ((void *)data + first_byte), len_byte);
> +	if (ret)
> +		return ret;
> +
> +	mcp25xxfd_convert_to_cpu(data, 1);
> +
> +	return 0;
> +}
> +
> +static int mcp25xxfd_cmd_reset(struct spi_device *spi)
> +{
> +	u8 cmd[2];
> +
> +	mcp25xxfd_calc_cmd_addr(INSTRUCTION_RESET, 0, cmd);
> +
> +	/* write the reset command */
> +	return mcp25xxfd_write(spi, cmd, 2);
> +}
> +
> +int mcp25xxfd_cmd_writen(struct spi_device *spi, u32 reg,
> +			 void *data, int n)
> +{
> +	u8 cmd[2];
> +	int ret;
> +
> +	mcp25xxfd_calc_cmd_addr(INSTRUCTION_WRITE, reg, cmd);
> +
> +	ret = mcp25xxfd_write_then_write(spi, &cmd, 2, data, n);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +/* read a register, but we are only interrested in a few bytes */
> +int mcp25xxfd_cmd_write_mask(struct spi_device *spi, u32 reg,
> +			     u32 data, u32 mask)
> +{
> +	int first_byte, last_byte, len_byte;
> +	u8 cmd[2];
> +
> +	/* check that at least one bit is set */
> +	if (!mask)
> +		return -EINVAL;
> +
> +	/* calculate first and last byte used */
> +	first_byte = mcp25xxfd_first_byte(mask);
> +	last_byte = mcp25xxfd_last_byte(mask);
> +	len_byte = last_byte - first_byte + 1;
> +
> +	/* prepare buffer */
> +	mcp25xxfd_calc_cmd_addr(INSTRUCTION_WRITE, reg + first_byte, cmd);
> +	data = cpu_to_le32(data);
> +
> +	return mcp25xxfd_write_then_write(spi,
> +					  cmd, sizeof(cmd),
> +					  ((void *)&data + first_byte),
> +					  len_byte);
> +}
> +
> +int mcp25xxfd_cmd_write_regs(struct spi_device *spi, u32 reg,
> +			     u32 *data, u32 bytes)
> +{
> +	int ret;
> +
> +	/* first transpose to controller format */
> +	mcp25xxfd_convert_from_cpu(data, bytes / sizeof(u32));
> +
> +	/* now write it */
> +	ret = mcp25xxfd_cmd_writen(spi, reg, data, bytes);
> +
> +	/* and convert it back to cpu format even if it fails */
> +	mcp25xxfd_convert_to_cpu(data, bytes / sizeof(u32));

sizeof(bytes) please, here and in other locations.

> +
> +	return ret;
> +}
> +
> +int mcp25xxfd_cmd_read_regs(struct spi_device *spi, u32 reg,
> +			    u32 *data, u32 bytes)
> +{
> +	int ret;
> +
> +	/* read it */
> +	ret = mcp25xxfd_cmd_readn(spi, reg, data, bytes);
> +
> +	/* and convert it to cpu format */
> +	mcp25xxfd_convert_to_cpu((u32 *)data, bytes / sizeof(u32));

Cast?

> +
> +	return ret;
> +}
> +
> +/* debugfs related */
> +#if defined(CONFIG_DEBUG_FS)
> +
> +int mcp25xxfd_dump_regs_range(struct seq_file *file, u32 start, u32 end)
> +{
> +	struct spi_device *spi = file->private;
> +	u32 data[32];
> +	int bytes = end - start + sizeof(u32);
> +	int i, l, count, ret;
> +
> +	for (count =  bytes / sizeof(u32); count > 0; count -= 32) {
> +		/* read up to 32 registers in one go */
> +		l = min(count, 32);
> +		ret = mcp25xxfd_cmd_read_regs(spi, start,
> +					      data, l * sizeof(u32));
> +		if (ret)
> +			return ret;
> +		/* dump those read registers */
> +		for (i = 0; i < l; i++, start += sizeof(u32))
> +			seq_printf(file, "Reg 0x%03x = 0x%08x\n",
> +				   start, data[i]);
> +	}
> +
> +	return 0;
> +}
> +
> +static int mcp25xxfd_dump_regs(struct seq_file *file, void *offset)
> +{
> +	return mcp25xxfd_dump_regs_range(file,
> +					 MCP25XXFD_OSC,
> +					 MCP25XXFD_ECCSTAT);
> +}
> +
> +static int mcp25xxfd_debugfs_setup(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct dentry *root, *regs;
> +	char name[64];
> +
> +	/* the base directory */
> +	snprintf(name, sizeof(name),
> +		 DEVICE_NAME "-%s",
> +		 dev_name(&priv->spi->dev));
> +	priv->debugfs_dir = debugfs_create_dir(name, NULL);
> +	root = priv->debugfs_dir;
> +
> +	/* expose some parameters related to clocks */
> +	debugfs_create_u32("spi_setup_speed_hz", 0444, root,
> +			   &priv->spi_setup_speed_hz);
> +	debugfs_create_u32("spi_normal_speed_hz", 0444, root,
> +			   &priv->spi_normal_speed_hz);
> +	debugfs_create_u32("spi_use_speed_hz", 0444, root,
> +			   &priv->spi_use_speed_hz);
> +	debugfs_create_u32("clk_user_mask", 0444, root, &priv->clk_user_mask);
> +
> +	/* expose the system registers */
> +	priv->debugfs_regs_dir = debugfs_create_dir("regs", root);
> +	regs = priv->debugfs_regs_dir;
> +	debugfs_create_x32("osc", 0444, regs, &priv->regs.osc);
> +	debugfs_create_x32("iocon", 0444, regs, &priv->regs.iocon);
> +	debugfs_create_x32("crc", 0444, regs, &priv->regs.crc);
> +	debugfs_create_x32("ecccon", 0444, regs, &priv->regs.ecccon);
> +
> +	/* dump the controller registers themselves */
> +	debugfs_create_devm_seqfile(&priv->spi->dev, "regs_live_dump",
> +				    root, mcp25xxfd_dump_regs);
> +
> +	return 0;
> +}
> +
> +static void mcp25xxfd_debugfs_remove(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +
> +	debugfs_remove_recursive(priv->debugfs_dir);
> +	priv->debugfs_dir = NULL;
> +}
> +#else
> +int mcp25xxfd_dump_regs_range(struct seq_file *file, u32 start, u32 end)
> +{
> +	return 0;
> +}
> +
> +static int mcp25xxfd_debugfs_setup(struct mcp25xxfd_priv *priv)
> +{
> +	return 0;
> +}
> +
> +static void mcp25xxfd_debugfs_remove(struct mcp25xxfd_priv *priv)
> +{
> +}
> +#endif

And could you please put all debugfs stuff into "mcp25xxfd_debugfs.[ch]"
and define macros to increment the statistics if CONFIG_DEBUG_FS is defined.

> +
> +/* HW Related */
> +int mcp25xxfd_clear_ecc_interrupts(struct spi_device *spi)
> +{
> +	u32 val, addr;
> +	int ret;
> +
> +	/* first report the error address */
> +	ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_ECCSTAT, &val);
> +	if (ret)
> +		return ret;
> +
> +	/* if no flags are set then nothing to do */
> +	if (!(val & (MCP25XXFD_ECCSTAT_SECIF | MCP25XXFD_ECCSTAT_DEDIF)))
> +		return 0;
> +
> +	addr = (val & MCP25XXFD_ECCSTAT_ERRADDR_MASK) >>
> +		MCP25XXFD_ECCSTAT_ERRADDR_SHIFT;
> +
> +	dev_err_ratelimited(&spi->dev,
> +			    "ECC %s bit error at %03x\n",
> +			    (val & MCP25XXFD_ECCSTAT_DEDIF) ?
> +			    "double" : "single",
> +			    addr);
> +
> +	/* and clear the error */
> +	return mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_ECCSTAT, 0,
> +					MCP25XXFD_ECCSTAT_SECIF |
> +					MCP25XXFD_ECCSTAT_DEDIF);
> +}
> +
> +int mcp25xxfd_clear_crc_interrupts(struct spi_device *spi)
> +{
> +	return mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_CRC, 0,
> +					MCP25XXFD_CRC_CRCERRIF |
> +					MCP25XXFD_CRC_FERRIF);
> +}
> +
> +int mcp25xxfd_clear_interrupts(struct spi_device *spi)
> +{
> +	mcp25xxfd_clear_ecc_interrupts(spi);
> +	mcp25xxfd_clear_crc_interrupts(spi);
> +
> +	return 0;
> +}
> +
> +static int mcp25xxfd_enable_ecc_interrupts(struct spi_device *spi,
> +					   bool enable)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	u32 mask = MCP25XXFD_ECCCON_SECIE | MCP25XXFD_ECCCON_DEDIE;
> +
> +	priv->regs.ecccon &= ~mask;
> +	priv->regs.ecccon |= MCP25XXFD_ECCCON_ECCEN | (enable ? mask : 0);
> +
> +	return mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_ECCCON,
> +					priv->regs.ecccon,
> +					MCP25XXFD_ECCCON_ECCEN | mask);
> +}
> +
> +static int mcp25xxfd_enable_crc_interrupts(struct spi_device *spi,
> +					   bool enable)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	u32 mask = MCP25XXFD_CRC_CRCERRIE | MCP25XXFD_CRC_FERRIE;
> +
> +	priv->regs.crc &= ~mask;
> +	priv->regs.crc |= enable ? mask : 0;
> +
> +	return mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_CRC,
> +					priv->regs.crc, mask);
> +}
> +
> +int mcp25xxfd_enable_interrupts(struct spi_device *spi,
> +				bool enable)
> +{
> +	/* error handling only on enable for this function */
> +	int ret = 0;
> +
> +	/* if we enable clear interrupt flags first */
> +	if (enable)
> +		ret = mcp25xxfd_clear_interrupts(spi);
> +	if (enable && ret)
> +		goto out;

Why not just:

	if (enable) {
		ret = mcp25xxfd_clear_interrupts(spi);
		if (ret)
			goto out;
	}

> +
> +	ret = mcp25xxfd_enable_ecc_interrupts(spi, enable);
> +	if (enable && ret)
> +		goto out;

Ditto and below.

> +
> +	ret = mcp25xxfd_enable_crc_interrupts(spi, enable);
> +	if (enable && ret)
> +		goto out_ecc;
> +
> +	/* if we disable interrupts clear interrupt flags last */
> +	if (!enable)
> +		mcp25xxfd_clear_interrupts(spi);
> +
> +	return 0;
> +
> +out_ecc:
> +	mcp25xxfd_enable_ecc_interrupts(spi, false);
> +out:
> +	return ret;
> +}
> +
> +static int _mcp25xxfd_waitfor_osc(struct spi_device *spi,
> +				  u32 waitfor,
> +				  u32 mask)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	unsigned long timeout;
> +	int ret;
> +
> +	/* wait for synced pll/osc/sclk */
> +	timeout = jiffies + MCP25XXFD_OSC_POLLING_JIFFIES;
> +	while (time_before_eq(jiffies, timeout)) {
> +		ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_OSC,
> +					 &priv->regs.osc);

Fits on one line! Quite often, you break the line early

> +		if (ret)
> +			return ret;
> +		/* check for expected bits to be set/unset */
> +		if ((priv->regs.osc & mask) == waitfor)
> +			return 0;
> +	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static int mcp25xxfd_configure_osc(struct spi_device *spi,
> +				   u32 value,
> +				   u32 waitfor,
> +				   u32 mask)

Why not putting the three arguments on one line... like you do
with many other functions. I thinkk it improves readability.

> +{
> +	int ret;
> +
> +	/* write the osc value to the controller - waking it if necessary */
> +	ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_OSC, value);
> +	if (ret)
> +		return ret;
> +
> +	/* wait for the clock to stabelize */
> +	ret = _mcp25xxfd_waitfor_osc(spi, waitfor, mask);
> +
> +	/* on timeout try again setting the register */
> +	if (ret == -ETIMEDOUT) {
> +		/* write the clock to the controller */
> +		ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_OSC, value);
> +		if (ret)
> +			return ret;
> +
> +		/* wait for the clock to stabelize */
> +		ret = _mcp25xxfd_waitfor_osc(spi, waitfor, mask);
> +	}
> +
> +	/* handle timeout special - report the fact */
> +	if (ret == -ETIMEDOUT)
> +		dev_err(&spi->dev,
> +			"Clock did not switch within the timeout period\n");
> +
> +	return ret;
> +}
> +
> +static u32 _mcp25xxfd_clkout_mask(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	u32 value = 0;
> +
> +	if (priv->config.clock_div2)
> +		value |= MCP25XXFD_OSC_SCLKDIV;
> +
> +	switch (priv->config.clock_odiv) {
> +	case 0:
> +		break;
> +	case 1:
> +		value |= MCP25XXFD_OSC_CLKODIV_1 <<
> +			MCP25XXFD_OSC_CLKODIV_SHIFT;

Ditto

> +		break;
> +	case 2:
> +		value |= MCP25XXFD_OSC_CLKODIV_2 <<
> +			MCP25XXFD_OSC_CLKODIV_SHIFT;
> +		break;
> +	case 4:
> +		value |= MCP25XXFD_OSC_CLKODIV_4 <<
> +			MCP25XXFD_OSC_CLKODIV_SHIFT;
> +		break;
> +	case 10:
> +		value |= MCP25XXFD_OSC_CLKODIV_10 <<
> +			MCP25XXFD_OSC_CLKODIV_SHIFT;
> +		break;
> +	default:
> +		/* this should never happen but is error-handled
> +		 * by the dt-parsing
> +		 */
> +		break;
> +	}
> +
> +	return value;
> +}
> +
> +static int _mcp25xxfd_start_clock(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	u32 value   = _mcp25xxfd_clkout_mask(spi);

Just one space before and after "=", please! Here and in other places.

> +	u32 waitfor = MCP25XXFD_OSC_OSCRDY;
> +	u32 mask    = waitfor |
> +		MCP25XXFD_OSC_OSCDIS |
> +		MCP25XXFD_OSC_PLLRDY |
> +		MCP25XXFD_OSC_PLLEN;

Less lines? I stop here. I think you know what I mean.

> +	/* enable PLL as well - set expectations */
> +	if (priv->config.clock_pll) {
> +		value   |= MCP25XXFD_OSC_PLLEN;
> +		waitfor |= MCP25XXFD_OSC_PLLRDY | MCP25XXFD_OSC_PLLEN;
> +	}
> +
> +	/* set the oscilator now */
> +	return mcp25xxfd_configure_osc(spi, value, waitfor, mask);
> +}
> +
> +static int _mcp25xxfd_stop_clock(struct spi_device *spi)
> +{
> +	u32 value   = _mcp25xxfd_clkout_mask(spi);
> +	u32 waitfor = 0;
> +	u32 mask    = MCP25XXFD_OSC_OSCDIS |
> +		MCP25XXFD_OSC_PLLRDY |
> +		MCP25XXFD_OSC_PLLEN;
> +	int ret;
> +
> +	ret = mcp25xxfd_configure_osc(spi, value, waitfor, mask);
> +	if (ret)
> +		return ret;
> +
> +	/* finally switch the controller mode to sleep
> +	 * by this time the controller should be in config mode already
> +	 * this way we wake to config mode again
> +	 */
> +	return mcp25xxfd_can_sleep_mode(spi);
> +}
> +
> +int mcp25xxfd_start_clock(struct spi_device *spi, int requestor_mask)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	int ret = 0;
> +
> +	/* without a clock there is nothing we can do... */
> +	if (IS_ERR(priv->clk))
> +		return IS_ERR(priv->clk);

	return PTR_ERR(priv->clk) ?

> +
> +	mutex_lock(&priv->clk_user_lock);
> +
> +	/* if clock is already started, then skip */
> +	if (priv->clk_user_mask & requestor_mask)
> +		goto out;
> +
> +	/* enable the clock on the host side*/
> +	ret = clk_prepare_enable(priv->clk);
> +	if (ret)
> +		goto out;
> +
> +	/* enable the clock on the controller side */
> +	ret = _mcp25xxfd_start_clock(spi);
> +	if (ret)
> +		goto out;
> +
> +	/* mark the clock for the specific component as started */
> +	priv->clk_user_mask |= requestor_mask;
> +
> +	/* and now we use the normal spi speed */
> +	priv->spi_use_speed_hz = priv->spi_normal_speed_hz;
> +
> +out:
> +	mutex_unlock(&priv->clk_user_lock);
> +
> +	return ret;
> +}
> +
> +int mcp25xxfd_stop_clock(struct spi_device *spi, int requestor_mask)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	int ret;
> +
> +	/* without a clock there is nothing we can do... */
> +	if (IS_ERR(priv->clk))
> +		return IS_ERR(priv->clk);
> +
> +	mutex_lock(&priv->clk_user_lock);
> +
> +	/* if the mask is empty then skip, as the clock is stopped */
> +	if (!priv->clk_user_mask)
> +		goto out;
> +
> +	/* clear the clock mask */
> +	priv->clk_user_mask &= ~requestor_mask;
> +
> +	/* if the mask is not empty then skip, as the clock is needed */
> +	if (priv->clk_user_mask)
> +		goto out;
> +
> +	/* and now we use the setup spi speed */
> +	priv->spi_use_speed_hz = priv->spi_setup_speed_hz;
> +
> +	/* stop the clock on the controller */
> +	ret = _mcp25xxfd_stop_clock(spi);
> +
> +	/* and we stop the clock on the host*/
> +	if (!IS_ERR(priv->clk))
> +		clk_disable_unprepare(priv->clk);
> +out:
> +	mutex_unlock(&priv->clk_user_lock);
> +
> +	return 0;
> +}
> +
> +static int mcp25xxfd_enable_ecc(struct spi_device *spi)
> +{
> +	u8 buffer[256];
> +	int i;
> +	int ret;

	int i, ret;

Like you do in many other function headers.

> +
> +	/* set up RAM ECC - enable interrupts sets it as well */
> +	ret = mcp25xxfd_enable_ecc_interrupts(spi, false);
> +	if (ret)
> +		return ret;
> +
> +	/* and clear SRAM so that no read fails from now on */
> +	memset(buffer, 0, sizeof(buffer));
> +	for (i = 0; i < MCP25XXFD_SRAM_SIZE; i += sizeof(buffer)) {
> +		ret = mcp25xxfd_cmd_writen(spi,
> +					   MCP25XXFD_SRAM_ADDR(i),
> +					   buffer, sizeof(buffer));
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int mcp25xxfd_hw_wake(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	int ret = 0;
> +
> +	/* enable power to controller */
> +	mcp25xxfd_power_enable(priv->power, 1);
> +
> +	/* if there is no sleep mask, then there is nothing to wake */
> +	if (!priv->clk_sleep_mask)
> +		return 0;
> +
> +	/* start the clocks */
> +	ret = mcp25xxfd_start_clock(spi, priv->clk_sleep_mask);
> +	if (ret)
> +		return 0;
> +
> +	/* clear the sleep mask */
> +	mutex_lock(&priv->clk_user_lock);
> +	priv->clk_sleep_mask = 0;
> +	mutex_unlock(&priv->clk_user_lock);
> +
> +	/* enable the interrupts again */
> +	return mcp25xxfd_enable_interrupts(spi, true);
> +}
> +
> +static int mcp25xxfd_hw_sleep(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +
> +	mutex_lock(&priv->clk_user_lock);
> +	priv->clk_sleep_mask = priv->clk_user_mask;
> +	mutex_unlock(&priv->clk_user_lock);
> +
> +	/* disable interrupts */
> +	mcp25xxfd_enable_interrupts(spi, false);
> +
> +	/* stop the clocks */
> +	mcp25xxfd_stop_clock(spi, priv->clk_sleep_mask);
> +
> +	/* disable power to controller */
> +	return mcp25xxfd_power_enable(priv->power, 0);
> +}
> +
> +static int mcp25xxfd_hw_probe(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	int ret;
> +
> +	/* Wait for oscillator startup timer after power up */
> +	mdelay(MCP25XXFD_OST_DELAY_MS);
> +
> +	/* send a "blind" reset, hoping we are in Config mode */
> +	mcp25xxfd_cmd_reset(spi);
> +
> +	/* Wait for oscillator startup again */
> +	mdelay(MCP25XXFD_OST_DELAY_MS);
> +
> +	/* check clock register that the clock is ready or disabled */
> +	ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_OSC,
> +				 &priv->regs.osc);
> +	if (ret)
> +		return ret;
> +
> +	/* there can only be one... */
> +	switch (priv->regs.osc &
> +		(MCP25XXFD_OSC_OSCRDY | MCP25XXFD_OSC_OSCDIS)) {
> +	case MCP25XXFD_OSC_OSCRDY: /* either the clock is ready */
> +		break;
> +	case MCP25XXFD_OSC_OSCDIS: /* or the clock is disabled */
> +		break;
> +	default:
> +		/* otherwise there is no valid device (or in strange state)
> +		 *
> +		 * if PLL is enabled but not ready, then there may be
> +		 * something "fishy"
> +		 * this happened during driver development
> +		 * (enabling pll, when when on wrong clock), so best warn
> +		 * about such a possibility
> +		 */
> +		if ((priv->regs.osc &
> +		     (MCP25XXFD_OSC_PLLEN | MCP25XXFD_OSC_PLLRDY))
> +		    == MCP25XXFD_OSC_PLLEN)
> +			dev_err(&spi->dev,
> +				"mcp25xxfd may be in a strange state - a power disconnect may be required\n");
> +
> +		return -ENODEV;
> +	}
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_OF_DYNAMIC
> +int mcp25xxfd_of_parse(struct mcp25xxfd_priv *priv)
> +{
> +	struct spi_device *spi = priv->spi;
> +	const struct device_node *np = spi->dev.of_node;
> +	u32 val;
> +	int ret;
> +
> +	priv->config.clock_div2 =
> +		of_property_read_bool(np, "microchip,clock-div2");
> +
> +	ret = of_property_read_u32_index(np, "microchip,clock-out-div",
> +					 0, &val);
> +	if (!ret) {
> +		switch (val) {
> +		case 0:
> +		case 1:
> +		case 2:
> +		case 4:
> +		case 10:
> +			priv->config.clock_odiv = val;
> +			break;
> +		default:
> +			dev_err(&spi->dev,
> +				"Invalid value in device tree for microchip,clock_out_div: %u - valid values: 0, 1, 2, 4, 10\n",
> +				val);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +#else
> +int mcp25xxfd_of_parse(struct mcp25xxfd_priv *priv)
> +{
> +	return 0;
> +}
> +#endif
> +
> +static const struct of_device_id mcp25xxfd_of_match[] = {
> +	{
> +		.compatible	= "microchip,mcp2517fd",
> +		.data		= (void *)CAN_MCP2517FD,
> +	},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, mcp25xxfd_of_match);
> +
> +static int mcp25xxfd_probe(struct spi_device *spi)
> +{
> +	const struct of_device_id *of_id =
> +		of_match_device(mcp25xxfd_of_match, &spi->dev);
> +	struct mcp25xxfd_priv *priv;
> +	struct clk *clk;
> +	int ret, freq;
> +
> +	/* as irq_create_fwspec_mapping() can return 0, check for it */
> +	if (spi->irq <= 0) {
> +		dev_err(&spi->dev, "no valid irq line defined: irq = %i\n",
> +			spi->irq);
> +		return -EINVAL;
> +	}
> +
> +	clk = devm_clk_get(&spi->dev, NULL);
> +	if (IS_ERR(clk))
> +		return PTR_ERR(clk);
> +
> +	freq = clk_get_rate(clk);
> +	if (freq < MCP25XXFD_MIN_CLOCK_FREQUENCY ||
> +	    freq > MCP25XXFD_MAX_CLOCK_FREQUENCY) {
> +		dev_err(&spi->dev,
> +			"Clock frequency %i is not in range [%i:%i]\n",
> +			freq,
> +			MCP25XXFD_MIN_CLOCK_FREQUENCY,
> +			MCP25XXFD_MAX_CLOCK_FREQUENCY);
> +		return -ERANGE;
> +	}
> +
> +	priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	/* assign some data */
> +	if (of_id)
> +		priv->model = (enum mcp25xxfd_model)of_id->data;
> +	else
> +		priv->model = spi_get_device_id(spi)->driver_data;
> +
> +	spi_set_drvdata(spi, priv);
> +	priv->spi = spi;
> +
> +	mutex_init(&priv->clk_user_lock);
> +
> +	/* enable the clock and mark as enabled */
> +	ret = clk_prepare_enable(clk);
> +	if (ret)
> +		goto out_free;
> +
> +	/* do not use the SCK clock divider of 2 */
> +	priv->config.clock_div2 = false;
> +
> +	/* clock output is divided by 10 */
> +	priv->config.clock_odiv = 10;
> +
> +	/* if we have a clock that is <= 4MHz, enable the pll */
> +	priv->config.clock_pll =
> +		(freq <= MCP25XXFD_AUTO_PLL_MAX_CLOCK_FREQUENCY);
> +
> +	/* check in device tree for overrrides */
> +	ret = mcp25xxfd_of_parse(priv);
> +	if (ret)
> +		return ret;
> +
> +	/* decide on the effective clock rate */
> +	priv->clock_freq = freq;
> +	if (priv->config.clock_pll)
> +		priv->clock_freq *= MCP25XXFD_PLL_MULTIPLIER;
> +	if (priv->config.clock_div2)
> +		priv->clock_freq /= MCP25XXFD_SCLK_DIVIDER;
> +
> +	/* calculate the clock frequencies to use
> +	 *
> +	 * setup clock speed is at most 1/4 the input clock speed
> +	 * the reason for the factor of 4 is that there is
> +	 * a clock divider in the controller that MAY be enabled in some
> +	 * circumstances so we may find a controller with that enabled
> +	 * during probe phase
> +	 */
> +	priv->spi_setup_speed_hz = freq / 4;
> +
> +	/* normal operation clock speeds */
> +	priv->spi_normal_speed_hz = priv->clock_freq / 2;
> +	if (priv->config.clock_div2) {
> +		priv->spi_setup_speed_hz /= MCP25XXFD_SCLK_DIVIDER;
> +		priv->spi_normal_speed_hz /= MCP25XXFD_SCLK_DIVIDER;
> +	}
> +	/* set limit on speed */
> +	if (spi->max_speed_hz) {
> +		priv->spi_setup_speed_hz = min_t(int,
> +						 priv->spi_setup_speed_hz,
> +						 spi->max_speed_hz);
> +		priv->spi_normal_speed_hz = min_t(int,
> +						  priv->spi_normal_speed_hz,
> +						  spi->max_speed_hz);
> +	}
> +
> +	/* use setup speed by default
> +	 * - this is switched when clock is enabled/disabled
> +	 */
> +	priv->spi_use_speed_hz = priv->spi_setup_speed_hz;
> +
> +	/* Configure the SPI bus */
> +	spi->bits_per_word = 8;
> +	ret = spi_setup(spi);
> +	if (ret)
> +		goto out_clk;
> +
> +	priv->power = devm_regulator_get_optional(&spi->dev, "vdd");
> +	if (PTR_ERR(priv->power) == -EPROBE_DEFER) {
> +		ret = -EPROBE_DEFER;
> +		goto out_clk;
> +	}
> +
> +	ret = mcp25xxfd_power_enable(priv->power, 1);
> +	if (ret)
> +		goto out_clk;
> +
> +	/* this will also enable the MCP25XXFD_CLK_USER_CAN clock */
> +	ret = mcp25xxfd_hw_probe(spi);
> +
> +	/* on error retry a second time */
> +	if (ret == -ENODEV) {
> +		ret = mcp25xxfd_hw_probe(spi);
> +		if (!ret)
> +			dev_info(&spi->dev,
> +				 "found device only during retry\n");
> +	}
> +	if (ret) {
> +		if (ret == -ENODEV)
> +			dev_err(&spi->dev,
> +				"Cannot initialize MCP%x. Wrong wiring? (oscilator register reads as %08x)\n",
> +				priv->model, priv->regs.osc);
> +		goto out_probe;
> +	}
> +
> +	/* enable the can controller clock */
> +	ret = mcp25xxfd_start_clock(spi, MCP25XXFD_CLK_USER_CAN);
> +	if (ret)
> +		goto out_probe;
> +
> +	/* try to identify the can-controller - we need the clock here */
> +	ret = mcp25xxfd_can_hw_probe(spi);
> +	if (ret)
> +		goto out_ctlclk;
> +
> +	/* add debugfs */
> +	ret = mcp25xxfd_debugfs_setup(spi);
> +	if (ret)
> +		goto out_ctlclk;
> +
> +	/* disable interrupts */
> +	ret = mcp25xxfd_enable_interrupts(spi, false);
> +	if (ret)
> +		goto out_debugfs;
> +
> +	/* setup ECC for SRAM */
> +	ret = mcp25xxfd_enable_ecc(spi);
> +	if (ret)
> +		goto out_debugfs;
> +
> +	/* and put controller to sleep by stopping the can clock */
> +	ret = mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
> +	if (ret)
> +		goto out_debugfs;
> +
> +	dev_info(&spi->dev,
> +		 "MCP%x successfully initialized.\n", priv->model);
> +	return 0;
> +
> +out_debugfs:
> +	mcp25xxfd_debugfs_remove(spi);
> +out_ctlclk:
> +	mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
> +out_probe:
> +	mcp25xxfd_power_enable(priv->power, 0);
> +out_clk:
> +	mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
> +out_free:
> +	mcp25xxfd_debugfs_remove(spi);
> +	dev_err(&spi->dev, "Probe failed, err=%d\n", -ret);
> +	return ret;
> +}
> +
> +static int mcp25xxfd_remove(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +
> +	/* clear all running clocks */
> +	mcp25xxfd_stop_clock(spi, priv->clk_user_mask);
> +
> +	mcp25xxfd_debugfs_remove(spi);
> +
> +	mcp25xxfd_power_enable(priv->power, 0);
> +
> +	if (!IS_ERR(priv->clk))
> +		clk_disable_unprepare(priv->clk);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused mcp25xxfd_suspend(struct device *dev)
> +{
> +	struct spi_device *spi = to_spi_device(dev);
> +
> +	return mcp25xxfd_hw_sleep(spi);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused mcp25xxfd_resume(struct device *dev)
> +{
> +	struct spi_device *spi = to_spi_device(dev);
> +
> +	return mcp25xxfd_hw_wake(spi);
> +}
> +
> +static SIMPLE_DEV_PM_OPS(mcp25xxfd_pm_ops, mcp25xxfd_suspend,
> +	mcp25xxfd_resume);
> +
> +static const struct spi_device_id mcp25xxfd_id_table[] = {
> +	{
> +		.name		= "mcp2517fd",
> +		.driver_data	= (kernel_ulong_t)CAN_MCP2517FD,
> +	},
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(spi, mcp25xxfd_id_table);
> +
> +static struct spi_driver mcp25xxfd_can_driver = {
> +	.driver = {
> +		.name = DEVICE_NAME,
> +		.of_match_table = mcp25xxfd_of_match,
> +		.pm = &mcp25xxfd_pm_ops,
> +	},
> +	.id_table = mcp25xxfd_id_table,
> +	.probe = mcp25xxfd_probe,
> +	.remove = mcp25xxfd_remove,
> +};
> +module_spi_driver(mcp25xxfd_can_driver);
> +
> +MODULE_AUTHOR("Martin Sperl <kernel@martin.sperl.org>");
> +MODULE_DESCRIPTION("Microchip 25XXFD CAN driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
> new file mode 100644
> index 000000000000..e5c7098a09c2
> --- /dev/null
> +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
> @@ -0,0 +1,228 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
> + *
> + * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
> + *
> + * Based on Microchip MCP251x CAN controller driver written by
> + * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
> + */
> +
> +/* controller details
> + *
> + *  It has 32 FIFOs (of up to 32 CAN-frames).
> + *
> + * There are 4 Fifo types which can get configured:
> + * * TEF - Transmission Event Fifo - which consumes FIFO 0
> + *   even if it is not configured
> + * * Tansmission Queue - for up to 32 Frames.
> + *   this queue reorders CAN frames to get transmitted following the
> + *   typical CAN dominant/recessive rules on the can bus itself.
> + *   This FIFO is optional.
> + * * TX FIFO: generic TX fifos that can contain arbitrary data
> + *   and which come with a configurable priority for transmission
> + *   It is also possible to have the Controller automatically trigger
> + *   a transfer when a Filter Rule for a RTR frame matches.
> + *   Each of these fifos in principle can get configured for distinct
> + *   dlc sizes (8 thru 64 bytes)
> + * * RX FIFO: generic RX fifo which is filled via filter-rules.
> + *   Each of these fifos in principle can get configured for distinct
> + *   dlc sizes (8 thru 64 bytes)
> + *   Unfortunately there is no filter rule that would allow triggering
> + *   on different frame sizes, so for all practical purposes the
> + *   RX fifos have to be of the same size (unless one wants to experience
> + *   lost data).
> + * When a Can Frame is transmitted from the TX Queue or an individual
> + * TX FIFO then a small TEF Frame can get added to the TEF FIFO queue
> + * to log the Transmission of the frame - this includes ID, Flags
> + * (including a custom identifier/index) and the timestamp (see below).
> + *
> + * The controller provides an optional free running counter with a divider
> + * for timestamping of RX frames as well as for TEF entries.
> + */
> +
> +/* Implementation notes:
> + *
> + * Right now we only use the CAN controller block to put us into deep sleep
> + * this means that the oscillator clock is turned off.
> + * So this is the only thing that we implement here right now
> + */
> +
> +#include "mcp25xxfd_can.h"
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/spi/spi.h>
> +
> +static int mcp25xxfd_can_get_mode(struct spi_device *spi,
> +				  u32 *mode_data)
> +{
> +	int ret;
> +
> +	ret = mcp25xxfd_cmd_read(spi, CAN_CON, mode_data);
> +	if (ret)
> +		return ret;
> +
> +	return (*mode_data & CAN_CON_OPMOD_MASK) >> CAN_CON_OPMOD_SHIFT;
> +}
> +
> +int mcp25xxfd_can_switch_mode(struct spi_device *spi,
> +			      u32 *mode_data, int mode)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	int ret, i;
> +
> +	/* get the current mode/register - if mode_data is -1
> +	 * this only happens during initialization phase
> +	 * when the can controller is not setup yet
> +	 * typically by calling mcp25xxfd_can_sleep_mode
> +	 */
> +	if (*mode_data == -1) {
> +		ret = mcp25xxfd_can_get_mode(spi, mode_data);
> +		if (ret < 0)
> +			return ret;
> +	}
> +
> +	/* compute the effective mode in osc*/
> +	*mode_data &= ~(CAN_CON_REQOP_MASK | CAN_CON_OPMOD_MASK);
> +	*mode_data |= (mode << CAN_CON_REQOP_SHIFT) |
> +		(mode << CAN_CON_OPMOD_SHIFT);
> +
> +	/* if the opmode is sleep then the oscilator will be disabled
> +	 * and also not ready, so fake this change
> +	 */
> +	if (mode == CAN_CON_MODE_SLEEP) {
> +		priv->regs.osc &= ~(MCP25XXFD_OSC_OSCRDY |
> +				    MCP25XXFD_OSC_PLLRDY |
> +				    MCP25XXFD_OSC_SCLKRDY);
> +		priv->regs.osc |= MCP25XXFD_OSC_OSCDIS;
> +	}
> +
> +	/* request the mode switch */
> +	ret = mcp25xxfd_cmd_write(spi, CAN_CON, *mode_data);
> +	if (ret)
> +		return ret;
> +
> +	/* if we are in sleep mode then return immediately
> +	 * the controller does not respond back!
> +	 */
> +	if (mode == CAN_CON_MODE_SLEEP)
> +		return 0;
> +
> +	/* wait for it to stabilize/switch mode
> +	 * we assume 256 rounds should be enough as this is > 12ms
> +	 * at 1MHz Can Bus speed without any extra overhead
> +	 *
> +	 * The assumption here is that it depends on bus activity
> +	 * how long it takes the controller to switch modes
> +	 */
> +	for (i = 0; i < 256; i++) {
> +		/* get the mode */
> +		ret = mcp25xxfd_can_get_mode(spi, mode_data);
> +		if (ret < 0)
> +			return ret;
> +		/* check that we have reached our mode */
> +		if (ret == mode)
> +			return 0;
> +	}
> +
> +	dev_err(&spi->dev, "Failed to switch to mode %u in time\n", mode);
> +	return -ETIMEDOUT;
> +}
> +
> +int mcp25xxfd_can_sleep_mode(struct spi_device *spi)
> +{
> +	u32 value = -1;
> +
> +	return mcp25xxfd_can_switch_mode(spi, &value, CAN_CON_MODE_SLEEP);
> +}
> +
> +int mcp25xxfd_can_hw_probe_modeswitch(struct spi_device *spi)
> +{
> +	u32 mode_data;
> +	int ret;
> +
> +	/* so we should be in config mode now, so move to INTERNAL_LOOPBACK */
> +	ret = mcp25xxfd_can_switch_mode(spi, &mode_data,
> +					CAN_CON_MODE_INTERNAL_LOOPBACK);
> +	if (ret) {
> +		dev_err(&spi->dev,
> +			"Failed to switch into loopback mode\n");
> +		return ret;
> +	}
> +
> +	/* and back into config mode */
> +	ret = mcp25xxfd_can_switch_mode(spi, &mode_data,
> +					CAN_CON_MODE_CONFIG);
> +	if (ret) {
> +		dev_err(&spi->dev,
> +			"Failed to switch back to config mode\n");
> +		return ret;
> +	}
> +
> +	/* so we have checked basic functionality successfully */
> +	return 0;
> +}
> +
> +int mcp25xxfd_can_hw_probe(struct spi_device *spi)
> +{
> +	u32 mode_data;
> +	int mode, ret;
> +
> +	/* read TXQCON - the TXEN bit should always read as 1 */
> +	ret = mcp25xxfd_cmd_read(spi, CAN_TXQCON, &mode_data);
> +	if (ret)
> +		return ret;
> +	if ((mode_data & CAN_TXQCON_TXEN) == 0) {
> +		dev_err(&spi->dev,
> +			"Register TXQCON does not have bit TXEN set - reads as %08x",
> +			mode_data);
> +		return -EINVAL;
> +	}
> +
> +	/* try to get the current mode */
> +	mode = mcp25xxfd_can_get_mode(spi, &mode_data);
> +	if (mode < 0)
> +		return mode;
> +
> +	/* we would expect to be in config mode, as a SPI-reset should
> +	 * have moved us into config mode.
> +	 * But then the documentation says that SPI-reset may only work
> +	 * reliably when already in config mode
> +	 */
> +
> +	/* so if we are in config mode then everything is fine
> +	 * and we check that a mode switch works propperly
> +	 */
> +	if (mode == CAN_CON_MODE_CONFIG)
> +		return mcp25xxfd_can_hw_probe_modeswitch(spi);
> +
> +	/* if the bitfield is 0 then there is something is wrong */
> +	if (!mode_data) {
> +		dev_err(&spi->dev,
> +			"got controller config register reading as 0\n");
> +		return -EINVAL;
> +	}
> +
> +	/* any other mode is unexpected */
> +	dev_err(&spi->dev,
> +		"Found controller in unexpected mode %i - register reads as %08x\n",
> +		mode, mode_data);
> +
> +	/* so try to move to config mode
> +	 * if this fails, then everything is lost and the controller
> +	 * is not identified
> +	 * This action MAY be destructive if a different device is connected
> +	 * but note that the first hurdle (oscillator) was already
> +	 * successful - so we should be safe...
> +	 */
> +	ret = mcp25xxfd_can_switch_mode(spi, &mode_data,
> +					CAN_CON_MODE_CONFIG);
> +	if (ret) {
> +		dev_err(&spi->dev,
> +			"Mode did not switch to config as expected - could not identify controller - register reads as %08x\n",
> +			mode_data);
> +		return -EINVAL;
> +	}
> +	/* check that modeswitch is really working */
> +	return mcp25xxfd_can_hw_probe_modeswitch(spi);
> +}
> diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
> new file mode 100644
> index 000000000000..fe0e93e28b62
> --- /dev/null
> +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
> @@ -0,0 +1,487 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
> + *
> + * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
> + *
> + * Based on Microchip MCP251x CAN controller driver written by
> + * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
> + */
> +
> +#include "mcp25xxfd.h"
> +#include <linux/bitops.h>
> +
> +#define CAN_SFR_BASE(x)			(0x000 + (x))
> +#define CAN_CON				CAN_SFR_BASE(0x00)

The prefix "CAN_" is not generic enough. Name clashes, e.g. with
"CAN_INT" are not that unlikely. Why not using "MCP25XXFD_" instead?

And could you please mov the register definitions into "mcp25xxfd_reg.h".

> +#  define CAN_CON_DNCNT_BITS		5
> +#  define CAN_CON_DNCNT_SHIFT		0
> +#  define CAN_CON_DNCNT_MASK					\
> +	GENMASK(CAN_CON_DNCNT_SHIFT + CAN_CON_DNCNT_BITS - 1,	\
> +		CAN_CON_DNCNT_SHIFT)
> +#  define CAN_CON_ISOCRCEN		BIT(5)
> +#  define CAN_CON_PXEDIS		BIT(6)
> +#  define CAN_CON_WAKFIL		BIT(8)
> +#  define CAN_CON_WFT_BITS		2
> +#  define CAN_CON_WFT_SHIFT		9
> +#  define CAN_CON_WFT_MASK					\
> +	GENMASK(CAN_CON_WFT_SHIFT + CAN_CON_WFT_BITS - 1,	\
> +		CAN_CON_WFT_SHIFT)
> +#  define CAN_CON_BUSY			BIT(11)
> +#  define CAN_CON_BRSDIS		BIT(12)
> +#  define CAN_CON_RTXAT			BIT(16)
> +#  define CAN_CON_ESIGM			BIT(17)
> +#  define CAN_CON_SERR2LOM		BIT(18)
> +#  define CAN_CON_STEF			BIT(19)
> +#  define CAN_CON_TXQEN			BIT(20)
> +#  define CAN_CON_OPMODE_BITS		3
> +#  define CAN_CON_OPMOD_SHIFT		21
> +#  define CAN_CON_OPMOD_MASK					\
> +	GENMASK(CAN_CON_OPMOD_SHIFT + CAN_CON_OPMODE_BITS - 1,	\
> +		CAN_CON_OPMOD_SHIFT)
> +#  define CAN_CON_REQOP_BITS		3
> +#  define CAN_CON_REQOP_SHIFT		24
> +#  define CAN_CON_REQOP_MASK					\
> +	GENMASK(CAN_CON_REQOP_SHIFT + CAN_CON_REQOP_BITS - 1,	\
> +		CAN_CON_REQOP_SHIFT)
> +#    define CAN_CON_MODE_MIXED			0
> +#    define CAN_CON_MODE_SLEEP			1
> +#    define CAN_CON_MODE_INTERNAL_LOOPBACK	2
> +#    define CAN_CON_MODE_LISTENONLY		3
> +#    define CAN_CON_MODE_CONFIG			4
> +#    define CAN_CON_MODE_EXTERNAL_LOOPBACK	5
> +#    define CAN_CON_MODE_CAN2_0			6
> +#    define CAN_CON_MODE_RESTRICTED		7
> +#  define CAN_CON_ABAT			BIT(27)
> +#  define CAN_CON_TXBWS_BITS		3
> +#  define CAN_CON_TXBWS_SHIFT		28
> +#  define CAN_CON_TXBWS_MASK					\
> +	GENMASK(CAN_CON_TXBWS_SHIFT + CAN_CON_TXBWS_BITS - 1,	\
> +		CAN_CON_TXBWS_SHIFT)
> +#  define CAN_CON_DEFAULT				\
> +	(CAN_CON_ISOCRCEN |				\
> +	 CAN_CON_PXEDIS |				\
> +	 CAN_CON_WAKFIL |				\
> +	 (3 << CAN_CON_WFT_SHIFT) |			\
> +	 CAN_CON_STEF |					\
> +	 CAN_CON_TXQEN |				\
> +	 (CAN_CON_MODE_CONFIG << CAN_CON_OPMOD_SHIFT) |	\
> +	 (CAN_CON_MODE_CONFIG << CAN_CON_REQOP_SHIFT))

Could you please align to the right, here and below.

> +#  define CAN_CON_DEFAULT_MASK	\
> +	(CAN_CON_DNCNT_MASK |	\
> +	 CAN_CON_ISOCRCEN |	\
> +	 CAN_CON_PXEDIS |	\
> +	 CAN_CON_WAKFIL |	\
> +	 CAN_CON_WFT_MASK |	\
> +	 CAN_CON_BRSDIS |	\
> +	 CAN_CON_RTXAT |	\
> +	 CAN_CON_ESIGM |	\
> +	 CAN_CON_SERR2LOM |	\
> +	 CAN_CON_STEF |		\
> +	 CAN_CON_TXQEN |	\
> +	 CAN_CON_OPMOD_MASK |	\
> +	 CAN_CON_REQOP_MASK |	\
> +	 CAN_CON_ABAT |		\
> +	 CAN_CON_TXBWS_MASK)
> +#define CAN_NBTCFG			CAN_SFR_BASE(0x04)
> +#  define CAN_NBTCFG_SJW_BITS		7
> +#  define CAN_NBTCFG_SJW_SHIFT		0
> +#  define CAN_NBTCFG_SJW_MASK					\
> +	GENMASK(CAN_NBTCFG_SJW_SHIFT + CAN_NBTCFG_SJW_BITS - 1, \
> +		CAN_NBTCFG_SJW_SHIFT)
> +#  define CAN_NBTCFG_TSEG2_BITS		7
> +#  define CAN_NBTCFG_TSEG2_SHIFT	8
> +#  define CAN_NBTCFG_TSEG2_MASK					    \
> +	GENMASK(CAN_NBTCFG_TSEG2_SHIFT + CAN_NBTCFG_TSEG2_BITS - 1, \
> +		CAN_NBTCFG_TSEG2_SHIFT)
> +#  define CAN_NBTCFG_TSEG1_BITS		8
> +#  define CAN_NBTCFG_TSEG1_SHIFT	16
> +#  define CAN_NBTCFG_TSEG1_MASK					    \
> +	GENMASK(CAN_NBTCFG_TSEG1_SHIFT + CAN_NBTCFG_TSEG1_BITS - 1, \
> +		CAN_NBTCFG_TSEG1_SHIFT)
> +#  define CAN_NBTCFG_BRP_BITS		8
> +#  define CAN_NBTCFG_BRP_SHIFT		24
> +#  define CAN_NBTCFG_BRP_MASK					\
> +	GENMASK(CAN_NBTCFG_BRP_SHIFT + CAN_NBTCFG_BRP_BITS - 1, \
> +		CAN_NBTCFG_BRP_SHIFT)
> +#define CAN_DBTCFG			CAN_SFR_BASE(0x08)
> +#  define CAN_DBTCFG_SJW_BITS		4
> +#  define CAN_DBTCFG_SJW_SHIFT		0
> +#  define CAN_DBTCFG_SJW_MASK					\
> +	GENMASK(CAN_DBTCFG_SJW_SHIFT + CAN_DBTCFG_SJW_BITS - 1, \
> +		CAN_DBTCFG_SJW_SHIFT)
> +#  define CAN_DBTCFG_TSEG2_BITS		4
> +#  define CAN_DBTCFG_TSEG2_SHIFT	8
> +#  define CAN_DBTCFG_TSEG2_MASK					    \
> +	GENMASK(CAN_DBTCFG_TSEG2_SHIFT + CAN_DBTCFG_TSEG2_BITS - 1, \
> +		CAN_DBTCFG_TSEG2_SHIFT)
> +#  define CAN_DBTCFG_TSEG1_BITS		5
> +#  define CAN_DBTCFG_TSEG1_SHIFT	16
> +#  define CAN_DBTCFG_TSEG1_MASK					    \
> +	GENMASK(CAN_DBTCFG_TSEG1_SHIFT + CAN_DBTCFG_TSEG1_BITS - 1, \
> +		CAN_DBTCFG_TSEG1_SHIFT)
> +#  define CAN_DBTCFG_BRP_BITS		8
> +#  define CAN_DBTCFG_BRP_SHIFT		24
> +#  define CAN_DBTCFG_BRP_MASK					\
> +	GENMASK(CAN_DBTCFG_BRP_SHIFT + CAN_DBTCFG_BRP_BITS - 1, \
> +		CAN_DBTCFG_BRP_SHIFT)
> +#define CAN_TDC				CAN_SFR_BASE(0x0C)
> +#  define CAN_TDC_TDCV_BITS		5
> +#  define CAN_TDC_TDCV_SHIFT		0
> +#  define CAN_TDC_TDCV_MASK					\
> +	GENMASK(CAN_TDC_TDCV_SHIFT + CAN_TDC_TDCV_BITS - 1,	\
> +		CAN_TDC_TDCV_SHIFT)
> +#  define CAN_TDC_TDCO_BITS		5
> +#  define CAN_TDC_TDCO_SHIFT		8
> +#  define CAN_TDC_TDCO_MASK					\
> +	GENMASK(CAN_TDC_TDCO_SHIFT + CAN_TDC_TDCO_BITS - 1,	\
> +		CAN_TDC_TDCO_SHIFT)
> +#  define CAN_TDC_TDCMOD_BITS		2
> +#  define CAN_TDC_TDCMOD_SHIFT		16
> +#  define CAN_TDC_TDCMOD_MASK					\
> +	GENMASK(CAN_TDC_TDCMOD_SHIFT + CAN_TDC_TDCMOD_BITS - 1, \
> +		CAN_TDC_TDCMOD_SHIFT)
> +#  define CAN_TDC_TDCMOD_DISABLED	0
> +#  define CAN_TDC_TDCMOD_MANUAL		1
> +#  define CAN_TDC_TDCMOD_AUTO		2
> +#  define CAN_TDC_SID11EN		BIT(24)
> +#  define CAN_TDC_EDGFLTEN		BIT(25)
> +#define CAN_TBC				CAN_SFR_BASE(0x10)
> +#define CAN_TSCON			CAN_SFR_BASE(0x14)
> +#  define CAN_TSCON_TBCPRE_BITS		10
> +#  define CAN_TSCON_TBCPRE_SHIFT	0
> +#  define CAN_TSCON_TBCPRE_MASK					    \
> +	GENMASK(CAN_TSCON_TBCPRE_SHIFT + CAN_TSCON_TBCPRE_BITS - 1, \
> +		CAN_TSCON_TBCPRE_SHIFT)
> +#  define CAN_TSCON_TBCEN		BIT(16)
> +#  define CAN_TSCON_TSEOF		BIT(17)
> +#  define CAN_TSCON_TSRES		BIT(18)
> +#define CAN_VEC				CAN_SFR_BASE(0x18)
> +#  define CAN_VEC_ICODE_BITS		7
> +#  define CAN_VEC_ICODE_SHIFT		0
> +#  define CAN_VEC_ICODE_MASK					    \
> +	GENMASK(CAN_VEC_ICODE_SHIFT + CAN_VEC_ICODE_BITS - 1,	    \
> +		CAN_VEC_ICODE_SHIFT)
> +#  define CAN_VEC_FILHIT_BITS		5
> +#  define CAN_VEC_FILHIT_SHIFT		8
> +#  define CAN_VEC_FILHIT_MASK					\
> +	GENMASK(CAN_VEC_FILHIT_SHIFT + CAN_VEC_FILHIT_BITS - 1, \
> +		CAN_VEC_FILHIT_SHIFT)
> +#  define CAN_VEC_TXCODE_BITS		7
> +#  define CAN_VEC_TXCODE_SHIFT		16
> +#  define CAN_VEC_TXCODE_MASK					\
> +	GENMASK(CAN_VEC_TXCODE_SHIFT + CAN_VEC_TXCODE_BITS - 1, \
> +		CAN_VEC_TXCODE_SHIFT)
> +#  define CAN_VEC_RXCODE_BITS		7
> +#  define CAN_VEC_RXCODE_SHIFT		24
> +#  define CAN_VEC_RXCODE_MASK					\
> +	GENMASK(CAN_VEC_RXCODE_SHIFT + CAN_VEC_RXCODE_BITS - 1, \
> +		CAN_VEC_RXCODE_SHIFT)
> +#define CAN_INT				CAN_SFR_BASE(0x1C)
> +#  define CAN_INT_IF_SHIFT		0
> +#  define CAN_INT_TXIF			BIT(0)
> +#  define CAN_INT_RXIF			BIT(1)
> +#  define CAN_INT_TBCIF			BIT(2)
> +#  define CAN_INT_MODIF			BIT(3)
> +#  define CAN_INT_TEFIF			BIT(4)
> +#  define CAN_INT_ECCIF			BIT(8)
> +#  define CAN_INT_SPICRCIF		BIT(9)
> +#  define CAN_INT_TXATIF		BIT(10)
> +#  define CAN_INT_RXOVIF		BIT(11)
> +#  define CAN_INT_SERRIF		BIT(12)
> +#  define CAN_INT_CERRIF		BIT(13)
> +#  define CAN_INT_WAKIF			BIT(14)
> +#  define CAN_INT_IVMIF			BIT(15)
> +#  define CAN_INT_IF_MASK		\
> +	(CAN_INT_TXIF |			\
> +	 CAN_INT_RXIF |			\
> +	 CAN_INT_TBCIF	|		\
> +	 CAN_INT_MODIF	|		\
> +	 CAN_INT_TEFIF	|		\
> +	 CAN_INT_ECCIF	|		\
> +	 CAN_INT_SPICRCIF |		\
> +	 CAN_INT_TXATIF |		\
> +	 CAN_INT_RXOVIF |		\
> +	 CAN_INT_CERRIF |		\
> +	 CAN_INT_SERRIF |		\
> +	 CAN_INT_WAKIF |		\
> +	 CAN_INT_IVMIF)
> +#  define CAN_INT_IE_SHIFT		16
> +#  define CAN_INT_TXIE			(CAN_INT_TXIF << CAN_INT_IE_SHIFT)
> +#  define CAN_INT_RXIE			(CAN_INT_RXIF << CAN_INT_IE_SHIFT)
> +#  define CAN_INT_TBCIE			(CAN_INT_TBCIF << CAN_INT_IE_SHIFT)
> +#  define CAN_INT_MODIE			(CAN_INT_MODIF << CAN_INT_IE_SHIFT)
> +#  define CAN_INT_TEFIE			(CAN_INT_TEFIF << CAN_INT_IE_SHIFT)
> +#  define CAN_INT_ECCIE			(CAN_INT_ECCIF << CAN_INT_IE_SHIFT)
> +#  define CAN_INT_SPICRCIE		\
> +	(CAN_INT_SPICRCIF << CAN_INT_IE_SHIFT)
> +#  define CAN_INT_TXATIE		(CAN_INT_TXATIF << CAN_INT_IE_SHIFT)
> +#  define CAN_INT_RXOVIE		(CAN_INT_RXOVIF << CAN_INT_IE_SHIFT)
> +#  define CAN_INT_CERRIE		(CAN_INT_CERRIF << CAN_INT_IE_SHIFT)
> +#  define CAN_INT_SERRIE		(CAN_INT_SERRIF << CAN_INT_IE_SHIFT)
> +#  define CAN_INT_WAKIE			(CAN_INT_WAKIF << CAN_INT_IE_SHIFT)
> +#  define CAN_INT_IVMIE			(CAN_INT_IVMIF << CAN_INT_IE_SHIFT)
> +#  define CAN_INT_IE_MASK		\
> +	(CAN_INT_TXIE |			\
> +	 CAN_INT_RXIE |			\
> +	 CAN_INT_TBCIE	|		\
> +	 CAN_INT_MODIE	|		\
> +	 CAN_INT_TEFIE	|		\
> +	 CAN_INT_ECCIE	|		\
> +	 CAN_INT_SPICRCIE |		\
> +	 CAN_INT_TXATIE |		\
> +	 CAN_INT_RXOVIE |		\
> +	 CAN_INT_CERRIE |		\
> +	 CAN_INT_SERRIE |		\
> +	 CAN_INT_WAKIE |		\
> +	 CAN_INT_IVMIE)
> +#define CAN_RXIF			CAN_SFR_BASE(0x20)
> +#define CAN_TXIF			CAN_SFR_BASE(0x24)
> +#define CAN_RXOVIF			CAN_SFR_BASE(0x28)
> +#define CAN_TXATIF			CAN_SFR_BASE(0x2C)
> +#define CAN_TXREQ			CAN_SFR_BASE(0x30)
> +#define CAN_TREC			CAN_SFR_BASE(0x34)
> +#  define CAN_TREC_REC_BITS		8
> +#  define CAN_TREC_REC_SHIFT		0
> +#  define CAN_TREC_REC_MASK				    \
> +	GENMASK(CAN_TREC_REC_SHIFT + CAN_TREC_REC_BITS - 1, \
> +		CAN_TREC_REC_SHIFT)
> +#  define CAN_TREC_TEC_BITS		8
> +#  define CAN_TREC_TEC_SHIFT		8
> +#  define CAN_TREC_TEC_MASK				    \
> +	GENMASK(CAN_TREC_TEC_SHIFT + CAN_TREC_TEC_BITS - 1, \
> +		CAN_TREC_TEC_SHIFT)
> +#  define CAN_TREC_EWARN		BIT(16)
> +#  define CAN_TREC_RXWARN		BIT(17)
> +#  define CAN_TREC_TXWARN		BIT(18)
> +#  define CAN_TREC_RXBP			BIT(19)
> +#  define CAN_TREC_TXBP			BIT(20)
> +#  define CAN_TREC_TXBO			BIT(21)
> +#define CAN_BDIAG0			CAN_SFR_BASE(0x38)
> +#  define CAN_BDIAG0_NRERRCNT_BITS	8
> +#  define CAN_BDIAG0_NRERRCNT_SHIFT	0
> +#  define CAN_BDIAG0_NRERRCNT_MASK					\
> +	GENMASK(CAN_BDIAG0_NRERRCNT_SHIFT + CAN_BDIAG0_NRERRCNT_BITS - 1, \
> +		CAN_BDIAG0_NRERRCNT_SHIFT)
> +#  define CAN_BDIAG0_NTERRCNT_BITS	8
> +#  define CAN_BDIAG0_NTERRCNT_SHIFT	8
> +#  define CAN_BDIAG0_NTERRCNT_MASK					\
> +	GENMASK(CAN_BDIAG0_NTERRCNT_SHIFT + CAN_BDIAG0_NTERRCNT_BITS - 1, \
> +		CAN_BDIAG0_NTERRCNT_SHIFT)
> +#  define CAN_BDIAG0_DRERRCNT_BITS	8
> +#  define CAN_BDIAG0_DRERRCNT_SHIFT	16
> +#  define CAN_BDIAG0_DRERRCNT_MASK					\
> +	GENMASK(CAN_BDIAG0_DRERRCNT_SHIFT + CAN_BDIAG0_DRERRCNT_BITS - 1, \
> +		CAN_BDIAG0_DRERRCNT_SHIFT)
> +#  define CAN_BDIAG0_DTERRCNT_BITS	8
> +#  define CAN_BDIAG0_DTERRCNT_SHIFT	24
> +#  define CAN_BDIAG0_DTERRCNT_MASK					\
> +	GENMASK(CAN_BDIAG0_DTERRCNT_SHIFT + CAN_BDIAG0_DTERRCNT_BITS - 1, \
> +		CAN_BDIAG0_DTERRCNT_SHIFT)
> +#define CAN_BDIAG1			CAN_SFR_BASE(0x3C)
> +#  define CAN_BDIAG1_EFMSGCNT_BITS	16
> +#  define CAN_BDIAG1_EFMSGCNT_SHIFT	0
> +#  define CAN_BDIAG1_EFMSGCNT_MASK					\
> +	GENMASK(CAN_BDIAG1_EFMSGCNT_SHIFT + CAN_BDIAG1_EFMSGCNT_BITS - 1, \
> +		CAN_BDIAG1_EFMSGCNT_SHIFT)
> +#  define CAN_BDIAG1_NBIT0ERR		BIT(16)
> +#  define CAN_BDIAG1_NBIT1ERR		BIT(17)
> +#  define CAN_BDIAG1_NACKERR		BIT(18)
> +#  define CAN_BDIAG1_NSTUFERR		BIT(19)
> +#  define CAN_BDIAG1_NFORMERR		BIT(20)
> +#  define CAN_BDIAG1_NCRCERR		BIT(21)
> +#  define CAN_BDIAG1_TXBOERR		BIT(23)
> +#  define CAN_BDIAG1_DBIT0ERR		BIT(24)
> +#  define CAN_BDIAG1_DBIT1ERR		BIT(25)
> +#  define CAN_BDIAG1_DFORMERR		BIT(27)
> +#  define CAN_BDIAG1_DSTUFERR		BIT(28)
> +#  define CAN_BDIAG1_DCRCERR		BIT(29)
> +#  define CAN_BDIAG1_ESI		BIT(30)
> +#  define CAN_BDIAG1_DLCMM		BIT(31)
> +#define CAN_TEFCON			CAN_SFR_BASE(0x40)
> +#  define CAN_TEFCON_TEFNEIE		BIT(0)
> +#  define CAN_TEFCON_TEFHIE		BIT(1)
> +#  define CAN_TEFCON_TEFFIE		BIT(2)
> +#  define CAN_TEFCON_TEFOVIE		BIT(3)
> +#  define CAN_TEFCON_TEFTSEN		BIT(5)
> +#  define CAN_TEFCON_UINC		BIT(8)
> +#  define CAN_TEFCON_FRESET		BIT(10)
> +#  define CAN_TEFCON_FSIZE_BITS		5
> +#  define CAN_TEFCON_FSIZE_SHIFT	24
> +#  define CAN_TEFCON_FSIZE_MASK					    \
> +	GENMASK(CAN_TEFCON_FSIZE_SHIFT + CAN_TEFCON_FSIZE_BITS - 1, \
> +		CAN_TEFCON_FSIZE_SHIFT)
> +#define CAN_TEFSTA			CAN_SFR_BASE(0x44)
> +#  define CAN_TEFSTA_TEFNEIF		BIT(0)
> +#  define CAN_TEFSTA_TEFHIF		BIT(1)
> +#  define CAN_TEFSTA_TEFFIF		BIT(2)
> +#  define CAN_TEFSTA_TEVOVIF		BIT(3)
> +#define CAN_TEFUA			CAN_SFR_BASE(0x48)
> +#define CAN_RESERVED			CAN_SFR_BASE(0x4C)
> +#define CAN_TXQCON			CAN_SFR_BASE(0x50)
> +#  define CAN_TXQCON_TXQNIE		BIT(0)
> +#  define CAN_TXQCON_TXQEIE		BIT(2)
> +#  define CAN_TXQCON_TXATIE		BIT(4)
> +#  define CAN_TXQCON_TXEN		BIT(7)
> +#  define CAN_TXQCON_UINC		BIT(8)
> +#  define CAN_TXQCON_TXREQ		BIT(9)
> +#  define CAN_TXQCON_FRESET		BIT(10)
> +#  define CAN_TXQCON_TXPRI_BITS		5
> +#  define CAN_TXQCON_TXPRI_SHIFT	16
> +#  define CAN_TXQCON_TXPRI_MASK					    \
> +	GENMASK(CAN_TXQCON_TXPRI_SHIFT + CAN_TXQCON_TXPRI_BITS - 1, \
> +		CAN_TXQCON_TXPRI_SHIFT)
> +#  define CAN_TXQCON_TXAT_BITS		2
> +#  define CAN_TXQCON_TXAT_SHIFT		21
> +#  define CAN_TXQCON_TXAT_MASK					    \
> +	GENMASK(CAN_TXQCON_TXAT_SHIFT + CAN_TXQCON_TXAT_BITS - 1,   \
> +		CAN_TXQCON_TXAT_SHIFT)
> +#  define CAN_TXQCON_FSIZE_BITS		5
> +#  define CAN_TXQCON_FSIZE_SHIFT	24
> +#  define CAN_TXQCON_FSIZE_MASK					    \
> +	GENMASK(CAN_TXQCON_FSIZE_SHIFT + CAN_TXQCON_FSIZE_BITS - 1, \
> +		CAN_TXQCON_FSIZE_SHIFT)
> +#  define CAN_TXQCON_PLSIZE_BITS	3
> +#  define CAN_TXQCON_PLSIZE_SHIFT	29
> +#  define CAN_TXQCON_PLSIZE_MASK				      \
> +	GENMASK(CAN_TXQCON_PLSIZE_SHIFT + CAN_TXQCON_PLSIZE_BITS - 1, \
> +		CAN_TXQCON_PLSIZE_SHIFT)
> +#    define CAN_TXQCON_PLSIZE_8		0
> +#    define CAN_TXQCON_PLSIZE_12	1
> +#    define CAN_TXQCON_PLSIZE_16	2
> +#    define CAN_TXQCON_PLSIZE_20	3
> +#    define CAN_TXQCON_PLSIZE_24	4
> +#    define CAN_TXQCON_PLSIZE_32	5
> +#    define CAN_TXQCON_PLSIZE_48	6
> +#    define CAN_TXQCON_PLSIZE_64	7
> +
> +#define CAN_TXQSTA			CAN_SFR_BASE(0x54)
> +#  define CAN_TXQSTA_TXQNIF		BIT(0)
> +#  define CAN_TXQSTA_TXQEIF		BIT(2)
> +#  define CAN_TXQSTA_TXATIF		BIT(4)
> +#  define CAN_TXQSTA_TXERR		BIT(5)
> +#  define CAN_TXQSTA_TXLARB		BIT(6)
> +#  define CAN_TXQSTA_TXABT		BIT(7)
> +#  define CAN_TXQSTA_TXQCI_BITS		5
> +#  define CAN_TXQSTA_TXQCI_SHIFT	8
> +#  define CAN_TXQSTA_TXQCI_MASK					    \
> +	GENMASK(CAN_TXQSTA_TXQCI_SHIFT + CAN_TXQSTA_TXQCI_BITS - 1, \
> +		CAN_TXQSTA_TXQCI_SHIFT)
> +
> +#define CAN_TXQUA			CAN_SFR_BASE(0x58)
> +#define CAN_FIFOCON(x)			CAN_SFR_BASE(0x5C + 12 * ((x) - 1))
> +#define CAN_FIFOCON_TFNRFNIE		BIT(0)
> +#define CAN_FIFOCON_TFHRFHIE		BIT(1)
> +#define CAN_FIFOCON_TFERFFIE		BIT(2)
> +#define CAN_FIFOCON_RXOVIE		BIT(3)
> +#define CAN_FIFOCON_TXATIE		BIT(4)
> +#define CAN_FIFOCON_RXTSEN		BIT(5)
> +#define CAN_FIFOCON_RTREN		BIT(6)
> +#define CAN_FIFOCON_TXEN		BIT(7)
> +#define CAN_FIFOCON_UINC		BIT(8)
> +#define CAN_FIFOCON_TXREQ		BIT(9)
> +#define CAN_FIFOCON_FRESET		BIT(10)
> +#  define CAN_FIFOCON_TXPRI_BITS	5
> +#  define CAN_FIFOCON_TXPRI_SHIFT	16
> +#  define CAN_FIFOCON_TXPRI_MASK					\
> +	GENMASK(CAN_FIFOCON_TXPRI_SHIFT + CAN_FIFOCON_TXPRI_BITS - 1,	\
> +		CAN_FIFOCON_TXPRI_SHIFT)
> +#  define CAN_FIFOCON_TXAT_BITS		2
> +#  define CAN_FIFOCON_TXAT_SHIFT	21
> +#  define CAN_FIFOCON_TXAT_MASK					    \
> +	GENMASK(CAN_FIFOCON_TXAT_SHIFT + CAN_FIFOCON_TXAT_BITS - 1, \
> +		CAN_FIFOCON_TXAT_SHIFT)
> +#  define CAN_FIFOCON_TXAT_ONE_SHOT	0
> +#  define CAN_FIFOCON_TXAT_THREE_SHOT	1
> +#  define CAN_FIFOCON_TXAT_UNLIMITED	2
> +#  define CAN_FIFOCON_FSIZE_BITS	5
> +#  define CAN_FIFOCON_FSIZE_SHIFT	24
> +#  define CAN_FIFOCON_FSIZE_MASK					\
> +	GENMASK(CAN_FIFOCON_FSIZE_SHIFT + CAN_FIFOCON_FSIZE_BITS - 1,	\
> +		CAN_FIFOCON_FSIZE_SHIFT)
> +#  define CAN_FIFOCON_PLSIZE_BITS	3
> +#  define CAN_FIFOCON_PLSIZE_SHIFT	29
> +#  define CAN_FIFOCON_PLSIZE_MASK					\
> +	GENMASK(CAN_FIFOCON_PLSIZE_SHIFT + CAN_FIFOCON_PLSIZE_BITS - 1, \
> +		CAN_FIFOCON_PLSIZE_SHIFT)
> +#define CAN_FIFOSTA(x)			CAN_SFR_BASE(0x60 + 12 * ((x) - 1))
> +#  define CAN_FIFOSTA_TFNRFNIF		BIT(0)
> +#  define CAN_FIFOSTA_TFHRFHIF		BIT(1)
> +#  define CAN_FIFOSTA_TFERFFIF		BIT(2)
> +#  define CAN_FIFOSTA_RXOVIF		BIT(3)
> +#  define CAN_FIFOSTA_TXATIF		BIT(4)
> +#  define CAN_FIFOSTA_TXERR		BIT(5)
> +#  define CAN_FIFOSTA_TXLARB		BIT(6)
> +#  define CAN_FIFOSTA_TXABT		BIT(7)
> +#  define CAN_FIFOSTA_FIFOCI_BITS	5
> +#  define CAN_FIFOSTA_FIFOCI_SHIFT	8
> +#  define CAN_FIFOSTA_FIFOCI_MASK					\
> +	GENMASK(CAN_FIFOSTA_FIFOCI_SHIFT + CAN_FIFOSTA_FIFOCI_BITS - 1, \
> +		CAN_FIFOSTA_FIFOCI_SHIFT)
> +#define CAN_FIFOUA(x)			CAN_SFR_BASE(0x64 + 12 * ((x) - 1))
> +#define CAN_FLTCON(x)			CAN_SFR_BASE(0x1D0 + ((x) & 0x1c))
> +#  define CAN_FILCON_SHIFT(x)		(((x) & 3) * 8)
> +#  define CAN_FILCON_BITS(x)		CAN_FILCON_BITS_
> +#  define CAN_FILCON_BITS_		4
> +	/* avoid macro reuse warning, so do not use GENMASK as above */
> +#  define CAN_FILCON_MASK(x)					\
> +	(GENMASK(CAN_FILCON_BITS_ - 1, 0) << CAN_FILCON_SHIFT(x))
> +#  define CAN_FIFOCON_FLTEN(x)		BIT(7 + CAN_FILCON_SHIFT(x))
> +#define CAN_FLTOBJ(x)			CAN_SFR_BASE(0x1F0 + 8 * (x))
> +#  define CAN_FILOBJ_SID_BITS		11
> +#  define CAN_FILOBJ_SID_SHIFT		0
> +#  define CAN_FILOBJ_SID_MASK					\
> +	GENMASK(CAN_FILOBJ_SID_SHIFT + CAN_FILOBJ_SID_BITS - 1, \
> +		CAN_FILOBJ_SID_SHIFT)
> +#  define CAN_FILOBJ_EID_BITS		18
> +#  define CAN_FILOBJ_EID_SHIFT		12
> +#  define CAN_FILOBJ_EID_MASK					\
> +	GENMASK(CAN_FILOBJ_EID_SHIFT + CAN_FILOBJ_EID_BITS - 1, \
> +		CAN_FILOBJ_EID_SHIFT)
> +#  define CAN_FILOBJ_SID11		BIT(29)
> +#  define CAN_FILOBJ_EXIDE		BIT(30)
> +#define CAN_FLTMASK(x)			CAN_SFR_BASE(0x1F4 + 8 * (x))
> +#  define CAN_FILMASK_MSID_BITS		11
> +#  define CAN_FILMASK_MSID_SHIFT	0
> +#  define CAN_FILMASK_MSID_MASK					    \
> +	GENMASK(CAN_FILMASK_MSID_SHIFT + CAN_FILMASK_MSID_BITS - 1, \
> +		CAN_FILMASK_MSID_SHIFT)
> +#  define CAN_FILMASK_MEID_BITS		18
> +#  define CAN_FILMASK_MEID_SHIFT	12
> +#  define CAN_FILMASK_MEID_MASK					    \
> +	GENMASK(CAN_FILMASK_MEID_SHIFT + CAN_FILMASK_MEID_BITS - 1, \
> +		CAN_FILMASK_MEID_SHIFT)
> +#  define CAN_FILMASK_MSID11		BIT(29)
> +#  define CAN_FILMASK_MIDE		BIT(30)
> +
> +#define CAN_OBJ_ID_SID_BITS		11
> +#define CAN_OBJ_ID_SID_SHIFT		0
> +#define CAN_OBJ_ID_SID_MASK					\
> +	GENMASK(CAN_OBJ_ID_SID_SHIFT + CAN_OBJ_ID_SID_BITS - 1, \
> +		CAN_OBJ_ID_SID_SHIFT)
> +#define CAN_OBJ_ID_EID_BITS		18
> +#define CAN_OBJ_ID_EID_SHIFT		11
> +#define CAN_OBJ_ID_EID_MASK					\
> +	GENMASK(CAN_OBJ_ID_EID_SHIFT + CAN_OBJ_ID_EID_BITS - 1, \
> +		CAN_OBJ_ID_EID_SHIFT)
> +#define CAN_OBJ_ID_SID_BIT11		BIT(29)
> +
> +#define CAN_OBJ_FLAGS_DLC_BITS		4
> +#define CAN_OBJ_FLAGS_DLC_SHIFT		0
> +#define CAN_OBJ_FLAGS_DLC_MASK					      \
> +	GENMASK(CAN_OBJ_FLAGS_DLC_SHIFT + CAN_OBJ_FLAGS_DLC_BITS - 1, \
> +		CAN_OBJ_FLAGS_DLC_SHIFT)
> +#define CAN_OBJ_FLAGS_IDE		BIT(4)
> +#define CAN_OBJ_FLAGS_RTR		BIT(5)
> +#define CAN_OBJ_FLAGS_BRS		BIT(6)
> +#define CAN_OBJ_FLAGS_FDF		BIT(7)
> +#define CAN_OBJ_FLAGS_ESI		BIT(8)
> +#define CAN_OBJ_FLAGS_SEQ_BITS		7
> +#define CAN_OBJ_FLAGS_SEQ_SHIFT		9
> +#define CAN_OBJ_FLAGS_SEQ_MASK					      \
> +	GENMASK(CAN_OBJ_FLAGS_SEQ_SHIFT + CAN_OBJ_FLAGS_SEQ_BITS - 1, \
> +		CAN_OBJ_FLAGS_SEQ_SHIFT)
> +#define CAN_OBJ_FLAGS_FILHIT_BITS	11
> +#define CAN_OBJ_FLAGS_FILHIT_SHIFT	5
> +#define CAN_OBJ_FLAGS_FILHIT_MASK				      \
> +	GENMASK(CAN_FLAGS_FILHIT_SHIFT + CAN_FLAGS_FILHIT_BITS - 1,   \
> +		CAN_FLAGS_FILHIT_SHIFT)
> --
> 2.11.0
> 

More soon...

Thanks for your patience.

Wolfgang.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH V5 2/4] can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver basics
  2019-01-21 12:31   ` Wolfgang Grandegger
@ 2019-01-21 18:29     ` Martin Sperl
  2019-01-21 19:25       ` Wolfgang Grandegger
  0 siblings, 1 reply; 12+ messages in thread
From: Martin Sperl @ 2019-01-21 18:29 UTC (permalink / raw)
  To: Wolfgang Grandegger
  Cc: linux-can, devicetree, Mark Kleine-Budde, Rob Herring, Mark Rutland

[-- Attachment #1: Type: text/plain, Size: 3505 bytes --]

Hi Wolfgang,

Thanks for the review!
I will add your feedback to v6 (which is already in version v6.7 fixing issues and more).


> On 21.01.2019, at 13:31, Wolfgang Grandegger <wg@grandegger.com> wrote:
> 
>> 
> 
> About file names and modularization: A "mcp25xxfd_xxx.c" should also have
> a header file "mcp25xxfd_xxx.h" defining the interface and use the
> prefix "mcp25xxfd_xxx". Therefore, "s/mcp25xxfd_base/mcp25xxfd" would
> make more sense.
> 

Base came into play to say it is minimal and I did not figure
out a way to get it working correctly without -base because
Of some conflicts with generated Module code if I 
remember correctly.

> Also, could you please put just the register definitions in
> "mcp25xxfd_reg.h".
> 
I just wanted to keep the included minimal hence the 2 files
that reflect the separation in the controller: mcp25xx (spi, clock, gpio)
specific and Can specific

>> 
>> +static inline void mcp25xxfd_convert_to_cpu(u32 *data, int n)
>> +{
>> +    int i;
>> +
>> +    for (i = 0; i < n; i++)
>> +        data[i] = le32_to_cpu(data[i]);
> 
> Is this optimized away if the endianess is the same?

Compiler should handle this automatically (also moved into 
the header In v6)

>> 
>> +static int mcp25xxfd_first_byte(u32 mask)
>> +{
>> +    return (mask & 0x0000ffff) ?
>> +        ((mask & 0x000000ff) ? 0 : 1) :
>> +        ((mask & 0x00ff0000) ? 2 : 3);
>> +}
> 
> inline?

Inline not needed - compiler will in-line automatically when sufficient.
This has moved to headers in v6 (where inline is then needed)

>> 
>> +static void mcp25xxfd_debugfs_remove(struct mcp25xxfd_priv *priv)
>> +{
>> +}
>> +#endif
> 
> And could you please put all debugfs stuff into "mcp25xxfd_debugfs.[ch]"
> and define macros to increment the statistics if CONFIG_DEBUG_FS is defined.

Some of the structures are only defined in the specific c file and
 this would require now moving the structure into the header files

Also I thought that this way it was contained in a location related
to where it is used.

>> 
>> +    struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
>> +    u32 value   = _mcp25xxfd_clkout_mask(spi);
> 
> Just one space before and after "=", please! Here and in other places.
Strange that chckpatch did not catch that...

> 
>> +#define CAN_SFR_BASE(x)            (0x000 + (x))
>> +#define CAN_CON                CAN_SFR_BASE(0x00)
> 
> The prefix "CAN_" is not generic enough. Name clashes, e.g. with
> "CAN_INT" are not that unlikely. Why not using "MCP25XXFD_" instead?
> And could you please mov the register definitions into "mcp25xxfd_reg.h".

Similar to above: can are can specific registers mcp2515 are ecc
Clock and gpio specific registers.

Also adding mcp25xxfd makes it longer and that incurs more
line breaks in code to fulfill the 80char rule...

>> 
>> +#  define CAN_CON_DEFAULT                \
>> +    (CAN_CON_ISOCRCEN |                \
>> +     CAN_CON_PXEDIS |                \
>> +     CAN_CON_WAKFIL |                \
>> +     (3 << CAN_CON_WFT_SHIFT) |            \
>> +     CAN_CON_STEF |                    \
>> +     CAN_CON_TXQEN |                \
>> +     (CAN_CON_MODE_CONFIG << CAN_CON_OPMOD_SHIFT) |    \
>> +     (CAN_CON_MODE_CONFIG << CAN_CON_REQOP_SHIFT))
> 
> Could you please align to the right, here and below.

Checkpatch would complain then if I understand your 
request correctly.

> 
> More soon...

Thanks, Martin

[-- Attachment #2: Type: text/html, Size: 7361 bytes --]

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH V5 4/4] can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver
  2018-12-21  9:29 ` [PATCH V5 4/4] can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver kernel
@ 2019-01-21 18:32   ` Wolfgang Grandegger
  0 siblings, 0 replies; 12+ messages in thread
From: Wolfgang Grandegger @ 2019-01-21 18:32 UTC (permalink / raw)
  To: kernel, linux-can, devicetree, Mark Kleine-Budde, Rob Herring,
	Mark Rutland
  Cc: Sukkin Pang, Gerhard Bertelsmann, Wilhelm Leichtfried,
	Thomas Kopp, Enrico Scholz, Brad Hanson, Teemu Keskinarkaus


Am 21.12.18 um 10:29 schrieb kernel@martin.sperl.org:
> From: Martin Sperl <kernel@martin.sperl.org>
> 
> Add un-optimized Can2.0 and CanFD support.
> 
> Optimizations and HW-bug-workarounds are separate patches
> 
> On a Rasperry pi 3 it is already able to process Can2.0 Frames
> with DLC=0 on a CAN bus with 1MHz. without losing any packets
> on the SPI side. Packets still get lost inside the network stack.
> 
> As for transmission on a Rpi3 we can saturate the CAN bus at 1MHz
> with Can2.0 frames with DLC=0 for the number of configured tx-fifos.
> Afterwards we need some time to recover the state before we can
> fill in the fifos again.
> 
> With 7 tx fifos we can: send those 7 frames in 0.33ms and then we
> wait for 0.26ms so that is a 56% duty cycle.
> 
> With 24 tx fifos this changes to: 1.19ms for 24 frames and then we
> wait for 0.52ms so that is a 70% duty cycle.
> 
> Signed-off-by: Martin Sperl <kernel@martin.sperl.org>
> 
> ---
> 
> Changelog:
>   V1 -> V2: new more generic name based on feedback from microchip
>             cleanup of code (checkpatch)
>             address all feedback on code
>             prepare code for GPIO handling (separate patches)
>             handle systemerrors in a much better/reliable way
>   V2 -> V3: added vendor-prefix for gpio-opendrain
>   V3 -> V4: resend
>   V4 -> V5: split can driver into a separate patch
>             code cleanup/rewrite
> 
> ---
>  drivers/net/can/spi/mcp25xxfd/Makefile             |   4 +
>  drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h          |  36 +
>  drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c     |  52 +-
>  drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c      | 593 ++++++++++++++-
>  drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h      | 275 ++++++-
>  drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c | 479 ++++++++++++
>  drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c  | 723 ++++++++++++++++++
>  drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c   | 208 ++++++
>  drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c   | 824 +++++++++++++++++++++

As already mentioned, please provide header files for
mcp25xxfd_can_[fifo|int|rx|tx].c.

>  9 files changed, 3160 insertions(+), 34 deletions(-)
>  create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c
>  create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c
>  create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c
>  create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c
> 
> diff --git a/drivers/net/can/spi/mcp25xxfd/Makefile b/drivers/net/can/spi/mcp25xxfd/Makefile
> index 5dd7f13674ec..6a27cec424de 100644
> --- a/drivers/net/can/spi/mcp25xxfd/Makefile
> +++ b/drivers/net/can/spi/mcp25xxfd/Makefile
> @@ -4,4 +4,8 @@
>  obj-$(CONFIG_CAN_MCP25XXFD)	+= mcp25xxfd.o
>  mcp25xxfd-objs                  := mcp25xxfd_base.o
>  mcp25xxfd-objs                  += mcp25xxfd_can.o
> +mcp25xxfd-objs                  += mcp25xxfd_can_fifo.o
> +mcp25xxfd-objs                  += mcp25xxfd_can_int.o
> +mcp25xxfd-objs                  += mcp25xxfd_can_rx.o
> +mcp25xxfd-objs                  += mcp25xxfd_can_tx.o
>  mcp25xxfd-objs                  += mcp25xxfd_gpio.o

Fits on less lines!

> diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
> index 73817a567219..624602ca898f 100644
> --- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
> +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
> @@ -107,6 +107,7 @@ enum mcp25xxfd_model {
> 
>  struct mcp25xxfd_priv {
>  	struct spi_device *spi;
> +	struct net_device *net;
>  	struct gpio_chip *gpio;
>  	struct clk *clk;
> 
> @@ -154,6 +155,11 @@ struct mcp25xxfd_priv {
>  		u32 ecccon;
>  	} regs;
> 
> +	/* flag to see if the irq is enabled */
> +	struct {
> +		int enabled;
> +	} irq;
> +
>  	/* debugfs related */
>  #if defined(CONFIG_DEBUG_FS)
>  	struct dentry *debugfs_dir;
> @@ -188,6 +194,28 @@ static inline void mcp25xxfd_convert_from_cpu(u32 *data, int n)
>  		data[i] = cpu_to_le32(data[i]);
>  }
> 
> +static inline void mcp25xxfd_calc_cmd_addr(u16 cmd, u16 addr, u8 *data)
> +{
> +	cmd = cmd | (addr & ADDRESS_MASK);
> +
> +	data[0] = (cmd >> 8) & 0xff;
> +	data[1] = (cmd >> 0) & 0xff;
> +}
> +
> +static inline int mcp25xxfd_first_byte(u32 mask)
> +{
> +	return (mask & 0x0000ffff) ?
> +		((mask & 0x000000ff) ? 0 : 1) :
> +		((mask & 0x00ff0000) ? 2 : 3);
> +}
> +
> +static inline int mcp25xxfd_last_byte(u32 mask)
> +{
> +	return (mask & 0xffff0000) ?
> +		((mask & 0xff000000) ? 3 : 2) :
> +		((mask & 0x0000ff00) ? 1 : 0);
> +}
> +
>  int mcp25xxfd_cmd_readn(struct spi_device *spi, u32 reg,
>  			void *data, int n);
>  int mcp25xxfd_cmd_read_mask(struct spi_device *spi, u32 reg,
> @@ -233,11 +261,19 @@ int mcp25xxfd_can_hw_probe(struct spi_device *spi);
>  /* clearing interrupt flags */
>  int mcp25xxfd_clear_crc_interrupts(struct spi_device *spi);
>  int mcp25xxfd_clear_ecc_interrupts(struct spi_device *spi);
> +int mcp25xxfd_clear_can_interrupts(struct spi_device *spi);
>  int mcp25xxfd_clear_interrupts(struct spi_device *spi);
> 
>  /* enabling interrupts */
>  int mcp25xxfd_enable_interrupts(struct spi_device *spi, bool enable);
> +int mcp25xxfd_enable_can_interrupts(struct spi_device *spi, bool enable);
> 
>  /* gpiolib support */
>  int mcp25xxfd_gpio_setup(struct spi_device *spi);
>  void mcp25xxfd_gpio_remove(struct spi_device *spi);
> +
> +/* can support */
> +int mcp25xxfd_can_setup(struct spi_device *spi);
> +void mcp25xxfd_can_remove(struct spi_device *spi);
> +int mcp25xxfd_can_suspend(struct spi_device *spi);
> +int mcp25xxfd_can_resume(struct spi_device *spi);
> diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
> index b0851d97bcbb..63c69139cfdf 100644
> --- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
> +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
> @@ -226,28 +226,6 @@ static int mcp25xxfd_write_then_write(struct spi_device *spi,
> 
>  /* mcp25xxfd spi command/protocol helper */
> 
> -static void mcp25xxfd_calc_cmd_addr(u16 cmd, u16 addr, u8 *data)
> -{
> -	cmd = cmd | (addr & ADDRESS_MASK);
> -
> -	data[0] = (cmd >> 8) & 0xff;
> -	data[1] = (cmd >> 0) & 0xff;
> -}
> -
> -static int mcp25xxfd_first_byte(u32 mask)
> -{
> -	return (mask & 0x0000ffff) ?
> -		((mask & 0x000000ff) ? 0 : 1) :
> -		((mask & 0x00ff0000) ? 2 : 3);
> -}
> -
> -static int mcp25xxfd_last_byte(u32 mask)
> -{
> -	return (mask & 0xffff0000) ?
> -		((mask & 0xff000000) ? 3 : 2) :
> -		((mask & 0x0000ff00) ? 1 : 0);
> -}
> -
>  /* read multiple bytes, transform some registers */
>  int mcp25xxfd_cmd_readn(struct spi_device *spi, u32 reg,
>  			void *data, int n)
> @@ -508,6 +486,7 @@ int mcp25xxfd_clear_interrupts(struct spi_device *spi)
>  {
>  	mcp25xxfd_clear_ecc_interrupts(spi);
>  	mcp25xxfd_clear_crc_interrupts(spi);
> +	mcp25xxfd_clear_can_interrupts(spi);
> 
>  	return 0;
>  }
> @@ -542,7 +521,7 @@ static int mcp25xxfd_enable_crc_interrupts(struct spi_device *spi,
>  int mcp25xxfd_enable_interrupts(struct spi_device *spi,
>  				bool enable)

I have some problems with your modularisation/abstraction! Why do the
functions of the interface "mcp25xxfd_base" use "spi" as main argument
and not "priv"? "priv" has already "priv->spi". There are even functions
which do not use "spi" at all! This could also save a lot of ...

>  {
> -	/* error handling only on enable for this function */
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);

... the line above.

>  	int ret = 0;
> 
>  	/* if we enable clear interrupt flags first */
> @@ -559,6 +538,21 @@ int mcp25xxfd_enable_interrupts(struct spi_device *spi,
>  	if (enable && ret)
>  		goto out_ecc;
> 
> +	ret = mcp25xxfd_enable_can_interrupts(spi, enable);
> +	if (enable && ret)
> +		goto out_ecc;
> +
> +	/* enable/disable interrupt as required */
> +	if (enable) {
> +		if (!priv->irq.enabled)
> +			enable_irq(spi->irq);
> +	} else {
> +		if (priv->irq.enabled)
> +			disable_irq(spi->irq);
> +	}
> +	/* mark enabled/disabled */
> +	priv->irq.enabled = enable;
> +
>  	/* if we disable interrupts clear interrupt flags last */
>  	if (!enable) 
>  		mcp25xxfd_clear_interrupts(spi);
> @@ -1110,15 +1104,22 @@ static int mcp25xxfd_probe(struct spi_device *spi)
>  	if (ret)
>  		goto out_debugfs;
> 
> +	/* add can (depends on debugfs) */
> +	ret = mcp25xxfd_can_setup(spi);
> +	if (ret)
> +		goto out_gpio;
> +
>  	/* and put controller to sleep by stopping the can clock */
>  	ret = mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
>  	if (ret)
> -		goto out_gpio;
> +		goto out_can;
> 
>  	dev_info(&spi->dev,
>  		 "MCP%x successfully initialized.\n", priv->model);
>  	return 0;
> 
> +out_can:
> +	mcp25xxfd_can_remove(spi);
>  out_gpio:
>  	mcp25xxfd_gpio_remove(spi);
>  out_debugfs:
> @@ -1139,6 +1140,9 @@ static int mcp25xxfd_remove(struct spi_device *spi)
>  {
>  	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> 
> +	/* remove can */
> +	mcp25xxfd_can_remove(spi);
> +
>  	/* remove gpio */
>  	mcp25xxfd_gpio_remove(spi);
> 
> diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
> index e5c7098a09c2..e827edb37da8 100644
> --- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
> +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
> @@ -43,18 +43,160 @@
> 
>  /* Implementation notes:
>   *
> - * Right now we only use the CAN controller block to put us into deep sleep
> - * this means that the oscillator clock is turned off.
> - * So this is the only thing that we implement here right now
> + * * the can related portion of the driver is split out into
> + *   * basic configuration (mcp25xxfd_can.c)
> + *   * can fifo configuration (mcp25xxfd_can_fifo.c)
> + *   * can interrupt handling (mcp25xxfd_can_int.c)
> + *   * can reception including interrupt handling (mcp25xxfd_can_rx.c)
> + *   * can transmission including interrupt handling (mcp25xxfd_can_tx.c)
> + * * the interrupt handler trys to issue the smallest amount of
> + *   spi messages to avoid spi stack overhead. for that it is more agressive
> + *   when reading registers that are close to each other - e.g:
> + *   * if there is one register that is not really needed then it will
> + *     still read it to avoid the startup and teardown costs of the
> + *     spi stack which results in latencies.
> + *   * when in can2.0 mode it will read all 8 data bytes even if DLC = 0
> + *     the header on its own is 12 bytes and then there are 2 bytes for
> + *     the spi overhead so the effecitve worsted case overhead introduced

Typo

> + *     is +57%
> + * * due to the reordering inside the controller the Transmission queue
> + *   feature is not used (it also makes the "next" frame freed less
> + *   predictable which would complicate the code as well.
> + *   the only advantage seen is that one could have more transmission
> + *   slots in the case of CAN2.0 only.
> + * * transmissions are instead handled by fixed priority fifos that can
> + *   get filled and triggered asyncronously.

Typo

> + *   The biggest drawback is that we are unable to sustain 100% CAN bus
> + *   usage for more than the <number of can fifos allocated> frames.
> + *   This means that we can have a duty cycle of about 7/8th of the CAN bus
> + *   (unless more fifos are reserved for transmission)
> + *   The only situation where this would be observable in real live
> + *   would be big transfers (for firmware updates or similar)
> + *   * this could have been better supported with a better
> + *     layout of FIFOCON, so that TXREQ and TXPRIO can be written
> + *     in a single transfer without a race condition
> + * * can transmissions are handled by using spi_async for submission and
> + *   scheduling. This somewhat impacts interrupt handling as some spi_sync
> + *   calls will defer to the spi kthread resulting in some context switches.
> + *   which introduces some latencies.
> + * * some special features of the can controller can only get configured
> + *   by the use of module parameters (some of which are changeable at
> + *   runtime via sysfs) due to the fact that netlink does not support:
> + *   * 3-shot submission
> + *   * selection of number of fifos
> + *   * configuarble delay between transmission of two can frames so that

Typo

> + *     the can bus is not monopolized by this device.
> + *   also some debug settings are controlable via modules.

Typo.

>   */
> 
>  #include "mcp25xxfd_can.h"

Specifying that first is unusal. What is your motivation to do so?

> +#include <linux/can/core.h>
> +#include <linux/can/dev.h>
> +#include <linux/debugfs.h>
>  #include <linux/device.h>
>  #include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/of.h>
> +#include <linux/regulator/consumer.h>
>  #include <linux/spi/spi.h>

A few of them are already in "mcp25xxfd_can.h".

> 
> -static int mcp25xxfd_can_get_mode(struct spi_device *spi,
> -				  u32 *mode_data)
> +/* module parameters */
> +unsigned int bw_sharing_log2bits;
> +module_param(bw_sharing_log2bits, uint, 0664);
> +MODULE_PARM_DESC(bw_sharing_log2bits,
> +		 "Delay between 2 transmissions in number of arbitration bit times\n");
> +/* everything related to bit timing */
> +static
> +const struct can_bittiming_const mcp25xxfd_can_nominal_bittiming_const = {
> +	.name           = DEVICE_NAME,
> +	.tseg1_min      = 2,
> +	.tseg1_max      = BIT(CAN_NBTCFG_TSEG1_BITS),
> +	.tseg2_min      = 1,
> +	.tseg2_max      = BIT(CAN_NBTCFG_TSEG2_BITS),
> +	.sjw_max        = BIT(CAN_NBTCFG_SJW_BITS),
> +	.brp_min        = 1,
> +	.brp_max        = BIT(CAN_NBTCFG_BRP_BITS),
> +	.brp_inc        = 1,
> +};
> +
> +static
> +const struct can_bittiming_const mcp25xxfd_can_data_bittiming_const = {
> +	.name           = DEVICE_NAME,
> +	.tseg1_min      = 1,
> +	.tseg1_max      = BIT(CAN_DBTCFG_TSEG1_BITS),
> +	.tseg2_min      = 1,
> +	.tseg2_max      = BIT(CAN_DBTCFG_TSEG2_BITS),
> +	.sjw_max        = BIT(CAN_DBTCFG_SJW_BITS),
> +	.brp_min        = 1,
> +	.brp_max        = BIT(CAN_DBTCFG_BRP_BITS),
> +	.brp_inc        = 1,

Just one space before and after "=".

> +};
> +
> +static int mcp25xxfd_can_do_set_nominal_bittiming(struct net_device *net)
> +{

The same question here, why are the functions of the module
"mcp25xxfd_can" use "struct net_device" as main argument and not "struct
mcp25xxfd_can_priv"?

> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct mcp25xxfd_priv *priv = cpriv->priv;
> +	struct can_bittiming *bt = &cpriv->can.bittiming;
> +	struct spi_device *spi = priv->spi;
> +
> +	int sjw = bt->sjw;
> +	int pseg2 = bt->phase_seg2;
> +	int pseg1 = bt->phase_seg1;
> +	int propseg = bt->prop_seg;
> +	int brp = bt->brp;
> +
> +	int tseg1 = propseg + pseg1;
> +	int tseg2 = pseg2;
> +
> +	/* calculate nominal bit timing */
> +	cpriv->regs.nbtcfg = ((sjw - 1) << CAN_NBTCFG_SJW_SHIFT) |
> +		((tseg2 - 1) << CAN_NBTCFG_TSEG2_SHIFT) |
> +		((tseg1 - 1) << CAN_NBTCFG_TSEG1_SHIFT) |
> +		((brp - 1) << CAN_NBTCFG_BRP_SHIFT);
> +
> +	return mcp25xxfd_cmd_write(spi, CAN_NBTCFG,
> +				   cpriv->regs.nbtcfg);

This function does not even use "net".

> +}
> +
> +static int mcp25xxfd_can_do_set_data_bittiming(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct mcp25xxfd_priv *priv = cpriv->priv;
> +	struct can_bittiming *bt = &cpriv->can.data_bittiming;
> +	struct spi_device *spi = priv->spi;
> +
> +	int sjw = bt->sjw;
> +	int pseg2 = bt->phase_seg2;
> +	int pseg1 = bt->phase_seg1;
> +	int propseg = bt->prop_seg;
> +	int brp = bt->brp;
> +
> +	int tseg1 = propseg + pseg1;
> +	int tseg2 = pseg2;
> +
> +	int ret;
> +
> +	/* set up Transmitter delay compensation */
> +	if (!cpriv->regs.tdc)
> +		cpriv->regs.tdc = CAN_TDC_EDGFLTEN |
> +			(CAN_TDC_TDCMOD_AUTO << CAN_TDC_TDCMOD_SHIFT);
> +	ret = mcp25xxfd_cmd_write(spi, CAN_TDC, cpriv->regs.tdc);
> +	if (ret)
> +		return ret;
> +
> +	/* calculate nominal bit timing */
> +	cpriv->regs.dbtcfg = ((sjw - 1) << CAN_DBTCFG_SJW_SHIFT) |
> +		((tseg2 - 1) << CAN_DBTCFG_TSEG2_SHIFT) |
> +		((tseg1 - 1) << CAN_DBTCFG_TSEG1_SHIFT) |
> +		((brp - 1) << CAN_DBTCFG_BRP_SHIFT);
> +
> +	return mcp25xxfd_cmd_write(spi, CAN_DBTCFG,
> +				   cpriv->regs.dbtcfg);
> +}
> +
> +/* configuration functions that are used by the base during initialization */
> +int mcp25xxfd_can_get_mode(struct spi_device *spi, u32 *mode_data)
>  {
>  	int ret;
> 
> @@ -65,11 +207,11 @@ static int mcp25xxfd_can_get_mode(struct spi_device *spi,
>  	return (*mode_data & CAN_CON_OPMOD_MASK) >> CAN_CON_OPMOD_SHIFT;
>  }
> 
> -int mcp25xxfd_can_switch_mode(struct spi_device *spi,
> -			      u32 *mode_data, int mode)
> +int mcp25xxfd_can_switch_mode_nowait(struct spi_device *spi,
> +				     u32 *mode_data, int mode)
>  {
>  	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> -	int ret, i;
> +	int ret;
> 
>  	/* get the current mode/register - if mode_data is -1
>  	 * this only happens during initialization phase
> @@ -98,7 +240,16 @@ int mcp25xxfd_can_switch_mode(struct spi_device *spi,
>  	}
> 
>  	/* request the mode switch */
> -	ret = mcp25xxfd_cmd_write(spi, CAN_CON, *mode_data);
> +	return mcp25xxfd_cmd_write(spi, CAN_CON, *mode_data);
> +}
> +
> +int mcp25xxfd_can_switch_mode(struct spi_device *spi,
> +			      u32 *mode_data, int mode)
> +{
> +	int ret, i;
> +
> +	/* trigger the mode switch itself */
> +	ret = mcp25xxfd_can_switch_mode_nowait(spi, mode_data, mode);
>  	if (ret)
>  		return ret;
> 
> @@ -226,3 +377,427 @@ int mcp25xxfd_can_hw_probe(struct spi_device *spi)
>  	/* check that modeswitch is really working */
>  	return mcp25xxfd_can_hw_probe_modeswitch(spi);
>  }
> +
> +/* debugfs related */
> +#if defined(CONFIG_DEBUG_FS)

As already mentioned, could you please move the DEBUG_FS part to
mcp25xxfd_debugfs.[ch].

> +
> +static int mcp25xxfd_can_dump_all_regs(struct seq_file *file, void *offset)
> +{
> +	return mcp25xxfd_dump_regs_range(file, CAN_CON, CAN_FLTMASK(31));
> +}
> +
> +static int mcp25xxfd_can_dump_regs(struct seq_file *file, void *offset)
> +{
> +	return mcp25xxfd_dump_regs_range(file, CAN_CON, CAN_TXQUA);
> +}
> +
> +static void mcp25xxfd_can_debugfs_add(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(priv->net);
> +	struct dentry *root = priv->debugfs_dir;
> +	struct dentry *cregs, *cstatus, *cfifotef, *cstats;
> +
> +	/* export config registers */
> +	cregs = debugfs_create_dir("can_regs", root);
> +	debugfs_create_x32("con",    0444, cregs, &cpriv->regs.con);
> +	debugfs_create_x32("tdc",    0774, cregs, &cpriv->regs.tdc);
> +	debugfs_create_x32("tscon",  0444, cregs, &cpriv->regs.tscon);
> +	debugfs_create_x32("tefcon", 0444, cregs, &cpriv->regs.tscon);
> +	debugfs_create_x32("nbtcfg", 0444, cregs, &cpriv->regs.nbtcfg);
> +	debugfs_create_x32("dbtcfg", 0444, cregs, &cpriv->regs.dbtcfg);
> +
> +	/* export status registers */
> +	cstatus = debugfs_create_dir("can_status", root);
> +	debugfs_create_x32("intf",    0444, cstatus,
> +			   &cpriv->status.intf);
> +	debugfs_create_x32("rx_if",   0444, cstatus,
> +			   &cpriv->status.rxif);
> +	debugfs_create_x32("tx_if",   0444, cstatus,
> +			   &cpriv->status.txif);
> +	debugfs_create_x32("rx_ovif", 0444, cstatus,
> +			   &cpriv->status.rxovif);
> +	debugfs_create_x32("tx_atif", 0444, cstatus,
> +			   &cpriv->status.txatif);
> +	debugfs_create_x32("tx_req",  0444, cstatus,
> +			   &cpriv->status.txreq);
> +	debugfs_create_x32("trec",    0444, cstatus,
> +			   &cpriv->status.trec);
> +
> +	cfifotef = debugfs_create_dir("can_tef", root);
> +	debugfs_create_u32("count",  0444, cfifotef,
> +			   &cpriv->fifos.tef.count);
> +	debugfs_create_u32("size",  0444, cfifotef,
> +			   &cpriv->fifos.tef.size);
> +
> +	/* dump the controller registers themselves */
> +	debugfs_create_devm_seqfile(&priv->spi->dev, "can_regs_live_dump",
> +				    root, mcp25xxfd_can_dump_regs);
> +	debugfs_create_devm_seqfile(&priv->spi->dev, "can_regs_full_live_dump",
> +				    root, mcp25xxfd_can_dump_all_regs);
> +
> +	/* and stats */
> +	cstats = debugfs_create_dir("can_stats", root);
> +# define DEBUGFS_CREATE(name, var) \
> +	debugfs_create_u64(name,  0444, cstats, &cpriv->stats.var)
> +	DEBUGFS_CREATE("int_calls",		 irq_calls);
> +	DEBUGFS_CREATE("int_loops",		 irq_loops);
> +	DEBUGFS_CREATE("int_system_error",	 int_serr_count);
> +	DEBUGFS_CREATE("int_system_error_tx",	 int_serr_tx_count);
> +	DEBUGFS_CREATE("int_system_error_rx",	 int_serr_rx_count);
> +	DEBUGFS_CREATE("int_mode_switch",	 int_mod_count);
> +	DEBUGFS_CREATE("int_rx",		 int_rx_count);
> +	DEBUGFS_CREATE("int_tx_attempt",	 int_txat_count);
> +	DEBUGFS_CREATE("int_tef",		 int_tef_count);
> +	DEBUGFS_CREATE("int_rx_overflow",	 int_rxov_count);
> +	DEBUGFS_CREATE("int_ecc_error",		 int_ecc_count);
> +	DEBUGFS_CREATE("int_rx_invalid_message", int_ivm_count);
> +	DEBUGFS_CREATE("int_crcerror",		 int_cerr_count);
> +
> +	DEBUGFS_CREATE("tx_frames_fd",		 tx_fd_count);
> +	DEBUGFS_CREATE("tx_frames_brs",		 tx_brs_count);

This function does not use "spi"! The argument is really misleading.

> +#undef DEBUGFS_CREATE
> +}
> +#else
> +static void mcp25xxfd_can_debugfs_add(struct spi_device *spi)
> +{
> +}
> +#endif
> +
> +int mcp25xxfd_clear_can_interrupts(struct spi_device *spi)
> +{
> +	return mcp25xxfd_cmd_write_mask(spi, CAN_INT, 0, CAN_INT_IF_MASK);
> +}
> +
> +int mcp25xxfd_enable_can_interrupts(struct spi_device *spi, bool enable)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct mcp25xxfd_can_priv *cpriv = priv->net ?
> +		netdev_priv(priv->net) : NULL;
> +	const u32 mask = CAN_INT_TEFIE |
> +		CAN_INT_RXIE |
> +		CAN_INT_MODIE |
> +		CAN_INT_SERRIE |
> +		CAN_INT_IVMIE |
> +		CAN_INT_CERRIE |
> +		CAN_INT_RXOVIE |
> +		CAN_INT_ECCIE;

Fits on less lines.

> +	u32 value = cpriv ? cpriv->status.intf : 0;
> +
> +	/* apply mask and */
> +	value &= ~(CAN_INT_IE_MASK);
> +	if (enable)
> +		value |= mask;
> +	/* write back if net is set up */
> +	if (cpriv)
> +		cpriv->status.intf = value;
> +
> +	/* and write to int register */
> +	return mcp25xxfd_cmd_write_mask(spi, CAN_INT, value, mask);
> +}
> +
> +static int mcp25xxfd_can_config(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct mcp25xxfd_priv *priv = cpriv->priv;
> +	struct spi_device *spi = priv->spi;
> +	int ret;
> +
> +	/* setup value of con_register */
> +	cpriv->regs.con = CAN_CON_STEF; /* enable TEF, disable TXQUEUE */
> +
> +	/* transmission bandwidth sharing bits */
> +	if (bw_sharing_log2bits > 12)
> +		bw_sharing_log2bits = 12;
> +	cpriv->regs.con |= bw_sharing_log2bits << CAN_CON_TXBWS_SHIFT;
> +
> +	/* non iso FD mode */
> +	if (!(cpriv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO))
> +		cpriv->regs.con |= CAN_CON_ISOCRCEN;
> +
> +	/* one shot */
> +	if (cpriv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
> +		cpriv->regs.con |= CAN_CON_RTXAT;
> +
> +	/* apply it now together with a mode switch */
> +	ret = mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
> +					CAN_CON_MODE_CONFIG);
> +	if (ret)
> +		return 0;
> +
> +	/* time stamp control register - 1ns resolution */
> +	cpriv->regs.tscon = 0;
> +	ret = mcp25xxfd_cmd_write(spi, CAN_TBC, 0);
> +	if (ret)
> +		return ret;
> +
> +	cpriv->regs.tscon = CAN_TSCON_TBCEN |
> +		((cpriv->can.clock.freq / 1000000)
> +		 << CAN_TSCON_TBCPRE_SHIFT);
> +	ret = mcp25xxfd_cmd_write(spi, CAN_TSCON, cpriv->regs.tscon);
> +	if (ret)
> +		return ret;
> +
> +	/* setup fifos */
> +	ret = mcp25xxfd_can_setup_fifos(net);
> +	if (ret)
> +		return ret;
> +
> +	/* setup can bittiming now - the do_set_bittiming methods
> +	 * are not used as they get callled before open

Typo.

> +	 */
> +	ret = mcp25xxfd_can_do_set_nominal_bittiming(net);
> +	if (ret)
> +		return ret;
> +	ret = mcp25xxfd_can_do_set_data_bittiming(net);
> +	if (ret)
> +		return ret;
> +
> +	return ret;
> +}
> +
> +static void mcp25xxfd_can_shutdown(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct mcp25xxfd_priv *priv = cpriv->priv;
> +	struct spi_device *spi = priv->spi;
> +
> +	/* switch us to CONFIG mode - this disables the controller */
> +	mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
> +				  CAN_CON_MODE_CONFIG);
> +}
> +
> +/* all ops */
> +
> +/* open and stop */
> +static int mcp25xxfd_can_open(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct mcp25xxfd_priv *priv = cpriv->priv;
> +	struct spi_device *spi = priv->spi;
> +	int ret;
> +
> +	netdev_err(net, "OPEN\n");

What's that good for? Please remove debugging code.

> +
> +	ret = open_candev(net);
> +	if (ret) {
> +		netdev_err(net, "unable to set initial baudrate!\n");
> +		return ret;
> +	}
> +
> +	/* clear those statistics */
> +	memset(&cpriv->stats, 0, sizeof(cpriv->stats));
> +
> +	/* request an IRQ but keep disabled for now */
> +	ret = request_threaded_irq(spi->irq, NULL,
> +				   mcp25xxfd_can_int,
> +				   IRQF_ONESHOT | IRQF_TRIGGER_LOW,
> +				   DEVICE_NAME, priv);

Why "priv", the mcp25xxfd_can_int() needs "cpriv" in the first place.

> +	if (ret) {
> +		dev_err(&spi->dev, "failed to acquire irq %d - %i\n",
> +			spi->irq, ret);
> +		goto out_candev;
> +	}
> +	disable_irq(spi->irq);
> +	priv->irq.enabled = false;
> +
> +	/* enable power to the transceiver */
> +	ret = mcp25xxfd_power_enable(cpriv->transceiver, 1);
> +	if (ret)
> +		goto out_irq;
> +
> +	/* enable clock (so that spi works) */
> +	ret = mcp25xxfd_start_clock(spi, MCP25XXFD_CLK_USER_CAN);
> +	if (ret)
> +		goto out_transceiver;
> +
> +	/* configure controller for reception */
> +	ret = mcp25xxfd_can_config(net);
> +	if (ret)
> +		goto out_canclock;
> +
> +	/* setting up state */
> +	cpriv->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> +	/* enable interrupts */
> +	ret = mcp25xxfd_enable_interrupts(spi, true);
> +	if (ret)
> +		goto out_canconfig;
> +
> +	/* switch to active mode */
> +	ret = mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
> +					(net->mtu == CAN_MTU) ?
> +					CAN_CON_MODE_CAN2_0 :
> +					CAN_CON_MODE_MIXED);
> +	if (ret)
> +		goto out_int;
> +
> +	/* start the tx_queue */
> +	mcp25xxfd_can_tx_queue_manage(net, TX_QUEUE_STATE_STARTED);
> +
> +	return 0;
> +
> +out_int:
> +	mcp25xxfd_enable_interrupts(spi, false);
> +out_canconfig:
> +	mcp25xxfd_can_release_fifos(net);
> +out_canclock:
> +	mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
> +out_transceiver:
> +	mcp25xxfd_power_enable(cpriv->transceiver, 0);
> +out_irq:
> +	free_irq(spi->irq, priv);
> +out_candev:
> +	close_candev(net);
> +	return ret;
> +}
> +
> +static int mcp25xxfd_can_stop(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct mcp25xxfd_priv *priv = cpriv->priv;
> +	struct spi_device *spi = priv->spi;
> +
> +	netdev_err(net, "STOP\n");
> +
> +	/* stop transmit queue */
> +	mcp25xxfd_can_tx_queue_manage(net, TX_QUEUE_STATE_STOPPED);
> +
> +	/* release debugfs */
> +	mcp25xxfd_can_release_fifos(net);
> +
> +	/* shutdown the can controller */
> +	mcp25xxfd_can_shutdown(net);
> +
> +	/* disable inerrupts on controller */
> +	mcp25xxfd_enable_interrupts(spi, false);
> +
> +	/* stop the clock */
> +	mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
> +
> +	/* and disable the transceiver */
> +	mcp25xxfd_power_enable(cpriv->transceiver, 0);
> +
> +	/* disable interrupt on host */
> +	free_irq(spi->irq, priv);
> +	priv->irq.enabled = false;
> +
> +	/* close the can_decice */
> +	close_candev(net);
> +
> +	return 0;
> +}
> +
> +/* mode setting */
> +static int mcp25xxfd_can_do_set_mode(struct net_device *net,
> +				     enum can_mode mode)
> +{
> +	switch (mode) {
> +	case CAN_MODE_START:
> +		break;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	return 0;
> +}
> +
> +/* binary error counters */
> +static int mcp25xxfd_can_get_berr_counter(const struct net_device *net,
> +					  struct can_berr_counter *bec)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +
> +	bec->txerr = (cpriv->status.trec & CAN_TREC_TEC_MASK) >>
> +		CAN_TREC_TEC_SHIFT;
> +	bec->rxerr = (cpriv->status.trec & CAN_TREC_REC_MASK) >>
> +		CAN_TREC_REC_SHIFT;
> +
> +	return 0;
> +}
> +
> +static const struct net_device_ops mcp25xxfd_netdev_ops = {
> +	.ndo_open = mcp25xxfd_can_open,
> +	.ndo_stop = mcp25xxfd_can_stop,
> +	.ndo_start_xmit = mcp25xxfd_can_start_xmit,
> +	.ndo_change_mtu = can_change_mtu,
> +};
> +
> +/* probe and remove */
> +int mcp25xxfd_can_setup(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct mcp25xxfd_can_priv *cpriv;
> +	struct net_device *net;
> +	struct regulator *transceiver;
> +	int ret;
> +
> +	/* get transceiver power regulator*/
> +	transceiver = devm_regulator_get_optional(&spi->dev,
> +						  "xceiver");
> +	if (PTR_ERR(transceiver) == -EPROBE_DEFER)
> +		return -EPROBE_DEFER;
> +
> +	/* allocate can device */
> +	net = alloc_candev(sizeof(*cpriv), TX_ECHO_SKB_MAX);
> +	if (!net)
> +		return -ENOMEM;
> +
> +	/* and do some cross-asignments */
> +	cpriv = netdev_priv(net);
> +	cpriv->priv = priv;
> +	priv->net = net;

Why is "net" attached to "priv". I think we have

> +	SET_NETDEV_DEV(net, &spi->dev);
> +
> +	net->netdev_ops = &mcp25xxfd_netdev_ops;
> +	net->flags |= IFF_ECHO;
> +
> +	cpriv->transceiver = transceiver;
> +
> +	cpriv->can.clock.freq = priv->clock_freq;
> +
> +	cpriv->can.bittiming_const =
> +		&mcp25xxfd_can_nominal_bittiming_const;
> +	cpriv->can.data_bittiming_const =
> +		&mcp25xxfd_can_data_bittiming_const;
> +	/* we are not setting bit-timing methods here as they get
> +	 * called by the framework before open so the controller is
> +	 * still in sleep mode, which does not help
> +	 */
> +	cpriv->can.do_set_mode =
> +		mcp25xxfd_can_do_set_mode;
> +	cpriv->can.do_get_berr_counter =
> +		mcp25xxfd_can_get_berr_counter;
> +	cpriv->can.ctrlmode_supported =
> +		CAN_CTRLMODE_FD |
> +		CAN_CTRLMODE_FD_NON_ISO |
> +		CAN_CTRLMODE_LOOPBACK |
> +		CAN_CTRLMODE_LISTENONLY |
> +		CAN_CTRLMODE_BERR_REPORTING |
> +		CAN_CTRLMODE_ONE_SHOT;
> +
> +	ret = register_candev(net);
> +	if (ret) {
> +		dev_err(&spi->dev, "Failed to register can device\n");
> +		goto out;
> +	}
> +
> +	mcp25xxfd_can_debugfs_add(spi);
> +
> +	return 0;
> +out:
> +	free_candev(net);
> +
> +	return ret;
> +}
> +
> +void mcp25xxfd_can_remove(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +
> +	if (priv->net) {
> +		unregister_candev(priv->net);
> +		free_candev(priv->net);
> +		priv->net = NULL;
> +	}
> +}
> diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
> index fe0e93e28b62..ce35518fdb28 100644
> --- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
> +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
> @@ -2,7 +2,7 @@
> 
>  /* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
>   *
> - * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
> + * Copyright 2018 Martin Sperl <kernel@martin.sperl.org>
>   *
>   * Based on Microchip MCP251x CAN controller driver written by
>   * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
> @@ -10,6 +10,12 @@
> 
>  #include "mcp25xxfd.h"
>  #include <linux/bitops.h>
> +#include <linux/can/core.h>
> +#include <linux/can/dev.h>
> +#include <linux/interrupt.h>
> +#include <linux/netdevice.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/spi/spi.h>
> 
>  #define CAN_SFR_BASE(x)			(0x000 + (x))
>  #define CAN_CON				CAN_SFR_BASE(0x00)
> @@ -205,6 +211,13 @@
>  	 CAN_INT_SERRIF |		\
>  	 CAN_INT_WAKIF |		\
>  	 CAN_INT_IVMIF)
> +#  define CAN_INT_IF_CLEAR_MASK		\
> +	(CAN_INT_TBCIF	|		\
> +	 CAN_INT_MODIF	|		\
> +	 CAN_INT_CERRIF |		\
> +	 CAN_INT_SERRIF |		\
> +	 CAN_INT_WAKIF |		\
> +	 CAN_INT_IVMIF)
>  #  define CAN_INT_IE_SHIFT		16
>  #  define CAN_INT_TXIE			(CAN_INT_TXIF << CAN_INT_IE_SHIFT)
>  #  define CAN_INT_RXIE			(CAN_INT_RXIF << CAN_INT_IE_SHIFT)
> @@ -485,3 +498,263 @@
>  #define CAN_OBJ_FLAGS_FILHIT_MASK				      \
>  	GENMASK(CAN_FLAGS_FILHIT_SHIFT + CAN_FLAGS_FILHIT_BITS - 1,   \
>  		CAN_FLAGS_FILHIT_SHIFT)
> +
> +#define TX_ECHO_SKB_MAX	32
> +
> +struct mcp25xxfd_fifo {
> +	u32 count;
> +	u32 start;
> +	u32 increment;
> +	u32 size;
> +	u32 priority_start;
> +	u32 priority_increment;
> +	u64 dlc_usage[16];
> +	u64 fd_count;
> +
> +	struct dentry *debugfs_dir;
> +};
> +
> +struct mcp25xxfd_obj_ts {
> +	/* using signed here to handle rollover correctly */
> +	s32 ts;
> +	/* positive fifos are rx, negative tx, 0 is not a valid fifo */
> +	s32 fifo;
> +};
> +
> +struct mcp25xxfd_can_priv {
> +	/* can_priv has to be the first one to be usable with alloc_candev
> +	 * which expects struct can_priv to be right at the start of the
> +	 * priv structure
> +	 */
> +	struct can_priv can;
> +	struct mcp25xxfd_priv *priv;
> +	struct regulator *transceiver;
> +
> +	/* the can mode currently active */
> +	int mode;
> +
> +	/* can config registers */
> +	struct {
> +		u32 con;
> +		u32 tdc;
> +		u32 tscon;
> +		u32 tefcon;
> +		u32 nbtcfg;
> +		u32 dbtcfg;
> +	} regs;
> +
> +	/* can status registers (mostly) - read in one go
> +	 * bdiag0 and bdiag1 are optional, but when
> +	 * berr counters are requested on a regular basis
> +	 * during high CAN-bus load this would trigger the fact
> +	 * that spi_sync would get queued for execution in the
> +	 * spi thread and the spi handler would not get
> +	 * called inline in the interrupt thread without any
> +	 * context switches or wakeups...
> +	 */
> +	struct {
> +		u32 intf;
> +		/* ASSERT(CAN_INT + 4 == CAN_RXIF) */
> +		u32 rxif;
> +		/* ASSERT(CAN_RXIF + 4 == CAN_TXIF) */
> +		u32 txif;
> +		/* ASSERT(CAN_TXIF + 4 == CAN_RXOVIF) */
> +		u32 rxovif;
> +		/* ASSERT(CAN_RXOVIF + 4 == CAN_TXATIF) */
> +		u32 txatif;
> +		/* ASSERT(CAN_TXATIF + 4 == CAN_TXREQ) */
> +		u32 txreq;
> +		/* ASSERT(CAN_TXREQ + 4 == CAN_TREC) */
> +		u32 trec;
> +	} status;
> +
> +	/* information of fifo setup */
> +	struct {
> +		/* define payload size and mode */
> +		u32 payload_size;
> +		u32 payload_mode;
> +
> +		/* infos on fifo layout */
> +		struct {
> +			u32 count;
> +			u32 size;
> +			u32 index;
> +		} tef;
> +		struct mcp25xxfd_fifo tx;
> +		struct mcp25xxfd_fifo rx;
> +
> +		/* the address and priority of all fifos */
> +		struct {
> +			u32 control;
> +			u32 status;
> +			u32 offset;
> +		} fifo_reg[32];
> +		struct {
> +			u32 is_tx;
> +			u32 priority;
> +			u64 use_count;
> +		} fifo_info[32];
> +
> +		/* queue of can frames that need to get submitted
> +		 * to the network stack during an interrupt loop in one go
> +		 * (this gets sorted by timestamp before submission
> +		 * and contains both rx frames as well tx frames that have
> +		 * gone over the CAN bus successfully
> +		 */
> +		struct mcp25xxfd_obj_ts submit_queue[32];
> +		int  submit_queue_count;
> +
> +		/* the tx queue of spi messages */
> +		struct mcp2517fd_tx_spi_message_queue *tx_queue;
> +
> +		/* the directory entry of the can_fifo debugfs directory */
> +		struct dentry *debugfs_dir;
> +	} fifos;
> +
> +	/* statistics */
> +	struct {
> +		u64 irq_calls;
> +		u64 irq_loops;
> +
> +		u64 int_serr_count;
> +		u64 int_serr_rx_count;
> +		u64 int_serr_tx_count;
> +		u64 int_mod_count;
> +		u64 int_rx_count;
> +		u64 int_txat_count;
> +		u64 int_tef_count;
> +		u64 int_rxov_count;
> +		u64 int_ecc_count;
> +		u64 int_ivm_count;
> +		u64 int_cerr_count;
> +
> +		u64 tx_fd_count;
> +		u64 tx_brs_count;
> +	} stats;
> +
> +	/* bus state */
> +	struct {
> +		u32 state;
> +		u32 new_state;
> +
> +		u32 bdiag[2];
> +	} bus;
> +
> +	/* can merror messages */
> +	struct {
> +		u32 id;
> +		u8  data[8];
> +	} error_frame;
> +
> +	/* a sram equivalent */
> +	u8 sram[MCP25XXFD_SRAM_SIZE];
> +};
> +
> +struct mcp25xxfd_obj_tef {
> +	u32 id;
> +	u32 flags;
> +	u32 ts;
> +};
> +
> +struct mcp25xxfd_obj_tx {
> +	u32 id;
> +	u32 flags;
> +	u8 data[];
> +};
> +
> +struct mcp25xxfd_obj_rx {
> +	u32 id;
> +	u32 flags;
> +	u32 ts;
> +	u8 data[];
> +};
> +
> +int mcp25xxfd_can_get_mode(struct spi_device *spi, u32 *mode_data);
> +int mcp25xxfd_can_switch_mode(struct spi_device *spi, u32 *mode_data,
> +			      int mode);
> +irqreturn_t mcp25xxfd_can_int(int irq, void *dev_id);
> +netdev_tx_t mcp25xxfd_can_start_xmit(struct sk_buff *skb,
> +				     struct net_device *net);
> +int mcp25xxfd_can_setup_fifos(struct net_device *net);
> +void mcp25xxfd_can_release_fifos(struct net_device *net);
> +
> +/* ideally these would be defined in uapi/linux/can.h */
> +#define CAN_EFF_SID_SHIFT		(CAN_EFF_ID_BITS - CAN_SFF_ID_BITS)



> +#define CAN_EFF_SID_BITS		CAN_SFF_ID_BITS
> +#define CAN_EFF_SID_MASK				      \
> +	GENMASK(CAN_EFF_SID_SHIFT + CAN_EFF_SID_BITS - 1,     \
> +		CAN_EFF_SID_SHIFT)
> +#define CAN_EFF_EID_SHIFT		0
> +#define CAN_EFF_EID_BITS		CAN_EFF_SID_SHIFT
> +#define CAN_EFF_EID_MASK				      \
> +	GENMASK(CAN_EFF_EID_SHIFT + CAN_EFF_EID_BITS - 1,     \
> +		CAN_EFF_EID_SHIFT)
> +
> +static inline
> +void mcp25xxfd_mcpid_to_canid(u32 mcp_id, u32 mcp_flags, u32 *can_id)
> +{
> +	u32 sid = (mcp_id & CAN_OBJ_ID_SID_MASK) >> CAN_OBJ_ID_SID_SHIFT;
> +	u32 eid = (mcp_id & CAN_OBJ_ID_EID_MASK) >> CAN_OBJ_ID_EID_SHIFT;
> +
> +	/* select normal or extended ids */
> +	if (mcp_flags & CAN_OBJ_FLAGS_IDE) {
> +		*can_id = (eid << CAN_EFF_EID_SHIFT) |
> +			(sid << CAN_EFF_SID_SHIFT) |
> +			CAN_EFF_FLAG;
> +	} else {
> +		*can_id = sid;
> +	}
> +	/* handle rtr */
> +	*can_id |= (mcp_flags & CAN_OBJ_FLAGS_RTR) ? CAN_RTR_FLAG : 0;
> +}
> +
> +static inline
> +void mcp25xxfd_canid_to_mcpid(u32 can_id, u32 *id, u32 *flags)
> +{
> +	/* depending on can_id flag compute extended or standard ids */
> +	if (can_id & CAN_EFF_FLAG) {
> +		int sid = (can_id & CAN_EFF_SID_MASK) >> CAN_EFF_SID_SHIFT;
> +		int eid = (can_id & CAN_EFF_EID_MASK) >> CAN_EFF_EID_SHIFT;
> +		*id = (eid << CAN_OBJ_ID_EID_SHIFT) |
> +			(sid << CAN_OBJ_ID_SID_SHIFT);
> +		*flags = CAN_OBJ_FLAGS_IDE;
> +	} else {
> +		*id = can_id & CAN_SFF_MASK;
> +		*flags = 0;
> +	}
> +
> +	/* Handle RTR */
> +	*flags |= (can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
> +}
> +
> +static inline
> +void mcp25xxfd_can_queue_frame(struct mcp25xxfd_can_priv *cpriv,
> +			       s32 fifo, s32 ts)
> +{
> +	int idx = cpriv->fifos.submit_queue_count;
> +
> +	cpriv->fifos.submit_queue[idx].fifo = fifo;
> +	cpriv->fifos.submit_queue[idx].ts = ts;
> +
> +	cpriv->fifos.submit_queue_count++;
> +}
> +
> +int mcp25xxfd_can_read_rx_frames(struct spi_device *spi);
> +int mcp25xxfd_can_submit_rx_frame(struct spi_device *spi, int fifo);
> +int mcp25xxfd_can_submit_tx_frame(struct spi_device *spi, int fifo);
> +
> +int mcp25xxfd_can_int_handle_txatif(struct spi_device *spi);
> +int mcp25xxfd_can_int_handle_tefif(struct spi_device *spi);
> +
> +int mcp25xxfd_can_tx_queue_alloc(struct net_device *net);
> +void mcp25xxfd_can_tx_queue_free(struct net_device *net);
> +void mcp25xxfd_can_tx_queue_restart(struct net_device *net);
> +void mcp25xxfd_can_tx_queue_manage(struct net_device *net, int state);
> +void mcp25xxfd_can_tx_queue_manage_nolock(struct net_device *net, int state);
> +# define TX_QUEUE_STATE_STOPPED  0
> +# define TX_QUEUE_STATE_STARTED  1
> +# define TX_QUEUE_STATE_RUNABLE 2
> +# define TX_QUEUE_STATE_RESTART  3
> +
> +int mcp25xxfd_can_switch_mode_nowait(struct spi_device *spi,
> +				     u32 *mode_data, int mode);
> diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c
> new file mode 100644
> index 000000000000..1a85fc57a955
> --- /dev/null
> +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c
> @@ -0,0 +1,479 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
> + *
> + * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
> + *
> + * Based on Microchip MCP251x CAN controller driver written by
> + * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
> + */
> +
> +/* here we define and configure the fifo layout */
> +
> +#include "mcp25xxfd_can.h"
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/spi/spi.h>
> +
> +/* some module parameters that are currently not configurable via netlink */
> +unsigned int tx_fifos;
> +module_param(tx_fifos, uint, 0664);
> +MODULE_PARM_DESC(tx_fifos, "Number of tx-fifos to configure\n");
> +
> +bool three_shot;
> +module_param(three_shot, bool, 0664);
> +MODULE_PARM_DESC(three_shot, "Use 3 shots when one-shot is requested");
> +
> +#if defined(CONFIG_DEBUG_FS)
> +static
> +void mcp25xxfd_can_setup_fifo_debugfs_rxtx(struct net_device *net,
> +					   const char *name,
> +					   struct mcp25xxfd_fifo *desc)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct mcp25xxfd_priv *priv = cpriv->priv;
> +
> +	/* remove old debug directory */
> +	debugfs_remove_recursive(desc->debugfs_dir);
> +
> +	/* create new one */
> +	desc->debugfs_dir = debugfs_create_dir(name, priv->debugfs_dir);
> +
> +	/* and fill it */
> +	debugfs_create_u32("count",  0444, desc->debugfs_dir,
> +			   &desc->count);
> +	debugfs_create_u32("size",  0444, desc->debugfs_dir,
> +			   &desc->size);
> +	debugfs_create_u32("start",  0444, desc->debugfs_dir,
> +			   &desc->start);
> +	debugfs_create_u32("increment",  0444, desc->debugfs_dir,
> +			   &desc->increment);
> +	debugfs_create_u32("priority_start",  0444, desc->debugfs_dir,
> +			   &desc->priority_start);
> +	debugfs_create_u32("priority_increment",  0444, desc->debugfs_dir,
> +			   &desc->priority_increment);
> +}
> +
> +static
> +void mcp25xxfd_can_setup_fifo_debugfs_link_rxtx(struct mcp25xxfd_fifo *desc,
> +						int index, int fifo)
> +{
> +	char name[4];
> +	char link[32];
> +
> +	snprintf(name, sizeof(name), "%02i", index);
> +	snprintf(link, sizeof(link), "../can_fifos/%02i", fifo);
> +
> +	debugfs_create_symlink(name, desc->debugfs_dir, link);
> +}
> +
> +static
> +void mcp25xxfd_can_setup_fifo_debugfs_present_fifo(struct net_device *net,
> +						   int index)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct dentry *debugfs_dir;
> +	char name[4];
> +
> +	snprintf(name, sizeof(name), "%02i", index);
> +	debugfs_dir = debugfs_create_dir(name, cpriv->fifos.debugfs_dir);
> +
> +	/* and the entries */
> +	debugfs_create_u32("is_tx", 0444, debugfs_dir,
> +			   &cpriv->fifos.fifo_info[index].is_tx);
> +	debugfs_create_x32("offset", 0444, debugfs_dir,
> +			   &cpriv->fifos.fifo_reg[index].offset);
> +	debugfs_create_u32("priority", 0444, debugfs_dir,
> +			   &cpriv->fifos.fifo_info[index].priority);
> +	debugfs_create_u64("use_count", 0444, debugfs_dir,
> +			   &cpriv->fifos.fifo_info[index].use_count);
> +}
> +
> +static
> +void mcp25xxfd_can_setup_fifo_debugfs_present_fifos(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct mcp25xxfd_priv *priv = cpriv->priv;
> +	int i;
> +
> +	/* create the directory if necessary */
> +	if (!cpriv->fifos.debugfs_dir)
> +		cpriv->fifos.debugfs_dir =
> +			debugfs_create_dir("can_fifos", priv->debugfs_dir);
> +
> +	/* now present all fifos
> +	 * - note that there is no fifo 0 - that is the TX-queue!
> +	 */
> +	for (i = 1; i < 32; i++)
> +		mcp25xxfd_can_setup_fifo_debugfs_present_fifo(net, i);
> +}
> +
> +static void mcp25xxfd_can_remove_fifo_debugfs(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +
> +	debugfs_remove_recursive(cpriv->fifos.debugfs_dir);
> +	cpriv->fifos.debugfs_dir = NULL;
> +	debugfs_remove_recursive(cpriv->fifos.rx.debugfs_dir);
> +	cpriv->fifos.rx.debugfs_dir = NULL;
> +	debugfs_remove_recursive(cpriv->fifos.tx.debugfs_dir);
> +	cpriv->fifos.tx.debugfs_dir = NULL;
> +}
> +
> +#else
> +
> +static
> +void mcp25xxfd_can_setup_fifo_debugfs_rxtx(struct net_device *net,
> +					   const char *name,
> +					   struct mcp25xxfd_fifo *desc)
> +{
> +}
> +
> +static
> +void mcp25xxfd_can_setup_fifo_debugfs_link_rxtx(struct mcp25xxfd_fifo *desc,
> +						int index, int fifo)
> +{
> +}
> +
> +static
> +void mcp25xxfd_can_setup_fifo_debugfs_present_fifos(struct net_device *net)
> +{
> +}
> +
> +static void mcp25xxfd_can_remove_fifo_debugfs(struct net_device *net)
> +{
> +}
> +#endif
> +
> +static int mcp25xxfd_can_get_fifo_address(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct mcp25xxfd_priv *priv = cpriv->priv;
> +	struct spi_device *spi = priv->spi;
> +	int ret;
> +
> +	/* write the config to the controller in one go */
> +	ret = mcp25xxfd_cmd_write_regs(spi, CAN_FIFOCON(0),
> +				       &cpriv->fifos.fifo_reg[0].control,
> +				       sizeof(cpriv->fifos.fifo_reg));
> +	if (ret)
> +		return ret;
> +
> +	/* we need to move out of config mode to force address computation */
> +	ret = mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
> +					CAN_CON_MODE_INTERNAL_LOOPBACK);
> +	if (ret)
> +		return ret;
> +
> +	/* and get back into config mode */
> +	ret = mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
> +					CAN_CON_MODE_CONFIG);
> +	if (ret)
> +		return ret;
> +
> +	/* read address and config back in */
> +	return mcp25xxfd_cmd_read_regs(spi, CAN_FIFOCON(0),
> +				       &cpriv->fifos.fifo_reg[0].control,
> +				       sizeof(cpriv->fifos.fifo_reg));
> +}
> +
> +static int mcp25xxfd_can_setup_fifo_config(struct net_device *net,
> +					   const char *name,
> +					   struct mcp25xxfd_fifo *desc,
> +					   u32 flags,
> +					   u32 flags_last)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	bool is_tx;
> +	u32 val;
> +	int index, prio, fifo, count;
> +
> +	/* present fifo type info */
> +	mcp25xxfd_can_setup_fifo_debugfs_rxtx(net, name, desc);
> +
> +	/* now setup the fifos themselves */
> +	for (index = 0,
> +	     fifo = desc->start,
> +	     count = desc->count,
> +	     prio = desc->priority_start;
> +	     count > 0;
> +	     index++,
> +	     fifo += desc->increment,
> +	     prio += desc->priority_increment,
> +	     count--) {
> +		/* select the effective value */
> +		val = (count > 1) ? flags : flags_last;
> +
> +		/* are we in tx mode */
> +		is_tx = flags & CAN_FIFOCON_TXEN;
> +		cpriv->fifos.fifo_info[fifo].is_tx = is_tx;
> +
> +		/* set priority if we are tx */
> +		if (is_tx) {
> +			cpriv->fifos.fifo_info[fifo].priority = prio;
> +			val |= (prio << CAN_FIFOCON_TXPRI_SHIFT);
> +		}
> +
> +		/* setup interface */
> +		cpriv->fifos.fifo_reg[fifo].control = val;
> +
> +		/* and link debugfs fifo */
> +		mcp25xxfd_can_setup_fifo_debugfs_link_rxtx(desc, index,
> +							   fifo);
> +	}
> +
> +	return 0;
> +}
> +
> +static int mcp25xxfd_can_setup_tx_fifo_config(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	u32 tx_flags;
> +
> +	/* TX Fifo configuration */
> +	tx_flags = CAN_FIFOCON_FRESET | /* reset FIFO */
> +		CAN_FIFOCON_TXEN | /* this is a TX_FIFO */
> +		CAN_FIFOCON_TXATIE | /* show up txatie flags in txatif reg */
> +		(cpriv->fifos.payload_mode << CAN_FIFOCON_PLSIZE_SHIFT) |
> +		(0 << CAN_FIFOCON_FSIZE_SHIFT); /* 1 FIFO only */
> +	if (cpriv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
> +		if (three_shot)
> +			tx_flags |= CAN_FIFOCON_TXAT_THREE_SHOT <<
> +				CAN_FIFOCON_TXAT_SHIFT;
> +		else
> +			tx_flags |= CAN_FIFOCON_TXAT_ONE_SHOT <<
> +				CAN_FIFOCON_TXAT_SHIFT;
> +	else
> +		tx_flags |= CAN_FIFOCON_TXAT_UNLIMITED <<
> +			CAN_FIFOCON_TXAT_SHIFT;
> +
> +	return mcp25xxfd_can_setup_fifo_config(net, "can_fifo_tx",
> +					       &cpriv->fifos.tx,
> +					       tx_flags, tx_flags);
> +}
> +
> +static int mcp25xxfd_can_setup_rx_fifo_config(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	u32 rx_flags, rx_flags_last;
> +
> +	/* RX Fifo configuration */
> +	rx_flags = CAN_FIFOCON_FRESET | /* reset FIFO */
> +		CAN_FIFOCON_RXTSEN | /* RX timestamps */
> +		CAN_FIFOCON_TFERFFIE | /* FIFO Full */
> +		CAN_FIFOCON_TFHRFHIE | /* FIFO Half Full*/
> +		CAN_FIFOCON_TFNRFNIE | /* FIFO not empty */
> +		(cpriv->fifos.payload_mode << CAN_FIFOCON_PLSIZE_SHIFT) |
> +		(0 << CAN_FIFOCON_FSIZE_SHIFT); /* 1 FIFO only */
> +	rx_flags_last = rx_flags | CAN_FIFOCON_RXOVIE;
> +
> +	/* configure the fifos */
> +	return mcp25xxfd_can_setup_fifo_config(net, "can_fifo_rx",
> +					       &cpriv->fifos.rx,
> +					       rx_flags, rx_flags_last);
> +}
> +
> +static int mcp25xxfd_can_clear_rx_filter_masks(struct spi_device *spi)
> +{
> +	u32 data[2 * 32]; /* 2 registers (match and mask) per filter */
> +
> +	memset(data, 0, sizeof(data));
> +
> +	return mcp25xxfd_cmd_write_regs(spi, CAN_FLTOBJ(0),
> +					data, sizeof(data));
> +}
> +
> +static int mcp25xxfd_can_setup_rx_filter_config(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct mcp25xxfd_priv *priv = cpriv->priv;
> +	struct spi_device *spi = priv->spi;
> +	struct mcp25xxfd_fifo *desc = &cpriv->fifos.rx;
> +	u8 filter_con[32];
> +	int filter, fifo, ret;
> +
> +	/* present all fifos */
> +	mcp25xxfd_can_setup_fifo_debugfs_present_fifos(net);
> +
> +	/* clear the filter mask - match any frame with every filter */
> +	ret = mcp25xxfd_can_clear_rx_filter_masks(spi);
> +	if (ret)
> +		return ret;
> +
> +	/* clear the filters and filter mappings for all filters */
> +	memset(filter_con, 0, sizeof(filter_con));
> +
> +	/* and now set up the rx filters */
> +	for (filter = 0, fifo = desc->start;
> +	     filter < desc->count;
> +	     filter++, fifo += desc->increment) {
> +		/* set up filter config - we can use the mask of filter 0 */
> +		filter_con[filter] = CAN_FIFOCON_FLTEN(0) |
> +			(fifo << CAN_FILCON_SHIFT(0));
> +	}
> +
> +	/* and set up filter control */
> +	return mcp25xxfd_cmd_write_regs(spi, CAN_FLTCON(0),
> +					(u32 *)filter_con,
> +					sizeof(filter_con));
> +}
> +
> +static int mcp25xxfd_can_compute_fifos(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	int tef_memory_used, tx_memory_used, rx_memory_available;
> +
> +	/* default settings as per MTU/CANFD */
> +	switch (net->mtu) {
> +	case CAN_MTU:
> +		/* mtu is 8 */
> +		cpriv->fifos.payload_size = 8;
> +		cpriv->fifos.payload_mode = CAN_TXQCON_PLSIZE_8;
> +
> +		/* 7 tx fifos */
> +		cpriv->fifos.tx.count = 7;
> +
> +		break;
> +	case CANFD_MTU:
> +		/* wish there was a way to have hw filters
> +		 * that can separate based on length ...
> +		 */
> +		/* MTU is 64 */
> +		cpriv->fifos.payload_size = 64;
> +		cpriv->fifos.payload_mode = CAN_TXQCON_PLSIZE_64;
> +
> +		/* 7 tx fifos */
> +		cpriv->fifos.tx.count = 7;
> +
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	/* compute effective sizes */
> +	cpriv->fifos.tef.size = sizeof(struct mcp25xxfd_obj_tef);
> +	cpriv->fifos.tx.size = sizeof(struct mcp25xxfd_obj_tx) +
> +		cpriv->fifos.payload_size;
> +	cpriv->fifos.rx.size = sizeof(struct mcp25xxfd_obj_rx) +
> +		cpriv->fifos.payload_size;
> +
> +	/* if defined as a module parameter modify the number of tx_fifos */
> +	if (tx_fifos) {
> +		netdev_info(net,
> +			    "Using %i tx-fifos as per module parameter\n",
> +			    tx_fifos);
> +		cpriv->fifos.tx.count = tx_fifos;
> +	}
> +
> +	/* there can be at the most 30 tx fifos (TEF and at least 1 RX fifo */
> +	if (cpriv->fifos.tx.count > 30) {
> +		netdev_err(net,
> +			   "There is an absolute maximum of 30 tx-fifos\n");
> +		return -EINVAL;
> +	}
> +
> +	/* set tef fifos to the number of tx fifos */
> +	cpriv->fifos.tef.count = cpriv->fifos.tx.count;
> +
> +	/* compute size of the tx fifos and TEF */
> +	tx_memory_used = cpriv->fifos.tx.count * cpriv->fifos.tx.size;
> +	tef_memory_used = cpriv->fifos.tef.count * cpriv->fifos.tef.size;
> +
> +	/* calculate evailable memory for RX_fifos */
> +	rx_memory_available = MCP25XXFD_BUFFER_TXRX_SIZE -
> +		tx_memory_used -
> +		tef_memory_used;
> +
> +	/* we need at least one RX Frame */
> +	if (rx_memory_available < cpriv->fifos.rx.size) {
> +		netdev_err(net,
> +			   "Configured %i tx-fifos exceeds available memory already\n",
> +			   cpriv->fifos.tx.count);
> +		return -EINVAL;
> +	}
> +
> +	/* calculate possible amount of RX fifos */
> +	cpriv->fifos.rx.count = rx_memory_available / cpriv->fifos.rx.size;
> +
> +	/* so now calculate effective number of rx-fifos
> +	 * there are only 31 fifos available in total,
> +	 * so we need to limit ourselves
> +	 */
> +	if (cpriv->fifos.rx.count + cpriv->fifos.tx.count > 31)
> +		cpriv->fifos.rx.count = 31 - cpriv->fifos.tx.count;
> +
> +	/* define the layout now that we have gotten everything */
> +	cpriv->fifos.tx.start = 1;
> +	cpriv->fifos.tx.increment = 1;
> +	/* highest priority is 31 */
> +	cpriv->fifos.tx.priority_start = 31;
> +	cpriv->fifos.tx.priority_increment = -1;
> +
> +	cpriv->fifos.rx.start = 1 + cpriv->fifos.tx.count;
> +	cpriv->fifos.rx.increment = 1;
> +	/* rx fifos do not have a priority */
> +	cpriv->fifos.rx.priority_start = 0;
> +	cpriv->fifos.rx.priority_increment = 0;
> +
> +	return 0;
> +}
> +
> +int mcp25xxfd_can_setup_fifos(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct mcp25xxfd_priv *priv = cpriv->priv;
> +	struct spi_device *spi = priv->spi;
> +	int ret;
> +
> +	/* compute fifos counts */
> +	ret = mcp25xxfd_can_compute_fifos(net);
> +	if (ret)
> +		return ret;
> +
> +	/* configure TEF */
> +	if (cpriv->fifos.tef.count)
> +		cpriv->regs.tefcon =
> +			CAN_TEFCON_FRESET |
> +			CAN_TEFCON_TEFNEIE |
> +			CAN_TEFCON_TEFTSEN |
> +			((cpriv->fifos.tef.count - 1) <<
> +			 CAN_TEFCON_FSIZE_SHIFT);
> +	else
> +		cpriv->regs.tefcon = 0;
> +	ret = mcp25xxfd_cmd_write(spi, CAN_TEFCON, cpriv->regs.tefcon);
> +	if (ret)
> +		return ret;
> +
> +	/* clear fifo_reg and fifo_info */
> +	memset(cpriv->fifos.fifo_reg, 0, sizeof(cpriv->fifos.fifo_reg));
> +	memset(cpriv->fifos.fifo_info, 0, sizeof(cpriv->fifos.fifo_info));
> +
> +	/* configure FIFOS themselves */
> +	ret = mcp25xxfd_can_setup_tx_fifo_config(net);
> +	if (ret)
> +		return ret;
> +	ret = mcp25xxfd_can_setup_rx_fifo_config(net);
> +	if (ret)
> +		return ret;
> +
> +	/* get fifo addresses */
> +	ret = mcp25xxfd_can_get_fifo_address(net);
> +	if (ret)
> +		return ret;
> +
> +	/* finally configure RX filters */
> +	ret = mcp25xxfd_can_setup_rx_filter_config(net);
> +	if (ret)
> +		return ret;
> +
> +	/* setup tx_fifo_queue */
> +	return mcp25xxfd_can_tx_queue_alloc(net);
> +}
> +
> +void mcp25xxfd_can_release_fifos(struct net_device *net)
> +{
> +	mcp25xxfd_can_tx_queue_free(net);
> +	mcp25xxfd_can_remove_fifo_debugfs(net);
> +}
> diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c
> new file mode 100644
> index 000000000000..842c714fbbfe
> --- /dev/null
> +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c
> @@ -0,0 +1,723 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
> + * implementation of can interrupt handling
> + *
> + * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
> + *
> + * Based on Microchip MCP251x CAN controller driver written by
> + * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
> + */
> +
> +#include "mcp25xxfd_can.h"
> +#include <linux/can/core.h>
> +#include <linux/can/dev.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/net.h>
> +#include <linux/netdevice.h>
> +#include <linux/slab.h>
> +#include <linux/sort.h>
> +
> +static void mcp25xxfd_can_error_skb(struct spi_device *spi)

Why do the functions in this file use "spi" as main main argument? It
belongs to the "mcp25xxfd_can" interface and should therefore use
"struct mcp25xxfd_can_priv".

> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);

Then we can drop the lines above...

> +	struct sk_buff *skb;
> +	struct can_frame *frame;
> +
> +	/* allocate error frame */
> +	skb = alloc_can_err_skb(net, &frame);

And use "cpriv->net" here. It's almost the same in most of the functions
in this and the other "mcp25xxfd_can_" files.

> +	if (!skb) {
> +		netdev_err(net, "cannot allocate error skb\n");
> +		return;
> +	}
> +
> +	/* setup can error frame data */
> +	frame->can_id |= cpriv->error_frame.id;
> +	memcpy(frame->data, cpriv->error_frame.data, sizeof(frame->data));
> +
> +	/* and submit it */
> +	netif_receive_skb(skb);
> +}
> +
> +static int mcp25xxfd_can_int_clear_int_flags(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	u32 clearable_irq_active = cpriv->status.intf & CAN_INT_IF_CLEAR_MASK;
> +	u32 clear_irq = cpriv->status.intf & (~CAN_INT_IF_CLEAR_MASK);
> +
> +	/* if no clearable flags are set then skip the whole transfer */
> +	if (!clearable_irq_active)
> +		return 0;
> +
> +	return mcp25xxfd_cmd_write_mask(spi, CAN_INT,
> +					clear_irq, clearable_irq_active);
> +}
> +
> +static int mcp25xxfd_can_ist_handle_serrif_txmab(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +
> +	net->stats.tx_fifo_errors++;
> +	net->stats.tx_errors++;
> +	cpriv->stats.int_serr_tx_count++;
> +
> +	/* and switch back into the correct mode */
> +	return mcp25xxfd_can_switch_mode_nowait(spi, &cpriv->regs.con,
> +						(net->mtu == CAN_MTU) ?
> +						CAN_CON_MODE_CAN2_0 :
> +						CAN_CON_MODE_MIXED);
> +}
> +
> +static int mcp25xxfd_can_ist_handle_serrif_rxmab(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +
> +	net->stats.rx_dropped++;
> +	net->stats.rx_errors++;
> +	cpriv->stats.int_serr_rx_count++;
> +
> +	return 0;
> +}
> +
> +static int mcp25xxfd_can_int_handle_serrif(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +
> +	if (!(cpriv->status.intf & CAN_INT_SERRIF))
> +		return 0;
> +
> +	/* increment statistics counter now */
> +	cpriv->stats.int_serr_count++;
> +
> +	/* interrupt flags have been cleared already */
> +
> +	/* Errors here are:
> +	 * * Bus Bandwidth Error: when a RX Message Assembly Buffer
> +	 *   is still full when the next message has already arrived
> +	 *   the recived message shall be ignored
> +	 * * TX MAB Underflow: when a TX Message is invalid
> +	 *   due to ECC errors or TXMAB underflow
> +	 *   in this situatioon the system will transition to
> +	 *   Restricted or Listen Only mode
> +	 */
> +
> +	cpriv->error_frame.id |= CAN_ERR_CRTL;
> +	cpriv->error_frame.data[1] |= CAN_ERR_CRTL_UNSPEC;
> +
> +	/* a mode change + invalid message would indicate
> +	 * TX MAB Underflow
> +	 */
> +	if ((cpriv->status.intf & CAN_INT_MODIF) &&
> +	    (cpriv->status.intf & CAN_INT_IVMIF)) {
> +		return mcp25xxfd_can_ist_handle_serrif_txmab(spi);
> +	}
> +
> +	/* for RX there is only the RXIF an indicator
> +	 * - surprizingly RX-MAB does not change mode or anything
> +	 */
> +	if (cpriv->status.intf & CAN_INT_RXIF)
> +		return mcp25xxfd_can_ist_handle_serrif_rxmab(spi);
> +
> +	/* the final case */
> +	dev_warn_ratelimited(&spi->dev,
> +			     "unidentified system error - intf =  %08x\n",
> +			     cpriv->status.intf);
> +
> +	return 0;
> +}
> +
> +static int mcp25xxfd_can_int_handle_modif(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	int mode;
> +	int ret;
> +
> +	/* Note that this irq does not get triggered in all situations
> +	 * for example SERRIF will move to RESTICTED or LISTENONLY
> +	 * but MODIF will not be raised!
> +	 */
> +
> +	if (!(cpriv->status.intf & CAN_INT_MODIF))
> +		return 0;
> +	cpriv->stats.int_mod_count++;
> +
> +	/* get the current mode */
> +	ret = mcp25xxfd_can_get_mode(spi, &mode);
> +	if (ret)
> +		return ret;
> +	mode = ret;
> +
> +	/* switches to the same mode as before are ignored
> +	 * - this typically happens if the driver is shortly
> +	 *   switching to a different mode and then returning to the
> +	 *   original mode
> +	 */
> +	if (mode == cpriv->mode)
> +		return 0;
> +
> +	/* if we are restricted, then return to "normal" mode */
> +	if (mode == CAN_CON_MODE_RESTRICTED) {
> +		cpriv->mode = mode;
> +		return mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
> +						 (net->mtu == CAN_MTU) ?
> +						 CAN_CON_MODE_CAN2_0 :
> +						 CAN_CON_MODE_MIXED);
> +	}
> +
> +	/* the controller itself will transition to sleep, so we ignore it */
> +	if (mode == CAN_CON_MODE_SLEEP) {
> +		cpriv->mode = mode;
> +		return 0;
> +	}
> +
> +	/* these we need to handle correctly, so warn and give context */
> +	dev_warn(&spi->dev,
> +		 "Controller unexpectedly switched from mode %u to %u\n",
> +		 cpriv->mode, mode);
> +
> +	/* assign the mode as current */
> +	cpriv->mode = mode;
> +
> +	return 0;
> +}
> +
> +static int mcp25xxfd_compare_obj_ts(const void *a, const void *b)
> +{
> +	s32 ats = ((struct mcp25xxfd_obj_ts *)a)->ts;
> +	s32 bts = ((struct mcp25xxfd_obj_ts *)b)->ts;
> +
> +	if (ats < bts)
> +		return -1;
> +	if (ats > bts)
> +		return 1;
> +	return 0;
> +}
> +
> +static int mcp25xxfd_can_submit_frames(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct mcp25xxfd_obj_ts *queue = cpriv->fifos.submit_queue;
> +	int count = cpriv->fifos.submit_queue_count;
> +	int i, fifo;
> +	int ret;
> +
> +	/* skip processing if the queue count is 0 */
> +	if (count == 0)
> +		goto out;
> +
> +	/* sort the fifos (rx and TEF) by receive timestamp */
> +	sort(queue, count, sizeof(*queue), mcp25xxfd_compare_obj_ts, NULL);
> +
> +	/* now submit the fifos  */
> +	for (i = 0; i < count; i++) {
> +		fifo = queue[i].fifo;
> +		if (fifo > 0)
> +			ret = mcp25xxfd_can_submit_rx_frame(spi, fifo);
> +		else
> +			ret = mcp25xxfd_can_submit_tx_frame(spi, -fifo);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/* if we have received or transmitted something
> +	 * and the IVMIE is disabled, then enable it
> +	 * this is mostly to avoid unnecessary interrupts during a
> +	 * disconnected CAN BUS
> +	 */
> +	if (!(cpriv->status.intf | CAN_INT_IVMIE)) {
> +		cpriv->status.intf |= CAN_INT_IVMIE;
> +		ret = mcp25xxfd_cmd_write_mask(spi, CAN_INT,
> +					       cpriv->status.intf,
> +					       CAN_INT_IVMIE);
> +		if (ret)
> +			return ret;
> +	}
> +
> +out:
> +	/* enable tx_queue if necessary */
> +	mcp25xxfd_can_tx_queue_restart(net);
> +
> +	return 0;
> +}
> +
> +static int mcp25xxfd_can_int_handle_rxif(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +
> +	if (!cpriv->status.rxif)
> +		return 0;
> +
> +	cpriv->stats.int_rx_count++;
> +
> +	/* read all the fifos */
> +	return mcp25xxfd_can_read_rx_frames(spi);
> +}
> +
> +static int mcp25xxfd_can_int_handle_rxovif(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	int ret, i;
> +
> +	if (!cpriv->status.rxovif)
> +		return 0;
> +	cpriv->stats.int_rxov_count++;
> +
> +	/* clear all fifos that have an overflow bit set */
> +	for (i = 0; i < 32; i++) {
> +		if (cpriv->status.rxovif & BIT(i)) {
> +			/* clear fifo status */
> +			ret = mcp25xxfd_cmd_write_mask(spi,
> +						       CAN_FIFOSTA(i),
> +						       0,
> +						       CAN_FIFOSTA_RXOVIF);
> +			if (ret)
> +				return ret;
> +
> +			/* update statistics */
> +			net->stats.rx_over_errors++;
> +			net->stats.rx_errors++;
> +
> +			/* and prepare ERROR FRAME */
> +			cpriv->error_frame.id |= CAN_ERR_CRTL;
> +			cpriv->error_frame.data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int mcp25xxfd_can_int_handle_eccif(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +
> +	if (!(cpriv->status.intf & CAN_INT_ECCIF))
> +		return 0;
> +
> +	cpriv->stats.int_ecc_count++;
> +
> +	/* and prepare ERROR FRAME */
> +	cpriv->error_frame.id |= CAN_ERR_CRTL;
> +	cpriv->error_frame.data[1] |= CAN_ERR_CRTL_UNSPEC;
> +
> +	/* delegate to interrupt cleaning */
> +	return mcp25xxfd_clear_ecc_interrupts(spi);
> +}
> +
> +static void mcp25xxfd_can_int_handle_ivmif_tx(struct spi_device *spi,
> +					      u32 *mask)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +
> +	/* check if it is really a known tx error */
> +	if ((cpriv->bus.bdiag[1] &
> +	     (CAN_BDIAG1_DBIT1ERR |
> +	      CAN_BDIAG1_DBIT0ERR |
> +	      CAN_BDIAG1_NACKERR |
> +	      CAN_BDIAG1_NBIT1ERR |
> +	      CAN_BDIAG1_NBIT0ERR
> +		     )) == 0)
> +		return;
> +
> +	/* mark it as a protocol error */
> +	cpriv->error_frame.id |= CAN_ERR_PROT;
> +
> +	/* and update statistics */
> +	net->stats.tx_errors++;
> +
> +	/* and handle all the known cases */
> +	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NACKERR) {
> +		/* TX-Frame not acknowledged - connected to CAN-bus? */
> +		*mask |= CAN_BDIAG1_NACKERR;
> +		cpriv->error_frame.data[2] |= CAN_ERR_PROT_TX;
> +		net->stats.tx_aborted_errors++;
> +	}
> +	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NBIT1ERR) {
> +		/* TX-Frame CAN-BUS Level is unexpectedly dominant */
> +		*mask |= CAN_BDIAG1_NBIT1ERR;
> +		net->stats.tx_carrier_errors++;
> +		cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT1;
> +	}
> +	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NBIT0ERR) {
> +		/* TX-Frame CAN-BUS Level is unexpectedly recessive */
> +		*mask |= CAN_BDIAG1_NBIT0ERR;
> +		net->stats.tx_carrier_errors++;
> +		cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT0;
> +	}
> +	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_DBIT1ERR) {
> +		/* TX-Frame CAN-BUS Level is unexpectedly dominant
> +		 * during data phase
> +		 */
> +		*mask |= CAN_BDIAG1_DBIT1ERR;
> +		net->stats.tx_carrier_errors++;
> +		cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT1;
> +	}
> +	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_DBIT0ERR) {
> +		/* TX-Frame CAN-BUS Level is unexpectedly recessive
> +		 * during data phase
> +		 */
> +		*mask |= CAN_BDIAG1_DBIT0ERR;
> +		net->stats.tx_carrier_errors++;
> +		cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT0;
> +	}
> +}
> +
> +static void mcp25xxfd_can_int_handle_ivmif_rx(struct spi_device *spi,
> +					      u32 *mask)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +
> +	/* check if it is really a known tx error */
> +	if ((cpriv->bus.bdiag[1] &
> +	     (CAN_BDIAG1_DCRCERR |
> +	      CAN_BDIAG1_DSTUFERR |
> +	      CAN_BDIAG1_DFORMERR |
> +	      CAN_BDIAG1_NCRCERR |
> +	      CAN_BDIAG1_NSTUFERR |
> +	      CAN_BDIAG1_NFORMERR
> +		     )) == 0)
> +		return;
> +
> +	/* mark it as a protocol error */
> +	cpriv->error_frame.id |= CAN_ERR_PROT;
> +
> +	/* and update statistics */
> +	net->stats.rx_errors++;
> +
> +	/* handle the cases */
> +	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_DCRCERR) {
> +		/* RX-Frame with bad CRC during data phase */
> +		*mask |= CAN_BDIAG1_DCRCERR;
> +		net->stats.rx_crc_errors++;
> +		cpriv->error_frame.data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
> +	}
> +	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_DSTUFERR) {
> +		/* RX-Frame with bad stuffing during data phase */
> +		*mask |= CAN_BDIAG1_DSTUFERR;
> +		net->stats.rx_frame_errors++;
> +		cpriv->error_frame.data[2] |= CAN_ERR_PROT_STUFF;
> +	}
> +	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_DFORMERR) {
> +		/* RX-Frame with bad format during data phase */
> +		*mask |= CAN_BDIAG1_DFORMERR;
> +		net->stats.rx_frame_errors++;
> +		cpriv->error_frame.data[2] |= CAN_ERR_PROT_FORM;
> +	}
> +	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NCRCERR) {
> +		/* RX-Frame with bad CRC during data phase */
> +		*mask |= CAN_BDIAG1_NCRCERR;
> +		net->stats.rx_crc_errors++;
> +		cpriv->error_frame.data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
> +	}
> +	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NSTUFERR) {
> +		/* RX-Frame with bad stuffing during data phase */
> +		*mask |= CAN_BDIAG1_NSTUFERR;
> +		net->stats.rx_frame_errors++;
> +		cpriv->error_frame.data[2] |= CAN_ERR_PROT_STUFF;
> +	}
> +	if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NFORMERR) {
> +		/* RX-Frame with bad format during data phase */
> +		*mask |= CAN_BDIAG1_NFORMERR;
> +		net->stats.rx_frame_errors++;
> +		cpriv->error_frame.data[2] |= CAN_ERR_PROT_FORM;
> +	}
> +}
> +
> +static int mcp25xxfd_can_int_handle_ivmif(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	u32 mask, bdiag1;
> +	int ret;
> +
> +	if (!(cpriv->status.intf & CAN_INT_IVMIF))
> +		return 0;
> +
> +	cpriv->stats.int_ivm_count++;
> +
> +	/* if we have a systemerror as well,
> +	 * then ignore it as they correlate
> +	 */
> +	if (cpriv->status.intf & CAN_INT_SERRIF)
> +		return 0;
> +
> +	/* read bus diagnostics */
> +	ret = mcp25xxfd_cmd_read_regs(spi, CAN_BDIAG0,
> +				      cpriv->bus.bdiag,
> +				      sizeof(cpriv->bus.bdiag));
> +	if (ret)
> +		return ret;
> +
> +	/* clear the masks of bits to clear */
> +	mask = 0;
> +
> +	/* check rx and tx errors */
> +	mcp25xxfd_can_int_handle_ivmif_tx(spi, &mask);
> +	mcp25xxfd_can_int_handle_ivmif_rx(spi, &mask);
> +
> +	/* clear flags if we have bits masked */
> +	if (!mask) {
> +		/* the unsupported case, where we are not
> +		 * clearing any registers
> +		 */
> +		dev_warn_once(&spi->dev,
> +			      "found IVMIF situation not supported by driver - bdiag = [0x%08x, 0x%08x]",
> +			      cpriv->bus.bdiag[0], cpriv->bus.bdiag[1]);
> +		return -EINVAL;
> +	}
> +
> +	/* clear the bits in bdiag1 */
> +	bdiag1 = cpriv->bus.bdiag[1] & (~mask);
> +	/* and write it */
> +	ret = mcp25xxfd_cmd_write_mask(spi, CAN_BDIAG1, bdiag1, mask);
> +	if (ret)
> +		return ret;
> +
> +	/* and clear the interrupt flag until we have received or transmited */
> +	cpriv->status.intf &= ~(CAN_INT_IVMIE);
> +	return mcp25xxfd_cmd_write_mask(spi, CAN_INT, cpriv->status.intf,
> +					CAN_INT_IVMIE);
> +}
> +
> +static int mcp25xxfd_can_int_handle_cerrif(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +
> +	if (!(cpriv->status.intf & CAN_INT_CERRIF))
> +		return 0;
> +
> +	/* this interrupt exists primarilly to counter possible
> +	 * bus off situations more detailed information
> +	 * can be found and controlled in the TREC register
> +	 */
> +
> +	cpriv->stats.int_cerr_count++;
> +
> +	netdev_warn(net, "CAN Bus error experienced");
> +
> +	return 0;
> +}
> +
> +static int mcp25xxfd_can_int_error_counters(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);

This functionm, like many others just needs "cpriv".

> +
> +	if (cpriv->status.trec & CAN_TREC_TXWARN) {
> +		cpriv->bus.new_state = CAN_STATE_ERROR_WARNING;
> +		cpriv->error_frame.id |= CAN_ERR_CRTL;
> +		cpriv->error_frame.data[1] |= CAN_ERR_CRTL_TX_WARNING;
> +	}
> +	if (cpriv->status.trec & CAN_TREC_RXWARN) {
> +		cpriv->bus.new_state = CAN_STATE_ERROR_WARNING;
> +		cpriv->error_frame.id |= CAN_ERR_CRTL;
> +		cpriv->error_frame.data[1] |= CAN_ERR_CRTL_RX_WARNING;
> +	}
> +	if (cpriv->status.trec & CAN_TREC_TXBP) {
> +		cpriv->bus.new_state = CAN_STATE_ERROR_PASSIVE;
> +		cpriv->error_frame.id |= CAN_ERR_CRTL;
> +		cpriv->error_frame.data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
> +	}
> +	if (cpriv->status.trec & CAN_TREC_RXBP) {
> +		cpriv->bus.new_state = CAN_STATE_ERROR_PASSIVE;
> +		cpriv->error_frame.id |= CAN_ERR_CRTL;
> +		cpriv->error_frame.data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
> +	}
> +	if (cpriv->status.trec & CAN_TREC_TXBO) {
> +		cpriv->bus.new_state = CAN_STATE_BUS_OFF;
> +		cpriv->error_frame.id |= CAN_ERR_BUSOFF;
> +	}
> +
> +	return 0;
> +}
> +
> +static int mcp25xxfd_can_int_error_handling(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +
> +	/* based on the last state state check the new state */
> +	switch (cpriv->can.state) {
> +	case CAN_STATE_ERROR_ACTIVE:
> +		if (cpriv->bus.new_state >= CAN_STATE_ERROR_WARNING &&
> +		    cpriv->bus.new_state <= CAN_STATE_BUS_OFF)
> +			cpriv->can.can_stats.error_warning++;
> +		/* fallthrough */
> +	case CAN_STATE_ERROR_WARNING:
> +		if (cpriv->bus.new_state >= CAN_STATE_ERROR_PASSIVE &&
> +		    cpriv->bus.new_state <= CAN_STATE_BUS_OFF)
> +			cpriv->can.can_stats.error_passive++;
> +		break;
> +	default:
> +		break;
> +	}
> +	cpriv->can.state = cpriv->bus.new_state;
> +
> +	/* and send error packet */
> +	if (cpriv->error_frame.id)
> +		mcp25xxfd_can_error_skb(spi);
> +
> +	/* handle BUS OFF */
> +	if (cpriv->can.state == CAN_STATE_BUS_OFF) {
> +		if (cpriv->can.restart_ms == 0) {
> +			cpriv->can.can_stats.bus_off++;
> +			can_bus_off(net);
> +		}
> +	} else {
> +		/* restart the tx queue if needed */
> +	}
> +
> +	return 0;
> +}
> +
> +static int mcp25xxfd_can_int_handle_status(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	int ret;
> +
> +	/* clear all the interrupts asap - we have them on file allready */
> +	ret = mcp25xxfd_can_int_clear_int_flags(spi);
> +	if (ret)
> +		return ret;
> +
> +	/* set up new state and error frame for this loop */
> +	cpriv->bus.new_state = cpriv->bus.state;
> +	memset(&cpriv->error_frame, 0, sizeof(cpriv->error_frame));
> +
> +	/* setup the process queue by clearing the counter */
> +	cpriv->fifos.submit_queue_count = 0;
> +
> +	/* handle interrupts */
> +
> +	/* system error interrupt needs to get handled first
> +	 * to get us out of restricted mode
> +	 */
> +	ret = mcp25xxfd_can_int_handle_serrif(spi);
> +	if (ret)
> +		return ret;
> +
> +	/* mode change interrupt */
> +	ret = mcp25xxfd_can_int_handle_modif(spi);
> +	if (ret)
> +		return ret;
> +
> +	/* handle the rx */
> +	ret = mcp25xxfd_can_int_handle_rxif(spi);
> +	if (ret)
> +		return ret;
> +	/* handle aborted TX FIFOs */
> +	ret = mcp25xxfd_can_int_handle_txatif(spi);
> +	if (ret)
> +		return ret;
> +
> +	/* handle the TEF */
> +	ret = mcp25xxfd_can_int_handle_tefif(spi);
> +	if (ret)
> +		return ret;
> +
> +	/* handle error interrupt flags */
> +	ret = mcp25xxfd_can_int_handle_rxovif(spi);
> +	if (ret)
> +		return ret;
> +
> +	/* sram ECC error interrupt */
> +	ret = mcp25xxfd_can_int_handle_eccif(spi);
> +	if (ret)
> +		return ret;
> +
> +	/* message format interrupt */
> +	ret = mcp25xxfd_can_int_handle_ivmif(spi);
> +	if (ret)
> +		return ret;
> +
> +	/* handle bus errors in more detail */
> +	ret = mcp25xxfd_can_int_handle_cerrif(spi);
> +	if (ret)
> +		return ret;
> +
> +	/* error counter handling */
> +	ret = mcp25xxfd_can_int_error_counters(spi);
> +	if (ret)
> +		return ret;
> +
> +	/* error counter handling */
> +	ret = mcp25xxfd_can_int_error_handling(spi);
> +	if (ret)
> +		return ret;
> +
> +	/* and submit can frames to network stack */
> +	ret = mcp25xxfd_can_submit_frames(spi);
> +
> +	return ret;
> +}
> +
> +irqreturn_t mcp25xxfd_can_int(int irq, void *dev_id)
> +{
> +	struct mcp25xxfd_priv *priv = dev_id;
> +	struct spi_device *spi = priv->spi;
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	int ret;
> +
> +	/* count interrupt calls */
> +	cpriv->stats.irq_calls++;
> +
> +	/* as long as we should be running */
> +	while (1) {
> +		/* count irq loops */
> +		cpriv->stats.irq_loops++;
> +
> +		/* read interrupt status flags in bulk */
> +		ret = mcp25xxfd_cmd_read_regs(spi, CAN_INT,
> +					      &cpriv->status.intf,
> +					      sizeof(cpriv->status));
> +		if (ret)
> +			return ret;

Does this return a legal return code?

> +
> +		/* only act if the IE mask configured has active IF bits
> +		 * otherwise the Interrupt line should be deasserted already
> +		 * so we can exit the loop
> +		 */
> +		if (((cpriv->status.intf >> CAN_INT_IE_SHIFT) &
> +		       cpriv->status.intf) == 0)
> +			break;
> +
> +		/* handle the status */
> +		ret = mcp25xxfd_can_int_handle_status(spi);
> +		if (ret)
> +			return ret;

Ditto

> +	}
> +
> +	return IRQ_HANDLED;
> +}
> diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c
> new file mode 100644
> index 000000000000..d5e4e2a18bb7
> --- /dev/null
> +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c
> @@ -0,0 +1,208 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
> + * implementation of can transmission
> + *
> + * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
> + *
> + * Based on Microchip MCP251x CAN controller driver written by
> + * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
> + */
> +
> +#include "mcp25xxfd_can.h"
> +#include <linux/can/core.h>
> +#include <linux/can/dev.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/netdevice.h>
> +#include <linux/skbuff.h>
> +#include <linux/slab.h>
> +#include <linux/spi/spi.h>
> +
> +/* module parameters */
> +bool do_not_submit_rx;
> +module_param(do_not_submit_rx, bool, 0664);
> +MODULE_PARM_DESC(do_not_submit_rx,
> +		 "do not submit rx frames to can stack - used to test performance of the spi layer");
> +
> +static
> +struct sk_buff *mcp25xxfd_can_submit_rx_normal_frame(struct net_device *net,
> +						     u32 id,
> +						     u32 dlc, u8 **data)

The prefix of the functions in this interface should be "mcp25xxfd_can_rx"!

> +{
> +	struct can_frame *frame;
> +	struct sk_buff *skb;
> +
> +	/* allocate frame */
> +	skb = alloc_can_skb(net, &frame);
> +	if (!skb)
> +		return NULL;
> +
> +	/* set id, dlc and flags */
> +	frame->can_id = id;
> +	frame->can_dlc = dlc;
> +
> +	/* and set the pointer to data */
> +	*data = frame->data;
> +
> +	return skb;
> +}
> +
> +/* it is almost identical except for the type of the frame... */
> +static
> +struct sk_buff *mcp25xxfd_can_submit_rx_fd_frame(struct net_device *net,
> +						 u32 id, u32 flags,
> +						 u32 len, u8 **data)
> +{
> +	struct canfd_frame *frame;
> +	struct sk_buff *skb;
> +
> +	/* allocate frame */
> +	skb = alloc_canfd_skb(net, &frame);
> +	if (!skb)
> +		return NULL;
> +
> +	/* set id, dlc and flags */
> +	frame->can_id = id;
> +	frame->len = len;
> +	frame->flags |= flags;
> +
> +	/* and set the pointer to data */
> +	*data = frame->data;
> +
> +	return skb;
> +}
> +
> +int mcp25xxfd_can_submit_rx_frame(struct spi_device *spi, int fifo)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	int addr = cpriv->fifos.fifo_reg[fifo].offset;
> +	struct mcp25xxfd_obj_rx *rx =
> +		(struct mcp25xxfd_obj_rx *)(cpriv->sram + addr);
> +	u8 *data = NULL;
> +	struct sk_buff *skb;
> +	u32 id, dlc, len, flags;
> +
> +	/* compute the can_id */
> +	mcp25xxfd_mcpid_to_canid(rx->id, rx->flags, &id);
> +
> +	/* and dlc */
> +	dlc = (rx->flags & CAN_OBJ_FLAGS_DLC_MASK) >>
> +		CAN_OBJ_FLAGS_DLC_SHIFT;
> +	len = can_dlc2len(dlc);
> +
> +	/* update stats */
> +	priv->net->stats.rx_packets++;
> +	priv->net->stats.rx_bytes += len;
> +	cpriv->fifos.rx.dlc_usage[dlc]++;
> +	if (rx->flags & CAN_OBJ_FLAGS_FDF)
> +		cpriv->fifos.rx.fd_count++;
> +
> +	/* allocate the skb buffer */
> +	if (rx->flags & CAN_OBJ_FLAGS_FDF) {
> +		flags = 0;
> +
> +		flags |= (flags & CAN_OBJ_FLAGS_BRS) ? CANFD_BRS : 0;
> +		flags |= (flags & CAN_OBJ_FLAGS_ESI) ? CANFD_ESI : 0;
> +		skb = mcp25xxfd_can_submit_rx_fd_frame(net, id, flags,
> +						       dlc, &data);
> +	} else {
> +		skb = mcp25xxfd_can_submit_rx_normal_frame(net, id,
> +							   len, &data);
> +	}
> +	if (!skb) {
> +		dev_err(&spi->dev, "cannot allocate RX skb\n");
> +		priv->net->stats.rx_dropped++;
> +		return -ENOMEM;
> +	}
> +
> +	/* copy the payload data */
> +	memcpy(data, rx->data, len);
> +
> +	/* and submit the frame */
> +	if (do_not_submit_rx)
> +		consume_skb(skb);
> +	else
> +		netif_rx_ni(skb);
> +
> +	return 0;
> +}
> +
> +static int mcp25xxfd_can_read_rx_frame(struct spi_device *spi,
> +				       int fifo)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	int addr = cpriv->fifos.fifo_reg[fifo].offset;
> +	struct mcp25xxfd_obj_rx *rx =
> +		(struct mcp25xxfd_obj_rx *)(cpriv->sram + addr);
> +	int len, ret;
> +
> +	/* we read the header plus 8 data bytes for "standard frames" */
> +	ret = mcp25xxfd_cmd_readn(spi, MCP25XXFD_SRAM_ADDR(addr),
> +				  rx, sizeof(*rx) + 8);
> +	if (ret)
> +		return ret;
> +
> +	/* transpose the headers to CPU format*/
> +	rx->id = le32_to_cpu(rx->id);
> +	rx->flags = le32_to_cpu(rx->flags);
> +	rx->ts = le32_to_cpu(rx->ts);
> +
> +	/* compute len */
> +	len = can_dlc2len((rx->flags & CAN_OBJ_FLAGS_DLC_MASK) >>
> +			  CAN_OBJ_FLAGS_DLC_SHIFT);
> +
> +	/* read the remaining data for canfd frames */
> +	if (net->mtu == CANFD_MTU && len > 8) {
> +		/* here the extra portion reading CanFD frames */
> +		ret = mcp25xxfd_cmd_readn(spi,
> +					  MCP25XXFD_SRAM_ADDR(addr) +
> +					  sizeof(*rx) + 8,
> +					  &rx->data[8], len - 8);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/* clear the rest of the buffer - just to be safe */
> +	memset(rx->data + len, 0,
> +	       ((net->mtu == CANFD_MTU) ? 64 : 8) - len);
> +
> +	/* increment the statistics counter */
> +	cpriv->fifos.fifo_info[fifo].use_count++;
> +
> +	/* add the fifo to the process queues */
> +	mcp25xxfd_can_queue_frame(cpriv, fifo, rx->ts);
> +
> +	/* and clear the interrupt flag for that fifo */
> +	return mcp25xxfd_cmd_write_mask(spi, CAN_FIFOCON(fifo),
> +					CAN_FIFOCON_FRESET,
> +					CAN_FIFOCON_FRESET);
> +}
> +
> +int mcp25xxfd_can_read_rx_frames(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	int i, fifo;
> +	int ret;
> +
> +	/* we can optimize here */
> +	for (i = 0, fifo = cpriv->fifos.rx.start;
> +	     i < cpriv->fifos.rx.count;
> +	     i++, fifo += cpriv->fifos.rx.increment) {
> +		if (cpriv->status.rxif & BIT(fifo)) {
> +			/* read the frame */
> +			ret = mcp25xxfd_can_read_rx_frame(spi, fifo);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c
> new file mode 100644
> index 000000000000..9d53ac04ae10
> --- /dev/null
> +++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c
> @@ -0,0 +1,824 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
> + * implementation of can transmission
> + *
> + * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
> + *
> + * Based on Microchip MCP251x CAN controller driver written by
> + * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
> + */
> +
> +#include "mcp25xxfd_can.h"
> +#include <linux/can/core.h>
> +#include <linux/can/dev.h>
> +#include <linux/device.h>
> +#include <linux/kernel.h>
> +#include <linux/list.h>
> +#include <linux/netdevice.h>
> +#include <linux/slab.h>
> +#include <linux/spi/spi.h>
> +
> +/* structure of a spi message that is prepared and can get deployed quickly */
> +struct mcp2517fd_tx_spi_message {
> +	/* the network device this is related to */
> +	struct net_device *net;
> +	/* the fifo this fills */
> +	u32 fifo;
> +	/* the xfer to fill in the fifo data */
> +	struct {
> +		struct spi_message msg;
> +		struct spi_transfer xfer;
> +		struct {
> +			u8 cmd[2];
> +			u8 header[sizeof(struct mcp25xxfd_obj_tx)];
> +			u8 data[64];
> +		} data;
> +	} fill_fifo;
> +	/* the xfer to enable transmission on the can bus */
> +	struct {
> +		struct spi_message msg;
> +		struct spi_transfer xfer;
> +		struct {
> +			u8 cmd[2];
> +			u8 data;
> +		} data;
> +	} trigger_fifo;
> +};
> +
> +struct mcp2517fd_tx_spi_message_queue {
> +	/* spinlock protecting the bitmaps
> +	 * as well as state and the skb_echo_* functions
> +	 */
> +	spinlock_t lock;
> +	/* bitmap of which fifo is in which stage */
> +	u32 idle;
> +	u32 in_fill_fifo_transfer;
> +	u32 in_trigger_fifo_transfer;
> +	u32 in_can_transfer;
> +	u32 transferred;
> +
> +	/* the queue state as seen per controller */
> +	int state;
> +
> +	/* spinlock protecting spi submission order */
> +	spinlock_t spi_lock;
> +
> +	/* map each fifo to a mcp2517fd_tx_spi_message */
> +	struct mcp2517fd_tx_spi_message *fifo2message[32];
> +
> +	/* the individual messages */
> +	struct mcp2517fd_tx_spi_message message[];
> +};
> +
> +/* mostly bit manipulations to move between stages */
> +static
> +struct mcp2517fd_tx_spi_message *mcp25xxfd_can_tx_queue_first_spi_message(
> +	struct mcp2517fd_tx_spi_message_queue *queue, u32 *bitmap)
> +{
> +	u32 first = ffs(*bitmap);
> +
> +	if (!first)
> +		return NULL;
> +
> +	return queue->fifo2message[first - 1];
> +}
> +
> +static
> +struct mcp2517fd_tx_spi_message *mcp25xxfd_can_tx_queue_last_spi_message(
> +	struct mcp2517fd_tx_spi_message_queue *queue, u32 *bitmap)
> +{
> +	u32 last = ffs(*bitmap);
> +
> +	if (!last)
> +		return NULL;
> +
> +	return queue->fifo2message[last - 1];
> +}
> +
> +static void mcp25xxfd_can_tx_queue_remove_spi_message(u32 *bitmap, int fifo)
> +{
> +	*bitmap &= ~BIT(fifo);
> +}
> +
> +static void mcp25xxfd_can_tx_queue_add_spi_message(u32 *bitmap, int fifo)
> +{
> +	*bitmap |= BIT(fifo);
> +}
> +
> +static void mcp25xxfd_can_tx_queue_move_spi_message(u32 *src, u32 *dest,
> +						    int fifo)
> +{
> +	mcp25xxfd_can_tx_queue_remove_spi_message(src, fifo);
> +	mcp25xxfd_can_tx_queue_add_spi_message(dest, fifo);
> +}
> +
> +static void mcp25xxfd_can_tx_spi_message_fill_fifo_complete(void *context)
> +{
> +	struct mcp2517fd_tx_spi_message *msg = context;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(msg->net);
> +	unsigned long flags;
> +
> +	/* reset transfer length to without data (DLC = 0) */
> +	msg->fill_fifo.xfer.len = sizeof(msg->fill_fifo.data.cmd) +
> +		sizeof(msg->fill_fifo.data.header);
> +
> +	/* we need to hold this lock to protect us from
> +	 * concurrent access by start_xmit
> +	 */
> +	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
> +
> +	/* move to in_trigger_fifo_transfer */
> +	mcp25xxfd_can_tx_queue_move_spi_message(
> +		&cpriv->fifos.tx_queue->in_fill_fifo_transfer,
> +		&cpriv->fifos.tx_queue->in_trigger_fifo_transfer,
> +		msg->fifo);
> +
> +	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
> +}
> +
> +static void mcp25xxfd_can_tx_spi_message_trigger_fifo_complete(void *context)
> +{
> +	struct mcp2517fd_tx_spi_message *msg = context;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(msg->net);
> +	unsigned long flags;
> +
> +	/* we need to hold this lock to protect us from
> +	 * concurrent access by the interrupt thread
> +	 */
> +	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
> +
> +	/* move to can_transfer */
> +	mcp25xxfd_can_tx_queue_move_spi_message(
> +		&cpriv->fifos.tx_queue->in_trigger_fifo_transfer,
> +		&cpriv->fifos.tx_queue->in_can_transfer,
> +		msg->fifo);
> +
> +	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
> +}
> +
> +#if defined(CONFIG_DEBUG_FS)
> +static void mcp25xxfd_can_tx_queue_debugfs(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct dentry *dir = cpriv->fifos.tx.debugfs_dir;
> +	struct mcp2517fd_tx_spi_message_queue *queue = cpriv->fifos.tx_queue;
> +
> +	debugfs_create_u32("netif_queue_state",  0444, dir,
> +			   &cpriv->fifos.tx_queue->state);
> +
> +	/* and for each queue stage the states */
> +	debugfs_create_x32("fifos_idle", 0444, dir,
> +			   &queue->idle);
> +	debugfs_create_x32("fifos_in_fill_fifo_transfer", 0444, dir,
> +			   &queue->in_fill_fifo_transfer);
> +	debugfs_create_x32("fifos_in_trigger_fifo_transfer", 0444, dir,
> +			   &queue->in_trigger_fifo_transfer);
> +	debugfs_create_x32("fifos_in_can_transfer", 0444, dir,
> +			   &queue->in_can_transfer);
> +	debugfs_create_x32("fifos_transferred", 0444, dir,
> +			   &queue->transferred);
> +}
> +#else
> +static void mcp25xxfd_can_tx_queue_debugfs(struct net_device *net)
> +{
> +}
> +#endif
> +
> +static
> +void mcp25xxfd_can_tx_message_init(struct net_device *net,
> +				   struct mcp2517fd_tx_spi_message *msg,
> +				   int fifo)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct mcp25xxfd_priv *priv = cpriv->priv;
> +	const u32 trigger = CAN_FIFOCON_TXREQ | CAN_FIFOCON_UINC;
> +	const int first_byte = mcp25xxfd_first_byte(trigger);
> +	u32 addr;
> +
> +	/* and initialize the structure */
> +	msg->net = net;
> +	msg->fifo = fifo;
> +
> +	/* init fill_fifo */
> +	spi_message_init(&msg->fill_fifo.msg);
> +	msg->fill_fifo.msg.complete =
> +		mcp25xxfd_can_tx_spi_message_fill_fifo_complete;
> +	msg->fill_fifo.msg.context = msg;
> +
> +	msg->fill_fifo.xfer.speed_hz = priv->spi_use_speed_hz;
> +	msg->fill_fifo.xfer.tx_buf = msg->fill_fifo.data.cmd;
> +	msg->fill_fifo.xfer.len = sizeof(msg->fill_fifo.data.cmd) +
> +		sizeof(msg->fill_fifo.data.header);
> +	spi_message_add_tail(&msg->fill_fifo.xfer, &msg->fill_fifo.msg);
> +
> +	addr = MCP25XXFD_SRAM_ADDR(cpriv->fifos.fifo_reg[fifo].offset);
> +	mcp25xxfd_calc_cmd_addr(INSTRUCTION_WRITE,
> +				addr, msg->fill_fifo.data.cmd);
> +
> +	/* init trigger_fifo */
> +	spi_message_init(&msg->trigger_fifo.msg);
> +	msg->trigger_fifo.msg.complete =
> +		mcp25xxfd_can_tx_spi_message_trigger_fifo_complete;
> +	msg->trigger_fifo.msg.context = msg;
> +
> +	msg->trigger_fifo.xfer.speed_hz = priv->spi_use_speed_hz;
> +	msg->trigger_fifo.xfer.tx_buf = msg->trigger_fifo.data.cmd;
> +	msg->trigger_fifo.xfer.len = sizeof(msg->trigger_fifo.data.cmd) +
> +		sizeof(msg->trigger_fifo.data.data);
> +	spi_message_add_tail(&msg->trigger_fifo.xfer, &msg->trigger_fifo.msg);
> +
> +	mcp25xxfd_calc_cmd_addr(INSTRUCTION_WRITE,
> +				CAN_FIFOCON(fifo) + first_byte,
> +				msg->trigger_fifo.data.cmd);
> +	msg->trigger_fifo.data.data = trigger >> (8 * first_byte);
> +
> +	/* and add to idle tx transfers */
> +	mcp25xxfd_can_tx_queue_add_spi_message(&cpriv->fifos.tx_queue->idle,
> +					       fifo);
> +}
> +
> +int mcp25xxfd_can_tx_queue_alloc(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	size_t size = sizeof(struct mcp2517fd_tx_spi_message_queue) +
> +		cpriv->fifos.tx.count *
> +		sizeof(struct mcp2517fd_tx_spi_message);
> +	struct mcp2517fd_tx_spi_message *msg;
> +	int i, fifo;
> +
> +	/* allocate the fifos as an array */
> +	cpriv->fifos.tx_queue = kzalloc(size, GFP_KERNEL);
> +	if (!cpriv->fifos.tx_queue)
> +		return -ENOMEM;
> +
> +	/* initialize the tx_queue structure */
> +	spin_lock_init(&cpriv->fifos.tx_queue->lock);
> +	spin_lock_init(&cpriv->fifos.tx_queue->spi_lock);
> +
> +	/* initialize the individual spi_message structures */
> +	for (i = 0, fifo = cpriv->fifos.tx.start;
> +	     i < cpriv->fifos.tx.count;
> +	     i++, fifo += cpriv->fifos.tx.increment) {
> +		msg = &cpriv->fifos.tx_queue->message[i];
> +		cpriv->fifos.tx_queue->fifo2message[fifo] = msg;
> +		mcp25xxfd_can_tx_message_init(net, msg, fifo);
> +	}
> +
> +	mcp25xxfd_can_tx_queue_debugfs(net);
> +
> +	return 0;
> +}
> +
> +void mcp25xxfd_can_tx_queue_free(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +
> +	/* eventually we may need to wait here
> +	 * for all transfers to have finished
> +	 */
> +
> +	kfree(cpriv->fifos.tx_queue);
> +	cpriv->fifos.tx_queue = NULL;
> +}
> +
> +void mcp25xxfd_can_tx_queue_manage_nolock(struct net_device *net, int state)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +
> +	/* skip early */
> +	if (state == cpriv->fifos.tx_queue->state)
> +		return;
> +
> +	/* start/stop netif_queue if necessary */
> +	switch (cpriv->fifos.tx_queue->state) {
> +	case TX_QUEUE_STATE_RUNABLE:
> +		switch (state) {
> +		case TX_QUEUE_STATE_RESTART:
> +		case TX_QUEUE_STATE_STARTED:
> +			netif_wake_queue(net);
> +			cpriv->fifos.tx_queue->state =
> +				TX_QUEUE_STATE_STARTED;
> +			break;
> +		}
> +		break;
> +	case TX_QUEUE_STATE_STOPPED:
> +		switch (state) {
> +		case TX_QUEUE_STATE_STARTED:
> +			netif_wake_queue(net);
> +			cpriv->fifos.tx_queue->state = state;
> +			break;
> +		}
> +		break;
> +	case TX_QUEUE_STATE_STARTED:
> +		switch (state) {
> +		case TX_QUEUE_STATE_RUNABLE:
> +		case TX_QUEUE_STATE_STOPPED:
> +			netif_stop_queue(net);
> +			cpriv->fifos.tx_queue->state = state;
> +			break;
> +		}
> +		break;
> +	default:
> +		WARN(true, "Unsupported tx_queue state: %i\n",
> +		     cpriv->fifos.tx_queue->state);
> +		break;
> +	}
> +}
> +
> +void mcp25xxfd_can_tx_queue_manage(struct net_device *net, int state)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
> +
> +	mcp25xxfd_can_tx_queue_manage_nolock(net, state);
> +
> +	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
> +}
> +
> +void mcp25xxfd_can_tx_queue_restart(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	unsigned long flags;
> +	u32 mask;
> +
> +	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
> +
> +	/* only move if there is nothing pending or idle */
> +	mask = cpriv->fifos.tx_queue->idle |
> +		cpriv->fifos.tx_queue->in_fill_fifo_transfer |
> +		cpriv->fifos.tx_queue->in_trigger_fifo_transfer |
> +		cpriv->fifos.tx_queue->in_can_transfer;
> +	if (mask)
> +		goto out;
> +
> +	/* move all items from transferred to idle */
> +	cpriv->fifos.tx_queue->idle |= cpriv->fifos.tx_queue->transferred;
> +	cpriv->fifos.tx_queue->transferred = 0;
> +
> +	/* and enable queue */
> +	mcp25xxfd_can_tx_queue_manage_nolock(net, TX_QUEUE_STATE_RESTART);
> +out:
> +	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
> +}
> +
> +static int mcp25xxfd_can_int_handle_txatif_fifo(struct spi_device *spi,
> +						int fifo)

The prefix here should be "mcp25xxfd_can_tx". Otherwise I would expect
that function in "mcp25xxfd_can_int.c".


> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	u32 val;
> +	unsigned long flags;
> +	int ret;
> +
> +	/* read fifo status */
> +	ret = mcp25xxfd_cmd_read(spi, CAN_FIFOSTA(fifo), &val);
> +	if (ret)
> +		return ret;
> +
> +	/* clear the relevant interrupt flags */
> +	ret = mcp25xxfd_cmd_write_mask(spi,
> +				       CAN_FIFOSTA(fifo),
> +				       0,
> +				       CAN_FIFOSTA_TXABT |
> +				       CAN_FIFOSTA_TXLARB |
> +				       CAN_FIFOSTA_TXERR |
> +				       CAN_FIFOSTA_TXATIF);
> +	if (ret)
> +		return ret;
> +
> +	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
> +	/* for specific cases we probably could trigger a retransmit
> +	 * instead of an abort.
> +	 */
> +
> +	/* and we release it from the echo_skb buffer
> +	 * NOTE: this is one place where packet delivery will not
> +	 * be ordered, as we do not have any timing information
> +	 * when this occurred
> +	 */
> +	can_get_echo_skb(net, fifo);
> +
> +	mcp25xxfd_can_tx_queue_move_spi_message(
> +		&cpriv->fifos.tx_queue->in_can_transfer,
> +		&cpriv->fifos.tx_queue->transferred,
> +		fifo);
> +
> +	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
> +
> +	/* but we need to run a bit of cleanup */
> +	cpriv->status.txif &= ~BIT(fifo);
> +	net->stats.tx_aborted_errors++;
> +
> +	/* handle all the known cases accordingly - ignoring FIFO full */
> +	val &= CAN_FIFOSTA_TXABT |
> +		CAN_FIFOSTA_TXLARB |
> +		CAN_FIFOSTA_TXERR;
> +	switch (val) {
> +	case CAN_FIFOSTA_TXERR:
> +		/* this indicates a possible bus error */
> +		break;
> +	default:
> +		dev_warn_ratelimited(&spi->dev,
> +				     "Unknown TX-Fifo abort condition: %08x - stopping tx-queue\n",
> +				     val);
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +int mcp25xxfd_can_int_handle_txatif(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	int i, fifo;
> +	int ret;
> +
> +	/* if txatif is unset, then there are no
> +	 * can frames that have been transmitted
> +	 * and need to get reingested into the network stack
> +	 */
> +	if (!cpriv->status.txatif)
> +		return 0;
> +	cpriv->stats.int_txat_count++;
> +
> +	/* process all the fifos with that flag set */
> +	for (i = 0, fifo = cpriv->fifos.tx.start;
> +	     i < cpriv->fifos.tx.count;
> +	     i++, fifo += cpriv->fifos.tx.increment) {
> +		if (cpriv->status.txatif & BIT(fifo)) {
> +			ret = mcp25xxfd_can_int_handle_txatif_fifo(spi,
> +								   fifo);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/* submit the fifo back to the network stack */
> +int mcp25xxfd_can_submit_tx_frame(struct spi_device *spi, int fifo)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct mcp25xxfd_obj_tx *tx = (struct mcp25xxfd_obj_tx *)
> +		(cpriv->sram + cpriv->fifos.fifo_reg[fifo].offset);
> +	int dlc = (tx->flags & CAN_OBJ_FLAGS_DLC_MASK) >>
> +		CAN_OBJ_FLAGS_DLC_SHIFT;
> +	unsigned long flags;
> +
> +	/* update counters */
> +	net->stats.tx_packets++;
> +	cpriv->fifos.tx.dlc_usage[dlc]++;
> +	priv->net->stats.tx_bytes += can_dlc2len(dlc);
> +	if (tx->flags & CAN_OBJ_FLAGS_FDF)
> +		cpriv->stats.tx_fd_count++;
> +	if (tx->flags & CAN_OBJ_FLAGS_BRS)
> +		cpriv->stats.tx_brs_count++;
> +
> +	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
> +
> +	/* release the echo buffer */
> +	can_get_echo_skb(priv->net, fifo);
> +
> +	/* move from in_can_transfer to transferred */
> +	mcp25xxfd_can_tx_queue_move_spi_message(
> +		&cpriv->fifos.tx_queue->in_can_transfer,
> +		&cpriv->fifos.tx_queue->transferred,
> +		fifo);
> +
> +	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
> +
> +	return 0;
> +}
> +
> +static int mcp25xxfd_can_int_handle_tefif_fifo(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct mcp25xxfd_obj_tef *tef;
> +	u32 tef_offset = cpriv->fifos.tef.index * cpriv->fifos.tef.size;
> +	int fifo, ret;
> +
> +	/* read the next TEF entry to get the transmit timestamp and fifo */
> +	tef = (struct mcp25xxfd_obj_tef *)(cpriv->sram + tef_offset);
> +	ret = mcp25xxfd_cmd_read_regs(spi, MCP25XXFD_SRAM_ADDR(tef_offset),
> +				      &tef->id, sizeof(*tef));
> +	if (ret)
> +		return ret;
> +
> +	/* now we can schedule the fifo for echo submission */
> +	fifo = (tef->flags & CAN_OBJ_FLAGS_SEQ_MASK) >>
> +		CAN_OBJ_FLAGS_SEQ_SHIFT;
> +	mcp25xxfd_can_queue_frame(cpriv, -fifo, tef->ts);
> +
> +	/* increment the tef index with wraparround */
> +	cpriv->fifos.tef.index++;
> +	if (cpriv->fifos.tef.index >= cpriv->fifos.tef.count)
> +		cpriv->fifos.tef.index = 0;
> +
> +	/* finally just increment the TEF pointer */
> +	return mcp25xxfd_cmd_write_mask(spi, CAN_TEFCON,
> +				 CAN_TEFCON_UINC,
> +				 CAN_TEFCON_UINC);
> +}
> +
> +static
> +int mcp25xxfd_can_int_handle_tefif_conservative(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	u32 tefsta;
> +	int ret;
> +
> +	netdev_warn(net,
> +		    "Something is wrong - we got a TEF interrupt but we were not able to detect a finished fifo\n");
> +
> +	/* we can assume that there is at least one,
> +	 * so fake the first read of TEFSTA
> +	 */
> +	tefsta = CAN_TEFSTA_TEFNEIF;
> +
> +	/* read the tef in an inefficient loop */
> +	while (tefsta & CAN_TEFSTA_TEFNEIF) {
> +		/* read one tef */
> +		ret = mcp25xxfd_can_int_handle_tefif_fifo(spi);
> +		if (ret)
> +			return ret;
> +
> +		/* read the TEF status again*/
> +		ret = mcp25xxfd_cmd_read_mask(spi, CAN_TEFSTA,
> +					      &tefsta, CAN_TEFSTA_TEFNEIF);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static
> +int mcp25xxfd_can_int_handle_tefif_oportunistic(struct spi_device *spi,
> +						u32 finished)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	int i, fifo, ret;
> +
> +	/* now iterate those */
> +	for (i = 0, fifo = cpriv->fifos.tx.start;
> +	     i < cpriv->fifos.tx.count;
> +	     i++, fifo += cpriv->fifos.tx.increment) {
> +		if (finished & BIT(fifo)) {
> +			ret = mcp25xxfd_can_int_handle_tefif_fifo(spi);
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +int mcp25xxfd_can_int_handle_tefif(struct spi_device *spi)
> +{
> +	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
> +	struct net_device *net = priv->net;
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	unsigned long flags;
> +	u32 finished;
> +
> +	if (!(cpriv->status.intf & CAN_INT_TEFIF))
> +		return 0;
> +	cpriv->stats.int_tef_count++;
> +
> +	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
> +
> +	/* compute finished fifos and clear them immediately */
> +	finished = (cpriv->fifos.tx_queue->in_can_transfer ^
> +		    cpriv->status.txreq) &
> +		cpriv->fifos.tx_queue->in_can_transfer;
> +
> +	cpriv->fifos.tx_queue->in_can_transfer &= ~finished;
> +	cpriv->fifos.tx_queue->transferred |= finished;
> +
> +	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
> +
> +	/* in case of a strange situation run in safe mode */
> +	if (!finished)
> +		return mcp25xxfd_can_int_handle_tefif_conservative(spi);
> +
> +	/* otherwise run in oportunistic mode */
> +	return mcp25xxfd_can_int_handle_tefif_oportunistic(spi, finished);
> +}
> +
> +static
> +void mcp25xxfd_can_tx_fill_fifo_common(struct net_device *net,
> +				       struct mcp2517fd_tx_spi_message *smsg,
> +				       struct mcp25xxfd_obj_tx *tx,
> +				       int dlc, u8 *data)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	int len = can_dlc2len(dlc);
> +
> +	/* update statistics */
> +	cpriv->fifos.tx.dlc_usage[dlc]++;
> +	cpriv->fifos.fifo_info[smsg->fifo].use_count++;
> +
> +	/* add fifo number as seq */
> +	tx->flags |= smsg->fifo << CAN_OBJ_FLAGS_SEQ_SHIFT;
> +
> +	/* copy data to tx->data for future reference */
> +	memcpy(tx->data, data, len);
> +
> +	/* transform header to controller format */
> +	mcp25xxfd_convert_from_cpu(&tx->id, sizeof(*tx) / sizeof(u32));
> +
> +	/* copy header + data to final location - we are not aligned */
> +	memcpy(smsg->fill_fifo.data.header, &tx->id, sizeof(*tx) + len);
> +
> +	/* convert it back to CPU format */
> +	mcp25xxfd_convert_to_cpu(&tx->id, sizeof(*tx) / sizeof(u32));
> +
> +	/* set up size of transfer */
> +	smsg->fill_fifo.xfer.len = sizeof(smsg->fill_fifo.data.cmd) +
> +		sizeof(smsg->fill_fifo.data.header) + len;
> +}
> +
> +static
> +void mcp25xxfd_can_tx_fill_fifo_fd(struct net_device *net,
> +				   struct canfd_frame *frame,
> +				   struct mcp2517fd_tx_spi_message *smsg,
> +				   struct mcp25xxfd_obj_tx *tx)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	int dlc = can_len2dlc(frame->len);
> +
> +	/* update some statistics */
> +	cpriv->stats.tx_fd_count++;
> +
> +	/* compute can id */
> +	mcp25xxfd_canid_to_mcpid(frame->can_id, &tx->id, &tx->flags);
> +
> +	/* setup flags */
> +	tx->flags |= dlc << CAN_OBJ_FLAGS_DLC_SHIFT;
> +	tx->flags |= (frame->can_id & CAN_EFF_FLAG) ? CAN_OBJ_FLAGS_IDE : 0;
> +	tx->flags |= (frame->can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
> +	if (frame->flags & CANFD_BRS) {
> +		tx->flags |= CAN_OBJ_FLAGS_BRS;
> +		cpriv->stats.tx_brs_count++;
> +	}
> +	tx->flags |= (frame->flags & CANFD_ESI) ? CAN_OBJ_FLAGS_ESI : 0;
> +	tx->flags |= CAN_OBJ_FLAGS_FDF;
> +
> +	/* and do common processing */
> +	mcp25xxfd_can_tx_fill_fifo_common(net, smsg, tx,
> +					  dlc, frame->data);
> +}
> +
> +static
> +void mcp25xxfd_can_tx_fill_fifo(struct net_device *net,
> +				struct can_frame *frame,
> +				struct mcp2517fd_tx_spi_message *smsg,
> +				struct mcp25xxfd_obj_tx *tx)
> +{
> +	/* set frame to valid dlc */
> +	if (frame->can_dlc > 8)
> +		frame->can_dlc = 8;
> +
> +	/* compute can id */
> +	mcp25xxfd_canid_to_mcpid(frame->can_id, &tx->id, &tx->flags);
> +
> +	/* setup flags */
> +	tx->flags |= frame->can_dlc << CAN_OBJ_FLAGS_DLC_SHIFT;
> +	tx->flags |= (frame->can_id & CAN_EFF_FLAG) ? CAN_OBJ_FLAGS_IDE : 0;
> +	tx->flags |= (frame->can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
> +
> +	/* and do common processing */
> +	mcp25xxfd_can_tx_fill_fifo_common(net, smsg, tx,
> +					  frame->can_dlc, frame->data);
> +}
> +
> +static struct mcp2517fd_tx_spi_message
> +*mcp25xxfd_can_tx_queue_get_next_fifo(struct net_device *net)
> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct mcp2517fd_tx_spi_message *smsg;
> +	unsigned long flags;
> +
> +	/* we need to hold this lock to protect us against
> +	 * concurrent modifications of cpriv->fifos.tx_queue->idle
> +	 * in the interrupt thread
> +	 */
> +	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
> +
> +	/* get the first entry from idle */
> +	if (cpriv->fifos.tx.increment > 0)
> +		smsg = mcp25xxfd_can_tx_queue_first_spi_message(
> +			cpriv->fifos.tx_queue, &cpriv->fifos.tx_queue->idle);
> +	else
> +		smsg = mcp25xxfd_can_tx_queue_last_spi_message(
> +			cpriv->fifos.tx_queue, &cpriv->fifos.tx_queue->idle);
> +	if (!smsg)
> +		goto out_busy;
> +
> +	/* and move the fifo to next stage */
> +	mcp25xxfd_can_tx_queue_move_spi_message(
> +		&cpriv->fifos.tx_queue->idle,
> +		&cpriv->fifos.tx_queue->in_fill_fifo_transfer,
> +		smsg->fifo);
> +
> +	/* if queue is empty then stop the network queue immediately */
> +	if (!cpriv->fifos.tx_queue->idle)
> +		mcp25xxfd_can_tx_queue_manage_nolock(net,
> +						     TX_QUEUE_STATE_RUNABLE);
> +out_busy:
> +	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
> +
> +	return smsg;
> +}
> +
> +/* submit the can message to the can-bus */
> +netdev_tx_t mcp25xxfd_can_start_xmit(struct sk_buff *skb,
> +				     struct net_device *net)

A better place for that function is probably "mcp25xxfd_can.c", together
with all other net ops.

> +{
> +	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
> +	struct mcp25xxfd_priv *priv = cpriv->priv;
> +	struct spi_device *spi = priv->spi;
> +	struct mcp2517fd_tx_spi_message *smsg;
> +	struct mcp25xxfd_obj_tx *tx;
> +	int addr;
> +	unsigned long flags;
> +	int ret;
> +
> +	/* invalid skb we can ignore */
> +	if (can_dropped_invalid_skb(net, skb))
> +		return NETDEV_TX_OK;
> +
> +	/* acquire lock on spi so that we are are not risking
> +	 * some reordering of spi messages when we are running
> +	 * start_xmit in multiple threads (on multiple cores)
> +	 */
> +	spin_lock_irqsave(&cpriv->fifos.tx_queue->spi_lock, flags);
> +
> +	/* get the fifo message structure to process now */
> +	smsg = mcp25xxfd_can_tx_queue_get_next_fifo(net);
> +	if (!smsg)
> +		goto out_busy;
> +
> +	/* compute the fifo in sram */
> +	addr = cpriv->fifos.fifo_reg[smsg->fifo].offset;
> +	tx = (struct mcp25xxfd_obj_tx *)(cpriv->sram + addr);
> +
> +	/* fill in message from skb->data depending on can2.0 or canfd */
> +	if (can_is_canfd_skb(skb))
> +		mcp25xxfd_can_tx_fill_fifo_fd(net,
> +					      (struct canfd_frame *)skb->data,
> +					      smsg, tx);
> +	else
> +		mcp25xxfd_can_tx_fill_fifo(net,
> +					   (struct can_frame *)skb->data,
> +					   smsg, tx);
> +
> +	/* submit the two messages asyncronously
> +	 * the reason why we separate transfers into two spi_messages is:
> +	 *  * because the spi framework (currently) does add a 10us delay
> +	 *    between 2 spi_transfers in a single spi_message when
> +	 *    change_cs is set - 2 consecutive spi messages show a shorter
> +	 *     cs disable phase increasing bus utilization
> +	 *  * this allows the interrupt handler to start spi messages earlier
> +	 *    so reducing latencies a bit and to allow for better concurrency
> +	 */
> +	ret = spi_async(spi, &smsg->fill_fifo.msg);
> +	if (ret)
> +		goto out_async_failed;
> +	ret = spi_async(spi, &smsg->trigger_fifo.msg);
> +	if (ret)
> +		goto out_async_failed;
> +
> +	/* unlock the spi bus */
> +	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->spi_lock, flags);
> +
> +	/* keep it for reference until the message really got transmitted */
> +	can_put_echo_skb(skb, net, smsg->fifo);
> +
> +	return NETDEV_TX_OK;
> +out_async_failed:
> +	netdev_err(net, "spi_async submission of fifo %i failed - %i\n",
> +		   smsg->fifo, ret);
> +
> +out_busy:
> +	/* stop the queue */
> +	mcp25xxfd_can_tx_queue_manage_nolock(net, TX_QUEUE_STATE_STOPPED);
> +
> +	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->spi_lock, flags);
> +
> +	return NETDEV_TX_BUSY;
> +}
> --
> 2.11.0
> 

I did not review all of the code yet. I think you should fix your
modularization first, especially the structures used for the interface
functions.

Furthermore I find that the name "priv" and "cpriv" are not really
meaningful and sometimes even confusing. Maybe "base/core" and "can"
would make the code more readable. What do you think?

Wolfgang.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH V5 2/4] can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver basics
  2019-01-21 18:29     ` Martin Sperl
@ 2019-01-21 19:25       ` Wolfgang Grandegger
  0 siblings, 0 replies; 12+ messages in thread
From: Wolfgang Grandegger @ 2019-01-21 19:25 UTC (permalink / raw)
  To: Martin Sperl
  Cc: linux-can, devicetree, Mark Kleine-Budde, Rob Herring, Mark Rutland

Hello Martin,

Am 21.01.19 um 19:29 schrieb Martin Sperl:
> Hi Wolfgang,
> 
> Thanks for the review!
> I will add your feedback to v6 (which is already in version v6.7 fixing
> issues and more).
> 
> 
> On 21.01.2019, at 13:31, Wolfgang Grandegger <wg@grandegger.com
> <mailto:wg@grandegger.com>> wrote:
> 
>>>
>>
>> About file names and modularization: A "mcp25xxfd_xxx.c" should also have
>> a header file "mcp25xxfd_xxx.h" defining the interface and use the
>> prefix "mcp25xxfd_xxx". Therefore, "s/mcp25xxfd_base/mcp25xxfd" would
>> make more sense.
>>
> 
> Base came into play to say it is minimal and I did not figure
> out a way to get it working correctly without -base because
> Of some conflicts with generated Module code if I 
> remember correctly.

mcp25xxfd_base.c is fine but then you should also use

- mcp25xxfd_base.h
- struct mcp25xxfd_base_[priv/info]
- function prefix "mcp25xxfd_base_".

This would make clear, to what file/interface the functions belong to.

>> Also, could you please put just the register definitions in
>> "mcp25xxfd_reg.h".
>>
> I just wanted to keep the included minimal hence the 2 files
> that reflect the separation in the controller: mcp25xx (spi, clock, gpio)
> specific and Can specific

Well, I don't thinks it makes sense to separate the register definitions
into CAN and non-CAN related registers. You have defined all register
and bits... even more than really needed. Therefore I prefer to have it
in a separate file to clearly separate it from the interface related
definitions.

> 
>>>
>>> +static inline void mcp25xxfd_convert_to_cpu(u32 *data, int n)
>>> +{
>>> +    int i;
>>> +
>>> +    for (i = 0; i < n; i++)
>>> +        data[i] = le32_to_cpu(data[i]);
>>
>> Is this optimized away if the endianess is the same?
> 
> Compiler should handle this automatically (also moved into 
> the header In v6)
> 
>>>
>>> +static int mcp25xxfd_first_byte(u32 mask)
>>> +{
>>> +    return (mask & 0x0000ffff) ?
>>> +        ((mask & 0x000000ff) ? 0 : 1) :
>>> +        ((mask & 0x00ff0000) ? 2 : 3);
>>> +}
>>
>> inline?
> 
> Inline not needed - compiler will in-line automatically when sufficient.
> This has moved to headers in v6 (where inline is then needed)

It was already set "inline" in patch 4/4.

>>>
>>> +static void mcp25xxfd_debugfs_remove(struct mcp25xxfd_priv *priv)
>>> +{
>>> +}
>>> +#endif
>>
>> And could you please put all debugfs stuff into "mcp25xxfd_debugfs.[ch]"
>> and define macros to increment the statistics if CONFIG_DEBUG_FS is
>> defined.
> 
> Some of the structures are only defined in the specific c file and
>  this would require now moving the structure into the header files
> 
> Also I thought that this way it was contained in a location related
> to where it is used.

The debugfs part is also a lot of code. Therefore my motivation is to
have it apart. And the statistics should only be incremented if
CONFIG_DEBUG_FS is "y". Therefore you need a macro for that purpose.

> 
>>>
>>> +    struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
>>> +    u32 value   = _mcp25xxfd_clkout_mask(spi);
>>
>> Just one space before and after "=", please! Here and in other places.
> Strange that chckpatch did not catch that...

I think checkpatch does not catch that issue.

> 
>>
>>> +#define CAN_SFR_BASE(x)            (0x000 + (x))
>>> +#define CAN_CON                CAN_SFR_BASE(0x00)
>>
>> The prefix "CAN_" is not generic enough. Name clashes, e.g. with
>> "CAN_INT" are not that unlikely. Why not using "MCP25XXFD_" instead?
>> And could you please mov the register definitions into "mcp25xxfd_reg.h".
> 
> Similar to above: can are can specific registers mcp2515 are ecc
> Clock and gpio specific registers.

But then the prefix should be "MCP25XXFD_CAN_". Well, these are
registers of the MCP2517FD and prefix "MCP25XXFD_<register-name>_" is
just fine.

> 
> Also adding mcp25xxfd makes it longer and that incurs more
> line breaks in code to fulfill the 80char rule...

The prefix "CAN_" is reserved for the CAN network and driver interface.
Especially "CAN_INT", etc. is not OK.

>>>
>>> +#  define CAN_CON_DEFAULT                \
>>> +    (CAN_CON_ISOCRCEN |                \
>>> +     CAN_CON_PXEDIS |                \
>>> +     CAN_CON_WAKFIL |                \
>>> +     (3 << CAN_CON_WFT_SHIFT) |            \
>>> +     CAN_CON_STEF |                    \
>>> +     CAN_CON_TXQEN |                \
>>> +     (CAN_CON_MODE_CONFIG << CAN_CON_OPMOD_SHIFT) |    \
>>> +     (CAN_CON_MODE_CONFIG << CAN_CON_REQOP_SHIFT))
>>
>> Could you please align to the right, here and below.
> 
> Checkpatch would complain then if I understand your 
> request correctly.

I cannot confirm that. I will complain if the line exceeds 80
chars... or what do you mean.

Wolfgang.

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2019-01-21 19:25 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-21  9:29 [PATCH V5 0/4] Microchip mcp25xxfd can controller driver kernel
2018-12-21  9:29 ` [PATCH V5 1/4] dt-binding: can: mcp25xxfd: document device tree bindings kernel
2018-12-21  9:29 ` [PATCH V5 2/4] can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver basics kernel
2019-01-21 12:31   ` Wolfgang Grandegger
2019-01-21 18:29     ` Martin Sperl
2019-01-21 19:25       ` Wolfgang Grandegger
2018-12-21  9:29 ` [PATCH V5 3/4] can: mcp25xxfd: add gpiolib support for GPIO0/1 (aka. INT0/INT1) kernel
2018-12-21  9:29 ` [PATCH V5 4/4] can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver kernel
2019-01-21 18:32   ` Wolfgang Grandegger
2019-01-10  8:30 ` [PATCH V5 0/4] Microchip mcp25xxfd can controller driver Wolfgang Grandegger
2019-01-10 16:37   ` kernel
2019-01-11  8:21     ` Wolfgang Grandegger

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.