linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
@ 2020-06-10  7:44 Manivannan Sadhasivam
  2020-06-10  7:44 ` [PATCH 1/6] dt-bindings: can: Document devicetree bindings for MCP25XXFD Manivannan Sadhasivam
                   ` (6 more replies)
  0 siblings, 7 replies; 26+ messages in thread
From: Manivannan Sadhasivam @ 2020-06-10  7:44 UTC (permalink / raw)
  To: wg, mkl; +Cc: kernel, linux-can, netdev, linux-kernel, Manivannan Sadhasivam

Hello,

This series adds CAN network driver support for Microchip MCP25XXFD CAN
Controller with MCP2517FD as the target controller version. This series is
mostly inspired (or taken) from the previous iterations posted by Martin Sperl.
I've trimmed down the parts which are not necessary for the initial version
to ease review. Still the series is relatively huge but I hope to get some
reviews (post -rcX ofc!).

Link to the origial series posted by Martin:
https://www.spinics.net/lists/devicetree/msg284462.html

I've not changed the functionality much but done some considerable amount of
cleanups and also preserved the authorship of Martin for all the patches he has
posted earlier. This series has been tested on 96Boards RB3 platform by myself
and Martin has tested the previous version on Rpi3 with external MCP2517FD
controller.

Thanks,
Mani

Manivannan Sadhasivam (1):
  MAINTAINERS: Add entry for Microchip MCP25XXFD CAN network driver

Martin Sperl (5):
  dt-bindings: can: Document devicetree bindings for MCP25XXFD
  can: mcp25xxfd: Add Microchip MCP25XXFD CAN-FD driver infrastructure
  can: mcp25xxfd: Add support for CAN reception
  can: mcp25xxfd: Add CAN transmission support
  can: mcp25xxfd: Optimize TEF read by avoiding unnecessary SPI
    transfers

 .../bindings/net/can/microchip,mcp25xxfd.yaml |  82 +++
 MAINTAINERS                                   |   8 +
 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 +
 .../net/can/spi/mcp25xxfd/mcp25xxfd_base.c    | 177 +++++
 .../net/can/spi/mcp25xxfd/mcp25xxfd_base.h    |  14 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c | 538 ++++++++++++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h |  52 ++
 .../can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c    | 305 ++++++++
 .../can/spi/mcp25xxfd/mcp25xxfd_can_fifo.h    |  16 +
 .../net/can/spi/mcp25xxfd/mcp25xxfd_can_id.h  |  69 ++
 .../net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c | 674 ++++++++++++++++++
 .../net/can/spi/mcp25xxfd/mcp25xxfd_can_int.h |  18 +
 .../can/spi/mcp25xxfd/mcp25xxfd_can_priv.h    | 144 ++++
 .../net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c  | 233 ++++++
 .../net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.h  |  18 +
 .../net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c  | 653 +++++++++++++++++
 .../net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.h  |  86 +++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.c | 226 ++++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.h |  84 +++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.c |  31 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.h |  15 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.c |  74 ++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.h |  16 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c |  71 ++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.h |  15 +
 .../net/can/spi/mcp25xxfd/mcp25xxfd_priv.h    |  50 ++
 .../net/can/spi/mcp25xxfd/mcp25xxfd_regs.h    | 661 +++++++++++++++++
 30 files changed, 4350 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.yaml
 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_base.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.h
 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_fifo.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_id.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h

-- 
2.17.1


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

* [PATCH 1/6] dt-bindings: can: Document devicetree bindings for MCP25XXFD
  2020-06-10  7:44 [PATCH 0/6] Add Microchip MCP25XXFD CAN driver Manivannan Sadhasivam
@ 2020-06-10  7:44 ` Manivannan Sadhasivam
  2020-06-10  7:44 ` [PATCH 2/6] can: mcp25xxfd: Add Microchip MCP25XXFD CAN-FD driver infrastructure Manivannan Sadhasivam
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 26+ messages in thread
From: Manivannan Sadhasivam @ 2020-06-10  7:44 UTC (permalink / raw)
  To: wg, mkl; +Cc: kernel, linux-can, netdev, linux-kernel, Manivannan Sadhasivam

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

Add devicetree YAML bindings for Microchip MCP25XXFD CAN controller.

Signed-off-by: Martin Sperl <kernel@martin.sperl.org>
[mani: converted to YAML binding]
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
 .../bindings/net/can/microchip,mcp25xxfd.yaml | 82 +++++++++++++++++++
 1 file changed, 82 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.yaml

diff --git a/Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.yaml b/Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.yaml
new file mode 100644
index 000000000000..7b87ec328515
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.yaml
@@ -0,0 +1,82 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/can/microchip,mcp25xxfd.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Microchip MCP25XXFD stand-alone CAN controller binding
+
+maintainers:
+  -  Martin Sperl <kernel@martin.sperl.org>
+  -  Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+
+properties:
+  compatible:
+    const: microchip,mcp2517fd
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  gpio-controller: true
+
+  "#gpio-cells":
+    const: 2
+
+  vdd-supply:
+    description: Regulator that powers the CAN controller
+
+  xceiver-supply:
+    description: Regulator that powers the CAN transceiver
+
+  microchip,clock-out-div:
+    description: Clock output pin divider
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+    enum: [0, 1, 2, 4, 10]
+    default: 10
+
+  microchip,clock-div2:
+    description: Divide the internal clock by 2
+    type: boolean
+
+  microchip,gpio-open-drain:
+    description: Enable open-drain for all pins
+    type: boolean
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - interrupts
+  - gpio-controller
+  - vdd-supply
+  - xceiver-supply
+
+additionalProperties: false
+
+examples:
+  - |
+    spi {
+           #address-cells = <1>;
+           #size-cells = <0>;
+
+           can0: can@1 {
+                   compatible = "microchip,mcp2517fd";
+                   reg = <1>;
+                   clocks = <&clk24m>;
+                   interrupt-parent = <&gpio4>;
+                   interrupts = <13 0x8>;
+                   vdd-supply = <&reg5v0>;
+                   xceiver-supply = <&reg5v0>;
+                   gpio-controller;
+                   #gpio-cells = <2>;
+           };
+    };
+
+...
-- 
2.17.1


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

* [PATCH 2/6] can: mcp25xxfd: Add Microchip MCP25XXFD CAN-FD driver infrastructure
  2020-06-10  7:44 [PATCH 0/6] Add Microchip MCP25XXFD CAN driver Manivannan Sadhasivam
  2020-06-10  7:44 ` [PATCH 1/6] dt-bindings: can: Document devicetree bindings for MCP25XXFD Manivannan Sadhasivam
@ 2020-06-10  7:44 ` Manivannan Sadhasivam
  2020-06-10  7:44 ` [PATCH 3/6] can: mcp25xxfd: Add support for CAN reception Manivannan Sadhasivam
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 26+ messages in thread
From: Manivannan Sadhasivam @ 2020-06-10  7:44 UTC (permalink / raw)
  To: wg, mkl; +Cc: kernel, linux-can, netdev, linux-kernel, Manivannan Sadhasivam

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

This commit adds basic driver support for the Microchip MCP25XXFD
CAN-FD controller series. This driver currently supports MCP2517FD as the
target 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 commit provides basic driver functionality such as setting up clocks,
regulators and the infrastructure for the CAN.

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>
[mani: trimmed the gpio part and done some cleanups]
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
 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        |   7 +
 .../net/can/spi/mcp25xxfd/mcp25xxfd_base.c    | 171 +++++
 .../net/can/spi/mcp25xxfd/mcp25xxfd_base.h    |  14 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c | 141 ++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h |  16 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.c | 226 ++++++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.h |  84 +++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.c |  31 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.h |  15 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.c |  74 ++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.h |  16 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c |  59 ++
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.h |  15 +
 .../net/can/spi/mcp25xxfd/mcp25xxfd_priv.h    |  48 ++
 .../net/can/spi/mcp25xxfd/mcp25xxfd_regs.h    | 656 ++++++++++++++++++
 18 files changed, 1582 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_base.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.h
 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_cmd.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h

diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig
index 1c50788055cb..d4b68eb5d386 100644
--- a/drivers/net/can/spi/Kconfig
+++ b/drivers/net/can/spi/Kconfig
@@ -15,4 +15,6 @@ config CAN_MCP251X
 	  Driver for the Microchip MCP251x and MCP25625 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 f115b2c46623..f56514541ce4 100644
--- a/drivers/net/can/spi/Makefile
+++ b/drivers/net/can/spi/Makefile
@@ -6,3 +6,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..d8fdb76a9578
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/Makefile
@@ -0,0 +1,7 @@
+obj-$(CONFIG_CAN_MCP25XXFD)	+= mcp25xxfd.o
+mcp25xxfd-objs                  := mcp25xxfd_base.o
+mcp25xxfd-objs                  += mcp25xxfd_can.o
+mcp25xxfd-objs                  += mcp25xxfd_cmd.o
+mcp25xxfd-objs                  += mcp25xxfd_crc.o
+mcp25xxfd-objs                  += mcp25xxfd_ecc.o
+mcp25xxfd-objs                  += mcp25xxfd_int.o
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..4be456df0998
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "mcp25xxfd_base.h"
+#include "mcp25xxfd_can.h"
+#include "mcp25xxfd_cmd.h"
+#include "mcp25xxfd_ecc.h"
+#include "mcp25xxfd_int.h"
+#include "mcp25xxfd_priv.h"
+
+int mcp25xxfd_base_power_enable(struct regulator *reg, int enable)
+{
+	if (enable)
+		return regulator_enable(reg);
+	else
+		return regulator_disable(reg);
+}
+
+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_base_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;
+	enum mcp25xxfd_model model;
+	u32 freq;
+	int ret;
+
+	/* Check whether valid IRQ line is defined or not */
+	if (spi->irq <= 0) {
+		dev_err(&spi->dev, "no valid irq line defined: irq = %i\n",
+			spi->irq);
+		return -EINVAL;
+	}
+
+	priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	spi_set_drvdata(spi, priv);
+	priv->spi = spi;
+
+	/* Assign device name */
+	snprintf(priv->device_name, sizeof(priv->device_name),
+		 DEVICE_NAME "-%s", dev_name(&priv->spi->dev));
+
+	/* assign model from of or driver_data */
+	if (of_id)
+		model = (enum mcp25xxfd_model)of_id->data;
+	else
+		model = spi_get_device_id(spi)->driver_data;
+
+	clk = devm_clk_get(&spi->dev, NULL);
+	if (IS_ERR(clk)) {
+		ret = PTR_ERR(clk);
+		goto out_free;
+	}
+
+	freq = clk_get_rate(clk);
+	if (!(freq == CLOCK_4_MHZ || freq == CLOCK_10_MHZ ||
+	      freq == CLOCK_40_MHZ)) {
+		ret = -ERANGE;
+		goto out_free;
+	}
+
+	ret = clk_prepare_enable(clk);
+	if (ret)
+		goto out_free;
+
+	priv->clk = clk;
+	priv->clock_freq = freq;
+
+	/* Configure the SPI bus */
+	spi->bits_per_word = 8;
+
+	/* The frequency of SCK has to be less than or equal to half the
+	 * frequency of SYSCLK.
+	 */
+	spi->max_speed_hz = freq / 2;
+	ret = spi_setup(spi);
+	if (ret)
+		goto out_clk;
+
+	priv->power = devm_regulator_get(&spi->dev, "vdd");
+	if (IS_ERR(priv->power)) {
+		if (PTR_ERR(priv->power) != -EPROBE_DEFER)
+			dev_err(&spi->dev, "failed to get vdd\n");
+		ret = PTR_ERR(priv->power);
+		goto out_clk;
+	}
+
+	ret = mcp25xxfd_base_power_enable(priv->power, 1);
+	if (ret)
+		goto out_clk;
+
+	/* disable interrupts */
+	ret = mcp25xxfd_int_enable(priv, false);
+	if (ret)
+		goto out_power;
+
+	/* setup ECC for SRAM */
+	ret = mcp25xxfd_ecc_enable(priv);
+	if (ret)
+		goto out_power;
+
+	dev_info(&spi->dev,
+		 "MCP%04x successfully initialized.\n", model);
+	return 0;
+
+out_power:
+	mcp25xxfd_base_power_enable(priv->power, 0);
+out_clk:
+	clk_disable_unprepare(clk);
+out_free:
+	dev_err(&spi->dev, "Probe failed, err=%d\n", -ret);
+	return ret;
+}
+
+static int mcp25xxfd_base_remove(struct spi_device *spi)
+{
+	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+	mcp25xxfd_base_power_enable(priv->power, 0);
+	clk_disable_unprepare(priv->clk);
+
+	return 0;
+}
+
+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,
+	},
+	.id_table = mcp25xxfd_id_table,
+	.probe = mcp25xxfd_base_probe,
+	.remove = mcp25xxfd_base_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_base.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.h
new file mode 100644
index 000000000000..4559ac60645c
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+#ifndef __MCP25XXFD_BASE_H
+#define __MCP25XXFD_BASE_H
+
+#include <linux/regulator/consumer.h>
+
+int mcp25xxfd_base_power_enable(struct regulator *reg, int enable);
+
+#endif /* __MCP25XXFD_BASE_H */
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..41a5ab508582
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+
+#include "mcp25xxfd_can.h"
+#include "mcp25xxfd_cmd.h"
+#include "mcp25xxfd_priv.h"
+#include "mcp25xxfd_regs.h"
+
+int mcp25xxfd_can_get_mode(struct mcp25xxfd_priv *priv, u32 *reg)
+{
+	int ret;
+
+	ret = mcp25xxfd_cmd_read(priv->spi, MCP25XXFD_CAN_CON, reg);
+	if (ret)
+		return ret;
+
+	return FIELD_GET(MCP25XXFD_CAN_CON_OPMOD_MASK, *reg);
+}
+
+static int mcp25xxfd_can_switch_mode(struct mcp25xxfd_priv *priv,
+				     u32 *reg, int mode)
+{
+	int ret, i;
+
+	ret = mcp25xxfd_can_get_mode(priv, reg);
+	if (ret < 0)
+		return ret;
+
+	/* Compute the effective mode in osc*/
+	*reg &= ~(MCP25XXFD_CAN_CON_REQOP_MASK |
+		  MCP25XXFD_CAN_CON_OPMOD_MASK);
+	*reg |= FIELD_PREP(MCP25XXFD_CAN_CON_REQOP_MASK, mode) |
+		FIELD_PREP(MCP25XXFD_CAN_CON_OPMOD_MASK, mode);
+
+	/* Request the mode switch */
+	ret = mcp25xxfd_cmd_write(priv->spi, MCP25XXFD_CAN_CON, *reg);
+	if (ret)
+		return ret;
+
+	/* Wait for 256 rounds to stabilize. This is essentially > 12ms
+	 * at 1MHz
+	 */
+	for (i = 0; i < 256; i++) {
+		/* get the mode */
+		ret = mcp25xxfd_can_get_mode(priv, reg);
+		if (ret < 0)
+			return ret;
+		/* check that we have reached our mode */
+		if (ret == mode)
+			return 0;
+	}
+
+	dev_err(&priv->spi->dev, "Failed to switch to mode %u in time\n",
+		mode);
+
+	return -ETIMEDOUT;
+}
+
+static int mcp25xxfd_can_probe_modeswitch(struct mcp25xxfd_priv *priv)
+{
+	u32 mode_data;
+	int ret;
+
+	/* We should be in config mode now, so move to INT_LOOPBACK */
+	ret = mcp25xxfd_can_switch_mode(priv, &mode_data,
+					MCP25XXFD_CAN_CON_MODE_INT_LOOPBACK);
+	if (ret) {
+		dev_err(&priv->spi->dev,
+			"Failed to switch into loopback mode\n");
+		return ret;
+	}
+
+	/* Switch back into config mode */
+	ret = mcp25xxfd_can_switch_mode(priv, &mode_data,
+					MCP25XXFD_CAN_CON_MODE_CONFIG);
+	if (ret) {
+		dev_err(&priv->spi->dev,
+			"Failed to switch back to config mode\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+int mcp25xxfd_can_probe(struct mcp25xxfd_priv *priv)
+{
+	struct spi_device *spi = priv->spi;
+	u32 mode_data;
+	int mode, ret;
+
+	/* For sanity check read TXQCON register. The TXEN bit should always
+	 * be read as 1.
+	 */
+	ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_CAN_TXQCON, &mode_data);
+	if (ret)
+		return ret;
+
+	if ((mode_data & MCP25XXFD_CAN_TXQCON_TXEN) == 0) {
+		dev_err(&spi->dev, "TXQCON does not have TXEN bit set");
+		return -EINVAL;
+	}
+
+	/* Try to get the current mode */
+	mode = mcp25xxfd_can_get_mode(priv, &mode_data);
+	if (mode < 0)
+		return mode;
+
+	/* 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 properly.
+	 */
+	if (mode == MCP25XXFD_CAN_CON_MODE_CONFIG)
+		return mcp25xxfd_can_probe_modeswitch(priv);
+
+	/* Any other mode is unexpected */
+	dev_err(&spi->dev,
+		"Found controller in unexpected mode: %d\n", mode);
+
+	/* Once again try to move to config mode. If this fails, we'll
+	 * bail out.
+	 */
+	ret = mcp25xxfd_can_switch_mode(priv, &mode_data,
+					MCP25XXFD_CAN_CON_MODE_CONFIG);
+	if (ret) {
+		dev_err(&priv->spi->dev,
+			"Unable to switch to config mode\n");
+		return -EINVAL;
+	}
+
+	/* Finally check if modeswitch is really working */
+	return mcp25xxfd_can_probe_modeswitch(priv);
+}
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..f54c716735fb
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+
+#ifndef __MCP25XXFD_CAN_H
+#define __MCP25XXFD_CAN_H
+
+#include "mcp25xxfd_priv.h"
+
+/* probe the can controller */
+int mcp25xxfd_can_probe(struct mcp25xxfd_priv *priv);
+
+#endif /* __MCP25XXFD_CAN_H */
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.c
new file mode 100644
index 000000000000..24d01e6f59d4
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+#include "mcp25xxfd_cmd.h"
+#include "mcp25xxfd_priv.h"
+
+static int mcp25xxfd_cmd_sync_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 spi_sync_transfer(spi, &xfer, 1);
+}
+
+static int mcp25xxfd_cmd_write_then_read(struct spi_device *spi,
+					 const void *tx_buf,
+					 unsigned int tx_len,
+					 void *rx_buf,
+					 unsigned int rx_len)
+{
+	struct spi_transfer xfer[2];
+	u8 *spi_tx, *spi_rx;
+	int xfers;
+	int ret;
+
+	spi_tx = kzalloc(tx_len + rx_len, GFP_KERNEL);
+	if (!spi_tx)
+		return -ENOMEM;
+
+	spi_rx = spi_tx + tx_len;
+	memset(xfer, 0, sizeof(xfer));
+
+	/* Special handling for half-duplex */
+	if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) {
+		xfers = 2;
+		xfer[0].tx_buf = spi_tx;
+		xfer[0].len = tx_len;
+		/* Offset for rx_buf needs to get aligned */
+		xfer[1].rx_buf = spi_rx + tx_len;
+		xfer[1].len = rx_len;
+	} else {
+		xfers = 1;
+		xfer[0].len = tx_len + rx_len;
+		xfer[0].tx_buf = spi_tx;
+		xfer[0].rx_buf = spi_rx;
+	}
+
+	memcpy(spi_tx, tx_buf, tx_len);
+	ret = spi_sync_transfer(spi, xfer, xfers);
+	if (ret)
+		goto out;
+
+	memcpy(rx_buf, xfer[0].rx_buf + tx_len, rx_len);
+
+out:
+	kfree(spi_tx);
+
+	return ret;
+}
+
+static int mcp25xxfd_cmd_write_then_write(struct spi_device *spi,
+					  const void *tx_buf,
+					  unsigned int tx_len,
+					  const void *tx2_buf,
+					  unsigned int tx2_len)
+{
+	struct spi_transfer xfer;
+	u8 *spi_tx;
+	int ret;
+
+	spi_tx = kzalloc(tx_len + tx2_len, GFP_KERNEL);
+	if (!spi_tx)
+		return -ENOMEM;
+
+	memset(&xfer, 0, sizeof(xfer));
+	xfer.len = tx_len + tx2_len;
+	xfer.tx_buf = spi_tx;
+
+	memcpy(spi_tx, tx_buf, tx_len);
+	memcpy(spi_tx + tx_len, tx2_buf, tx2_len);
+
+	ret = spi_sync_transfer(spi, &xfer, 1);
+	kfree(spi_tx);
+
+	return ret;
+}
+
+/* Read multiple bytes from registers */
+int mcp25xxfd_cmd_read_multi(struct spi_device *spi, u32 reg,
+			     void *data, int n)
+{
+	u8 cmd[2];
+	int ret;
+
+	mcp25xxfd_cmd_calc(MCP25XXFD_INSTRUCTION_READ, reg, cmd);
+
+	ret = mcp25xxfd_cmd_write_then_read(spi, &cmd, 2, data, n);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+int mcp25xxfd_cmd_read_mask(struct spi_device *spi, u32 reg,
+			    u32 *data, u32 mask)
+{
+	int first_byte, last_byte, len_byte;
+	int ret;
+
+	/* Make sure at least one bit is set */
+	if (!mask)
+		return -EINVAL;
+
+	/* Calculate first and last byte used */
+	first_byte = mcp25xxfd_cmd_first_byte(mask);
+	last_byte = mcp25xxfd_cmd_last_byte(mask);
+	len_byte = last_byte - first_byte + 1;
+
+	*data = 0;
+	ret = mcp25xxfd_cmd_read_multi(spi, reg + first_byte,
+				       ((void *)data + first_byte), len_byte);
+	if (ret)
+		return ret;
+
+	mcp25xxfd_cmd_convert_to_cpu(data, 1);
+
+	return 0;
+}
+
+/* Write multiple bytes to registers */
+int mcp25xxfd_cmd_write_multi(struct spi_device *spi, u32 reg,
+			      void *data, int n)
+{
+	int ret;
+	u8 cmd[2];
+
+	mcp25xxfd_cmd_calc(MCP25XXFD_INSTRUCTION_WRITE, reg, cmd);
+
+	ret = mcp25xxfd_cmd_write_then_write(spi, &cmd, 2, data, n);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+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];
+
+	/* Make sure at least one bit is set */
+	if (!mask)
+		return -EINVAL;
+
+	/* calculate first and last byte used */
+	first_byte = mcp25xxfd_cmd_first_byte(mask);
+	last_byte = mcp25xxfd_cmd_last_byte(mask);
+	len_byte = last_byte - first_byte + 1;
+
+	mcp25xxfd_cmd_calc(MCP25XXFD_INSTRUCTION_WRITE,
+			   reg + first_byte, cmd);
+
+	mcp25xxfd_cmd_convert_from_cpu(&data, 1);
+
+	return mcp25xxfd_cmd_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;
+
+	mcp25xxfd_cmd_convert_from_cpu(data, bytes / sizeof(bytes));
+
+	ret = mcp25xxfd_cmd_write_multi(spi, reg, data, bytes);
+
+	mcp25xxfd_cmd_convert_to_cpu(data, bytes / sizeof(bytes));
+
+	return ret;
+}
+
+int mcp25xxfd_cmd_read_regs(struct spi_device *spi, u32 reg,
+			    u32 *data, u32 bytes)
+{
+	int ret;
+
+	ret = mcp25xxfd_cmd_read_multi(spi, reg, data, bytes);
+
+	mcp25xxfd_cmd_convert_to_cpu((u32 *)data, bytes / sizeof(bytes));
+
+	return ret;
+}
+
+int mcp25xxfd_cmd_reset(struct spi_device *spi)
+{
+	u8 *cmd;
+	int ret;
+
+	cmd = kzalloc(2, GFP_KERNEL);
+	if (!cmd)
+		return -ENOMEM;
+
+	mcp25xxfd_cmd_calc(MCP25XXFD_INSTRUCTION_RESET, 0, cmd);
+
+	ret = mcp25xxfd_cmd_sync_write(spi, cmd, 2);
+
+	kfree(cmd);
+
+	return ret;
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.h
new file mode 100644
index 000000000000..a61815f97bf6
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_cmd.h
@@ -0,0 +1,84 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+
+#ifndef __MCP25XXFD_CMD_H
+#define __MCP25XXFD_CMD_H
+
+#include <linux/byteorder/generic.h>
+#include <linux/spi/spi.h>
+
+/* SPI commands */
+#define MCP25XXFD_INSTRUCTION_RESET		0x0000
+#define MCP25XXFD_INSTRUCTION_READ		0x3000
+#define MCP25XXFD_INSTRUCTION_WRITE		0x2000
+#define MCP25XXFD_INSTRUCTION_READ_CRC		0xB000
+#define MCP25XXFD_INSTRUCTION_WRITE_CRC		0xA000
+#define MCP25XXFD_INSTRUCTION_WRITE_SAVE	0xC000
+
+#define MCP25XXFD_ADDRESS_MASK			0x0fff
+
+static inline void mcp25xxfd_cmd_convert_to_cpu(u32 *data, int n)
+{
+	le32_to_cpu_array(data, n);
+}
+
+static inline void mcp25xxfd_cmd_convert_from_cpu(u32 *data, int n)
+{
+	cpu_to_le32_array(data, n);
+}
+
+static inline void mcp25xxfd_cmd_calc(u16 cmd, u16 addr, u8 *data)
+{
+	cmd = cmd | (addr & MCP25XXFD_ADDRESS_MASK);
+
+	data[0] = (cmd >> 8) & 0xff;
+	data[1] = (cmd >> 0) & 0xff;
+}
+
+static inline int mcp25xxfd_cmd_first_byte(u32 mask)
+{
+	return (mask & 0x0000ffff) ?
+		((mask & 0x000000ff) ? 0 : 1) :
+		((mask & 0x00ff0000) ? 2 : 3);
+}
+
+static inline int mcp25xxfd_cmd_last_byte(u32 mask)
+{
+	return (mask & 0xffff0000) ?
+		((mask & 0xff000000) ? 3 : 2) :
+		((mask & 0x0000ff00) ? 1 : 0);
+}
+
+int mcp25xxfd_cmd_read_multi(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_write_multi(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_cmd_reset(struct spi_device *spi);
+
+#endif /* __MCP25XXFD_CMD_H */
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.c
new file mode 100644
index 000000000000..b893d8009448
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include "mcp25xxfd_cmd.h"
+#include "mcp25xxfd_crc.h"
+#include "mcp25xxfd_regs.h"
+#include "mcp25xxfd_priv.h"
+
+int mcp25xxfd_crc_enable_int(struct mcp25xxfd_priv *priv, bool enable)
+{
+	u32 mask = MCP25XXFD_CRC_CRCERRIE | MCP25XXFD_CRC_FERRIE;
+
+	priv->regs.crc &= ~mask;
+	priv->regs.crc |= enable ? mask : 0;
+
+	return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_CRC,
+					priv->regs.crc, mask);
+}
+
+int mcp25xxfd_crc_clear_int(struct mcp25xxfd_priv *priv)
+{
+	return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_CRC, 0,
+					MCP25XXFD_CRC_CRCERRIF |
+					MCP25XXFD_CRC_FERRIF);
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.h
new file mode 100644
index 000000000000..6e42fe0fad0f
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_crc.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+#ifndef __MCP25XXFD_CRC_H
+#define __MCP25XXFD_CRC_H
+
+#include "mcp25xxfd_priv.h"
+
+int mcp25xxfd_crc_enable_int(struct mcp25xxfd_priv *priv, bool enable);
+int mcp25xxfd_crc_clear_int(struct mcp25xxfd_priv *priv);
+
+#endif /* __MCP25XXFD_CRC_H */
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.c
new file mode 100644
index 000000000000..56e2c4fbf52d
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include "mcp25xxfd_cmd.h"
+#include "mcp25xxfd_ecc.h"
+#include "mcp25xxfd_priv.h"
+#include "mcp25xxfd_regs.h"
+
+int mcp25xxfd_ecc_clear_int(struct mcp25xxfd_priv *priv)
+{
+	u32 val, addr;
+	int ret;
+
+	/* First report the error address */
+	ret = mcp25xxfd_cmd_read(priv->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(&priv->spi->dev, "ECC %s bit error at %03x\n",
+			    (val & MCP25XXFD_ECCSTAT_DEDIF) ?
+			    "double" : "single",
+			    addr);
+
+	/* Clear the error */
+	return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_ECCSTAT, 0,
+					MCP25XXFD_ECCSTAT_SECIF |
+					MCP25XXFD_ECCSTAT_DEDIF);
+}
+
+int mcp25xxfd_ecc_enable_int(struct mcp25xxfd_priv *priv, bool enable)
+{
+	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(priv->spi, MCP25XXFD_ECCCON,
+					priv->regs.ecccon,
+					MCP25XXFD_ECCCON_ECCEN | mask);
+}
+
+int mcp25xxfd_ecc_enable(struct mcp25xxfd_priv *priv)
+{
+	u8 buffer[256];
+	int i, ret;
+
+	ret = mcp25xxfd_ecc_enable_int(priv, false);
+	if (ret)
+		return ret;
+
+	memset(buffer, 0, sizeof(buffer));
+	for (i = 0; i < MCP25XXFD_SRAM_SIZE; i += sizeof(buffer)) {
+		ret = mcp25xxfd_cmd_write_multi(priv->spi,
+						MCP25XXFD_SRAM_ADDR(i),
+						buffer, sizeof(buffer));
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.h
new file mode 100644
index 000000000000..117f58c65a46
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_ecc.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+#ifndef __MCP25XXFD_ECC_H
+#define __MCP25XXFD_ECC_H
+
+#include "mcp25xxfd_priv.h"
+
+int mcp25xxfd_ecc_clear_int(struct mcp25xxfd_priv *priv);
+int mcp25xxfd_ecc_enable_int(struct mcp25xxfd_priv *priv, bool enable);
+int mcp25xxfd_ecc_enable(struct mcp25xxfd_priv *priv);
+
+#endif /* __MCP25XXFD_ECC_H */
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c
new file mode 100644
index 000000000000..5e274d452646
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+
+#include "mcp25xxfd_crc.h"
+#include "mcp25xxfd_ecc.h"
+#include "mcp25xxfd_int.h"
+#include "mcp25xxfd_priv.h"
+
+int mcp25xxfd_int_clear(struct mcp25xxfd_priv *priv)
+{
+	int ret;
+
+	ret = mcp25xxfd_ecc_clear_int(priv);
+	if (ret)
+		return ret;
+
+	return mcp25xxfd_crc_clear_int(priv);
+}
+
+int mcp25xxfd_int_enable(struct mcp25xxfd_priv *priv, bool enable)
+{
+	int ret;
+
+	/* If we enable interrupts, then clear interrupt first */
+	if (enable) {
+		ret = mcp25xxfd_int_clear(priv);
+		if (ret)
+			return ret;
+	}
+
+	ret = mcp25xxfd_crc_enable_int(priv, enable);
+	if (ret)
+		return ret;
+
+	ret = mcp25xxfd_ecc_enable(priv);
+	if (ret)
+		goto out_crc;
+
+	ret = mcp25xxfd_ecc_enable_int(priv, enable);
+	if (ret)
+		goto out_crc;
+
+	/* If we disable interrupts, then clear interrupt flags last */
+	if (!enable)
+		mcp25xxfd_int_clear(priv);
+
+	return 0;
+
+out_crc:
+	mcp25xxfd_crc_enable_int(priv, false);
+	return ret;
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.h
new file mode 100644
index 000000000000..4daf0182d1af
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+#ifndef __MCP25XXFD_INT_H
+#define __MCP25XXFD_INT_H
+
+#include "mcp25xxfd_priv.h"
+
+int mcp25xxfd_int_clear(struct mcp25xxfd_priv *priv);
+int mcp25xxfd_int_enable(struct mcp25xxfd_priv *priv, bool enable);
+
+#endif /* __MCP25XXFD_INT_H */
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h
new file mode 100644
index 000000000000..8bc7a599224c
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+
+#ifndef __MCP25XXFD_PRIV_H
+#define __MCP25XXFD_PRIV_H
+
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include "mcp25xxfd_regs.h"
+
+#define DEVICE_NAME "mcp25xxfd"
+#define CLOCK_4_MHZ 4000000
+#define CLOCK_10_MHZ 10000000
+#define CLOCK_40_MHZ 40000000
+
+enum mcp25xxfd_model {
+	CAN_MCP2517FD	= 0x2517,
+};
+
+struct mcp25xxfd_priv {
+	struct spi_device *spi;
+	struct clk *clk;
+
+	/* actual model of the mcp25xxfd */
+	enum mcp25xxfd_model model;
+
+	/* full device name used for interrupts */
+	char device_name[32];
+
+	int clock_freq;
+	struct regulator *power;
+
+	/* configuration registers */
+	struct {
+		u32 osc;
+		u32 iocon;
+		u32 crc;
+		u32 ecccon;
+	} regs;
+};
+
+#endif /* __MCP25XXFD_PRIV_H */
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h
new file mode 100644
index 000000000000..b500cb46b9a4
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h
@@ -0,0 +1,656 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+
+#ifndef __MCP25XXFD_REGS_H
+#define __MCP25XXFD_REGS_H
+
+#include <linux/bitops.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
+
+/* GPIO, clock, ecc related register definitions of Controller itself */
+#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)
+
+/* CAN related register definitions of Controller CAN block */
+#define MCP25XXFD_CAN_SFR_BASE(x)		(0x000 + (x))
+#define MCP25XXFD_CAN_CON						\
+	MCP25XXFD_CAN_SFR_BASE(0x00)
+#  define MCP25XXFD_CAN_CON_DNCNT_BITS		5
+#  define MCP25XXFD_CAN_CON_DNCNT_SHIFT		0
+#  define MCP25XXFD_CAN_CON_DNCNT_MASK					\
+	GENMASK(MCP25XXFD_CAN_CON_DNCNT_SHIFT +				\
+		MCP25XXFD_CAN_CON_DNCNT_BITS - 1,			\
+		MCP25XXFD_CAN_CON_DNCNT_SHIFT)
+#  define MCP25XXFD_CAN_CON_ISOCRCEN		BIT(5)
+#  define MCP25XXFD_CAN_CON_PXEDIS		BIT(6)
+#  define MCP25XXFD_CAN_CON_WAKFIL		BIT(8)
+#  define MCP25XXFD_CAN_CON_WFT_BITS		2
+#  define MCP25XXFD_CAN_CON_WFT_SHIFT		9
+#  define MCP25XXFD_CAN_CON_WFT_MASK					\
+	GENMASK(MCP25XXFD_CAN_CON_WFT_SHIFT +				\
+		MCP25XXFD_CAN_CON_WFT_BITS - 1,				\
+		MCP25XXFD_CAN_CON_WFT_SHIFT)
+#  define MCP25XXFD_CAN_CON_BUSY		BIT(11)
+#  define MCP25XXFD_CAN_CON_BRSDIS		BIT(12)
+#  define MCP25XXFD_CAN_CON_RTXAT		BIT(16)
+#  define MCP25XXFD_CAN_CON_ESIGM		BIT(17)
+#  define MCP25XXFD_CAN_CON_SERR2LOM		BIT(18)
+#  define MCP25XXFD_CAN_CON_STEF		BIT(19)
+#  define MCP25XXFD_CAN_CON_TXQEN		BIT(20)
+#  define MCP25XXFD_CAN_CON_OPMODE_BITS		3
+#  define MCP25XXFD_CAN_CON_OPMOD_SHIFT		21
+#  define MCP25XXFD_CAN_CON_OPMOD_MASK					\
+	GENMASK(MCP25XXFD_CAN_CON_OPMOD_SHIFT +				\
+		MCP25XXFD_CAN_CON_OPMODE_BITS - 1,			\
+		MCP25XXFD_CAN_CON_OPMOD_SHIFT)
+#  define MCP25XXFD_CAN_CON_REQOP_BITS		3
+#  define MCP25XXFD_CAN_CON_REQOP_SHIFT		24
+#  define MCP25XXFD_CAN_CON_REQOP_MASK					\
+	GENMASK(MCP25XXFD_CAN_CON_REQOP_SHIFT +				\
+		MCP25XXFD_CAN_CON_REQOP_BITS - 1,			\
+		MCP25XXFD_CAN_CON_REQOP_SHIFT)
+#    define MCP25XXFD_CAN_CON_MODE_MIXED	0
+#    define MCP25XXFD_CAN_CON_MODE_SLEEP	1
+#    define MCP25XXFD_CAN_CON_MODE_INT_LOOPBACK	2
+#    define MCP25XXFD_CAN_CON_MODE_LISTENONLY	3
+#    define MCP25XXFD_CAN_CON_MODE_CONFIG	4
+#    define MCP25XXFD_CAN_CON_MODE_EXT_LOOPBACK	5
+#    define MCP25XXFD_CAN_CON_MODE_CAN2_0	6
+#    define MCP25XXFD_CAN_CON_MODE_RESTRICTED	7
+#  define MCP25XXFD_CAN_CON_ABAT		BIT(27)
+#  define MCP25XXFD_CAN_CON_TXBWS_BITS		3
+#  define MCP25XXFD_CAN_CON_TXBWS_SHIFT		28
+#  define MCP25XXFD_CAN_CON_TXBWS_MASK					\
+	GENMASK(MCP25XXFD_CAN_CON_TXBWS_SHIFT +				\
+		MCP25XXFD_CAN_CON_TXBWS_BITS - 1,			\
+		MCP25XXFD_CAN_CON_TXBWS_SHIFT)
+#  define MCP25XXFD_CAN_CON_DEFAULT					\
+	(MCP25XXFD_CAN_CON_ISOCRCEN |					\
+	 MCP25XXFD_CAN_CON_PXEDIS |					\
+	 MCP25XXFD_CAN_CON_WAKFIL |					\
+	 (3 << MCP25XXFD_CAN_CON_WFT_SHIFT) |				\
+	 MCP25XXFD_CAN_CON_STEF |					\
+	 MCP25XXFD_CAN_CON_TXQEN |					\
+	 (MCP25XXFD_CAN_CON_MODE_CONFIG << MCP25XXFD_CAN_CON_OPMOD_SHIFT) | \
+	 (MCP25XXFD_CAN_CON_MODE_CONFIG << MCP25XXFD_CAN_CON_REQOP_SHIFT))
+#  define MCP25XXFD_CAN_CON_DEFAULT_MASK				\
+	(MCP25XXFD_CAN_CON_DNCNT_MASK |					\
+	 MCP25XXFD_CAN_CON_ISOCRCEN |					\
+	 MCP25XXFD_CAN_CON_PXEDIS |					\
+	 MCP25XXFD_CAN_CON_WAKFIL |					\
+	 MCP25XXFD_CAN_CON_WFT_MASK |					\
+	 MCP25XXFD_CAN_CON_BRSDIS |					\
+	 MCP25XXFD_CAN_CON_RTXAT |					\
+	 MCP25XXFD_CAN_CON_ESIGM |					\
+	 MCP25XXFD_CAN_CON_SERR2LOM |					\
+	 MCP25XXFD_CAN_CON_STEF |					\
+	 MCP25XXFD_CAN_CON_TXQEN |					\
+	 MCP25XXFD_CAN_CON_OPMOD_MASK |					\
+	 MCP25XXFD_CAN_CON_REQOP_MASK |					\
+	 MCP25XXFD_CAN_CON_ABAT |					\
+	 MCP25XXFD_CAN_CON_TXBWS_MASK)
+#define MCP25XXFD_CAN_NBTCFG			MCP25XXFD_CAN_SFR_BASE(0x04)
+#  define MCP25XXFD_CAN_NBTCFG_SJW_BITS		7
+#  define MCP25XXFD_CAN_NBTCFG_SJW_SHIFT	0
+#  define MCP25XXFD_CAN_NBTCFG_SJW_MASK					\
+	GENMASK(MCP25XXFD_CAN_NBTCFG_SJW_SHIFT +			\
+		MCP25XXFD_CAN_NBTCFG_SJW_BITS - 1,			\
+		MCP25XXFD_CAN_NBTCFG_SJW_SHIFT)
+#  define MCP25XXFD_CAN_NBTCFG_TSEG2_BITS	7
+#  define MCP25XXFD_CAN_NBTCFG_TSEG2_SHIFT	8
+#  define MCP25XXFD_CAN_NBTCFG_TSEG2_MASK				\
+	GENMASK(MCP25XXFD_CAN_NBTCFG_TSEG2_SHIFT +			\
+		MCP25XXFD_CAN_NBTCFG_TSEG2_BITS - 1,			\
+		MCP25XXFD_CAN_NBTCFG_TSEG2_SHIFT)
+#  define MCP25XXFD_CAN_NBTCFG_TSEG1_BITS	8
+#  define MCP25XXFD_CAN_NBTCFG_TSEG1_SHIFT	16
+#  define MCP25XXFD_CAN_NBTCFG_TSEG1_MASK				\
+	GENMASK(MCP25XXFD_CAN_NBTCFG_TSEG1_SHIFT +			\
+		MCP25XXFD_CAN_NBTCFG_TSEG1_BITS - 1,			\
+		MCP25XXFD_CAN_NBTCFG_TSEG1_SHIFT)
+#  define MCP25XXFD_CAN_NBTCFG_BRP_BITS		8
+#  define MCP25XXFD_CAN_NBTCFG_BRP_SHIFT	24
+#  define MCP25XXFD_CAN_NBTCFG_BRP_MASK					\
+	GENMASK(MCP25XXFD_CAN_NBTCFG_BRP_SHIFT +			\
+		MCP25XXFD_CAN_NBTCFG_BRP_BITS - 1,			\
+		MCP25XXFD_CAN_NBTCFG_BRP_SHIFT)
+#define MCP25XXFD_CAN_DBTCFG			MCP25XXFD_CAN_SFR_BASE(0x08)
+#  define MCP25XXFD_CAN_DBTCFG_SJW_BITS		4
+#  define MCP25XXFD_CAN_DBTCFG_SJW_SHIFT	0
+#  define MCP25XXFD_CAN_DBTCFG_SJW_MASK					\
+	GENMASK(MCP25XXFD_CAN_DBTCFG_SJW_SHIFT +			\
+		MCP25XXFD_CAN_DBTCFG_SJW_BITS - 1,			\
+		MCP25XXFD_CAN_DBTCFG_SJW_SHIFT)
+#  define MCP25XXFD_CAN_DBTCFG_TSEG2_BITS	4
+#  define MCP25XXFD_CAN_DBTCFG_TSEG2_SHIFT	8
+#  define MCP25XXFD_CAN_DBTCFG_TSEG2_MASK				\
+	GENMASK(MCP25XXFD_CAN_DBTCFG_TSEG2_SHIFT +			\
+		MCP25XXFD_CAN_DBTCFG_TSEG2_BITS - 1,			\
+		MCP25XXFD_CAN_DBTCFG_TSEG2_SHIFT)
+#  define MCP25XXFD_CAN_DBTCFG_TSEG1_BITS	5
+#  define MCP25XXFD_CAN_DBTCFG_TSEG1_SHIFT	16
+#  define MCP25XXFD_CAN_DBTCFG_TSEG1_MASK				\
+	GENMASK(MCP25XXFD_CAN_DBTCFG_TSEG1_SHIFT +			\
+		MCP25XXFD_CAN_DBTCFG_TSEG1_BITS - 1,			\
+		MCP25XXFD_CAN_DBTCFG_TSEG1_SHIFT)
+#  define MCP25XXFD_CAN_DBTCFG_BRP_BITS		8
+#  define MCP25XXFD_CAN_DBTCFG_BRP_SHIFT	24
+#  define MCP25XXFD_CAN_DBTCFG_BRP_MASK					\
+	GENMASK(MCP25XXFD_CAN_DBTCFG_BRP_SHIFT +			\
+		MCP25XXFD_CAN_DBTCFG_BRP_BITS - 1,			\
+		MCP25XXFD_CAN_DBTCFG_BRP_SHIFT)
+#define MCP25XXFD_CAN_TDC			MCP25XXFD_CAN_SFR_BASE(0x0C)
+#  define MCP25XXFD_CAN_TDC_TDCV_BITS		5
+#  define MCP25XXFD_CAN_TDC_TDCV_SHIFT		0
+#  define MCP25XXFD_CAN_TDC_TDCV_MASK					\
+	GENMASK(MCP25XXFD_CAN_TDC_TDCV_SHIFT +				\
+		MCP25XXFD_CAN_TDC_TDCV_BITS - 1,			\
+		MCP25XXFD_CAN_TDC_TDCV_SHIFT)
+#  define MCP25XXFD_CAN_TDC_TDCO_BITS		5
+#  define MCP25XXFD_CAN_TDC_TDCO_SHIFT		8
+#  define MCP25XXFD_CAN_TDC_TDCO_MASK					\
+	GENMASK(MCP25XXFD_CAN_TDC_TDCO_SHIFT +				\
+		MCP25XXFD_CAN_TDC_TDCO_BITS - 1,			\
+		MCP25XXFD_CAN_TDC_TDCO_SHIFT)
+#  define MCP25XXFD_CAN_TDC_TDCMOD_BITS		2
+#  define MCP25XXFD_CAN_TDC_TDCMOD_SHIFT	16
+#  define MCP25XXFD_CAN_TDC_TDCMOD_MASK					\
+	GENMASK(MCP25XXFD_CAN_TDC_TDCMOD_SHIFT +			\
+		MCP25XXFD_CAN_TDC_TDCMOD_BITS - 1,			\
+		MCP25XXFD_CAN_TDC_TDCMOD_SHIFT)
+#  define MCP25XXFD_CAN_TDC_TDCMOD_DISABLED	0
+#  define MCP25XXFD_CAN_TDC_TDCMOD_MANUAL	1
+#  define MCP25XXFD_CAN_TDC_TDCMOD_AUTO		2
+#  define MCP25XXFD_CAN_TDC_SID11EN		BIT(24)
+#  define MCP25XXFD_CAN_TDC_EDGFLTEN		BIT(25)
+#define MCP25XXFD_CAN_TBC			MCP25XXFD_CAN_SFR_BASE(0x10)
+#define MCP25XXFD_CAN_TSCON			MCP25XXFD_CAN_SFR_BASE(0x14)
+#  define MCP25XXFD_CAN_TSCON_TBCPRE_BITS	10
+#  define MCP25XXFD_CAN_TSCON_TBCPRE_SHIFT	0
+#  define MCP25XXFD_CAN_TSCON_TBCPRE_MASK				\
+	GENMASK(MCP25XXFD_CAN_TSCON_TBCPRE_SHIFT +			\
+		MCP25XXFD_CAN_TSCON_TBCPRE_BITS - 1,			\
+		MCP25XXFD_CAN_TSCON_TBCPRE_SHIFT)
+#  define MCP25XXFD_CAN_TSCON_TBCEN		BIT(16)
+#  define MCP25XXFD_CAN_TSCON_TSEOF		BIT(17)
+#  define MCP25XXFD_CAN_TSCON_TSRES		BIT(18)
+#define MCP25XXFD_CAN_VEC			MCP25XXFD_CAN_SFR_BASE(0x18)
+#  define MCP25XXFD_CAN_VEC_ICODE_BITS		7
+#  define MCP25XXFD_CAN_VEC_ICODE_SHIFT		0
+#  define MCP25XXFD_CAN_VEC_ICODE_MASK					\
+	GENMASK(MCP25XXFD_CAN_VEC_ICODE_SHIFT +				\
+		MCP25XXFD_CAN_VEC_ICODE_BITS - 1,			\
+		MCP25XXFD_CAN_VEC_ICODE_SHIFT)
+#  define MCP25XXFD_CAN_VEC_FILHIT_BITS		5
+#  define MCP25XXFD_CAN_VEC_FILHIT_SHIFT	8
+#  define MCP25XXFD_CAN_VEC_FILHIT_MASK					\
+	GENMASK(MCP25XXFD_CAN_VEC_FILHIT_SHIFT +			\
+		MCP25XXFD_CAN_VEC_FILHIT_BITS - 1,			\
+		MCP25XXFD_CAN_VEC_FILHIT_SHIFT)
+#  define MCP25XXFD_CAN_VEC_TXCODE_BITS		7
+#  define MCP25XXFD_CAN_VEC_TXCODE_SHIFT	16
+#  define MCP25XXFD_CAN_VEC_TXCODE_MASK					\
+	GENMASK(MCP25XXFD_CAN_VEC_TXCODE_SHIFT +			\
+		MCP25XXFD_CAN_VEC_TXCODE_BITS - 1,			\
+		MCP25XXFD_CAN_VEC_TXCODE_SHIFT)
+#  define MCP25XXFD_CAN_VEC_RXCODE_BITS		7
+#  define MCP25XXFD_CAN_VEC_RXCODE_SHIFT	24
+#  define MCP25XXFD_CAN_VEC_RXCODE_MASK					\
+	GENMASK(MCP25XXFD_CAN_VEC_RXCODE_SHIFT +			\
+		MCP25XXFD_CAN_VEC_RXCODE_BITS - 1,			\
+		MCP25XXFD_CAN_VEC_RXCODE_SHIFT)
+#define MCP25XXFD_CAN_INT			MCP25XXFD_CAN_SFR_BASE(0x1C)
+#  define MCP25XXFD_CAN_INT_IF_SHIFT		0
+#  define MCP25XXFD_CAN_INT_TXIF		BIT(0)
+#  define MCP25XXFD_CAN_INT_RXIF		BIT(1)
+#  define MCP25XXFD_CAN_INT_TBCIF		BIT(2)
+#  define MCP25XXFD_CAN_INT_MODIF		BIT(3)
+#  define MCP25XXFD_CAN_INT_TEFIF		BIT(4)
+#  define MCP25XXFD_CAN_INT_ECCIF		BIT(8)
+#  define MCP25XXFD_CAN_INT_SPICRCIF		BIT(9)
+#  define MCP25XXFD_CAN_INT_TXATIF		BIT(10)
+#  define MCP25XXFD_CAN_INT_RXOVIF		BIT(11)
+#  define MCP25XXFD_CAN_INT_SERRIF		BIT(12)
+#  define MCP25XXFD_CAN_INT_CERRIF		BIT(13)
+#  define MCP25XXFD_CAN_INT_WAKIF		BIT(14)
+#  define MCP25XXFD_CAN_INT_IVMIF		BIT(15)
+#  define MCP25XXFD_CAN_INT_IF_MASK					\
+	(MCP25XXFD_CAN_INT_TXIF |					\
+	 MCP25XXFD_CAN_INT_RXIF |					\
+	 MCP25XXFD_CAN_INT_TBCIF |					\
+	 MCP25XXFD_CAN_INT_MODIF |					\
+	 MCP25XXFD_CAN_INT_TEFIF |					\
+	 MCP25XXFD_CAN_INT_ECCIF |					\
+	 MCP25XXFD_CAN_INT_SPICRCIF |					\
+	 MCP25XXFD_CAN_INT_TXATIF |					\
+	 MCP25XXFD_CAN_INT_RXOVIF |					\
+	 MCP25XXFD_CAN_INT_CERRIF |					\
+	 MCP25XXFD_CAN_INT_SERRIF |					\
+	 MCP25XXFD_CAN_INT_WAKIF |					\
+	 MCP25XXFD_CAN_INT_IVMIF)
+#  define MCP25XXFD_CAN_INT_IF_CLEAR_MASK				\
+	(MCP25XXFD_CAN_INT_TBCIF  |					\
+	 MCP25XXFD_CAN_INT_MODIF  |					\
+	 MCP25XXFD_CAN_INT_CERRIF |					\
+	 MCP25XXFD_CAN_INT_SERRIF |					\
+	 MCP25XXFD_CAN_INT_WAKIF |					\
+	 MCP25XXFD_CAN_INT_IVMIF)
+#  define MCP25XXFD_CAN_INT_IE_SHIFT		16
+#  define MCP25XXFD_CAN_INT_TXIE					\
+	(MCP25XXFD_CAN_INT_TXIF << MCP25XXFD_CAN_INT_IE_SHIFT)
+#  define MCP25XXFD_CAN_INT_RXIE					\
+	(MCP25XXFD_CAN_INT_RXIF << MCP25XXFD_CAN_INT_IE_SHIFT)
+#  define MCP25XXFD_CAN_INT_TBCIE					\
+	(MCP25XXFD_CAN_INT_TBCIF << MCP25XXFD_CAN_INT_IE_SHIFT)
+#  define MCP25XXFD_CAN_INT_MODIE					\
+	(MCP25XXFD_CAN_INT_MODIF << MCP25XXFD_CAN_INT_IE_SHIFT)
+#  define MCP25XXFD_CAN_INT_TEFIE					\
+	(MCP25XXFD_CAN_INT_TEFIF << MCP25XXFD_CAN_INT_IE_SHIFT)
+#  define MCP25XXFD_CAN_INT_ECCIE					\
+	(MCP25XXFD_CAN_INT_ECCIF << MCP25XXFD_CAN_INT_IE_SHIFT)
+#  define MCP25XXFD_CAN_INT_SPICRCIE					\
+	(MCP25XXFD_CAN_INT_SPICRCIF << MCP25XXFD_CAN_INT_IE_SHIFT)
+#  define MCP25XXFD_CAN_INT_TXATIE					\
+	(MCP25XXFD_CAN_INT_TXATIF << MCP25XXFD_CAN_INT_IE_SHIFT)
+#  define MCP25XXFD_CAN_INT_RXOVIE					\
+	(MCP25XXFD_CAN_INT_RXOVIF << MCP25XXFD_CAN_INT_IE_SHIFT)
+#  define MCP25XXFD_CAN_INT_CERRIE					\
+	(MCP25XXFD_CAN_INT_CERRIF << MCP25XXFD_CAN_INT_IE_SHIFT)
+#  define MCP25XXFD_CAN_INT_SERRIE					\
+	(MCP25XXFD_CAN_INT_SERRIF << MCP25XXFD_CAN_INT_IE_SHIFT)
+#  define MCP25XXFD_CAN_INT_WAKIE					\
+	(MCP25XXFD_CAN_INT_WAKIF << MCP25XXFD_CAN_INT_IE_SHIFT)
+#  define MCP25XXFD_CAN_INT_IVMIE					\
+	(MCP25XXFD_CAN_INT_IVMIF << MCP25XXFD_CAN_INT_IE_SHIFT)
+#  define MCP25XXFD_CAN_INT_IE_MASK					\
+	(MCP25XXFD_CAN_INT_TXIE |					\
+	 MCP25XXFD_CAN_INT_RXIE |					\
+	 MCP25XXFD_CAN_INT_TBCIE |					\
+	 MCP25XXFD_CAN_INT_MODIE |					\
+	 MCP25XXFD_CAN_INT_TEFIE |					\
+	 MCP25XXFD_CAN_INT_ECCIE |					\
+	 MCP25XXFD_CAN_INT_SPICRCIE |					\
+	 MCP25XXFD_CAN_INT_TXATIE |					\
+	 MCP25XXFD_CAN_INT_RXOVIE |					\
+	 MCP25XXFD_CAN_INT_CERRIE |					\
+	 MCP25XXFD_CAN_INT_SERRIE |					\
+	 MCP25XXFD_CAN_INT_WAKIE |					\
+	 MCP25XXFD_CAN_INT_IVMIE)
+#define MCP25XXFD_CAN_RXIF			MCP25XXFD_CAN_SFR_BASE(0x20)
+#define MCP25XXFD_CAN_TXIF			MCP25XXFD_CAN_SFR_BASE(0x24)
+#define MCP25XXFD_CAN_RXOVIF			MCP25XXFD_CAN_SFR_BASE(0x28)
+#define MCP25XXFD_CAN_TXATIF			MCP25XXFD_CAN_SFR_BASE(0x2C)
+#define MCP25XXFD_CAN_TXREQ			MCP25XXFD_CAN_SFR_BASE(0x30)
+#define MCP25XXFD_CAN_TREC			MCP25XXFD_CAN_SFR_BASE(0x34)
+#  define MCP25XXFD_CAN_TREC_REC_BITS		8
+#  define MCP25XXFD_CAN_TREC_REC_SHIFT		0
+#  define MCP25XXFD_CAN_TREC_REC_MASK					\
+	GENMASK(MCP25XXFD_CAN_TREC_REC_SHIFT +				\
+		MCP25XXFD_CAN_TREC_REC_BITS - 1,			\
+		MCP25XXFD_CAN_TREC_REC_SHIFT)
+#  define MCP25XXFD_CAN_TREC_TEC_BITS		8
+#  define MCP25XXFD_CAN_TREC_TEC_SHIFT		8
+#  define MCP25XXFD_CAN_TREC_TEC_MASK					\
+	GENMASK(MCP25XXFD_CAN_TREC_TEC_SHIFT +				\
+		MCP25XXFD_CAN_TREC_TEC_BITS - 1,			\
+		MCP25XXFD_CAN_TREC_TEC_SHIFT)
+#  define MCP25XXFD_CAN_TREC_EWARN		BIT(16)
+#  define MCP25XXFD_CAN_TREC_RXWARN		BIT(17)
+#  define MCP25XXFD_CAN_TREC_TXWARN		BIT(18)
+#  define MCP25XXFD_CAN_TREC_RXBP		BIT(19)
+#  define MCP25XXFD_CAN_TREC_TXBP		BIT(20)
+#  define MCP25XXFD_CAN_TREC_TXBO		BIT(21)
+#define MCP25XXFD_CAN_BDIAG0			MCP25XXFD_CAN_SFR_BASE(0x38)
+#  define MCP25XXFD_CAN_BDIAG0_NRERRCNT_BITS	8
+#  define MCP25XXFD_CAN_BDIAG0_NRERRCNT_SHIFT	0
+#  define MCP25XXFD_CAN_BDIAG0_NRERRCNT_MASK				\
+	GENMASK(MCP25XXFD_CAN_BDIAG0_NRERRCNT_SHIFT +			\
+		MCP25XXFD_CAN_BDIAG0_NRERRCNT_BITS - 1,			\
+		MCP25XXFD_CAN_BDIAG0_NRERRCNT_SHIFT)
+#  define MCP25XXFD_CAN_BDIAG0_NTERRCNT_BITS	8
+#  define MCP25XXFD_CAN_BDIAG0_NTERRCNT_SHIFT	8
+#  define MCP25XXFD_CAN_BDIAG0_NTERRCNT_MASK				\
+	GENMASK(MCP25XXFD_CAN_BDIAG0_NTERRCNT_SHIFT +			\
+		MCP25XXFD_CAN_BDIAG0_NTERRCNT_BITS - 1,			\
+		MCP25XXFD_CAN_BDIAG0_NTERRCNT_SHIFT)
+#  define MCP25XXFD_CAN_BDIAG0_DRERRCNT_BITS	8
+#  define MCP25XXFD_CAN_BDIAG0_DRERRCNT_SHIFT	16
+#  define MCP25XXFD_CAN_BDIAG0_DRERRCNT_MASK				\
+	GENMASK(MCP25XXFD_CAN_BDIAG0_DRERRCNT_SHIFT +			\
+		MCP25XXFD_CAN_BDIAG0_DRERRCNT_BITS - 1,			\
+		MCP25XXFD_CAN_BDIAG0_DRERRCNT_SHIFT)
+#  define MCP25XXFD_CAN_BDIAG0_DTERRCNT_BITS	8
+#  define MCP25XXFD_CAN_BDIAG0_DTERRCNT_SHIFT	24
+#  define MCP25XXFD_CAN_BDIAG0_DTERRCNT_MASK				\
+	GENMASK(MCP25XXFD_CAN_BDIAG0_DTERRCNT_SHIFT +			\
+		MCP25XXFD_CAN_BDIAG0_DTERRCNT_BITS - 1,			\
+		MCP25XXFD_CAN_BDIAG0_DTERRCNT_SHIFT)
+#define MCP25XXFD_CAN_BDIAG1			MCP25XXFD_CAN_SFR_BASE(0x3C)
+#  define MCP25XXFD_CAN_BDIAG1_EFMSGCNT_BITS	16
+#  define MCP25XXFD_CAN_BDIAG1_EFMSGCNT_SHIFT	0
+#  define MCP25XXFD_CAN_BDIAG1_EFMSGCNT_MASK				\
+	GENMASK(MCP25XXFD_CAN_BDIAG1_EFMSGCNT_SHIFT +			\
+		MCP25XXFD_CAN_BDIAG1_EFMSGCNT_BITS - 1,			\
+		MCP25XXFD_CAN_BDIAG1_EFMSGCNT_SHIFT)
+#  define MCP25XXFD_CAN_BDIAG1_NBIT0ERR		BIT(16)
+#  define MCP25XXFD_CAN_BDIAG1_NBIT1ERR		BIT(17)
+#  define MCP25XXFD_CAN_BDIAG1_NACKERR		BIT(18)
+#  define MCP25XXFD_CAN_BDIAG1_NSTUFERR		BIT(19)
+#  define MCP25XXFD_CAN_BDIAG1_NFORMERR		BIT(20)
+#  define MCP25XXFD_CAN_BDIAG1_NCRCERR		BIT(21)
+#  define MCP25XXFD_CAN_BDIAG1_TXBOERR		BIT(23)
+#  define MCP25XXFD_CAN_BDIAG1_DBIT0ERR		BIT(24)
+#  define MCP25XXFD_CAN_BDIAG1_DBIT1ERR		BIT(25)
+#  define MCP25XXFD_CAN_BDIAG1_DFORMERR		BIT(27)
+#  define MCP25XXFD_CAN_BDIAG1_DSTUFERR		BIT(28)
+#  define MCP25XXFD_CAN_BDIAG1_DCRCERR		BIT(29)
+#  define MCP25XXFD_CAN_BDIAG1_ESI		BIT(30)
+#  define MCP25XXFD_CAN_BDIAG1_DLCMM		BIT(31)
+#define MCP25XXFD_CAN_TEFCON			MCP25XXFD_CAN_SFR_BASE(0x40)
+#  define MCP25XXFD_CAN_TEFCON_TEFNEIE		BIT(0)
+#  define MCP25XXFD_CAN_TEFCON_TEFHIE		BIT(1)
+#  define MCP25XXFD_CAN_TEFCON_TEFFIE		BIT(2)
+#  define MCP25XXFD_CAN_TEFCON_TEFOVIE		BIT(3)
+#  define MCP25XXFD_CAN_TEFCON_TEFTSEN		BIT(5)
+#  define MCP25XXFD_CAN_TEFCON_UINC		BIT(8)
+#  define MCP25XXFD_CAN_TEFCON_FRESET		BIT(10)
+#  define MCP25XXFD_CAN_TEFCON_FSIZE_BITS	5
+#  define MCP25XXFD_CAN_TEFCON_FSIZE_SHIFT	24
+#  define MCP25XXFD_CAN_TEFCON_FSIZE_MASK				\
+	GENMASK(MCP25XXFD_CAN_TEFCON_FSIZE_SHIFT +			\
+		MCP25XXFD_CAN_TEFCON_FSIZE_BITS - 1,			\
+		MCP25XXFD_CAN_TEFCON_FSIZE_SHIFT)
+#define MCP25XXFD_CAN_TEFSTA			MCP25XXFD_CAN_SFR_BASE(0x44)
+#  define MCP25XXFD_CAN_TEFSTA_TEFNEIF		BIT(0)
+#  define MCP25XXFD_CAN_TEFSTA_TEFHIF		BIT(1)
+#  define MCP25XXFD_CAN_TEFSTA_TEFFIF		BIT(2)
+#  define MCP25XXFD_CAN_TEFSTA_TEVOVIF		BIT(3)
+#define MCP25XXFD_CAN_TEFUA			MCP25XXFD_CAN_SFR_BASE(0x48)
+#define MCP25XXFD_CAN_RESERVED			MCP25XXFD_CAN_SFR_BASE(0x4C)
+#define MCP25XXFD_CAN_TXQCON			MCP25XXFD_CAN_SFR_BASE(0x50)
+#  define MCP25XXFD_CAN_TXQCON_TXQNIE		BIT(0)
+#  define MCP25XXFD_CAN_TXQCON_TXQEIE		BIT(2)
+#  define MCP25XXFD_CAN_TXQCON_TXATIE		BIT(4)
+#  define MCP25XXFD_CAN_TXQCON_TXEN		BIT(7)
+#  define MCP25XXFD_CAN_TXQCON_UINC		BIT(8)
+#  define MCP25XXFD_CAN_TXQCON_TXREQ		BIT(9)
+#  define MCP25XXFD_CAN_TXQCON_FRESET		BIT(10)
+#  define MCP25XXFD_CAN_TXQCON_TXPRI_BITS	5
+#  define MCP25XXFD_CAN_TXQCON_TXPRI_SHIFT	16
+#  define MCP25XXFD_CAN_TXQCON_TXPRI_MASK				\
+	GENMASK(MCP25XXFD_CAN_TXQCON_TXPRI_SHIFT +			\
+		MCP25XXFD_CAN_TXQCON_TXPRI_BITS - 1,			\
+		MCP25XXFD_CAN_TXQCON_TXPRI_SHIFT)
+#  define MCP25XXFD_CAN_TXQCON_TXAT_BITS	2
+#  define MCP25XXFD_CAN_TXQCON_TXAT_SHIFT	21
+#  define MCP25XXFD_CAN_TXQCON_TXAT_MASK				\
+	GENMASK(MCP25XXFD_CAN_TXQCON_TXAT_SHIFT +			\
+		#MCP25XXFD_CAN_TXQCON_TXAT_BITS - 1,			\
+		MCP25XXFD_CAN_TXQCON_TXAT_SHIFT)
+#  define MCP25XXFD_CAN_TXQCON_FSIZE_BITS	5
+#  define MCP25XXFD_CAN_TXQCON_FSIZE_SHIFT	24
+#  define MCP25XXFD_CAN_TXQCON_FSIZE_MASK				\
+	GENMASK(MCP25XXFD_CAN_TXQCON_FSIZE_SHIFT +			\
+		MCP25XXFD_CAN_TXQCON_FSIZE_BITS - 1,			\
+		MCP25XXFD_CAN_TXQCON_FSIZE_SHIFT)
+#  define MCP25XXFD_CAN_TXQCON_PLSIZE_BITS	3
+#  define MCP25XXFD_CAN_TXQCON_PLSIZE_SHIFT	29
+#  define MCP25XXFD_CAN_TXQCON_PLSIZE_MASK				\
+	GENMASK(MCP25XXFD_CAN_TXQCON_PLSIZE_SHIFT +			\
+		MCP25XXFD_CAN_TXQCON_PLSIZE_BITS - 1,			\
+		MCP25XXFD_CAN_TXQCON_PLSIZE_SHIFT)
+#    define MCP25XXFD_CAN_TXQCON_PLSIZE_8	0
+#    define MCP25XXFD_CAN_TXQCON_PLSIZE_12	1
+#    define MCP25XXFD_CAN_TXQCON_PLSIZE_16	2
+#    define MCP25XXFD_CAN_TXQCON_PLSIZE_20	3
+#    define MCP25XXFD_CAN_TXQCON_PLSIZE_24	4
+#    define MCP25XXFD_CAN_TXQCON_PLSIZE_32	5
+#    define MCP25XXFD_CAN_TXQCON_PLSIZE_48	6
+#    define MCP25XXFD_CAN_TXQCON_PLSIZE_64	7
+
+#define MCP25XXFD_CAN_TXQSTA			MCP25XXFD_CAN_SFR_BASE(0x54)
+#  define MCP25XXFD_CAN_TXQSTA_TXQNIF		BIT(0)
+#  define MCP25XXFD_CAN_TXQSTA_TXQEIF		BIT(2)
+#  define MCP25XXFD_CAN_TXQSTA_TXATIF		BIT(4)
+#  define MCP25XXFD_CAN_TXQSTA_TXERR		BIT(5)
+#  define MCP25XXFD_CAN_TXQSTA_TXLARB		BIT(6)
+#  define MCP25XXFD_CAN_TXQSTA_TXABT		BIT(7)
+#  define MCP25XXFD_CAN_TXQSTA_TXQCI_BITS	5
+#  define MCP25XXFD_CAN_TXQSTA_TXQCI_SHIFT	8
+#  define MCP25XXFD_CAN_TXQSTA_TXQCI_MASK				\
+	GENMASK(MCP25XXFD_CAN_TXQSTA_TXQCI_SHIFT +			\
+		MCP25XXFD_CAN_TXQSTA_TXQCI_BITS - 1,			\
+		MCP25XXFD_CAN_TXQSTA_TXQCI_SHIFT)
+
+#define MCP25XXFD_CAN_TXQUA			MCP25XXFD_CAN_SFR_BASE(0x58)
+#define MCP25XXFD_CAN_FIFOCON(x)					\
+	MCP25XXFD_CAN_SFR_BASE(0x5C + 12 * ((x) - 1))
+#define MCP25XXFD_CAN_FIFOCON_TFNRFNIE		BIT(0)
+#define MCP25XXFD_CAN_FIFOCON_TFHRFHIE		BIT(1)
+#define MCP25XXFD_CAN_FIFOCON_TFERFFIE		BIT(2)
+#define MCP25XXFD_CAN_FIFOCON_RXOVIE		BIT(3)
+#define MCP25XXFD_CAN_FIFOCON_TXATIE		BIT(4)
+#define MCP25XXFD_CAN_FIFOCON_RXTSEN		BIT(5)
+#define MCP25XXFD_CAN_FIFOCON_RTREN		BIT(6)
+#define MCP25XXFD_CAN_FIFOCON_TXEN		BIT(7)
+#define MCP25XXFD_CAN_FIFOCON_UINC		BIT(8)
+#define MCP25XXFD_CAN_FIFOCON_TXREQ		BIT(9)
+#define MCP25XXFD_CAN_FIFOCON_FRESET		BIT(10)
+#  define MCP25XXFD_CAN_FIFOCON_TXPRI_BITS	5
+#  define MCP25XXFD_CAN_FIFOCON_TXPRI_SHIFT	16
+#  define MCP25XXFD_CAN_FIFOCON_TXPRI_MASK				\
+	GENMASK(MCP25XXFD_CAN_FIFOCON_TXPRI_SHIFT +			\
+		MCP25XXFD_CAN_FIFOCON_TXPRI_BITS - 1,			\
+		MCP25XXFD_CAN_FIFOCON_TXPRI_SHIFT)
+#  define MCP25XXFD_CAN_FIFOCON_TXAT_BITS	2
+#  define MCP25XXFD_CAN_FIFOCON_TXAT_SHIFT	21
+#  define MCP25XXFD_CAN_FIFOCON_TXAT_MASK				\
+	GENMASK(MCP25XXFD_CAN_FIFOCON_TXAT_SHIFT +			\
+		MCP25XXFD_CAN_FIFOCON_TXAT_BITS - 1,			\
+		MCP25XXFD_CAN_FIFOCON_TXAT_SHIFT)
+#  define MCP25XXFD_CAN_FIFOCON_TXAT_ONE_SHOT	0
+#  define MCP25XXFD_CAN_FIFOCON_TXAT_THREE_SHOT	1
+#  define MCP25XXFD_CAN_FIFOCON_TXAT_UNLIMITED	2
+#  define MCP25XXFD_CAN_FIFOCON_FSIZE_BITS	5
+#  define MCP25XXFD_CAN_FIFOCON_FSIZE_SHIFT	24
+#  define MCP25XXFD_CAN_FIFOCON_FSIZE_MASK				\
+	GENMASK(MCP25XXFD_CAN_FIFOCON_FSIZE_SHIFT +			\
+		MCP25XXFD_CAN_FIFOCON_FSIZE_BITS - 1,			\
+		MCP25XXFD_CAN_FIFOCON_FSIZE_SHIFT)
+#  define MCP25XXFD_CAN_FIFOCON_PLSIZE_BITS	3
+#  define MCP25XXFD_CAN_FIFOCON_PLSIZE_SHIFT	29
+#  define MCP25XXFD_CAN_FIFOCON_PLSIZE_MASK				\
+	GENMASK(MCP25XXFD_CAN_FIFOCON_PLSIZE_SHIFT +			\
+		MCP25XXFD_CAN_FIFOCON_PLSIZE_BITS - 1,			\
+		MCP25XXFD_CAN_FIFOCON_PLSIZE_SHIFT)
+#define MCP25XXFD_CAN_FIFOSTA(x)					\
+	MCP25XXFD_CAN_SFR_BASE(0x60 + 12 * ((x) - 1))
+#  define MCP25XXFD_CAN_FIFOSTA_TFNRFNIF	BIT(0)
+#  define MCP25XXFD_CAN_FIFOSTA_TFHRFHIF	BIT(1)
+#  define MCP25XXFD_CAN_FIFOSTA_TFERFFIF	BIT(2)
+#  define MCP25XXFD_CAN_FIFOSTA_RXOVIF		BIT(3)
+#  define MCP25XXFD_CAN_FIFOSTA_TXATIF		BIT(4)
+#  define MCP25XXFD_CAN_FIFOSTA_TXERR		BIT(5)
+#  define MCP25XXFD_CAN_FIFOSTA_TXLARB		BIT(6)
+#  define MCP25XXFD_CAN_FIFOSTA_TXABT		BIT(7)
+#  define MCP25XXFD_CAN_FIFOSTA_FIFOCI_BITS	5
+#  define MCP25XXFD_CAN_FIFOSTA_FIFOCI_SHIFT	8
+#  define MCP25XXFD_CAN_FIFOSTA_FIFOCI_MASK				\
+	GENMASK(MCP25XXFD_CAN_FIFOSTA_FIFOCI_SHIFT +			\
+		MCP25XXFD_CAN_FIFOSTA_FIFOCI_BITS - 1,			\
+		MCP25XXFD_CAN_FIFOSTA_FIFOCI_SHIFT)
+#define MCP25XXFD_CAN_FIFOUA(x)						\
+	MCP25XXFD_CAN_SFR_BASE(0x64 + 12 * ((x) - 1))
+#define MCP25XXFD_CAN_FLTCON(x)						\
+	MCP25XXFD_CAN_SFR_BASE(0x1D0 + ((x) & 0x1c))
+#  define MCP25XXFD_CAN_FILCON_SHIFT(x)		(((x) & 3) * 8)
+#  define MCP25XXFD_CAN_FILCON_BITS(x)		MCP25XXFD_CAN_FILCON_BITS_
+#  define MCP25XXFD_CAN_FILCON_BITS_		4
+	/* avoid macro reuse warning, so do not use GENMASK as above */
+#  define MCP25XXFD_CAN_FILCON_MASK(x)					\
+	(GENMASK(MCP25XXFD_CAN_FILCON_BITS_ - 1, 0) <<			\
+	 MCP25XXFD_CAN_FILCON_SHIFT(x))
+#  define MCP25XXFD_CAN_FIFOCON_FLTEN(x)				\
+	BIT(7 + MCP25XXFD_CAN_FILCON_SHIFT(x))
+#define MCP25XXFD_CAN_FLTOBJ(x)						\
+	MCP25XXFD_CAN_SFR_BASE(0x1F0 + 8 * (x))
+#  define MCP25XXFD_CAN_FILOBJ_SID_BITS		11
+#  define MCP25XXFD_CAN_FILOBJ_SID_SHIFT	0
+#  define MCP25XXFD_CAN_FILOBJ_SID_MASK					\
+	GENMASK(MCP25XXFD_CAN_FILOBJ_SID_SHIFT +			\
+		MCP25XXFD_CAN_FILOBJ_SID_BITS - 1,			\
+		MCP25XXFD_CAN_FILOBJ_SID_SHIFT)
+#  define MCP25XXFD_CAN_FILOBJ_EID_BITS		18
+#  define MCP25XXFD_CAN_FILOBJ_EID_SHIFT	12
+#  define MCP25XXFD_CAN_FILOBJ_EID_MASK					\
+	GENMASK(MCP25XXFD_CAN_FILOBJ_EID_SHIFT +			\
+		MCP25XXFD_CAN_FILOBJ_EID_BITS - 1,			\
+		MCP25XXFD_CAN_FILOBJ_EID_SHIFT)
+#  define MCP25XXFD_CAN_FILOBJ_SID11		BIT(29)
+#  define MCP25XXFD_CAN_FILOBJ_EXIDE		BIT(30)
+#define MCP25XXFD_CAN_FLTMASK(x)					\
+	MCP25XXFD_CAN_SFR_BASE(0x1F4 + 8 * (x))
+#  define MCP25XXFD_CAN_FILMASK_MSID_BITS	11
+#  define MCP25XXFD_CAN_FILMASK_MSID_SHIFT	0
+#  define MCP25XXFD_CAN_FILMASK_MSID_MASK				\
+	GENMASK(MCP25XXFD_CAN_FILMASK_MSID_SHIFT +			\
+		MCP25XXFD_CAN_FILMASK_MSID_BITS - 1,			\
+		MCP25XXFD_CAN_FILMASK_MSID_SHIFT)
+#  define MCP25XXFD_CAN_FILMASK_MEID_BITS	18
+#  define MCP25XXFD_CAN_FILMASK_MEID_SHIFT	12
+#  define MCP25XXFD_CAN_FILMASK_MEID_MASK				\
+	GENMASK(MCP25XXFD_CAN_FILMASK_MEID_SHIFT +			\
+		MCP25XXFD_CAN_FILMASK_MEID_BITS - 1,			\
+		MCP25XXFD_CAN_FILMASK_MEID_SHIFT)
+#  define MCP25XXFD_CAN_FILMASK_MSID11		BIT(29)
+#  define MCP25XXFD_CAN_FILMASK_MIDE		BIT(30)
+
+/* the FIFO Objects in SRAM */
+#define MCP25XXFD_SRAM_SIZE 2048
+#define MCP25XXFD_SRAM_ADDR(x) (0x400 + (x))
+
+/* memory structure in sram for tx fifos */
+struct mcp25xxfd_can_obj_tx {
+	u32 id;
+	u32 flags;
+	u8 data[];
+};
+
+/* memory structure in sram for rx fifos */
+struct mcp25xxfd_can_obj_rx {
+	u32 id;
+	u32 flags;
+	u32 ts;
+	u8 data[];
+};
+
+/* memory structure in sram for tef fifos */
+struct mcp25xxfd_can_obj_tef {
+	u32 id;
+	u32 flags;
+	u32 ts;
+};
+
+#define MCP25XXFD_CAN_OBJ_ID_SID_BITS		11
+#define MCP25XXFD_CAN_OBJ_ID_SID_SHIFT		0
+#define MCP25XXFD_CAN_OBJ_ID_SID_MASK					\
+	GENMASK(MCP25XXFD_CAN_OBJ_ID_SID_SHIFT +			\
+		MCP25XXFD_CAN_OBJ_ID_SID_BITS - 1,			\
+		MCP25XXFD_CAN_OBJ_ID_SID_SHIFT)
+#define MCP25XXFD_CAN_OBJ_ID_EID_BITS		18
+#define MCP25XXFD_CAN_OBJ_ID_EID_SHIFT		11
+#define MCP25XXFD_CAN_OBJ_ID_EID_MASK					\
+	GENMASK(MCP25XXFD_CAN_OBJ_ID_EID_SHIFT +			\
+		MCP25XXFD_CAN_OBJ_ID_EID_BITS - 1,			\
+		MCP25XXFD_CAN_OBJ_ID_EID_SHIFT)
+#define MCP25XXFD_CAN_OBJ_ID_SID_BIT11		BIT(29)
+
+#define MCP25XXFD_CAN_OBJ_FLAGS_DLC_BITS	4
+#define MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT	0
+#define MCP25XXFD_CAN_OBJ_FLAGS_DLC_MASK				\
+	GENMASK(MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT +			\
+		MCP25XXFD_CAN_OBJ_FLAGS_DLC_BITS - 1,			\
+		MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT)
+#define MCP25XXFD_CAN_OBJ_FLAGS_IDE		BIT(4)
+#define MCP25XXFD_CAN_OBJ_FLAGS_RTR		BIT(5)
+#define MCP25XXFD_CAN_OBJ_FLAGS_BRS		BIT(6)
+#define MCP25XXFD_CAN_OBJ_FLAGS_FDF		BIT(7)
+#define MCP25XXFD_CAN_OBJ_FLAGS_ESI		BIT(8)
+#define MCP25XXFD_CAN_OBJ_FLAGS_SEQ_BITS	7
+#define MCP25XXFD_CAN_OBJ_FLAGS_SEQ_SHIFT	9
+#define MCP25XXFD_CAN_OBJ_FLAGS_SEQ_MASK				\
+	GENMASK(MCP25XXFD_CAN_OBJ_FLAGS_SEQ_SHIFT +			\
+		MCP25XXFD_CAN_OBJ_FLAGS_SEQ_BITS - 1,			\
+		MCP25XXFD_CAN_OBJ_FLAGS_SEQ_SHIFT)
+#define MCP25XXFD_CAN_OBJ_FLAGS_FILHIT_BITS	11
+#define MCP25XXFD_CAN_OBJ_FLAGS_FILHIT_SHIFT	5
+#define MCP25XXFD_CAN_OBJ_FLAGS_FILHIT_MASK				\
+	GENMASK(MCP25XXFD_CAN_FLAGS_FILHIT_SHIFT +			\
+		MCP25XXFD_CAN_FLAGS_FILHIT_BITS - 1,			\
+		MCP25XXFD_CAN_FLAGS_FILHIT_SHIFT)
+
+#endif /* __MCP25XXFD_REGS_H */
-- 
2.17.1


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

* [PATCH 3/6] can: mcp25xxfd: Add support for CAN reception
  2020-06-10  7:44 [PATCH 0/6] Add Microchip MCP25XXFD CAN driver Manivannan Sadhasivam
  2020-06-10  7:44 ` [PATCH 1/6] dt-bindings: can: Document devicetree bindings for MCP25XXFD Manivannan Sadhasivam
  2020-06-10  7:44 ` [PATCH 2/6] can: mcp25xxfd: Add Microchip MCP25XXFD CAN-FD driver infrastructure Manivannan Sadhasivam
@ 2020-06-10  7:44 ` Manivannan Sadhasivam
  2020-06-10  7:44 ` [PATCH 4/6] can: mcp25xxfd: Add CAN transmission support Manivannan Sadhasivam
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 26+ messages in thread
From: Manivannan Sadhasivam @ 2020-06-10  7:44 UTC (permalink / raw)
  To: wg, mkl; +Cc: kernel, linux-can, netdev, linux-kernel, Manivannan Sadhasivam

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

Add un-optimized CAN2.0 and CAN-FD reception support.

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.

Signed-off-by: Martin Sperl <kernel@martin.sperl.org>
[mani: misc cleanups for upstream]
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
 drivers/net/can/spi/mcp25xxfd/Makefile        |   3 +
 .../net/can/spi/mcp25xxfd/mcp25xxfd_base.c    |   6 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c | 395 ++++++++++-
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h |  35 +
 .../can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c    | 228 ++++++
 .../can/spi/mcp25xxfd/mcp25xxfd_can_fifo.h    |  16 +
 .../net/can/spi/mcp25xxfd/mcp25xxfd_can_id.h  |  69 ++
 .../net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c | 655 ++++++++++++++++++
 .../net/can/spi/mcp25xxfd/mcp25xxfd_can_int.h |  18 +
 .../can/spi/mcp25xxfd/mcp25xxfd_can_priv.h    | 131 ++++
 .../net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c  | 233 +++++++
 .../net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.h  |  18 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c |  14 +-
 .../net/can/spi/mcp25xxfd/mcp25xxfd_priv.h    |   2 +
 .../net/can/spi/mcp25xxfd/mcp25xxfd_regs.h    |   5 +
 15 files changed, 1823 insertions(+), 5 deletions(-)
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_id.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.h

diff --git a/drivers/net/can/spi/mcp25xxfd/Makefile b/drivers/net/can/spi/mcp25xxfd/Makefile
index d8fdb76a9578..5787bdd57a9d 100644
--- a/drivers/net/can/spi/mcp25xxfd/Makefile
+++ b/drivers/net/can/spi/mcp25xxfd/Makefile
@@ -1,6 +1,9 @@
 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_cmd.o
 mcp25xxfd-objs                  += mcp25xxfd_crc.o
 mcp25xxfd-objs                  += mcp25xxfd_ecc.o
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
index 4be456df0998..c6b67c54a3cd 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
@@ -123,6 +123,11 @@ static int mcp25xxfd_base_probe(struct spi_device *spi)
 	if (ret)
 		goto out_power;
 
+	/* setting up CAN */
+	ret = mcp25xxfd_can_setup(priv);
+	if (ret)
+		goto out_power;
+
 	dev_info(&spi->dev,
 		 "MCP%04x successfully initialized.\n", model);
 	return 0;
@@ -140,6 +145,7 @@ static int mcp25xxfd_base_remove(struct spi_device *spi)
 {
 	struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
 
+	mcp25xxfd_can_remove(priv);
 	mcp25xxfd_base_power_enable(priv->power, 0);
 	clk_disable_unprepare(priv->clk);
 
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
index 41a5ab508582..2ac78024c171 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
@@ -5,15 +5,119 @@
  * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
  */
 
+#include <linux/bitfield.h>
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
 #include <linux/device.h>
+#include <linux/interrupt.h>
 #include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
 #include <linux/spi/spi.h>
 
+#include "mcp25xxfd_base.h"
+#include "mcp25xxfd_can_fifo.h"
+#include "mcp25xxfd_can_int.h"
+#include "mcp25xxfd_can_priv.h"
 #include "mcp25xxfd_can.h"
 #include "mcp25xxfd_cmd.h"
+#include "mcp25xxfd_int.h"
 #include "mcp25xxfd_priv.h"
 #include "mcp25xxfd_regs.h"
 
+#include <uapi/linux/can/netlink.h>
+
+/* 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(MCP25XXFD_CAN_NBTCFG_TSEG1_BITS),
+	.tseg2_min      = 1,
+	.tseg2_max      = BIT(MCP25XXFD_CAN_NBTCFG_TSEG2_BITS),
+	.sjw_max        = BIT(MCP25XXFD_CAN_NBTCFG_SJW_BITS),
+	.brp_min        = 1,
+	.brp_max        = BIT(MCP25XXFD_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(MCP25XXFD_CAN_DBTCFG_TSEG1_BITS),
+	.tseg2_min      = 1,
+	.tseg2_max      = BIT(MCP25XXFD_CAN_DBTCFG_TSEG2_BITS),
+	.sjw_max        = BIT(MCP25XXFD_CAN_DBTCFG_SJW_BITS),
+	.brp_min        = 1,
+	.brp_max        = BIT(MCP25XXFD_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 can_bittiming *bt = &cpriv->can.bittiming;
+	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) << MCP25XXFD_CAN_NBTCFG_SJW_SHIFT) |
+		((tseg2 - 1) << MCP25XXFD_CAN_NBTCFG_TSEG2_SHIFT) |
+		((tseg1 - 1) << MCP25XXFD_CAN_NBTCFG_TSEG1_SHIFT) |
+		((brp - 1) << MCP25XXFD_CAN_NBTCFG_BRP_SHIFT);
+
+	return mcp25xxfd_cmd_write(cpriv->priv->spi, MCP25XXFD_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 tdco;
+	int ret;
+
+	/* set up Transmitter delay compensation */
+	cpriv->regs.tdc = FIELD_PREP(MCP25XXFD_CAN_TDC_TDCMOD_MASK,
+				     MCP25XXFD_CAN_TDC_TDCMOD_AUTO);
+
+	/* configure TDC offsets */
+	tdco = clamp_t(int, bt->brp * tseg1, -64, 63);
+	cpriv->regs.tdc &= ~MCP25XXFD_CAN_TDC_TDCO_MASK;
+	cpriv->regs.tdc |= FIELD_PREP(MCP25XXFD_CAN_TDC_TDCO_MASK, tdco);
+
+	/* set TDC */
+	ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_CAN_TDC, cpriv->regs.tdc);
+	if (ret)
+		return ret;
+
+	/* calculate data bit timing */
+	cpriv->regs.dbtcfg =
+		FIELD_PREP(MCP25XXFD_CAN_DBTCFG_SJW_MASK, (sjw - 1)) |
+		FIELD_PREP(MCP25XXFD_CAN_DBTCFG_TSEG2_MASK, (tseg2 - 1)) |
+		FIELD_PREP(MCP25XXFD_CAN_DBTCFG_TSEG1_MASK, (tseg1 - 1)) |
+		FIELD_PREP(MCP25XXFD_CAN_DBTCFG_BRP_MASK, (brp - 1));
+
+	return mcp25xxfd_cmd_write(spi, MCP25XXFD_CAN_DBTCFG,
+				   cpriv->regs.dbtcfg);
+}
+
 int mcp25xxfd_can_get_mode(struct mcp25xxfd_priv *priv, u32 *reg)
 {
 	int ret;
@@ -25,10 +129,10 @@ int mcp25xxfd_can_get_mode(struct mcp25xxfd_priv *priv, u32 *reg)
 	return FIELD_GET(MCP25XXFD_CAN_CON_OPMOD_MASK, *reg);
 }
 
-static int mcp25xxfd_can_switch_mode(struct mcp25xxfd_priv *priv,
-				     u32 *reg, int mode)
+int mcp25xxfd_can_switch_mode_no_wait(struct mcp25xxfd_priv *priv,
+				      u32 *reg, int mode)
 {
-	int ret, i;
+	int ret;
 
 	ret = mcp25xxfd_can_get_mode(priv, reg);
 	if (ret < 0)
@@ -41,7 +145,15 @@ static int mcp25xxfd_can_switch_mode(struct mcp25xxfd_priv *priv,
 		FIELD_PREP(MCP25XXFD_CAN_CON_OPMOD_MASK, mode);
 
 	/* Request the mode switch */
-	ret = mcp25xxfd_cmd_write(priv->spi, MCP25XXFD_CAN_CON, *reg);
+	return mcp25xxfd_cmd_write(priv->spi, MCP25XXFD_CAN_CON, *reg);
+}
+
+int mcp25xxfd_can_switch_mode(struct mcp25xxfd_priv *priv, u32 *reg, int mode)
+{
+	int ret, i;
+
+	/* trigger the mode switch itself */
+	ret = mcp25xxfd_can_switch_mode_no_wait(priv, reg, mode);
 	if (ret)
 		return ret;
 
@@ -139,3 +251,278 @@ int mcp25xxfd_can_probe(struct mcp25xxfd_priv *priv)
 	/* Finally check if modeswitch is really working */
 	return mcp25xxfd_can_probe_modeswitch(priv);
 }
+
+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 = MCP25XXFD_CAN_CON_STEF; /* enable TEF, disable TXQ */
+
+	/* non iso FD mode */
+	if (!(cpriv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO))
+		cpriv->regs.con |= MCP25XXFD_CAN_CON_ISOCRCEN;
+
+	/* one shot */
+	if (cpriv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+		cpriv->regs.con |= MCP25XXFD_CAN_CON_RTXAT;
+
+	/* apply it now together with a mode switch */
+	ret = mcp25xxfd_can_switch_mode(cpriv->priv, &cpriv->regs.con,
+					MCP25XXFD_CAN_CON_MODE_CONFIG);
+	if (ret)
+		return 0;
+
+	/* time stamp control register - 1ns resolution */
+	cpriv->regs.tscon = 0;
+	ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_CAN_TBC, 0);
+	if (ret)
+		return ret;
+
+	cpriv->regs.tscon = MCP25XXFD_CAN_TSCON_TBCEN |
+			    FIELD_PREP(MCP25XXFD_CAN_TSCON_TBCPRE_MASK,
+				       ((cpriv->can.clock.freq / 1000000)));
+	ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_CAN_TSCON, cpriv->regs.tscon);
+	if (ret)
+		return ret;
+
+	/* setup fifos */
+	ret = mcp25xxfd_can_fifo_setup(cpriv);
+	if (ret)
+		return ret;
+
+	/* setup can bittiming now - the do_set_bittiming methods
+	 * are not used as they get called 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;
+}
+
+/* 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 = FIELD_PREP(MCP25XXFD_CAN_TREC_TEC_MASK,
+				cpriv->status.trec);
+	bec->rxerr = FIELD_PREP(MCP25XXFD_CAN_TREC_REC_MASK,
+				cpriv->status.trec);
+
+	return 0;
+}
+
+static int mcp25xxfd_can_open(struct net_device *net)
+{
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct spi_device *spi = cpriv->priv->spi;
+	int ret, mode;
+
+	ret = open_candev(net);
+	if (ret) {
+		netdev_err(net, "unable to set initial baudrate!\n");
+		return ret;
+	}
+
+	/* request an IRQ but keep disabled for now */
+	ret = request_threaded_irq(spi->irq, NULL,
+				   mcp25xxfd_can_int,
+				   IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+				   cpriv->priv->device_name, cpriv);
+	if (ret) {
+		dev_err(&spi->dev, "failed to acquire irq %d - %i\n",
+			spi->irq, ret);
+		goto out_candev;
+	}
+
+	disable_irq(spi->irq);
+	cpriv->irq.allocated = true;
+	cpriv->irq.enabled = false;
+
+	/* enable power to the transceiver */
+	ret = mcp25xxfd_base_power_enable(cpriv->transceiver, 1);
+	if (ret)
+		goto out_irq;
+
+	/* configure controller for reception */
+	ret = mcp25xxfd_can_config(net);
+	if (ret)
+		goto out_power;
+
+	/* setting up state */
+	cpriv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+	/* enable interrupts */
+	ret = mcp25xxfd_int_enable(cpriv->priv, true);
+	if (ret)
+		goto out_canconfig;
+
+	if (cpriv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
+		mode = MCP25XXFD_CAN_CON_MODE_EXT_LOOPBACK;
+	else if (cpriv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+		mode = MCP25XXFD_CAN_CON_MODE_LISTENONLY;
+	else if (cpriv->can.ctrlmode & CAN_CTRLMODE_FD)
+		mode = MCP25XXFD_CAN_CON_MODE_MIXED;
+	else
+		mode = MCP25XXFD_CAN_CON_MODE_CAN2_0;
+
+	/* switch to active mode */
+	ret = mcp25xxfd_can_switch_mode(cpriv->priv, &cpriv->regs.con, mode);
+	if (ret)
+		goto out_int;
+
+	return 0;
+
+out_int:
+	mcp25xxfd_int_enable(cpriv->priv, false);
+out_canconfig:
+	mcp25xxfd_can_fifo_release(cpriv);
+out_power:
+	mcp25xxfd_base_power_enable(cpriv->transceiver, 0);
+out_irq:
+	free_irq(spi->irq, cpriv);
+	cpriv->irq.allocated = false;
+	cpriv->irq.enabled = false;
+out_candev:
+	close_candev(net);
+	return ret;
+}
+
+static void mcp25xxfd_can_shutdown(struct mcp25xxfd_can_priv *cpriv)
+{
+	/* switch us to CONFIG mode - this disables the controller */
+	mcp25xxfd_can_switch_mode(cpriv->priv, &cpriv->regs.con,
+				  MCP25XXFD_CAN_CON_MODE_CONFIG);
+}
+
+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;
+
+	/* shutdown the can controller */
+	mcp25xxfd_can_shutdown(cpriv);
+
+	/* disable inerrupts on controller */
+	mcp25xxfd_int_enable(cpriv->priv, false);
+
+	/* disable the transceiver */
+	mcp25xxfd_base_power_enable(cpriv->transceiver, 0);
+
+	/* disable interrupt on host */
+	free_irq(spi->irq, cpriv);
+	cpriv->irq.allocated = false;
+	cpriv->irq.enabled = false;
+
+	/* close the can_decice */
+	close_candev(net);
+
+	return 0;
+}
+
+static const struct net_device_ops mcp25xxfd_netdev_ops = {
+	.ndo_open = mcp25xxfd_can_open,
+	.ndo_stop = mcp25xxfd_can_stop,
+	.ndo_change_mtu = can_change_mtu,
+};
+
+/* probe and remove */
+int mcp25xxfd_can_setup(struct mcp25xxfd_priv *priv)
+{
+	struct spi_device *spi = priv->spi;
+	struct mcp25xxfd_can_priv *cpriv;
+	struct net_device *net;
+	struct regulator *transceiver;
+	int ret;
+
+	/* get transceiver power regulator*/
+	transceiver = devm_regulator_get(&spi->dev, "xceiver");
+	if (PTR_ERR(transceiver) == -EPROBE_DEFER)
+		return PTR_ERR(transceiver);
+
+	/* allocate can device */
+	net = alloc_candev(sizeof(*cpriv), TX_ECHO_SKB_MAX);
+	if (!net)
+		return -ENOMEM;
+
+	cpriv = netdev_priv(net);
+	cpriv->priv = priv;
+	priv->cpriv = cpriv;
+
+	/* setup network */
+	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 would be still in sleep
+	 * mode, which does not help as things are configured in open instead.
+	 */
+	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;
+	}
+
+	return 0;
+
+out:
+	free_candev(net);
+	priv->cpriv = NULL;
+
+	return ret;
+}
+
+void mcp25xxfd_can_remove(struct mcp25xxfd_priv *priv)
+{
+	if (priv->cpriv) {
+		unregister_candev(priv->cpriv->can.dev);
+		free_candev(priv->cpriv->can.dev);
+		priv->cpriv = NULL;
+	}
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
index f54c716735fb..4b18b5bb3d45 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
@@ -8,9 +8,44 @@
 #ifndef __MCP25XXFD_CAN_H
 #define __MCP25XXFD_CAN_H
 
+#include "mcp25xxfd_can_priv.h"
 #include "mcp25xxfd_priv.h"
+#include "mcp25xxfd_regs.h"
+
+/* get the optimal controller target mode */
+static inline
+int mcp25xxfd_can_targetmode(struct mcp25xxfd_can_priv *cpriv)
+{
+	return (cpriv->can.dev->mtu == CAN_MTU) ?
+		MCP25XXFD_CAN_CON_MODE_CAN2_0 : MCP25XXFD_CAN_CON_MODE_MIXED;
+}
+
+static inline
+void mcp25xxfd_can_queue_frame(struct mcp25xxfd_can_priv *cpriv,
+			       s32 fifo, u16 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++;
+}
+
+/* get the current controller mode */
+int mcp25xxfd_can_get_mode(struct mcp25xxfd_priv *priv, u32 *reg);
+
+/* switch controller mode */
+int mcp25xxfd_can_switch_mode_no_wait(struct mcp25xxfd_priv *priv,
+				      u32 *reg, int mode);
+int mcp25xxfd_can_switch_mode(struct mcp25xxfd_priv *priv,
+			      u32 *reg, int mode);
 
 /* probe the can controller */
 int mcp25xxfd_can_probe(struct mcp25xxfd_priv *priv);
 
+/* setup and the can controller net interface */
+int mcp25xxfd_can_setup(struct mcp25xxfd_priv *priv);
+void mcp25xxfd_can_remove(struct mcp25xxfd_priv *priv);
+
 #endif /* __MCP25XXFD_CAN_H */
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..4bd776772d2d
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+
+/* here we define and configure the fifo layout */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "mcp25xxfd_can.h"
+#include "mcp25xxfd_can_fifo.h"
+#include "mcp25xxfd_can_priv.h"
+#include "mcp25xxfd_cmd.h"
+
+static int mcp25xxfd_can_fifo_get_address(struct mcp25xxfd_can_priv *cpriv)
+{
+	int fifo, ret;
+
+	/* we need to move out of config mode to force address computation */
+	ret = mcp25xxfd_can_switch_mode(cpriv->priv, &cpriv->regs.con,
+					MCP25XXFD_CAN_CON_MODE_INT_LOOPBACK);
+	if (ret)
+		return ret;
+
+	/* and get back into config mode */
+	ret = mcp25xxfd_can_switch_mode(cpriv->priv, &cpriv->regs.con,
+					MCP25XXFD_CAN_CON_MODE_CONFIG);
+	if (ret)
+		return ret;
+
+	/* read address and config back in */
+	for (fifo = 1; fifo < 32; fifo++) {
+		ret = mcp25xxfd_cmd_read(cpriv->priv->spi,
+					 MCP25XXFD_CAN_FIFOUA(fifo),
+					 &cpriv->fifos.info[fifo].offset);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int mcp25xxfd_can_fifo_setup_config(struct mcp25xxfd_can_priv *cpriv,
+					   struct mcp25xxfd_fifo *desc,
+					   u32 flags, u32 flags_last)
+{
+	u32 val;
+	int i, p, f, c, ret;
+
+	for (i = 0, f = desc->start, c = desc->count, p = 31;
+	     c > 0; i++, f++, p--, c--) {
+		val = (c > 1) ? flags : flags_last;
+
+		/* write the config to the controller in one go */
+		ret = mcp25xxfd_cmd_write(cpriv->priv->spi,
+					  MCP25XXFD_CAN_FIFOCON(f), val);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int mcp25xxfd_can_fifo_setup_rx(struct mcp25xxfd_can_priv *cpriv)
+{
+	u32 rx_flags = MCP25XXFD_CAN_FIFOCON_FRESET |     /* reset FIFO */
+		MCP25XXFD_CAN_FIFOCON_RXTSEN |            /* RX timestamps */
+		MCP25XXFD_CAN_FIFOCON_TFERFFIE |          /* FIFO Full */
+		MCP25XXFD_CAN_FIFOCON_TFHRFHIE |          /* FIFO Half Full*/
+		MCP25XXFD_CAN_FIFOCON_TFNRFNIE |          /* FIFO not empty */
+		(cpriv->fifos.payload_mode <<
+		 MCP25XXFD_CAN_FIFOCON_PLSIZE_SHIFT) |
+		(0 << MCP25XXFD_CAN_FIFOCON_FSIZE_SHIFT); /* 1 FIFO deep */
+	/* enable overflow int on last fifo */
+	u32 rx_flags_last = rx_flags | MCP25XXFD_CAN_FIFOCON_RXOVIE;
+
+	return mcp25xxfd_can_fifo_setup_config(cpriv, &cpriv->fifos.rx,
+					       rx_flags, rx_flags_last);
+}
+
+static int mcp25xxfd_can_fifo_setup_rxfilter(struct mcp25xxfd_can_priv *cpriv)
+{
+	u8 filter_con[32];
+	int c, f;
+
+	/* clear the filters and filter mappings for all filters */
+	memset(filter_con, 0, sizeof(filter_con));
+
+	/* and now set up the rx filters */
+	for (c = 0, f = cpriv->fifos.rx.start; c < cpriv->fifos.rx.count;
+	     c++, f++) {
+		/* set up filter config - we can use the mask of filter 0 */
+		filter_con[c] = MCP25XXFD_CAN_FIFOCON_FLTEN(0) |
+			(f << MCP25XXFD_CAN_FILCON_SHIFT(0));
+	}
+
+	/* and set up filter control */
+	return mcp25xxfd_cmd_write_regs(cpriv->priv->spi,
+					MCP25XXFD_CAN_FLTCON(0),
+					(u32 *)filter_con, sizeof(filter_con));
+}
+
+static int mcp25xxfd_can_fifo_compute(struct mcp25xxfd_can_priv *cpriv)
+{
+	int rx_memory_available;
+
+	switch (cpriv->can.dev->mtu) {
+	case CAN_MTU:
+		/* MTU is 8 */
+		cpriv->fifos.payload_size = 8;
+		cpriv->fifos.payload_mode = MCP25XXFD_CAN_TXQCON_PLSIZE_8;
+
+		break;
+	case CANFD_MTU:
+		/* MTU is 64 */
+		cpriv->fifos.payload_size = 64;
+		cpriv->fifos.payload_mode = MCP25XXFD_CAN_TXQCON_PLSIZE_64;
+
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* compute effective sizes */
+	cpriv->fifos.rx.size = sizeof(struct mcp25xxfd_can_obj_rx) +
+		cpriv->fifos.payload_size;
+
+	/* calculate evailable memory for RX_fifos */
+	rx_memory_available = MCP25XXFD_SRAM_SIZE;
+
+	/* calculate possible amount of RX fifos */
+	cpriv->fifos.rx.count = rx_memory_available / cpriv->fifos.rx.size;
+
+	/* 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 > 31)
+		cpriv->fifos.rx.count = 31;
+
+	cpriv->fifos.rx.start = 1;
+
+	return 0;
+}
+
+static int mcp25xxfd_can_fifo_clear_regs(struct mcp25xxfd_can_priv *cpriv,
+					 u32 start, u32 end)
+{
+	size_t len = end - start;
+	u8 *data;
+	int ret;
+
+	data = kzalloc(len, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	ret = mcp25xxfd_cmd_write_regs(cpriv->priv->spi,
+				       start, (u32 *)data, len);
+
+	kfree(data);
+
+	return ret;
+}
+
+static int mcp25xxfd_can_fifo_clear(struct mcp25xxfd_can_priv *cpriv)
+{
+	int ret;
+
+	memset(&cpriv->fifos.info, 0, sizeof(cpriv->fifos.info));
+	memset(&cpriv->fifos.rx, 0, sizeof(cpriv->fifos.rx));
+
+	/* clear FIFO config */
+	ret = mcp25xxfd_can_fifo_clear_regs(cpriv, MCP25XXFD_CAN_FIFOCON(1),
+					    MCP25XXFD_CAN_FIFOCON(32));
+	if (ret)
+		return ret;
+
+	/* clear the filter mask - match any frame with every filter */
+	return mcp25xxfd_can_fifo_clear_regs(cpriv, MCP25XXFD_CAN_FLTCON(0),
+					     MCP25XXFD_CAN_FLTCON(32));
+}
+
+int mcp25xxfd_can_fifo_setup(struct mcp25xxfd_can_priv *cpriv)
+{
+	int ret;
+
+	/* clear fifo config */
+	ret = mcp25xxfd_can_fifo_clear(cpriv);
+	if (ret)
+		return ret;
+
+	ret = mcp25xxfd_can_fifo_compute(cpriv);
+	if (ret)
+		return ret;
+
+	cpriv->regs.tefcon = 0;
+	ret = mcp25xxfd_cmd_write(cpriv->priv->spi, MCP25XXFD_CAN_TEFCON,
+				  cpriv->regs.tefcon);
+	if (ret)
+		return ret;
+
+	ret = mcp25xxfd_cmd_write(cpriv->priv->spi, MCP25XXFD_CAN_TXQCON, 0);
+	if (ret)
+		return ret;
+
+	ret = mcp25xxfd_can_fifo_setup_rx(cpriv);
+	if (ret)
+		return ret;
+
+	ret = mcp25xxfd_can_fifo_setup_rxfilter(cpriv);
+	if (ret)
+		return ret;
+
+	/* get fifo addresses */
+	ret = mcp25xxfd_can_fifo_get_address(cpriv);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+void mcp25xxfd_can_fifo_release(struct mcp25xxfd_can_priv *cpriv)
+{
+	mcp25xxfd_can_fifo_clear(cpriv);
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.h
new file mode 100644
index 000000000000..ed2daa05220a
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+
+#ifndef __MCP25XXFD_CAN_FIFO_H
+#define __MCP25XXFD_CAN_FIFO_H
+
+#include "mcp25xxfd_can_priv.h"
+
+int mcp25xxfd_can_fifo_setup(struct mcp25xxfd_can_priv *cpriv);
+void mcp25xxfd_can_fifo_release(struct mcp25xxfd_can_priv *cpriv);
+
+#endif /* __MCP25XXFD_CAN_FIFO_H */
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_id.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_id.h
new file mode 100644
index 000000000000..00a6c6639bd5
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_id.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+
+#ifndef __MCP25XXFD_CAN_IF_H
+#define __MCP25XXFD_CAN_IF_H
+
+#include <uapi/linux/can.h>
+
+#include "mcp25xxfd_can_id.h"
+#include "mcp25xxfd_regs.h"
+
+/* ideally these would be defined in uapi/linux/can.h */
+#define MCP25XXFD_CAN_EFF_SID_SHIFT	(CAN_EFF_ID_BITS - CAN_SFF_ID_BITS)
+#define MCP25XXFD_CAN_EFF_SID_BITS	CAN_SFF_ID_BITS
+#define MCP25XXFD_CAN_EFF_SID_MASK					\
+	GENMASK(MCP25XXFD_CAN_EFF_SID_SHIFT + MCP25XXFD_CAN_EFF_SID_BITS - 1, \
+		MCP25XXFD_CAN_EFF_SID_SHIFT)
+#define MCP25XXFD_CAN_EFF_EID_SHIFT	0
+#define MCP25XXFD_CAN_EFF_EID_BITS	MCP25XXFD_CAN_EFF_SID_SHIFT
+#define MCP25XXFD_CAN_EFF_EID_MASK					\
+	GENMASK(MCP25XXFD_CAN_EFF_EID_SHIFT + MCP25XXFD_CAN_EFF_EID_BITS - 1, \
+		MCP25XXFD_CAN_EFF_EID_SHIFT)
+
+static inline
+void mcp25xxfd_can_id_from_mcp25xxfd(u32 mcp_id, u32 mcp_flags, u32 *can_id)
+{
+	u32 sid = (mcp_id & MCP25XXFD_CAN_OBJ_ID_SID_MASK) >>
+		MCP25XXFD_CAN_OBJ_ID_SID_SHIFT;
+	u32 eid = (mcp_id & MCP25XXFD_CAN_OBJ_ID_EID_MASK) >>
+		MCP25XXFD_CAN_OBJ_ID_EID_SHIFT;
+
+	/* select normal or extended ids */
+	if (mcp_flags & MCP25XXFD_CAN_OBJ_FLAGS_IDE) {
+		*can_id = (eid << MCP25XXFD_CAN_EFF_EID_SHIFT) |
+			(sid << MCP25XXFD_CAN_EFF_SID_SHIFT) |
+			CAN_EFF_FLAG;
+	} else {
+		*can_id = sid << MCP25XXFD_CAN_EFF_EID_SHIFT;
+	}
+	/* handle rtr */
+	*can_id |= (mcp_flags & MCP25XXFD_CAN_OBJ_FLAGS_RTR) ? CAN_RTR_FLAG : 0;
+}
+
+static inline
+void mcp25xxfd_can_id_to_mcp25xxfd(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 & MCP25XXFD_CAN_EFF_SID_MASK) >>
+			MCP25XXFD_CAN_EFF_SID_SHIFT;
+		int eid = (can_id & MCP25XXFD_CAN_EFF_EID_MASK) >>
+			MCP25XXFD_CAN_EFF_EID_SHIFT;
+		*id = (eid << MCP25XXFD_CAN_OBJ_ID_EID_SHIFT) |
+			(sid << MCP25XXFD_CAN_OBJ_ID_SID_SHIFT);
+		*flags = MCP25XXFD_CAN_OBJ_FLAGS_IDE;
+	} else {
+		*id = can_id & CAN_SFF_MASK;
+		*flags = 0;
+	}
+
+	/* Handle RTR */
+	*flags |= (can_id & CAN_RTR_FLAG) ? MCP25XXFD_CAN_OBJ_FLAGS_RTR : 0;
+}
+
+#endif /* __MCP25XXFD_CAN_IF_H */
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..83656b2604df
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c
@@ -0,0 +1,655 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+
+#include "mcp25xxfd_regs.h"
+#include "mcp25xxfd_can.h"
+#include "mcp25xxfd_can_int.h"
+#include "mcp25xxfd_can_priv.h"
+#include "mcp25xxfd_can_rx.h"
+#include "mcp25xxfd_cmd.h"
+#include "mcp25xxfd_ecc.h"
+
+#define MCP25XXFD_RESCHEDULE_TIMES 4
+
+static void mcp25xxfd_can_int_send_error_skb(struct mcp25xxfd_can_priv *cpriv)
+{
+	struct net_device *net = cpriv->can.dev;
+	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_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_int_submit_frames(struct mcp25xxfd_can_priv *cpriv)
+{
+	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 tx - actually TEF) by receive timestamp */
+	sort(queue, count, sizeof(*queue),
+	     mcp25xxfd_can_int_compare_obj_ts, NULL);
+
+	/* now submit the fifos  */
+	for (i = 0; i < count; i++) {
+		fifo = queue[i].fifo;
+		ret = mcp25xxfd_can_rx_submit_frame(cpriv, 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 when CAN bus is disconnected.
+	 */
+	if (!(cpriv->status.intf | MCP25XXFD_CAN_INT_IVMIE)) {
+		cpriv->status.intf |= MCP25XXFD_CAN_INT_IVMIE;
+		ret = mcp25xxfd_cmd_write_mask(cpriv->priv->spi,
+					       MCP25XXFD_CAN_INT,
+					       cpriv->status.intf,
+					       MCP25XXFD_CAN_INT_IVMIE);
+		if (ret)
+			return ret;
+	}
+
+out:
+	return 0;
+}
+
+static int mcp25xxfd_can_int_clear_int_flags(struct mcp25xxfd_can_priv *cpriv)
+{
+	u32 clearable_irq_active = cpriv->status.intf &
+		MCP25XXFD_CAN_INT_IF_CLEAR_MASK;
+	u32 clear_irq = cpriv->status.intf & (~MCP25XXFD_CAN_INT_IF_CLEAR_MASK);
+
+	if (!clearable_irq_active)
+		return 0;
+
+	return mcp25xxfd_cmd_write_mask(cpriv->priv->spi, MCP25XXFD_CAN_INT,
+					clear_irq, clearable_irq_active);
+}
+
+static
+int mcp25xxfd_can_int_handle_serrif_txmab(struct mcp25xxfd_can_priv *cpriv)
+{
+	int mode = mcp25xxfd_can_targetmode(cpriv);
+
+	cpriv->can.dev->stats.tx_fifo_errors++;
+	cpriv->can.dev->stats.tx_errors++;
+
+	/* data7 contains custom mcp25xxfd error flags */
+	cpriv->error_frame.data[7] |= MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_SERR_TX;
+
+	/* and switch back into the correct mode */
+	return mcp25xxfd_can_switch_mode_no_wait(cpriv->priv,
+						 &cpriv->regs.con, mode);
+}
+
+static
+int mcp25xxfd_can_int_handle_serrif_rxmab(struct mcp25xxfd_can_priv *cpriv)
+{
+	cpriv->can.dev->stats.rx_dropped++;
+	cpriv->can.dev->stats.rx_errors++;
+
+	/* data7 contains custom mcp25xxfd error flags */
+	cpriv->error_frame.data[7] |= MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_SERR_RX;
+
+	return 0;
+}
+
+static int mcp25xxfd_can_int_handle_serrif(struct mcp25xxfd_can_priv *cpriv)
+{
+	if (!(cpriv->status.intf & MCP25XXFD_CAN_INT_SERRIF))
+		return 0;
+
+	/* 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;
+
+	/* mode change + invalid message would indicate TX MAB Underflow */
+	if ((cpriv->status.intf & MCP25XXFD_CAN_INT_MODIF) &&
+	    (cpriv->status.intf & MCP25XXFD_CAN_INT_IVMIF)) {
+		return mcp25xxfd_can_int_handle_serrif_txmab(cpriv);
+	}
+
+	/* for RX there is only the RXIF an indicator - surprizingly RX-MAB
+	 * does not change mode or anything
+	 */
+	if (cpriv->status.intf & MCP25XXFD_CAN_INT_RXIF)
+		return mcp25xxfd_can_int_handle_serrif_rxmab(cpriv);
+
+	dev_warn_ratelimited(&cpriv->priv->spi->dev,
+			     "unidentified system interrupt - intf =  %08x\n",
+			     cpriv->status.intf);
+
+	return 0;
+}
+
+static int mcp25xxfd_can_int_handle_modif(struct mcp25xxfd_can_priv *cpriv)
+{
+	struct spi_device *spi = cpriv->priv->spi;
+	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 & MCP25XXFD_CAN_INT_MODIF))
+		return 0;
+
+	/* get the current mode */
+	ret = mcp25xxfd_can_get_mode(cpriv->priv, &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 == MCP25XXFD_CAN_CON_MODE_RESTRICTED) {
+		cpriv->mode = mode;
+		mode = mcp25xxfd_can_targetmode(cpriv);
+		return mcp25xxfd_can_switch_mode_no_wait(cpriv->priv,
+							 &cpriv->regs.con,
+							 mode);
+	}
+
+	/* the controller itself will transition to sleep, so we ignore it */
+	if (mode == MCP25XXFD_CAN_CON_MODE_SLEEP) {
+		cpriv->mode = mode;
+		return 0;
+	}
+
+	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_can_int_handle_eccif(struct mcp25xxfd_can_priv *cpriv)
+{
+	if (!(cpriv->status.intf & MCP25XXFD_CAN_INT_ECCIF))
+		return 0;
+
+	/* and prepare ERROR FRAME */
+	cpriv->error_frame.id |= CAN_ERR_CRTL;
+	cpriv->error_frame.data[1] |= CAN_ERR_CRTL_UNSPEC;
+	/* data7 contains custom mcp25xxfd error flags */
+	cpriv->error_frame.data[7] |= MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_ECC;
+
+	/* delegate to interrupt cleaning */
+	return mcp25xxfd_ecc_clear_int(cpriv->priv);
+}
+
+static void mcp25xxfd_can_int_handle_ivmif_tx(struct mcp25xxfd_can_priv *cpriv,
+					      u32 *mask)
+{
+	/* check if it is really a known tx error */
+	if ((cpriv->bus.bdiag[1] &
+	     (MCP25XXFD_CAN_BDIAG1_DBIT1ERR |
+	      MCP25XXFD_CAN_BDIAG1_DBIT0ERR |
+	      MCP25XXFD_CAN_BDIAG1_NACKERR |
+	      MCP25XXFD_CAN_BDIAG1_NBIT1ERR |
+	      MCP25XXFD_CAN_BDIAG1_NBIT0ERR
+		     )) == 0)
+		return;
+
+	/* mark it as a protocol error */
+	cpriv->error_frame.id |= CAN_ERR_PROT;
+
+	/* and update statistics */
+	cpriv->can.dev->stats.tx_errors++;
+
+	/* and handle all the known cases */
+	if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NACKERR) {
+		/* TX-Frame not acknowledged - connected to CAN-bus? */
+		*mask |= MCP25XXFD_CAN_BDIAG1_NACKERR;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_TX;
+		cpriv->can.dev->stats.tx_aborted_errors++;
+	}
+
+	if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NBIT1ERR) {
+		/* TX-Frame CAN-BUS Level is unexpectedly dominant */
+		*mask |= MCP25XXFD_CAN_BDIAG1_NBIT1ERR;
+		cpriv->can.dev->stats.tx_carrier_errors++;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT1;
+	}
+
+	if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NBIT0ERR) {
+		/* TX-Frame CAN-BUS Level is unexpectedly recessive */
+		*mask |= MCP25XXFD_CAN_BDIAG1_NBIT0ERR;
+		cpriv->can.dev->stats.tx_carrier_errors++;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT0;
+	}
+
+	if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_DBIT1ERR) {
+		/* TX-Frame CAN-BUS Level is unexpectedly dominant
+		 * during data phase
+		 */
+		*mask |= MCP25XXFD_CAN_BDIAG1_DBIT1ERR;
+		cpriv->can.dev->stats.tx_carrier_errors++;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT1;
+	}
+
+	if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_DBIT0ERR) {
+		/* TX-Frame CAN-BUS Level is unexpectedly recessive
+		 * during data phase
+		 */
+		*mask |= MCP25XXFD_CAN_BDIAG1_DBIT0ERR;
+		cpriv->can.dev->stats.tx_carrier_errors++;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT0;
+	}
+}
+
+static void mcp25xxfd_can_int_handle_ivmif_rx(struct mcp25xxfd_can_priv *cpriv,
+					      u32 *mask)
+{
+	/* check if it is really a known tx error */
+	if ((cpriv->bus.bdiag[1] &
+	     (MCP25XXFD_CAN_BDIAG1_DCRCERR |
+	      MCP25XXFD_CAN_BDIAG1_DSTUFERR |
+	      MCP25XXFD_CAN_BDIAG1_DFORMERR |
+	      MCP25XXFD_CAN_BDIAG1_NCRCERR |
+	      MCP25XXFD_CAN_BDIAG1_NSTUFERR |
+	      MCP25XXFD_CAN_BDIAG1_NFORMERR
+		     )) == 0)
+		return;
+
+	/* mark it as a protocol error */
+	cpriv->error_frame.id |= CAN_ERR_PROT;
+
+	/* and update statistics */
+	cpriv->can.dev->stats.rx_errors++;
+
+	/* handle the cases */
+	if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_DCRCERR) {
+		/* RX-Frame with bad CRC during data phase */
+		*mask |= MCP25XXFD_CAN_BDIAG1_DCRCERR;
+		cpriv->can.dev->stats.rx_crc_errors++;
+		cpriv->error_frame.data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
+	}
+
+	if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_DSTUFERR) {
+		/* RX-Frame with bad stuffing during data phase */
+		*mask |= MCP25XXFD_CAN_BDIAG1_DSTUFERR;
+		cpriv->can.dev->stats.rx_frame_errors++;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_STUFF;
+	}
+
+	if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_DFORMERR) {
+		/* RX-Frame with bad format during data phase */
+		*mask |= MCP25XXFD_CAN_BDIAG1_DFORMERR;
+		cpriv->can.dev->stats.rx_frame_errors++;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_FORM;
+	}
+
+	if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NCRCERR) {
+		/* RX-Frame with bad CRC during data phase */
+		*mask |= MCP25XXFD_CAN_BDIAG1_NCRCERR;
+		cpriv->can.dev->stats.rx_crc_errors++;
+		cpriv->error_frame.data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
+	}
+
+	if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NSTUFERR) {
+		/* RX-Frame with bad stuffing during data phase */
+		*mask |= MCP25XXFD_CAN_BDIAG1_NSTUFERR;
+		cpriv->can.dev->stats.rx_frame_errors++;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_STUFF;
+	}
+
+	if (cpriv->bus.bdiag[1] & MCP25XXFD_CAN_BDIAG1_NFORMERR) {
+		/* RX-Frame with bad format during data phase */
+		*mask |= MCP25XXFD_CAN_BDIAG1_NFORMERR;
+		cpriv->can.dev->stats.rx_frame_errors++;
+		cpriv->error_frame.data[2] |= CAN_ERR_PROT_FORM;
+	}
+}
+
+static int mcp25xxfd_can_int_handle_ivmif(struct mcp25xxfd_can_priv *cpriv)
+{
+	struct spi_device *spi = cpriv->priv->spi;
+	u32 mask, bdiag1;
+	int ret;
+
+	if (!(cpriv->status.intf & MCP25XXFD_CAN_INT_IVMIF))
+		return 0;
+
+	if (cpriv->status.intf & MCP25XXFD_CAN_INT_SERRIF)
+		return 0;
+
+	ret = mcp25xxfd_cmd_read_regs(spi, MCP25XXFD_CAN_BDIAG0,
+				      cpriv->bus.bdiag,
+				      sizeof(cpriv->bus.bdiag));
+	if (ret)
+		return ret;
+
+	mask = 0;
+
+	/* check rx and tx errors */
+	mcp25xxfd_can_int_handle_ivmif_tx(cpriv, &mask);
+	mcp25xxfd_can_int_handle_ivmif_rx(cpriv, &mask);
+
+	/* clear flags if we have bits masked */
+	if (!mask) {
+		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;
+	}
+
+	bdiag1 = cpriv->bus.bdiag[1] & (~mask);
+	ret = mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_CAN_BDIAG1, bdiag1, mask);
+	if (ret)
+		return ret;
+
+	/* clear the interrupt flag until we have received or transmited */
+	cpriv->status.intf &= ~(MCP25XXFD_CAN_INT_IVMIE);
+	return mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_CAN_INT,
+					cpriv->status.intf,
+					MCP25XXFD_CAN_INT_IVMIE);
+}
+
+static int mcp25xxfd_can_int_handle_cerrif(struct mcp25xxfd_can_priv *cpriv)
+{
+	if (!(cpriv->status.intf & MCP25XXFD_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
+	 */
+
+	netdev_warn(cpriv->can.dev, "CAN Bus error experienced");
+
+	return 0;
+}
+
+static int mcp25xxfd_can_int_error_counters(struct mcp25xxfd_can_priv *cpriv)
+{
+	if (cpriv->status.trec & MCP25XXFD_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 & MCP25XXFD_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 & MCP25XXFD_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 & MCP25XXFD_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 & MCP25XXFD_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 mcp25xxfd_can_priv *cpriv)
+{
+	/* 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;
+
+	/* send error packet */
+	if (cpriv->error_frame.id)
+		mcp25xxfd_can_int_send_error_skb(cpriv);
+
+	/* 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(cpriv->can.dev);
+		}
+	}
+
+	return 0;
+}
+
+static int mcp25xxfd_can_int_handle_status(struct mcp25xxfd_can_priv *cpriv)
+{
+	int ret;
+
+	ret = mcp25xxfd_can_int_clear_int_flags(cpriv);
+	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;
+
+	/* system error interrupt needs to get handled first
+	 * to get us out of restricted mode
+	 */
+	ret = mcp25xxfd_can_int_handle_serrif(cpriv);
+	if (ret)
+		return ret;
+
+	/* mode change interrupt */
+	ret = mcp25xxfd_can_int_handle_modif(cpriv);
+	if (ret)
+		return ret;
+
+	/* handle the rx */
+	ret = mcp25xxfd_can_rx_handle_int_rxif(cpriv);
+	if (ret)
+		return ret;
+
+	/* handle error interrupt flags */
+	ret = mcp25xxfd_can_rx_handle_int_rxovif(cpriv);
+	if (ret)
+		return ret;
+
+	/* sram ECC error interrupt */
+	ret = mcp25xxfd_can_int_handle_eccif(cpriv);
+	if (ret)
+		return ret;
+
+	/* message format interrupt */
+	ret = mcp25xxfd_can_int_handle_ivmif(cpriv);
+	if (ret)
+		return ret;
+
+	/* handle bus errors in more detail */
+	ret = mcp25xxfd_can_int_handle_cerrif(cpriv);
+	if (ret)
+		return ret;
+
+	/* error counter handling */
+	ret = mcp25xxfd_can_int_error_counters(cpriv);
+	if (ret)
+		return ret;
+
+	/* error counter handling */
+	ret = mcp25xxfd_can_int_error_handling(cpriv);
+	if (ret)
+		return ret;
+
+	/* and submit can frames to network stack */
+	ret = mcp25xxfd_can_int_submit_frames(cpriv);
+
+	return ret;
+}
+
+irqreturn_t mcp25xxfd_can_int(int irq, void *dev_id)
+{
+	struct mcp25xxfd_can_priv *cpriv = dev_id;
+	int loops, ret;
+
+	/* loop forever unless we need to exit */
+	for (loops = 0; true; loops++) {
+		/* read interrupt status flags in bulk */
+		ret = mcp25xxfd_cmd_read_regs(cpriv->priv->spi,
+					      MCP25XXFD_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 >> MCP25XXFD_CAN_INT_IE_SHIFT) &
+		       cpriv->status.intf) == 0)
+			break;
+
+		/* handle the status */
+		ret = mcp25xxfd_can_int_handle_status(cpriv);
+		if (ret)
+			return ret;
+
+		/* allow voluntarily rescheduling every so often to avoid
+		 * long CS lows at the end of a transfer on low power CPUs
+		 * avoiding SERR happening
+		 */
+		if (loops % MCP25XXFD_RESCHEDULE_TIMES == 0)
+			cond_resched();
+	}
+
+	return IRQ_HANDLED;
+}
+
+int mcp25xxfd_can_int_clear(struct mcp25xxfd_priv *priv)
+{
+	return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_CAN_INT, 0,
+					MCP25XXFD_CAN_INT_IF_MASK);
+}
+
+int mcp25xxfd_can_int_enable(struct mcp25xxfd_priv *priv, bool enable)
+{
+	struct mcp25xxfd_can_priv *cpriv = priv->cpriv;
+	const u32 mask = MCP25XXFD_CAN_INT_TEFIE |
+		MCP25XXFD_CAN_INT_RXIE |
+		MCP25XXFD_CAN_INT_MODIE |
+		MCP25XXFD_CAN_INT_SERRIE |
+		MCP25XXFD_CAN_INT_IVMIE |
+		MCP25XXFD_CAN_INT_CERRIE |
+		MCP25XXFD_CAN_INT_RXOVIE |
+		MCP25XXFD_CAN_INT_ECCIE;
+	u32 value = cpriv ? cpriv->status.intf : 0;
+	int ret;
+
+	value &= ~(MCP25XXFD_CAN_INT_IE_MASK);
+	if (enable)
+		value |= mask;
+
+	ret = mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_CAN_INT,
+				       value, mask);
+	if (ret)
+		return ret;
+
+	if (!cpriv)
+		return 0;
+
+	cpriv->status.intf = value;
+	if (cpriv->irq.allocated) {
+		if (enable && !cpriv->irq.enabled)
+			enable_irq(cpriv->priv->spi->irq);
+		if (!enable && cpriv->irq.enabled)
+			disable_irq(cpriv->priv->spi->irq);
+		cpriv->irq.enabled = enable;
+	} else {
+		cpriv->irq.enabled = false;
+	}
+
+	return 0;
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.h
new file mode 100644
index 000000000000..aa67a5da9271
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+#ifndef __MCP25XXFD_CAN_INT_H
+#define __MCP25XXFD_CAN_INT_H
+
+#include "mcp25xxfd_priv.h"
+#include <linux/irqreturn.h>
+
+int mcp25xxfd_can_int_clear(struct mcp25xxfd_priv *priv);
+int mcp25xxfd_can_int_enable(struct mcp25xxfd_priv *priv, bool enable);
+
+irqreturn_t mcp25xxfd_can_int(int irq, void *dev_id);
+
+#endif /* __MCP25XXFD_CAN_INT_H */
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h
new file mode 100644
index 000000000000..e043b262a868
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+
+#ifndef __MCP25XXFD_CAN_PRIV_H
+#define __MCP25XXFD_CAN_PRIV_H
+
+#include <linux/can/dev.h>
+#include <linux/dcache.h>
+
+#include "mcp25xxfd_priv.h"
+
+#define TX_ECHO_SKB_MAX	32
+
+/* information on each fifo type */
+struct mcp25xxfd_fifo {
+	u32 count;
+	u32 start;
+	u32 size;
+};
+
+/* used for sorting incoming messages */
+struct mcp25xxfd_obj_ts {
+	s32 ts; /* using signed to handle rollover correctly when sorting */
+	u16 fifo;
+};
+
+/* general info on each fifo */
+struct mcp25xxfd_fifo_info {
+	u32 offset;
+	u32 priority;
+};
+
+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;
+
+	/* interrupt state */
+	struct {
+		int enabled;
+		int allocated;
+	} irq;
+
+	/* 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 */
+
+		/* info on each fifo */
+		struct mcp25xxfd_fifo_info info[32];
+
+		/* extra info on rx fifo groups */
+		struct mcp25xxfd_fifo rx;
+
+		/* 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;
+	} fifos;
+
+	/* bus state */
+	struct {
+		u32 state;
+		u32 new_state;
+		u32 bdiag[2];
+	} bus;
+
+	/* can error messages */
+	struct {
+		u32 id;
+		u8  data[8];
+	} error_frame;
+
+	/* a copy of mcp25xxfd-sram in ram */
+	u8 sram[MCP25XXFD_SRAM_SIZE];
+};
+
+#endif /* __MCP25XXFD_CAN_PRIV_H */
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..5e3f706e7a3f
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 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/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>
+
+#include "mcp25xxfd_cmd.h"
+#include "mcp25xxfd_can.h"
+#include "mcp25xxfd_can_id.h"
+#include "mcp25xxfd_can_priv.h"
+#include "mcp25xxfd_can_rx.h"
+
+static struct sk_buff *
+mcp25xxfd_can_rx_submit_normal_frame(struct mcp25xxfd_can_priv *cpriv,
+				     u32 id, u32 dlc, u8 **data)
+{
+	struct can_frame *frame;
+	struct sk_buff *skb;
+
+	/* allocate frame */
+	skb = alloc_can_skb(cpriv->can.dev, &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_rx_submit_fd_frame(struct mcp25xxfd_can_priv *cpriv,
+				 u32 id, u32 flags, u32 len, u8 **data)
+{
+	struct canfd_frame *frame;
+	struct sk_buff *skb;
+
+	/* allocate frame */
+	skb = alloc_canfd_skb(cpriv->can.dev, &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_rx_submit_frame(struct mcp25xxfd_can_priv *cpriv, int fifo)
+{
+	struct net_device *net = cpriv->can.dev;
+	int addr = cpriv->fifos.info[fifo].offset;
+	struct mcp25xxfd_can_obj_rx *rx =
+		(struct mcp25xxfd_can_obj_rx *)(cpriv->sram + addr);
+	u8 *data = NULL;
+	struct sk_buff *skb;
+	u32 id, dlc, len, flags;
+
+	/* compute the can_id */
+	mcp25xxfd_can_id_from_mcp25xxfd(rx->id, rx->flags, &id);
+
+	/* and dlc */
+	dlc = (rx->flags & MCP25XXFD_CAN_OBJ_FLAGS_DLC_MASK) >>
+		MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT;
+	len = can_dlc2len(dlc);
+
+	/* update stats */
+	net->stats.rx_packets++;
+	net->stats.rx_bytes += len;
+
+	/* allocate the skb buffer */
+	if (rx->flags & MCP25XXFD_CAN_OBJ_FLAGS_FDF) {
+		flags = 0;
+		flags |= (rx->flags & MCP25XXFD_CAN_OBJ_FLAGS_BRS) ?
+			CANFD_BRS : 0;
+		flags |= (rx->flags & MCP25XXFD_CAN_OBJ_FLAGS_ESI) ?
+			CANFD_ESI : 0;
+		skb = mcp25xxfd_can_rx_submit_fd_frame(cpriv, id, flags,
+						       len, &data);
+	} else {
+		skb = mcp25xxfd_can_rx_submit_normal_frame(cpriv, id,
+							   len, &data);
+	}
+	if (!skb) {
+		netdev_err(net, "cannot allocate RX skb\n");
+		net->stats.rx_dropped++;
+		return -ENOMEM;
+	}
+
+	/* copy the payload data */
+	memcpy(data, rx->data, len);
+
+	/* and submit the frame */
+	netif_rx_ni(skb);
+
+	return 0;
+}
+
+static int mcp25xxfd_can_rx_read_frame(struct mcp25xxfd_can_priv *cpriv,
+				       int fifo, int prefetch_bytes)
+{
+	struct spi_device *spi = cpriv->priv->spi;
+	struct net_device *net = cpriv->can.dev;
+	int addr = cpriv->fifos.info[fifo].offset;
+	struct mcp25xxfd_can_obj_rx *rx =
+		(struct mcp25xxfd_can_obj_rx *)(cpriv->sram + addr);
+	int dlc;
+	int len, ret;
+
+	/* we read the header plus prefetch_bytes */
+	ret = mcp25xxfd_cmd_read_multi(spi, MCP25XXFD_SRAM_ADDR(addr),
+				       rx, sizeof(*rx) + prefetch_bytes);
+	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 */
+	dlc = (rx->flags & MCP25XXFD_CAN_OBJ_FLAGS_DLC_MASK) >>
+		MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT;
+	len = can_dlc2len(min_t(int, dlc, (net->mtu == CANFD_MTU) ? 15 : 8));
+
+	/* read the remaining data for canfd frames */
+	if (len > prefetch_bytes) {
+		/* here the extra portion reading data after prefetch */
+		ret = mcp25xxfd_cmd_read_multi(spi,
+					       MCP25XXFD_SRAM_ADDR(addr) +
+					       sizeof(*rx) + prefetch_bytes,
+					       &rx->data[prefetch_bytes],
+					       len - prefetch_bytes);
+		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);
+
+	/* 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, MCP25XXFD_CAN_FIFOCON(fifo),
+					MCP25XXFD_CAN_FIFOCON_FRESET,
+					MCP25XXFD_CAN_FIFOCON_FRESET);
+}
+
+static int mcp25xxfd_can_rx_read_frames(struct mcp25xxfd_can_priv *cpriv)
+{
+	int i, f, prefetch;
+	int ret;
+
+	prefetch = 8;
+	/* TODO: Optimize this */
+	for (i = 0, f = cpriv->fifos.rx.start; i < cpriv->fifos.rx.count;
+	     i++, f++) {
+		if (cpriv->status.rxif & BIT(f)) {
+			/* read the frame */
+			ret = mcp25xxfd_can_rx_read_frame(cpriv, f, prefetch);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+int mcp25xxfd_can_rx_handle_int_rxif(struct mcp25xxfd_can_priv *cpriv)
+{
+	if (!cpriv->status.rxif)
+		return 0;
+
+	/* read all the fifos */
+	return mcp25xxfd_can_rx_read_frames(cpriv);
+}
+
+int mcp25xxfd_can_rx_handle_int_rxovif(struct mcp25xxfd_can_priv *cpriv)
+{
+	u32 mask = MCP25XXFD_CAN_FIFOSTA_RXOVIF;
+	int ret, i, reg;
+
+	if (!cpriv->status.rxovif)
+		return 0;
+
+	/* clear all fifos that have an overflow bit set */
+	for (i = 0; i < 32; i++) {
+		if (cpriv->status.rxovif & BIT(i)) {
+			/* clear fifo status */
+			reg = MCP25XXFD_CAN_FIFOSTA(i);
+			ret = mcp25xxfd_cmd_write_mask(cpriv->priv->spi,
+						       reg, 0, mask);
+			if (ret)
+				return ret;
+
+			/* update statistics */
+			cpriv->can.dev->stats.rx_over_errors++;
+			cpriv->can.dev->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;
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.h
new file mode 100644
index 000000000000..71953e2f3615
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+
+#ifndef __MCP25XXFD_CAN_RX_H
+#define __MCP25XXFD_CAN_RX_H
+
+#include "mcp25xxfd_priv.h"
+
+int mcp25xxfd_can_rx_submit_frame(struct mcp25xxfd_can_priv *cpriv, int fifo);
+
+int mcp25xxfd_can_rx_handle_int_rxif(struct mcp25xxfd_can_priv *cpriv);
+int mcp25xxfd_can_rx_handle_int_rxovif(struct mcp25xxfd_can_priv *cpriv);
+
+#endif /* __MCP25XXFD_CAN_RX_H */
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c
index 5e274d452646..182172b6c59c 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_int.c
@@ -8,6 +8,7 @@
 #include <linux/kernel.h>
 #include <linux/spi/spi.h>
 
+#include "mcp25xxfd_can_int.h"
 #include "mcp25xxfd_crc.h"
 #include "mcp25xxfd_ecc.h"
 #include "mcp25xxfd_int.h"
@@ -21,7 +22,11 @@ int mcp25xxfd_int_clear(struct mcp25xxfd_priv *priv)
 	if (ret)
 		return ret;
 
-	return mcp25xxfd_crc_clear_int(priv);
+	ret = mcp25xxfd_crc_clear_int(priv);
+	if (ret)
+		return ret;
+
+	return mcp25xxfd_can_int_clear(priv);
 }
 
 int mcp25xxfd_int_enable(struct mcp25xxfd_priv *priv, bool enable)
@@ -47,12 +52,19 @@ int mcp25xxfd_int_enable(struct mcp25xxfd_priv *priv, bool enable)
 	if (ret)
 		goto out_crc;
 
+	ret = mcp25xxfd_can_int_enable(priv, enable);
+	if (ret)
+		goto out_ecc;
+
 	/* If we disable interrupts, then clear interrupt flags last */
 	if (!enable)
 		mcp25xxfd_int_clear(priv);
 
 	return 0;
 
+out_ecc:
+	mcp25xxfd_ecc_enable_int(priv, false);
+
 out_crc:
 	mcp25xxfd_crc_enable_int(priv, false);
 	return ret;
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h
index 8bc7a599224c..85c27a7f6785 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_priv.h
@@ -23,9 +23,11 @@ enum mcp25xxfd_model {
 	CAN_MCP2517FD	= 0x2517,
 };
 
+struct mcp25xxfd_can_priv;
 struct mcp25xxfd_priv {
 	struct spi_device *spi;
 	struct clk *clk;
+	struct mcp25xxfd_can_priv *cpriv;
 
 	/* actual model of the mcp25xxfd */
 	enum mcp25xxfd_model model;
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h
index b500cb46b9a4..222527439c70 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_regs.h
@@ -653,4 +653,9 @@ struct mcp25xxfd_can_obj_tef {
 		MCP25XXFD_CAN_FLAGS_FILHIT_BITS - 1,			\
 		MCP25XXFD_CAN_FLAGS_FILHIT_SHIFT)
 
+/* custom status error */
+#define MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_SERR_RX BIT(0)
+#define MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_SERR_TX BIT(1)
+#define MCP25XXFD_CAN_ERR_DATA7_MCP25XXFD_ECC	  BIT(2)
+
 #endif /* __MCP25XXFD_REGS_H */
-- 
2.17.1


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

* [PATCH 4/6] can: mcp25xxfd: Add CAN transmission support
  2020-06-10  7:44 [PATCH 0/6] Add Microchip MCP25XXFD CAN driver Manivannan Sadhasivam
                   ` (2 preceding siblings ...)
  2020-06-10  7:44 ` [PATCH 3/6] can: mcp25xxfd: Add support for CAN reception Manivannan Sadhasivam
@ 2020-06-10  7:44 ` Manivannan Sadhasivam
  2020-06-10  7:44 ` [PATCH 5/6] can: mcp25xxfd: Optimize TEF read by avoiding unnecessary SPI transfers Manivannan Sadhasivam
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 26+ messages in thread
From: Manivannan Sadhasivam @ 2020-06-10  7:44 UTC (permalink / raw)
  To: wg, mkl; +Cc: kernel, linux-can, netdev, linux-kernel, Manivannan Sadhasivam

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

Add un-optimized CAN2.0 and CAN-FD transmission support.

On a Rpi3 we can saturate the CAN bus at 1MHz transmitting 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>
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
 drivers/net/can/spi/mcp25xxfd/Makefile        |   1 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c |  10 +
 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h |   3 +-
 .../can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c    |  93 ++-
 .../net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c |  21 +-
 .../can/spi/mcp25xxfd/mcp25xxfd_can_priv.h    |  15 +-
 .../net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c  |   2 +-
 .../net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c  | 620 ++++++++++++++++++
 .../net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.h  |  86 +++
 9 files changed, 839 insertions(+), 12 deletions(-)
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c
 create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.h

diff --git a/drivers/net/can/spi/mcp25xxfd/Makefile b/drivers/net/can/spi/mcp25xxfd/Makefile
index 5787bdd57a9d..a586b2555ff9 100644
--- a/drivers/net/can/spi/mcp25xxfd/Makefile
+++ b/drivers/net/can/spi/mcp25xxfd/Makefile
@@ -4,6 +4,7 @@ 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_cmd.o
 mcp25xxfd-objs                  += mcp25xxfd_crc.o
 mcp25xxfd-objs                  += mcp25xxfd_ecc.o
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
index 2ac78024c171..1417f1a22d6e 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
@@ -20,6 +20,7 @@
 #include "mcp25xxfd_can_fifo.h"
 #include "mcp25xxfd_can_int.h"
 #include "mcp25xxfd_can_priv.h"
+#include "mcp25xxfd_can_tx.h"
 #include "mcp25xxfd_can.h"
 #include "mcp25xxfd_cmd.h"
 #include "mcp25xxfd_int.h"
@@ -395,6 +396,10 @@ static int mcp25xxfd_can_open(struct net_device *net)
 	if (ret)
 		goto out_int;
 
+	/* start the tx_queue */
+	mcp25xxfd_can_tx_queue_manage(cpriv,
+				      MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED);
+
 	return 0;
 
 out_int:
@@ -425,6 +430,10 @@ static int mcp25xxfd_can_stop(struct net_device *net)
 	struct mcp25xxfd_priv *priv = cpriv->priv;
 	struct spi_device *spi = priv->spi;
 
+	/* stop transmit queue */
+	mcp25xxfd_can_tx_queue_manage(cpriv,
+				      MCP25XXFD_CAN_TX_QUEUE_STATE_STOPPED);
+
 	/* shutdown the can controller */
 	mcp25xxfd_can_shutdown(cpriv);
 
@@ -448,6 +457,7 @@ static int mcp25xxfd_can_stop(struct net_device *net)
 static const struct net_device_ops mcp25xxfd_netdev_ops = {
 	.ndo_open = mcp25xxfd_can_open,
 	.ndo_stop = mcp25xxfd_can_stop,
+	.ndo_start_xmit = mcp25xxfd_can_tx_start_xmit,
 	.ndo_change_mtu = can_change_mtu,
 };
 
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
index 4b18b5bb3d45..8ec20827f099 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
@@ -22,12 +22,13 @@ int mcp25xxfd_can_targetmode(struct mcp25xxfd_can_priv *cpriv)
 
 static inline
 void mcp25xxfd_can_queue_frame(struct mcp25xxfd_can_priv *cpriv,
-			       s32 fifo, u16 ts)
+			       s32 fifo, u16 ts, bool is_rx)
 {
 	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[idx].is_rx = is_rx;
 
 	cpriv->fifos.submit_queue_count++;
 }
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c
index 4bd776772d2d..4a1ad250bdaf 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c
@@ -5,8 +5,6 @@
  * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
  */
 
-/* here we define and configure the fifo layout */
-
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/spi/spi.h>
@@ -14,6 +12,7 @@
 #include "mcp25xxfd_can.h"
 #include "mcp25xxfd_can_fifo.h"
 #include "mcp25xxfd_can_priv.h"
+#include "mcp25xxfd_can_tx.h"
 #include "mcp25xxfd_cmd.h"
 
 static int mcp25xxfd_can_fifo_get_address(struct mcp25xxfd_can_priv *cpriv)
@@ -55,6 +54,15 @@ static int mcp25xxfd_can_fifo_setup_config(struct mcp25xxfd_can_priv *cpriv,
 	     c > 0; i++, f++, p--, c--) {
 		val = (c > 1) ? flags : flags_last;
 
+		/* are we in tx mode? */
+		if (flags & MCP25XXFD_CAN_FIFOCON_TXEN) {
+			cpriv->fifos.info[f].is_rx = false;
+			cpriv->fifos.info[f].priority = p;
+			val |= (p << MCP25XXFD_CAN_FIFOCON_TXPRI_SHIFT);
+		} else {
+			cpriv->fifos.info[f].is_rx = true;
+		}
+
 		/* write the config to the controller in one go */
 		ret = mcp25xxfd_cmd_write(cpriv->priv->spi,
 					  MCP25XXFD_CAN_FIFOCON(f), val);
@@ -65,6 +73,27 @@ static int mcp25xxfd_can_fifo_setup_config(struct mcp25xxfd_can_priv *cpriv,
 	return 0;
 }
 
+static int mcp25xxfd_can_fifo_setup_tx(struct mcp25xxfd_can_priv *cpriv)
+{
+	u32 tx_flags = MCP25XXFD_CAN_FIFOCON_FRESET |     /* reset FIFO */
+		MCP25XXFD_CAN_FIFOCON_TXEN |              /* a tx FIFO */
+		MCP25XXFD_CAN_FIFOCON_TXATIE |            /* state in txatif */
+		(cpriv->fifos.payload_mode <<
+		 MCP25XXFD_CAN_FIFOCON_PLSIZE_SHIFT) |    /* paylod size */
+		(0 << MCP25XXFD_CAN_FIFOCON_FSIZE_SHIFT); /* 1 FIFO deep */
+
+	/* handle oneshot/three-shot */
+	if (cpriv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+		tx_flags |= MCP25XXFD_CAN_FIFOCON_TXAT_ONE_SHOT <<
+			    MCP25XXFD_CAN_FIFOCON_TXAT_SHIFT;
+	else
+		tx_flags |= MCP25XXFD_CAN_FIFOCON_TXAT_UNLIMITED <<
+			    MCP25XXFD_CAN_FIFOCON_TXAT_SHIFT;
+
+	return mcp25xxfd_can_fifo_setup_config(cpriv, &cpriv->fifos.tx,
+					       tx_flags, tx_flags);
+}
+
 static int mcp25xxfd_can_fifo_setup_rx(struct mcp25xxfd_can_priv *cpriv)
 {
 	u32 rx_flags = MCP25XXFD_CAN_FIFOCON_FRESET |     /* reset FIFO */
@@ -106,7 +135,7 @@ static int mcp25xxfd_can_fifo_setup_rxfilter(struct mcp25xxfd_can_priv *cpriv)
 
 static int mcp25xxfd_can_fifo_compute(struct mcp25xxfd_can_priv *cpriv)
 {
-	int rx_memory_available;
+	int tef_memory_used, tx_memory_used, rx_memory_available;
 
 	switch (cpriv->can.dev->mtu) {
 	case CAN_MTU:
@@ -114,23 +143,48 @@ static int mcp25xxfd_can_fifo_compute(struct mcp25xxfd_can_priv *cpriv)
 		cpriv->fifos.payload_size = 8;
 		cpriv->fifos.payload_mode = MCP25XXFD_CAN_TXQCON_PLSIZE_8;
 
+		/* 7 tx fifos */
+		cpriv->fifos.tx.count = 7;
+
 		break;
 	case CANFD_MTU:
 		/* MTU is 64 */
 		cpriv->fifos.payload_size = 64;
 		cpriv->fifos.payload_mode = MCP25XXFD_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_can_obj_tef);
+	cpriv->fifos.tx.size = sizeof(struct mcp25xxfd_can_obj_tx) +
+		cpriv->fifos.payload_size;
 	cpriv->fifos.rx.size = sizeof(struct mcp25xxfd_can_obj_rx) +
 		cpriv->fifos.payload_size;
 
+	/* 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_SRAM_SIZE;
+	rx_memory_available = MCP25XXFD_SRAM_SIZE - tx_memory_used -
+		tef_memory_used;
+
+	/* we need at least one RX Frame */
+	if (rx_memory_available < cpriv->fifos.rx.size) {
+		netdev_err(cpriv->can.dev,
+			   "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;
@@ -138,10 +192,11 @@ static int mcp25xxfd_can_fifo_compute(struct mcp25xxfd_can_priv *cpriv)
 	/* 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 > 31)
-		cpriv->fifos.rx.count = 31;
+	if (cpriv->fifos.rx.count + cpriv->fifos.tx.count > 31)
+		cpriv->fifos.rx.count = 31 - cpriv->fifos.tx.count;
 
-	cpriv->fifos.rx.start = 1;
+	cpriv->fifos.tx.start = 1;
+	cpriv->fifos.rx.start = cpriv->fifos.tx.start + cpriv->fifos.tx.count;
 
 	return 0;
 }
@@ -170,6 +225,7 @@ static int mcp25xxfd_can_fifo_clear(struct mcp25xxfd_can_priv *cpriv)
 	int ret;
 
 	memset(&cpriv->fifos.info, 0, sizeof(cpriv->fifos.info));
+	memset(&cpriv->fifos.tx, 0, sizeof(cpriv->fifos.tx));
 	memset(&cpriv->fifos.rx, 0, sizeof(cpriv->fifos.rx));
 
 	/* clear FIFO config */
@@ -196,16 +252,31 @@ int mcp25xxfd_can_fifo_setup(struct mcp25xxfd_can_priv *cpriv)
 	if (ret)
 		return ret;
 
-	cpriv->regs.tefcon = 0;
+	/* configure TEF */
+	if (cpriv->fifos.tef.count)
+		cpriv->regs.tefcon =
+			MCP25XXFD_CAN_TEFCON_FRESET |
+			MCP25XXFD_CAN_TEFCON_TEFNEIE |
+			MCP25XXFD_CAN_TEFCON_TEFTSEN |
+			((cpriv->fifos.tef.count - 1) <<
+			 MCP25XXFD_CAN_TEFCON_FSIZE_SHIFT);
+	else
+		cpriv->regs.tefcon = 0;
 	ret = mcp25xxfd_cmd_write(cpriv->priv->spi, MCP25XXFD_CAN_TEFCON,
 				  cpriv->regs.tefcon);
 	if (ret)
 		return ret;
 
+	/* TXQueue disabled */
 	ret = mcp25xxfd_cmd_write(cpriv->priv->spi, MCP25XXFD_CAN_TXQCON, 0);
 	if (ret)
 		return ret;
 
+	/* configure FIFOS themselves */
+	ret = mcp25xxfd_can_fifo_setup_tx(cpriv);
+	if (ret)
+		return ret;
+
 	ret = mcp25xxfd_can_fifo_setup_rx(cpriv);
 	if (ret)
 		return ret;
@@ -219,10 +290,16 @@ int mcp25xxfd_can_fifo_setup(struct mcp25xxfd_can_priv *cpriv)
 	if (ret)
 		return ret;
 
+	/* setup tx_fifo_queue */
+	ret = mcp25xxfd_can_tx_queue_alloc(cpriv);
+	if (ret)
+		return ret;
+
 	return 0;
 }
 
 void mcp25xxfd_can_fifo_release(struct mcp25xxfd_can_priv *cpriv)
 {
+	mcp25xxfd_can_tx_queue_free(cpriv);
 	mcp25xxfd_can_fifo_clear(cpriv);
 }
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c
index 83656b2604df..b3cf3e77c299 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c
@@ -23,6 +23,7 @@
 #include "mcp25xxfd_can_int.h"
 #include "mcp25xxfd_can_priv.h"
 #include "mcp25xxfd_can_rx.h"
+#include "mcp25xxfd_can_tx.h"
 #include "mcp25xxfd_cmd.h"
 #include "mcp25xxfd_ecc.h"
 
@@ -80,7 +81,9 @@ static int mcp25xxfd_can_int_submit_frames(struct mcp25xxfd_can_priv *cpriv)
 	/* now submit the fifos  */
 	for (i = 0; i < count; i++) {
 		fifo = queue[i].fifo;
-		ret = mcp25xxfd_can_rx_submit_frame(cpriv, fifo);
+		ret = (queue[i].is_rx) ?
+			mcp25xxfd_can_rx_submit_frame(cpriv, fifo) :
+			mcp25xxfd_can_tx_submit_frame(cpriv, fifo);
 		if (ret)
 			return ret;
 	}
@@ -100,6 +103,9 @@ static int mcp25xxfd_can_int_submit_frames(struct mcp25xxfd_can_priv *cpriv)
 	}
 
 out:
+	/* enable tx_queue if necessary */
+	mcp25xxfd_can_tx_queue_restart(cpriv);
+
 	return 0;
 }
 
@@ -496,6 +502,9 @@ static int mcp25xxfd_can_int_error_handling(struct mcp25xxfd_can_priv *cpriv)
 			cpriv->can.can_stats.bus_off++;
 			can_bus_off(cpriv->can.dev);
 		}
+	} else {
+		/* restart the tx queue if needed */
+		mcp25xxfd_can_tx_queue_restart(cpriv);
 	}
 
 	return 0;
@@ -533,6 +542,16 @@ static int mcp25xxfd_can_int_handle_status(struct mcp25xxfd_can_priv *cpriv)
 	if (ret)
 		return ret;
 
+	/* handle aborted TX FIFOs */
+	ret = mcp25xxfd_can_tx_handle_int_txatif(cpriv);
+	if (ret)
+		return ret;
+
+	/* handle the TEF */
+	ret = mcp25xxfd_can_tx_handle_int_tefif(cpriv);
+	if (ret)
+		return ret;
+
 	/* handle error interrupt flags */
 	ret = mcp25xxfd_can_rx_handle_int_rxovif(cpriv);
 	if (ret)
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h
index e043b262a868..99c3ef6d08e0 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_priv.h
@@ -26,10 +26,12 @@ struct mcp25xxfd_fifo {
 struct mcp25xxfd_obj_ts {
 	s32 ts; /* using signed to handle rollover correctly when sorting */
 	u16 fifo;
+	s16 is_rx;
 };
 
 /* general info on each fifo */
 struct mcp25xxfd_fifo_info {
+	u32 is_rx;
 	u32 offset;
 	u32 priority;
 };
@@ -95,10 +97,18 @@ struct mcp25xxfd_can_priv {
 
 		/* infos on fifo layout */
 
+		/* TEF */
+		struct {
+			u32 count;
+			u32 size;
+			u32 index;
+		} tef;
+
 		/* info on each fifo */
 		struct mcp25xxfd_fifo_info info[32];
 
-		/* extra info on rx fifo groups */
+		/* extra info on rx/tx fifo groups */
+		struct mcp25xxfd_fifo tx;
 		struct mcp25xxfd_fifo rx;
 
 		/* queue of can frames that need to get submitted
@@ -109,6 +119,9 @@ struct mcp25xxfd_can_priv {
 		 */
 		struct mcp25xxfd_obj_ts submit_queue[32];
 		int  submit_queue_count;
+
+		/* the tx queue of spi messages */
+		struct mcp25xxfd_tx_spi_message_queue *tx_queue;
 	} fifos;
 
 	/* bus state */
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c
index 5e3f706e7a3f..da99145e0c94 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c
@@ -163,7 +163,7 @@ static int mcp25xxfd_can_rx_read_frame(struct mcp25xxfd_can_priv *cpriv,
 	memset(rx->data + len, 0, ((net->mtu == CANFD_MTU) ? 64 : 8) - len);
 
 	/* add the fifo to the process queues */
-	mcp25xxfd_can_queue_frame(cpriv, fifo, rx->ts);
+	mcp25xxfd_can_queue_frame(cpriv, fifo, rx->ts, true);
 
 	/* and clear the interrupt flag for that fifo */
 	return mcp25xxfd_cmd_write_mask(spi, MCP25XXFD_CAN_FIFOCON(fifo),
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..5ea1e525e776
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c
@@ -0,0 +1,620 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 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/bitfield.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>
+
+#include "mcp25xxfd_can.h"
+#include "mcp25xxfd_can_id.h"
+#include "mcp25xxfd_can_tx.h"
+#include "mcp25xxfd_cmd.h"
+#include "mcp25xxfd_regs.h"
+
+static struct mcp25xxfd_tx_spi_message *
+mcp25xxfd_can_tx_queue_first_spi_message(struct mcp25xxfd_tx_spi_message_queue *
+					 queue, u32 *bitmap)
+{
+	u32 first = ffs(*bitmap);
+
+	if (!first)
+		return NULL;
+
+	return queue->fifo2message[first - 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 mcp25xxfd_tx_spi_message *msg = context;
+	struct mcp25xxfd_can_priv *cpriv = msg->cpriv;
+	struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue;
+	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);
+
+	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+	/* move to in_trigger_fifo_transfer */
+	mcp25xxfd_can_tx_queue_move_spi_message(&q->in_fill_fifo_transfer,
+						&q->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 mcp25xxfd_tx_spi_message *msg = context;
+	struct mcp25xxfd_can_priv *cpriv = msg->cpriv;
+	struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue;
+	unsigned long flags;
+
+	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+	/* move to can_transfer */
+	mcp25xxfd_can_tx_queue_move_spi_message(&q->in_trigger_fifo_transfer,
+						&q->in_can_transfer,
+						msg->fifo);
+
+	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+}
+
+static
+void mcp25xxfd_can_tx_message_init(struct mcp25xxfd_can_priv *cpriv,
+				   struct mcp25xxfd_tx_spi_message *msg,
+				   int fifo)
+{
+	const u32 trigger = MCP25XXFD_CAN_FIFOCON_TXREQ |
+		MCP25XXFD_CAN_FIFOCON_UINC;
+	const int first_byte = mcp25xxfd_cmd_first_byte(trigger);
+	u32 addr;
+
+	msg->cpriv = cpriv;
+	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.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.info[fifo].offset);
+	mcp25xxfd_cmd_calc(MCP25XXFD_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.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_cmd_calc(MCP25XXFD_INSTRUCTION_WRITE,
+			   MCP25XXFD_CAN_FIFOCON(fifo) + first_byte,
+			   msg->trigger_fifo.data.cmd);
+	msg->trigger_fifo.data.data = trigger >> (8 * first_byte);
+
+	/* add to idle tx transfers */
+	mcp25xxfd_can_tx_queue_add_spi_message(&cpriv->fifos.tx_queue->idle,
+					       fifo);
+}
+
+static
+void mcp25xxfd_can_tx_queue_manage_nolock(struct mcp25xxfd_can_priv *cpriv,
+					  int state)
+{
+	struct net_device *net = cpriv->can.dev;
+
+	if (state == cpriv->fifos.tx_queue->state)
+		return;
+
+	/* start/stop netif_queue if necessary */
+	switch (cpriv->fifos.tx_queue->state) {
+	case MCP25XXFD_CAN_TX_QUEUE_STATE_RUNABLE:
+		switch (state) {
+		case MCP25XXFD_CAN_TX_QUEUE_STATE_RESTART:
+		case MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED:
+			netif_wake_queue(net);
+			cpriv->fifos.tx_queue->state =
+				MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED;
+			break;
+		}
+		break;
+	case MCP25XXFD_CAN_TX_QUEUE_STATE_STOPPED:
+		switch (state) {
+		case MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED:
+			netif_wake_queue(net);
+			cpriv->fifos.tx_queue->state = state;
+			break;
+		}
+		break;
+	case MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED:
+		switch (state) {
+		case MCP25XXFD_CAN_TX_QUEUE_STATE_RUNABLE:
+		case MCP25XXFD_CAN_TX_QUEUE_STATE_STOPPED:
+			netif_stop_queue(net);
+			cpriv->fifos.tx_queue->state = state;
+			break;
+		}
+		break;
+	default:
+		netdev_err(cpriv->can.dev, "Unsupported tx_queue state: %i\n",
+			   cpriv->fifos.tx_queue->state);
+		break;
+	}
+}
+
+void mcp25xxfd_can_tx_queue_manage(struct mcp25xxfd_can_priv *cpriv, int state)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+	mcp25xxfd_can_tx_queue_manage_nolock(cpriv, state);
+
+	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+}
+
+void mcp25xxfd_can_tx_queue_restart(struct mcp25xxfd_can_priv *cpriv)
+{
+	u32 state = MCP25XXFD_CAN_TX_QUEUE_STATE_RESTART;
+	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(cpriv, state);
+out:
+	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+}
+
+static
+int mcp25xxfd_can_tx_handle_int_tefif_fifo(struct mcp25xxfd_can_priv *cpriv)
+{
+	u32 tef_offset = cpriv->fifos.tef.index * cpriv->fifos.tef.size;
+	struct mcp25xxfd_can_obj_tef *tef =
+		(struct mcp25xxfd_can_obj_tef *)(cpriv->sram + tef_offset);
+	int fifo, ret;
+	unsigned long flags;
+
+	/* read the next TEF entry to get the transmit timestamp and fifo */
+	ret = mcp25xxfd_cmd_read_regs(cpriv->priv->spi,
+				      MCP25XXFD_SRAM_ADDR(tef_offset),
+				      &tef->id, sizeof(*tef));
+	if (ret)
+		return ret;
+
+	/* get the fifo from tef */
+	fifo = FIELD_GET(MCP25XXFD_CAN_OBJ_FLAGS_SEQ_MASK, tef->flags);
+
+	/* check that the fifo is valid */
+	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+	if ((cpriv->fifos.tx_queue->in_can_transfer & BIT(fifo)) == 0)
+		netdev_err(cpriv->can.dev,
+			   "tefif: fifo %i not pending - tef data: id: %08x flags: %08x, ts: %08x - this may be a problem with spi signal quality- try reducing spi-clock speed if this can get reproduced",
+			   fifo, tef->id, tef->flags, tef->ts);
+	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+
+	/* now we can schedule the fifo for echo submission */
+	mcp25xxfd_can_queue_frame(cpriv, fifo, tef->ts, false);
+
+	/* increment the tef index with wraparound */
+	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(cpriv->priv->spi, MCP25XXFD_CAN_TEFCON,
+					MCP25XXFD_CAN_TEFCON_UINC,
+					MCP25XXFD_CAN_TEFCON_UINC);
+}
+
+static int
+mcp25xxfd_can_tx_handle_int_tefif_conservative(struct mcp25xxfd_can_priv *cpriv)
+{
+	u32 tefsta;
+	int ret;
+
+	/* read the TEF status */
+	ret = mcp25xxfd_cmd_read_mask(cpriv->priv->spi, MCP25XXFD_CAN_TEFSTA,
+				      &tefsta, MCP25XXFD_CAN_TEFSTA_TEFNEIF);
+	if (ret)
+		return ret;
+
+	/* read the tef in an inefficient loop */
+	while (tefsta & MCP25XXFD_CAN_TEFSTA_TEFNEIF) {
+		/* read one tef */
+		ret = mcp25xxfd_can_tx_handle_int_tefif_fifo(cpriv);
+		if (ret)
+			return ret;
+
+		/* read the TEF status */
+		ret = mcp25xxfd_cmd_read_mask(cpriv->priv->spi,
+					      MCP25XXFD_CAN_TEFSTA, &tefsta,
+					      MCP25XXFD_CAN_TEFSTA_TEFNEIF);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+int mcp25xxfd_can_tx_handle_int_tefif(struct mcp25xxfd_can_priv *cpriv)
+{
+	unsigned long flags;
+	u32 finished;
+
+	if (!(cpriv->status.intf & MCP25XXFD_CAN_INT_TEFIF))
+		return 0;
+
+	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;
+
+	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+
+	return mcp25xxfd_can_tx_handle_int_tefif_conservative(cpriv);
+}
+
+static
+void mcp25xxfd_can_tx_fill_fifo_common(struct mcp25xxfd_can_priv *cpriv,
+				       struct mcp25xxfd_tx_spi_message *smsg,
+				       struct mcp25xxfd_can_obj_tx *tx,
+				       int dlc, u8 *data)
+{
+	int len = can_dlc2len(dlc);
+
+	/* add fifo number as seq */
+	tx->flags |= smsg->fifo << MCP25XXFD_CAN_OBJ_FLAGS_SEQ_SHIFT;
+
+	/* copy data to tx->data for future reference */
+	memcpy(tx->data, data, len);
+
+	/* transform header to controller format */
+	mcp25xxfd_cmd_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);
+
+	/* transfers to sram should be a multiple of 4 and be zero padded */
+	for (; len & 3; len++)
+		*(smsg->fill_fifo.data.header + sizeof(*tx) + len) = 0;
+
+	/* convert it back to CPU format */
+	mcp25xxfd_cmd_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 mcp25xxfd_can_priv *cpriv,
+				   struct canfd_frame *frame,
+				   struct mcp25xxfd_tx_spi_message *smsg,
+				   struct mcp25xxfd_can_obj_tx *tx)
+{
+	int dlc = can_len2dlc(frame->len);
+
+	/* compute can id */
+	mcp25xxfd_can_id_to_mcp25xxfd(frame->can_id, &tx->id, &tx->flags);
+
+	/* setup flags */
+	tx->flags |= dlc << MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT;
+	tx->flags |= (frame->can_id & CAN_EFF_FLAG) ?
+		MCP25XXFD_CAN_OBJ_FLAGS_IDE : 0;
+	tx->flags |= (frame->can_id & CAN_RTR_FLAG) ?
+		MCP25XXFD_CAN_OBJ_FLAGS_RTR : 0;
+	if (frame->flags & CANFD_BRS)
+		tx->flags |= MCP25XXFD_CAN_OBJ_FLAGS_BRS;
+
+	tx->flags |= (frame->flags & CANFD_ESI) ?
+		MCP25XXFD_CAN_OBJ_FLAGS_ESI : 0;
+	tx->flags |= MCP25XXFD_CAN_OBJ_FLAGS_FDF;
+
+	/* and do common processing */
+	mcp25xxfd_can_tx_fill_fifo_common(cpriv, smsg, tx, dlc, frame->data);
+}
+
+static
+void mcp25xxfd_can_tx_fill_fifo(struct mcp25xxfd_can_priv *cpriv,
+				struct can_frame *frame,
+				struct mcp25xxfd_tx_spi_message *smsg,
+				struct mcp25xxfd_can_obj_tx *tx)
+{
+	/* set frame to valid dlc */
+	if (frame->can_dlc > 8)
+		frame->can_dlc = 8;
+
+	/* compute can id */
+	mcp25xxfd_can_id_to_mcp25xxfd(frame->can_id, &tx->id, &tx->flags);
+
+	/* setup flags */
+	tx->flags |= frame->can_dlc << MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT;
+	tx->flags |= (frame->can_id & CAN_EFF_FLAG) ?
+		MCP25XXFD_CAN_OBJ_FLAGS_IDE : 0;
+	tx->flags |= (frame->can_id & CAN_RTR_FLAG) ?
+		MCP25XXFD_CAN_OBJ_FLAGS_RTR : 0;
+
+	/* and do common processing */
+	mcp25xxfd_can_tx_fill_fifo_common(cpriv, smsg, tx, frame->can_dlc,
+					  frame->data);
+}
+
+static struct mcp25xxfd_tx_spi_message *
+mcp25xxfd_can_tx_queue_get_next_fifo(struct mcp25xxfd_can_priv *cpriv)
+{
+	u32 state = MCP25XXFD_CAN_TX_QUEUE_STATE_RUNABLE;
+	struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue;
+	struct mcp25xxfd_tx_spi_message *smsg;
+	unsigned long flags;
+
+	spin_lock_irqsave(&q->lock, flags);
+
+	/* get the first entry from idle */
+	smsg = mcp25xxfd_can_tx_queue_first_spi_message(q, &q->idle);
+	if (!smsg)
+		goto out_busy;
+
+	/* and move the fifo to next stage */
+	mcp25xxfd_can_tx_queue_move_spi_message(&q->idle,
+						&q->in_fill_fifo_transfer,
+						smsg->fifo);
+
+	/* if queue is empty then stop the network queue immediately */
+	if (!q->idle)
+		mcp25xxfd_can_tx_queue_manage_nolock(cpriv, state);
+out_busy:
+	spin_unlock_irqrestore(&q->lock, flags);
+
+	return smsg;
+}
+
+/* submit the can message to the can-bus */
+netdev_tx_t mcp25xxfd_can_tx_start_xmit(struct sk_buff *skb,
+					struct net_device *net)
+{
+	u32 state = MCP25XXFD_CAN_TX_QUEUE_STATE_STOPPED;
+	struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+	struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue;
+	struct mcp25xxfd_priv *priv = cpriv->priv;
+	struct spi_device *spi = priv->spi;
+	struct mcp25xxfd_tx_spi_message *smsg;
+	struct mcp25xxfd_can_obj_tx *tx;
+	unsigned long flags;
+	int ret;
+
+	/* invalid skb we can ignore */
+	if (can_dropped_invalid_skb(net, skb))
+		return NETDEV_TX_OK;
+
+	spin_lock_irqsave(&q->spi_lock, flags);
+
+	/* get the fifo message structure to process now */
+	smsg = mcp25xxfd_can_tx_queue_get_next_fifo(cpriv);
+	if (!smsg)
+		goto out_busy;
+
+	/* compute the fifo in sram */
+	tx = (struct mcp25xxfd_can_obj_tx *)
+		(cpriv->sram + cpriv->fifos.info[smsg->fifo].offset);
+
+	/* fill in message from skb->data depending on can2.0 or canfd */
+	if (can_is_canfd_skb(skb))
+		mcp25xxfd_can_tx_fill_fifo_fd(cpriv,
+					      (struct canfd_frame *)skb->data,
+					      smsg, tx);
+	else
+		mcp25xxfd_can_tx_fill_fifo(cpriv,
+					   (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
+	 *    (code reduction with a fix in spi core would be aprox.50 lines)
+	 *  * this allows the interrupt handler to start spi messages earlier
+	 *    so reducing latencies a bit and to allow for better concurrency
+	 *  * this separation - in the future - may get used to fill fifos
+	 *    early and reduce the delay on "rollover"
+	 */
+	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;
+
+	spin_unlock_irqrestore(&q->spi_lock, flags);
+
+	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:
+	mcp25xxfd_can_tx_queue_manage_nolock(cpriv, state);
+	spin_unlock_irqrestore(&q->spi_lock, flags);
+
+	return NETDEV_TX_BUSY;
+}
+
+/* submit the fifo back to the network stack */
+int mcp25xxfd_can_tx_submit_frame(struct mcp25xxfd_can_priv *cpriv, int fifo)
+{
+	struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue;
+	struct mcp25xxfd_can_obj_tx *tx = (struct mcp25xxfd_can_obj_tx *)
+		(cpriv->sram + cpriv->fifos.info[fifo].offset);
+	int dlc = (tx->flags & MCP25XXFD_CAN_OBJ_FLAGS_DLC_MASK) >>
+		MCP25XXFD_CAN_OBJ_FLAGS_DLC_SHIFT;
+	unsigned long flags;
+
+	/* update counters */
+	cpriv->can.dev->stats.tx_packets++;
+	cpriv->can.dev->stats.tx_bytes += can_dlc2len(dlc);
+
+	spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+	/* release the echo buffer */
+	can_get_echo_skb(cpriv->can.dev, fifo);
+
+	/* move from in_can_transfer to transferred */
+	mcp25xxfd_can_tx_queue_move_spi_message(&q->in_can_transfer,
+						&q->transferred, fifo);
+
+	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+
+	return 0;
+}
+
+static int
+mcp25xxfd_can_tx_handle_int_txatif_fifo(struct mcp25xxfd_can_priv *cpriv,
+					int fifo)
+{
+	struct mcp25xxfd_tx_spi_message_queue *q = cpriv->fifos.tx_queue;
+	unsigned long flags;
+	u32 val;
+	int ret;
+
+	ret = mcp25xxfd_cmd_read(cpriv->priv->spi,
+				 MCP25XXFD_CAN_FIFOSTA(fifo), &val);
+	if (ret)
+		return ret;
+
+	ret = mcp25xxfd_cmd_write_mask(cpriv->priv->spi,
+				       MCP25XXFD_CAN_FIFOSTA(fifo), 0,
+				       MCP25XXFD_CAN_FIFOSTA_TXABT |
+				       MCP25XXFD_CAN_FIFOSTA_TXLARB |
+				       MCP25XXFD_CAN_FIFOSTA_TXERR |
+				       MCP25XXFD_CAN_FIFOSTA_TXATIF);
+	if (ret)
+		return ret;
+
+	spin_lock_irqsave(&q->lock, flags);
+
+	can_get_echo_skb(cpriv->can.dev, fifo);
+	mcp25xxfd_can_tx_queue_move_spi_message(&q->in_can_transfer,
+						&q->transferred, fifo);
+
+	spin_unlock_irqrestore(&q->lock, flags);
+
+	cpriv->status.txif &= ~BIT(fifo);
+	cpriv->can.dev->stats.tx_aborted_errors++;
+
+	return 0;
+}
+
+int mcp25xxfd_can_tx_handle_int_txatif(struct mcp25xxfd_can_priv *cpriv)
+{
+	int i, f, ret;
+
+	if (!cpriv->status.txatif)
+		return 0;
+
+	/* process all the fifos with txatif flag set */
+	for (i = 0, f = cpriv->fifos.tx.start; i < cpriv->fifos.tx.count;
+	     i++, f++) {
+		if (cpriv->status.txatif & BIT(f)) {
+			ret = mcp25xxfd_can_tx_handle_int_txatif_fifo(cpriv, f);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+int mcp25xxfd_can_tx_queue_alloc(struct mcp25xxfd_can_priv *cpriv)
+{
+	struct mcp25xxfd_tx_spi_message *msg;
+	size_t size = sizeof(struct mcp25xxfd_tx_spi_message_queue) +
+		cpriv->fifos.tx.count * sizeof(*msg);
+	int i, f;
+
+	cpriv->fifos.tx_queue = kzalloc(size, GFP_KERNEL);
+	if (!cpriv->fifos.tx_queue)
+		return -ENOMEM;
+
+	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, f = cpriv->fifos.tx.start; i < cpriv->fifos.tx.count;
+	     i++, f++) {
+		msg = &cpriv->fifos.tx_queue->message[i];
+		cpriv->fifos.tx_queue->fifo2message[f] = msg;
+		mcp25xxfd_can_tx_message_init(cpriv, msg, f);
+	}
+
+	return 0;
+}
+
+void mcp25xxfd_can_tx_queue_free(struct mcp25xxfd_can_priv *cpriv)
+{
+	kfree(cpriv->fifos.tx_queue);
+	cpriv->fifos.tx_queue = NULL;
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.h
new file mode 100644
index 000000000000..1947b3420d58
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2019 Martin Sperl <kernel@martin.sperl.org>
+ */
+
+#ifndef __MCP25XXFD_CAN_TX_H
+#define __MCP25XXFD_CAN_TX_H
+
+#include <linux/spinlock.h>
+#include <linux/spi/spi.h>
+
+#include "mcp25xxfd_can_priv.h"
+
+/* structure of a spi message that is prepared and can get submitted quickly */
+struct mcp25xxfd_tx_spi_message {
+	/* the network device this is related to */
+	struct mcp25xxfd_can_priv *cpriv;
+	/* 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_can_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 mcp25xxfd_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;
+#define MCP25XXFD_CAN_TX_QUEUE_STATE_STOPPED 0
+#define MCP25XXFD_CAN_TX_QUEUE_STATE_STARTED 1
+#define MCP25XXFD_CAN_TX_QUEUE_STATE_RUNABLE 2
+#define MCP25XXFD_CAN_TX_QUEUE_STATE_RESTART 3
+
+	/* spinlock protecting spi submission order */
+	spinlock_t spi_lock;
+
+	/* map each fifo to a mcp25xxfd_tx_spi_message */
+	struct mcp25xxfd_tx_spi_message *fifo2message[32];
+
+	/* the individual messages */
+	struct mcp25xxfd_tx_spi_message message[];
+};
+
+int mcp25xxfd_can_tx_submit_frame(struct mcp25xxfd_can_priv *cpriv, int fifo);
+void mcp25xxfd_can_tx_queue_restart(struct mcp25xxfd_can_priv *cpriv);
+
+int mcp25xxfd_can_tx_handle_int_txatif(struct mcp25xxfd_can_priv *cpriv);
+int mcp25xxfd_can_tx_handle_int_tefif(struct mcp25xxfd_can_priv *cpriv);
+
+netdev_tx_t mcp25xxfd_can_tx_start_xmit(struct sk_buff *skb,
+					struct net_device *net);
+
+void mcp25xxfd_can_tx_queue_manage(struct mcp25xxfd_can_priv *cpriv, int state);
+
+int mcp25xxfd_can_tx_queue_alloc(struct mcp25xxfd_can_priv *cpriv);
+void mcp25xxfd_can_tx_queue_free(struct mcp25xxfd_can_priv *cpriv);
+
+#endif /* __MCP25XXFD_CAN_TX_H */
-- 
2.17.1


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

* [PATCH 5/6] can: mcp25xxfd: Optimize TEF read by avoiding unnecessary SPI transfers
  2020-06-10  7:44 [PATCH 0/6] Add Microchip MCP25XXFD CAN driver Manivannan Sadhasivam
                   ` (3 preceding siblings ...)
  2020-06-10  7:44 ` [PATCH 4/6] can: mcp25xxfd: Add CAN transmission support Manivannan Sadhasivam
@ 2020-06-10  7:44 ` Manivannan Sadhasivam
  2020-06-10  7:44 ` [PATCH 6/6] MAINTAINERS: Add entry for Microchip MCP25XXFD CAN network driver Manivannan Sadhasivam
  2020-06-11 16:26 ` [PATCH 0/6] Add Microchip MCP25XXFD CAN driver Marc Kleine-Budde
  6 siblings, 0 replies; 26+ messages in thread
From: Manivannan Sadhasivam @ 2020-06-10  7:44 UTC (permalink / raw)
  To: wg, mkl; +Cc: kernel, linux-can, netdev, linux-kernel, Manivannan Sadhasivam

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

We have already enough information to know how many tx-messages have
been terminated so that we do not have to query TEF every time if
there is anything pending but we can read the tefs blindly.

This avoids 1 SPI transfer per TEF read.

Signed-off-by: Martin Sperl <kernel@martin.sperl.org>
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
 .../net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c  | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c
index 5ea1e525e776..6f066cb95844 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c
@@ -263,6 +263,13 @@ int mcp25xxfd_can_tx_handle_int_tefif_fifo(struct mcp25xxfd_can_priv *cpriv)
 					MCP25XXFD_CAN_TEFCON_UINC);
 }
 
+/* reading TEF entries can be made even more efficient by reading
+ * multiple TEF entries in one go.
+ * Under the assumption that we have count(TEF) >= count(TX_FIFO)
+ * we can even release TEFs early (before we read them)
+ * (and potentially restarting the transmit-queue early aswell)
+ */
+
 static int
 mcp25xxfd_can_tx_handle_int_tefif_conservative(struct mcp25xxfd_can_priv *cpriv)
 {
@@ -293,6 +300,25 @@ mcp25xxfd_can_tx_handle_int_tefif_conservative(struct mcp25xxfd_can_priv *cpriv)
 	return 0;
 }
 
+static int
+mcp25xxfd_can_tx_handle_int_tefif_optimized(struct mcp25xxfd_can_priv *cpriv,
+					    u32 finished)
+{
+	int i, fifo, ret;
+
+	/* now iterate those */
+	for (i = 0, fifo = cpriv->fifos.tx.start; i < cpriv->fifos.tx.count;
+	     i++, fifo++) {
+		if (finished & BIT(fifo)) {
+			ret = mcp25xxfd_can_tx_handle_int_tefif_fifo(cpriv);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
 int mcp25xxfd_can_tx_handle_int_tefif(struct mcp25xxfd_can_priv *cpriv)
 {
 	unsigned long flags;
@@ -310,6 +336,13 @@ int mcp25xxfd_can_tx_handle_int_tefif(struct mcp25xxfd_can_priv *cpriv)
 
 	spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
 
+	/* run in optimized mode if possible */
+	if (finished)
+		return mcp25xxfd_can_tx_handle_int_tefif_optimized(cpriv,
+								   finished);
+	/* otherwise play it safe */
+	netdev_warn(cpriv->can.dev,
+		    "Something is wrong - we got a TEF interrupt but we were not able to detect a finished fifo\n");
 	return mcp25xxfd_can_tx_handle_int_tefif_conservative(cpriv);
 }
 
-- 
2.17.1


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

* [PATCH 6/6] MAINTAINERS: Add entry for Microchip MCP25XXFD CAN network driver
  2020-06-10  7:44 [PATCH 0/6] Add Microchip MCP25XXFD CAN driver Manivannan Sadhasivam
                   ` (4 preceding siblings ...)
  2020-06-10  7:44 ` [PATCH 5/6] can: mcp25xxfd: Optimize TEF read by avoiding unnecessary SPI transfers Manivannan Sadhasivam
@ 2020-06-10  7:44 ` Manivannan Sadhasivam
  2020-06-11 16:26 ` [PATCH 0/6] Add Microchip MCP25XXFD CAN driver Marc Kleine-Budde
  6 siblings, 0 replies; 26+ messages in thread
From: Manivannan Sadhasivam @ 2020-06-10  7:44 UTC (permalink / raw)
  To: wg, mkl; +Cc: kernel, linux-can, netdev, linux-kernel, Manivannan Sadhasivam

Add MAINTAINERS entry for Microchip MCP25XXFD CAN network driver.

Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
 MAINTAINERS | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index b816a453b10e..591b6fc2d83a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10360,6 +10360,14 @@ L:	linux-input@vger.kernel.org
 S:	Maintained
 F:	drivers/hid/hid-mcp2221.c
 
+MCP25XXFD CAN NETWORK DRIVER
+M:	Martin Sperl <kernel@martin.sperl.org>
+M:	Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+L:	linux-can@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/net/can/microchip,mcp25xxfd.yaml
+F:	drivers/net/can/mcp25xxfd/
+
 MCP4018 AND MCP4531 MICROCHIP DIGITAL POTENTIOMETER DRIVERS
 M:	Peter Rosin <peda@axentia.se>
 L:	linux-iio@vger.kernel.org
-- 
2.17.1


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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-10  7:44 [PATCH 0/6] Add Microchip MCP25XXFD CAN driver Manivannan Sadhasivam
                   ` (5 preceding siblings ...)
  2020-06-10  7:44 ` [PATCH 6/6] MAINTAINERS: Add entry for Microchip MCP25XXFD CAN network driver Manivannan Sadhasivam
@ 2020-06-11 16:26 ` Marc Kleine-Budde
  2020-06-11 20:30   ` Marc Kleine-Budde
                     ` (3 more replies)
  6 siblings, 4 replies; 26+ messages in thread
From: Marc Kleine-Budde @ 2020-06-11 16:26 UTC (permalink / raw)
  To: Manivannan Sadhasivam, wg; +Cc: kernel, linux-can, netdev, linux-kernel

On 6/10/20 9:44 AM, Manivannan Sadhasivam wrote:
> Hello,
> 
> This series adds CAN network driver support for Microchip MCP25XXFD CAN
> Controller with MCP2517FD as the target controller version. This series is
> mostly inspired (or taken) from the previous iterations posted by Martin Sperl.
> I've trimmed down the parts which are not necessary for the initial version
> to ease review. Still the series is relatively huge but I hope to get some
> reviews (post -rcX ofc!).
> 
> Link to the origial series posted by Martin:
> https://www.spinics.net/lists/devicetree/msg284462.html
> 
> I've not changed the functionality much but done some considerable amount of
> cleanups and also preserved the authorship of Martin for all the patches he has
> posted earlier. This series has been tested on 96Boards RB3 platform by myself
> and Martin has tested the previous version on Rpi3 with external MCP2517FD
> controller.

I initially started looking at Martin's driver and it was not using several
modern CAN driver infrastructures. I then posted some cleanup patches but Martin
was not working on the driver any more. Then I decided to rewrite the driver,
that is the one I'm hoping to mainline soon.

Can you give it a try?

https://github.com/marckleinebudde/linux/commits/v5.6-rpi/mcp25xxfd-20200607-41

Marc

-- 
Pengutronix e.K.                 | Marc Kleine-Budde           |
Embedded Linux                   | https://www.pengutronix.de  |
Vertretung West/Dortmund         | Phone: +49-231-2826-924     |
Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-11 16:26 ` [PATCH 0/6] Add Microchip MCP25XXFD CAN driver Marc Kleine-Budde
@ 2020-06-11 20:30   ` Marc Kleine-Budde
  2020-06-12 11:27     ` Marc Kleine-Budde
  2020-06-17 16:59   ` Kurt Van Dijck
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 26+ messages in thread
From: Marc Kleine-Budde @ 2020-06-11 20:30 UTC (permalink / raw)
  To: Manivannan Sadhasivam, wg; +Cc: kernel, linux-can, netdev, linux-kernel

On 6/11/20 6:26 PM, Marc Kleine-Budde wrote:
> I initially started looking at Martin's driver and it was not using several
> modern CAN driver infrastructures. I then posted some cleanup patches but Martin
> was not working on the driver any more. Then I decided to rewrite the driver,
> that is the one I'm hoping to mainline soon.

Seems the driver still suffers from the same problems the last time I looked at it:

I'm on a raspi4 with a mcp2518fd connected to spi0 cs0 and cs1.

Running a canfdtest -vg can0; canfdtest -v can1. It  runs into this problem on a
unloaded system and scaling_governor=performance  within minutes:

> Jun 11 21:27:08 rpi4 kernel: mcp25xxfd spi0.0 can1: Something is wrong - we got a TEF interrupt but we were not able to detect a finished fifo
> Jun 11 21:27:08 rpi4 kernel: mcp25xxfd spi0.0 can1: Something is wrong - we got a TEF interrupt but we were not able to detect a finished fifo
> Jun 11 21:27:08 rpi4 kernel: mcp25xxfd spi0.0 can1: tefif: fifo 6 not pending - tef data: id: 00000078 flags: 00000c08, ts: 0addbed3 - this may be a problem with spi signal quality- try reducing spi-clock speed if this can get reproduced
> Jun 11 21:27:08 rpi4 kernel: mcp25xxfd spi0.0 can1: tefif: fifo 7 not pending - tef data: id: 00000078 flags: 00000e08, ts: 0addc2e5 - this may be a problem with spi signal quality- try reducing spi-clock speed if this can get reproduced
> Jun 11 21:27:08 rpi4 kernel: mcp25xxfd spi0.0 can1: tefif: fifo 1 not pending - tef data: id: 00000078 flags: 00000208, ts: 0addc701 - this may be a problem with spi signal quality- try reducing spi-clock speed if this can get reproduced
> Jun 11 21:27:08 rpi4 kernel: mcp25xxfd spi0.0 can1: tefif: fifo 2 not pending - tef data: id: 00000078 flags: 00000408, ts: 0addcb1b - this may be a problem with spi signal quality- try reducing spi-clock speed if this can get reproduced

Marc

-- 
Pengutronix e.K.                 | Marc Kleine-Budde           |
Embedded Linux                   | https://www.pengutronix.de  |
Vertretung West/Dortmund         | Phone: +49-231-2826-924     |
Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-11 20:30   ` Marc Kleine-Budde
@ 2020-06-12 11:27     ` Marc Kleine-Budde
  0 siblings, 0 replies; 26+ messages in thread
From: Marc Kleine-Budde @ 2020-06-12 11:27 UTC (permalink / raw)
  To: Manivannan Sadhasivam, wg; +Cc: kernel, linux-can, netdev, linux-kernel

On 6/11/20 10:30 PM, Marc Kleine-Budde wrote:
> On 6/11/20 6:26 PM, Marc Kleine-Budde wrote:
>> I initially started looking at Martin's driver and it was not using several
>> modern CAN driver infrastructures. I then posted some cleanup patches but Martin
>> was not working on the driver any more. Then I decided to rewrite the driver,
>> that is the one I'm hoping to mainline soon.
> 
> Seems the driver still suffers from the same problems the last time I looked at it:
> 
> I'm on a raspi4 with a mcp2518fd connected to spi0 cs0 and cs1.
> 
> Running a canfdtest -vg can0; canfdtest -v can1. It  runs into this problem on a
> unloaded system and scaling_governor=performance  within minutes:
> 
>> Jun 11 21:27:08 rpi4 kernel: mcp25xxfd spi0.0 can1: Something is wrong - we got a TEF interrupt but we were not able to detect a finished fifo
>> Jun 11 21:27:08 rpi4 kernel: mcp25xxfd spi0.0 can1: Something is wrong - we got a TEF interrupt but we were not able to detect a finished fifo
>> Jun 11 21:27:08 rpi4 kernel: mcp25xxfd spi0.0 can1: tefif: fifo 6 not pending - tef data: id: 00000078 flags: 00000c08, ts: 0addbed3 - this may be a problem with spi signal quality- try reducing spi-clock speed if this can get reproduced
>> Jun 11 21:27:08 rpi4 kernel: mcp25xxfd spi0.0 can1: tefif: fifo 7 not pending - tef data: id: 00000078 flags: 00000e08, ts: 0addc2e5 - this may be a problem with spi signal quality- try reducing spi-clock speed if this can get reproduced
>> Jun 11 21:27:08 rpi4 kernel: mcp25xxfd spi0.0 can1: tefif: fifo 1 not pending - tef data: id: 00000078 flags: 00000208, ts: 0addc701 - this may be a problem with spi signal quality- try reducing spi-clock speed if this can get reproduced
>> Jun 11 21:27:08 rpi4 kernel: mcp25xxfd spi0.0 can1: tefif: fifo 2 not pending - tef data: id: 00000078 flags: 00000408, ts: 0addcb1b - this may be a problem with spi signal quality- try reducing spi-clock speed if this can get reproduced

Further two mcp2517 on the rpi4, unloaded system, performance dovernor running
cansequence -v can0 -p; cansequence can1 -v; gives:

> Jun 12 12:17:53 rpi4 kernel: mcp25xxfd spi1.0: ECC single bit error at 800
> Jun 12 12:17:53 rpi4 kernel: mcp25xxfd spi1.0: ECC single bit error at 800
> Jun 12 12:17:54 rpi4 kernel: mcp25xxfd spi1.0: ECC double bit error at 800
> Jun 12 12:17:54 rpi4 kernel: mcp25xxfd spi1.0: unidentified system interrupt - intf =  b91a1118
> Jun 12 12:17:54 rpi4 kernel: mcp25xxfd spi1.0: ECC double bit error at 800

I don't see these problems with my driver.

Marc

-- 
Pengutronix e.K.                 | Marc Kleine-Budde           |
Embedded Linux                   | https://www.pengutronix.de  |
Vertretung West/Dortmund         | Phone: +49-231-2826-924     |
Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-11 16:26 ` [PATCH 0/6] Add Microchip MCP25XXFD CAN driver Marc Kleine-Budde
  2020-06-11 20:30   ` Marc Kleine-Budde
@ 2020-06-17 16:59   ` Kurt Van Dijck
  2020-06-17 22:36     ` Marc Kleine-Budde
  2020-06-18  8:55   ` Manivannan Sadhasivam
  2020-06-18 12:06   ` Manivannan Sadhasivam
  3 siblings, 1 reply; 26+ messages in thread
From: Kurt Van Dijck @ 2020-06-17 16:59 UTC (permalink / raw)
  To: Marc Kleine-Budde
  Cc: Manivannan Sadhasivam, wg, kernel, linux-can, netdev, linux-kernel

On do, 11 jun 2020 18:26:19 +0200, Marc Kleine-Budde wrote:
> On 6/10/20 9:44 AM, Manivannan Sadhasivam wrote:
> > Hello,
> > 
> > This series adds CAN network driver support for Microchip MCP25XXFD CAN
> > Controller with MCP2517FD as the target controller version. This series is
> > mostly inspired (or taken) from the previous iterations posted by Martin Sperl.
> > I've trimmed down the parts which are not necessary for the initial version
> > to ease review. Still the series is relatively huge but I hope to get some
> > reviews (post -rcX ofc!).
> > 
> > Link to the origial series posted by Martin:
> > https://www.spinics.net/lists/devicetree/msg284462.html
> > 
> > I've not changed the functionality much but done some considerable amount of
> > cleanups and also preserved the authorship of Martin for all the patches he has
> > posted earlier. This series has been tested on 96Boards RB3 platform by myself
> > and Martin has tested the previous version on Rpi3 with external MCP2517FD
> > controller.
> 
> I initially started looking at Martin's driver and it was not using several
> modern CAN driver infrastructures. I then posted some cleanup patches but Martin
> was not working on the driver any more. Then I decided to rewrite the driver,
> that is the one I'm hoping to mainline soon.
> 
> Can you give it a try?
> 
> https://github.com/marckleinebudde/linux/commits/v5.6-rpi/mcp25xxfd-20200607-41

Hi Marc,

I'm in the process of getting a Variscite imx8m mini SOM online, with
MCP2517FD. The 4.19 kernel that comes with it, has a driver that is
clearly inspired by the one of Martin Sperl (not investigated too much
yet). I have problems of probing the chip when the bus is under full
load (under not full load, the probing only occasionally fails) due to
the modeswitch test.

Is there a real difference in yours between the rpi and sunxi branches?
Is there much evolution since -36 or would you mind to backport your
latest -42 to 4.19?

I will work on this the upcoming days. I can't do extensive tests, but
rather a works-for-me test that includes bitrate probe.
I only have 1 such board right now, and no other FD hardware.

Kurt
> 
> Marc

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-17 16:59   ` Kurt Van Dijck
@ 2020-06-17 22:36     ` Marc Kleine-Budde
  2020-06-18 12:30       ` Kurt Van Dijck
  0 siblings, 1 reply; 26+ messages in thread
From: Marc Kleine-Budde @ 2020-06-17 22:36 UTC (permalink / raw)
  To: Manivannan Sadhasivam, wg, kernel, linux-can, netdev,
	linux-kernel, Kurt Van Dijck

On 6/17/20 6:59 PM, Kurt Van Dijck wrote:
> I'm in the process of getting a Variscite imx8m mini SOM online, with

Have your heard about the imx8m plus? It has CAN cores! We have a board in the
office to play with. :)

> MCP2517FD. The 4.19 kernel that comes with it, has a driver that is

You shall not start projects with 1,5 years old kernel.
And you probably shall not use vendor kernel for new projects.
:D

> clearly inspired by the one of Martin Sperl (not investigated too much
> yet). I have problems of probing the chip when the bus is under full
> load (under not full load, the probing only occasionally fails) due to
> the modeswitch test.
> 
> Is there a real difference in yours between the rpi and sunxi branches?

The sunxi branch has some sunxi SPI driver improvements, the rpi branch some for
the raspi SPI drivers. All branches with the same -xx should have the same
mcp25xxfd driver.

With the exception the latest version is v5.6-rpi/mcp25xxfd-20200607-41, which
is cleaned up for mainlining (it has the logging and dump stuff removed).

> Is there much evolution since -36 or would you mind to backport your
> latest -42 to 4.19?

Not much, some bus off cleanups, however I've backported all changes to
v4.19-rpi/mcp25xxfd-20200429-41 (debug and log is still included).

When you port this to your mx8 take all from (including)

    097701d1ea4f can: dev: avoid long lines
to
    v4.19-rpi/mcp25xxfd-20200429-41

> I will work on this the upcoming days. I can't do extensive tests, but
> rather a works-for-me test that includes bitrate probe.
> I only have 1 such board right now, and no other FD hardware.

Great, looking for feedback! Especially how the driver copes with your fully
loaded bus test.

regards,
Marc

-- 
Pengutronix e.K.                 | Marc Kleine-Budde           |
Embedded Linux                   | https://www.pengutronix.de  |
Vertretung West/Dortmund         | Phone: +49-231-2826-924     |
Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-11 16:26 ` [PATCH 0/6] Add Microchip MCP25XXFD CAN driver Marc Kleine-Budde
  2020-06-11 20:30   ` Marc Kleine-Budde
  2020-06-17 16:59   ` Kurt Van Dijck
@ 2020-06-18  8:55   ` Manivannan Sadhasivam
  2020-06-20  2:43     ` Manivannan Sadhasivam
  2020-06-22 11:47     ` Marc Kleine-Budde
  2020-06-18 12:06   ` Manivannan Sadhasivam
  3 siblings, 2 replies; 26+ messages in thread
From: Manivannan Sadhasivam @ 2020-06-18  8:55 UTC (permalink / raw)
  To: Marc Kleine-Budde; +Cc: wg, kernel, linux-can, netdev, linux-kernel

Hi,

On 0611, Marc Kleine-Budde wrote:
> On 6/10/20 9:44 AM, Manivannan Sadhasivam wrote:
> > Hello,
> > 
> > This series adds CAN network driver support for Microchip MCP25XXFD CAN
> > Controller with MCP2517FD as the target controller version. This series is
> > mostly inspired (or taken) from the previous iterations posted by Martin Sperl.
> > I've trimmed down the parts which are not necessary for the initial version
> > to ease review. Still the series is relatively huge but I hope to get some
> > reviews (post -rcX ofc!).
> > 
> > Link to the origial series posted by Martin:
> > https://www.spinics.net/lists/devicetree/msg284462.html
> > 
> > I've not changed the functionality much but done some considerable amount of
> > cleanups and also preserved the authorship of Martin for all the patches he has
> > posted earlier. This series has been tested on 96Boards RB3 platform by myself
> > and Martin has tested the previous version on Rpi3 with external MCP2517FD
> > controller.
> 
> I initially started looking at Martin's driver and it was not using several
> modern CAN driver infrastructures. I then posted some cleanup patches but Martin
> was not working on the driver any more. Then I decided to rewrite the driver,
> that is the one I'm hoping to mainline soon.
> 

So how should we proceed from here? It is okay for me to work on adding some
features and also fixing the issues you've reported so far. But I want to reach
a consensus before moving forward.

If you think that it makes sense to go with your set of patches, then I need an
estimate on when you'll post the first revision.

> Can you give it a try?
> 
> https://github.com/marckleinebudde/linux/commits/v5.6-rpi/mcp25xxfd-20200607-41
> 

Sure thing. Will do.

Thanks,
Mani

> Marc
> 
> -- 
> Pengutronix e.K.                 | Marc Kleine-Budde           |
> Embedded Linux                   | https://www.pengutronix.de  |
> Vertretung West/Dortmund         | Phone: +49-231-2826-924     |
> Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-11 16:26 ` [PATCH 0/6] Add Microchip MCP25XXFD CAN driver Marc Kleine-Budde
                     ` (2 preceding siblings ...)
  2020-06-18  8:55   ` Manivannan Sadhasivam
@ 2020-06-18 12:06   ` Manivannan Sadhasivam
  2020-06-22 11:41     ` Marc Kleine-Budde
  3 siblings, 1 reply; 26+ messages in thread
From: Manivannan Sadhasivam @ 2020-06-18 12:06 UTC (permalink / raw)
  To: Marc Kleine-Budde; +Cc: wg, kernel, linux-can, netdev, linux-kernel

Hi,

On 0611, Marc Kleine-Budde wrote:
> On 6/10/20 9:44 AM, Manivannan Sadhasivam wrote:
> > Hello,
> > 
> > This series adds CAN network driver support for Microchip MCP25XXFD CAN
> > Controller with MCP2517FD as the target controller version. This series is
> > mostly inspired (or taken) from the previous iterations posted by Martin Sperl.
> > I've trimmed down the parts which are not necessary for the initial version
> > to ease review. Still the series is relatively huge but I hope to get some
> > reviews (post -rcX ofc!).
> > 
> > Link to the origial series posted by Martin:
> > https://www.spinics.net/lists/devicetree/msg284462.html
> > 
> > I've not changed the functionality much but done some considerable amount of
> > cleanups and also preserved the authorship of Martin for all the patches he has
> > posted earlier. This series has been tested on 96Boards RB3 platform by myself
> > and Martin has tested the previous version on Rpi3 with external MCP2517FD
> > controller.
> 
> I initially started looking at Martin's driver and it was not using several
> modern CAN driver infrastructures. I then posted some cleanup patches but Martin
> was not working on the driver any more. Then I decided to rewrite the driver,
> that is the one I'm hoping to mainline soon.
> 
> Can you give it a try?
> 
> https://github.com/marckleinebudde/linux/commits/v5.6-rpi/mcp25xxfd-20200607-41
> 

Tested this version on my board with MCP2518FD and it works fine. I'm okay with
moving forward with your version and would like to contribute. Please let me
know how we can move forward.

Thanks,
Mani

> Marc
> 
> -- 
> Pengutronix e.K.                 | Marc Kleine-Budde           |
> Embedded Linux                   | https://www.pengutronix.de  |
> Vertretung West/Dortmund         | Phone: +49-231-2826-924     |
> Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-17 22:36     ` Marc Kleine-Budde
@ 2020-06-18 12:30       ` Kurt Van Dijck
  2020-06-18 12:35         ` Marc Kleine-Budde
  0 siblings, 1 reply; 26+ messages in thread
From: Kurt Van Dijck @ 2020-06-18 12:30 UTC (permalink / raw)
  To: Marc Kleine-Budde
  Cc: Manivannan Sadhasivam, wg, kernel, linux-can, netdev, linux-kernel

On do, 18 jun 2020 00:36:29 +0200, Marc Kleine-Budde wrote:
> On 6/17/20 6:59 PM, Kurt Van Dijck wrote:
> > I'm in the process of getting a Variscite imx8m mini SOM online, with
> 
> Have your heard about the imx8m plus? It has CAN cores! We have a board in the
> office to play with. :)
> 
> > MCP2517FD. The 4.19 kernel that comes with it, has a driver that is
> 
> You shall not start projects with 1,5 years old kernel.
> And you probably shall not use vendor kernel for new projects.
> :D

your rpi kernel starts of v4.19.119 (or so), where the variscite starts
of v4.19.35.
The result was quite some list of merge conflicts, on top of a vendor
kernel, so I took your advise and switched to the latest and greatest
5.7.3.
Luckily, we need no sound (yet) and no video. :-)

> 
> Not much, some bus off cleanups, however I've backported all changes to
> v4.19-rpi/mcp25xxfd-20200429-41 (debug and log is still included).
> 
> When you port this to your mx8 take all from (including)
> 
>     097701d1ea4f can: dev: avoid long lines
> to
>     v4.19-rpi/mcp25xxfd-20200429-41
> 

I'll merge one of your latest ...

Kurt

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-18 12:30       ` Kurt Van Dijck
@ 2020-06-18 12:35         ` Marc Kleine-Budde
  2020-06-22 10:25           ` Kurt Van Dijck
  0 siblings, 1 reply; 26+ messages in thread
From: Marc Kleine-Budde @ 2020-06-18 12:35 UTC (permalink / raw)
  To: Manivannan Sadhasivam, wg, kernel, linux-can, netdev, linux-kernel

On 6/18/20 2:30 PM, Kurt Van Dijck wrote:
> On do, 18 jun 2020 00:36:29 +0200, Marc Kleine-Budde wrote:
>> On 6/17/20 6:59 PM, Kurt Van Dijck wrote:
>>> I'm in the process of getting a Variscite imx8m mini SOM online, with
>>
>> Have your heard about the imx8m plus? It has CAN cores! We have a board in the
>> office to play with. :)
>>
>>> MCP2517FD. The 4.19 kernel that comes with it, has a driver that is
>>
>> You shall not start projects with 1,5 years old kernel.
>> And you probably shall not use vendor kernel for new projects.
>> :D
> 
> your rpi kernel starts of v4.19.119 (or so), where the variscite starts
> of v4.19.35.

You're missing some stable backports for the kernel then.

> The result was quite some list of merge conflicts, on top of a vendor
> kernel, so I took your advise and switched to the latest and greatest
> 5.7.3.

\o/

> Luckily, we need no sound (yet) and no video. :-)

regards,
Marc

-- 
Pengutronix e.K.                 | Marc Kleine-Budde           |
Embedded Linux                   | https://www.pengutronix.de  |
Vertretung West/Dortmund         | Phone: +49-231-2826-924     |
Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-18  8:55   ` Manivannan Sadhasivam
@ 2020-06-20  2:43     ` Manivannan Sadhasivam
  2020-06-22 11:47     ` Marc Kleine-Budde
  1 sibling, 0 replies; 26+ messages in thread
From: Manivannan Sadhasivam @ 2020-06-20  2:43 UTC (permalink / raw)
  To: Marc Kleine-Budde; +Cc: wg, kernel, linux-can, netdev, linux-kernel

On Thu, Jun 18, 2020 at 02:25:33PM +0530, Manivannan Sadhasivam wrote:
> Hi,
> 
> On 0611, Marc Kleine-Budde wrote:
> > On 6/10/20 9:44 AM, Manivannan Sadhasivam wrote:
> > > Hello,
> > > 
> > > This series adds CAN network driver support for Microchip MCP25XXFD CAN
> > > Controller with MCP2517FD as the target controller version. This series is
> > > mostly inspired (or taken) from the previous iterations posted by Martin Sperl.
> > > I've trimmed down the parts which are not necessary for the initial version
> > > to ease review. Still the series is relatively huge but I hope to get some
> > > reviews (post -rcX ofc!).
> > > 
> > > Link to the origial series posted by Martin:
> > > https://www.spinics.net/lists/devicetree/msg284462.html
> > > 
> > > I've not changed the functionality much but done some considerable amount of
> > > cleanups and also preserved the authorship of Martin for all the patches he has
> > > posted earlier. This series has been tested on 96Boards RB3 platform by myself
> > > and Martin has tested the previous version on Rpi3 with external MCP2517FD
> > > controller.
> > 
> > I initially started looking at Martin's driver and it was not using several
> > modern CAN driver infrastructures. I then posted some cleanup patches but Martin
> > was not working on the driver any more. Then I decided to rewrite the driver,
> > that is the one I'm hoping to mainline soon.
> > 
> 
> So how should we proceed from here? It is okay for me to work on adding some
> features and also fixing the issues you've reported so far. But I want to reach
> a consensus before moving forward.
> 
> If you think that it makes sense to go with your set of patches, then I need an
> estimate on when you'll post the first revision.
> 

Ping!

> > Can you give it a try?
> > 
> > https://github.com/marckleinebudde/linux/commits/v5.6-rpi/mcp25xxfd-20200607-41
> > 
> 
> Sure thing. Will do.
> 
> Thanks,
> Mani
> 
> > Marc
> > 
> > -- 
> > Pengutronix e.K.                 | Marc Kleine-Budde           |
> > Embedded Linux                   | https://www.pengutronix.de  |
> > Vertretung West/Dortmund         | Phone: +49-231-2826-924     |
> > Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-18 12:35         ` Marc Kleine-Budde
@ 2020-06-22 10:25           ` Kurt Van Dijck
  2020-06-22 10:55             ` Marc Kleine-Budde
  0 siblings, 1 reply; 26+ messages in thread
From: Kurt Van Dijck @ 2020-06-22 10:25 UTC (permalink / raw)
  To: Marc Kleine-Budde
  Cc: Manivannan Sadhasivam, wg, kernel, linux-can, netdev, linux-kernel

Hey Marc,

On do, 18 jun 2020 14:35:28 +0200, Marc Kleine-Budde wrote:
> On 6/18/20 2:30 PM, Kurt Van Dijck wrote:
> > On do, 18 jun 2020 00:36:29 +0200, Marc Kleine-Budde wrote:
> >> On 6/17/20 6:59 PM, Kurt Van Dijck wrote:
> >>> I'm in the process of getting a Variscite imx8m mini SOM online, with
> >>
> >> Have your heard about the imx8m plus? It has CAN cores! We have a board in the
> >> office to play with. :)
> >>
> >>> MCP2517FD. The 4.19 kernel that comes with it, has a driver that is
> >>
> >> You shall not start projects with 1,5 years old kernel.
> >> And you probably shall not use vendor kernel for new projects.
> >> :D
> > 
> > your rpi kernel starts of v4.19.119 (or so), where the variscite starts
> > of v4.19.35.
> 
> You're missing some stable backports for the kernel then.
> 
> > The result was quite some list of merge conflicts, on top of a vendor
> > kernel, so I took your advise and switched to the latest and greatest
> > 5.7.3.
> 
> \o/

I got my board up with a 5.7, despite device-tree problems completely
unrelated to CAN.
It seems to work well with a fully-loaded CAN bus (cangen -g0 ...).
So that is a real improvement.
I will need to add the listen-only mode soon.

Kurt

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-22 10:25           ` Kurt Van Dijck
@ 2020-06-22 10:55             ` Marc Kleine-Budde
  2020-06-22 12:30               ` Kurt Van Dijck
  0 siblings, 1 reply; 26+ messages in thread
From: Marc Kleine-Budde @ 2020-06-22 10:55 UTC (permalink / raw)
  To: Manivannan Sadhasivam, wg, kernel, linux-can, netdev, linux-kernel

On 6/22/20 12:25 PM, Kurt Van Dijck wrote:
> I got my board up with a 5.7, despite device-tree problems completely
> unrelated to CAN.

\o/

> It seems to work well with a fully-loaded CAN bus (cangen -g0 ...).
> So that is a real improvement.

Can I add your Tested-by?

> I will need to add the listen-only mode soon.

Patches are always welcome!

regards,
Marc

-- 
Pengutronix e.K.                 | Marc Kleine-Budde           |
Embedded Linux                   | https://www.pengutronix.de  |
Vertretung West/Dortmund         | Phone: +49-231-2826-924     |
Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-18 12:06   ` Manivannan Sadhasivam
@ 2020-06-22 11:41     ` Marc Kleine-Budde
  0 siblings, 0 replies; 26+ messages in thread
From: Marc Kleine-Budde @ 2020-06-22 11:41 UTC (permalink / raw)
  To: Manivannan Sadhasivam; +Cc: wg, kernel, linux-can, netdev, linux-kernel

On 6/18/20 2:06 PM, Manivannan Sadhasivam wrote:
>> https://github.com/marckleinebudde/linux/commits/v5.6-rpi/mcp25xxfd-20200607-41
>>
> 
> Tested this version on my board with MCP2518FD and it works fine. I'm okay with
> moving forward with your version and would like to contribute. Please let me
> know how we can move forward.

Can I add your Tested-by?

Marc

-- 
Pengutronix e.K.                 | Marc Kleine-Budde           |
Embedded Linux                   | https://www.pengutronix.de  |
Vertretung West/Dortmund         | Phone: +49-231-2826-924     |
Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-18  8:55   ` Manivannan Sadhasivam
  2020-06-20  2:43     ` Manivannan Sadhasivam
@ 2020-06-22 11:47     ` Marc Kleine-Budde
  1 sibling, 0 replies; 26+ messages in thread
From: Marc Kleine-Budde @ 2020-06-22 11:47 UTC (permalink / raw)
  To: Manivannan Sadhasivam; +Cc: wg, kernel, linux-can, netdev, linux-kernel

On 6/18/20 10:55 AM, Manivannan Sadhasivam wrote:
> So how should we proceed from here? It is okay for me to work on adding some
> features and also fixing the issues you've reported so far. But I want to reach
> a consensus before moving forward.
> 
> If you think that it makes sense to go with your set of patches, then I need an
> estimate on when you'll post the first revision.

Done, I've posted my current version, which is -41.

Marc

-- 
Pengutronix e.K.                 | Marc Kleine-Budde           |
Embedded Linux                   | https://www.pengutronix.de  |
Vertretung West/Dortmund         | Phone: +49-231-2826-924     |
Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-22 10:55             ` Marc Kleine-Budde
@ 2020-06-22 12:30               ` Kurt Van Dijck
  2020-06-22 12:43                 ` Kurt Van Dijck
  0 siblings, 1 reply; 26+ messages in thread
From: Kurt Van Dijck @ 2020-06-22 12:30 UTC (permalink / raw)
  To: Marc Kleine-Budde
  Cc: Manivannan Sadhasivam, wg, kernel, linux-can, netdev, linux-kernel

On ma, 22 jun 2020 12:55:41 +0200, Marc Kleine-Budde wrote:
> Date:   Mon, 22 Jun 2020 12:55:41 +0200
> From: Marc Kleine-Budde <mkl@pengutronix.de>
> To: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>,
>  wg@grandegger.com, kernel@martin.sperl.org, linux-can@vger.kernel.org,
>  netdev@vger.kernel.org, linux-kernel@vger.kernel.org
> Subject: Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
> User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101
>  Thunderbird/68.9.0
> 
> On 6/22/20 12:25 PM, Kurt Van Dijck wrote:
> > I got my board up with a 5.7, despite device-tree problems completely
> > unrelated to CAN.
> 
> \o/
> 
> > It seems to work well with a fully-loaded CAN bus (cangen -g0 ...).
> > So that is a real improvement.
> 
> Can I add your Tested-by?
yes.
> 
> > I will need to add the listen-only mode soon.
> 
> Patches are always welcome!
> 
> regards,
> Marc
> 
> -- 
> Pengutronix e.K.                 | Marc Kleine-Budde           |
> Embedded Linux                   | https://www.pengutronix.de  |
> Vertretung West/Dortmund         | Phone: +49-231-2826-924     |
> Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-22 12:30               ` Kurt Van Dijck
@ 2020-06-22 12:43                 ` Kurt Van Dijck
  2020-06-22 12:54                   ` Marc Kleine-Budde
  0 siblings, 1 reply; 26+ messages in thread
From: Kurt Van Dijck @ 2020-06-22 12:43 UTC (permalink / raw)
  To: Marc Kleine-Budde, Manivannan Sadhasivam, wg, kernel, linux-can,
	netdev, linux-kernel

Marc,

I get RX-0: FIFO overflows in listen-only mode (back-to-back burst of
the single other node).
The SPI peripheral does not use DMA :-(.
Do you have, by accident, some freescale SPI fixes lying around?

It's not the biggest problem on my side, but is proves the system not
being guarded against load.

Kurt

On ma, 22 jun 2020 14:30:31 +0200, Kurt Van Dijck wrote:
> 
> On ma, 22 jun 2020 12:55:41 +0200, Marc Kleine-Budde wrote:
> > On 6/22/20 12:25 PM, Kurt Van Dijck wrote:
> > > I got my board up with a 5.7, despite device-tree problems completely
> > > unrelated to CAN.
> > 
> > \o/
> > 
> > > It seems to work well with a fully-loaded CAN bus (cangen -g0 ...).
> > > So that is a real improvement.
> > 
> > Can I add your Tested-by?
> yes.
> > 
> > > I will need to add the listen-only mode soon.
> > 
> > Patches are always welcome!
> > 
> > regards,
> > Marc

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-22 12:43                 ` Kurt Van Dijck
@ 2020-06-22 12:54                   ` Marc Kleine-Budde
  2020-06-22 13:26                     ` Kurt Van Dijck
  0 siblings, 1 reply; 26+ messages in thread
From: Marc Kleine-Budde @ 2020-06-22 12:54 UTC (permalink / raw)
  To: Manivannan Sadhasivam, wg, kernel, linux-can, netdev, linux-kernel

On 6/22/20 2:43 PM, Kurt Van Dijck wrote:
> I get RX-0: FIFO overflows in listen-only mode (back-to-back burst of
> the single other node).

Single other node? Who's ACKing the CAN frames?

> The SPI peripheral does not use DMA :-(.

The SPI messages are quite small, so DMA wont help either. Getting rid of the
IRQ and polling for completion is the way to go.

> Do you have, by accident, some freescale SPI fixes lying around?

nope

> It's not the biggest problem on my side, but is proves the system not
> being guarded against load.

Do you have freq scaling activated?

Marc

-- 
Pengutronix e.K.                 | Marc Kleine-Budde           |
Embedded Linux                   | https://www.pengutronix.de  |
Vertretung West/Dortmund         | Phone: +49-231-2826-924     |
Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-22 12:54                   ` Marc Kleine-Budde
@ 2020-06-22 13:26                     ` Kurt Van Dijck
  2020-06-22 13:41                       ` Marc Kleine-Budde
  0 siblings, 1 reply; 26+ messages in thread
From: Kurt Van Dijck @ 2020-06-22 13:26 UTC (permalink / raw)
  To: Marc Kleine-Budde
  Cc: Manivannan Sadhasivam, wg, kernel, linux-can, netdev, linux-kernel

On ma, 22 jun 2020 14:54:15 +0200, Marc Kleine-Budde wrote:
> On 6/22/20 2:43 PM, Kurt Van Dijck wrote:
> > I get RX-0: FIFO overflows in listen-only mode (back-to-back burst of
> > the single other node).
> 
> Single other node? Who's ACKing the CAN frames?

hence the back-to-back burst.

> 
> > The SPI peripheral does not use DMA :-(.
> 
> The SPI messages are quite small, so DMA wont help either. Getting rid of the
> IRQ and polling for completion is the way to go.
> 
> > Do you have, by accident, some freescale SPI fixes lying around?
> 
> nope
> 
> > It's not the biggest problem on my side, but is proves the system not
> > being guarded against load.
> 
> Do you have freq scaling activated?

Not yet.

The device tree needs upgrading ... grrr

> 
> Marc
> 
> -- 
> Pengutronix e.K.                 | Marc Kleine-Budde           |
> Embedded Linux                   | https://www.pengutronix.de  |
> Vertretung West/Dortmund         | Phone: +49-231-2826-924     |
> Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-5555 |

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

* Re: [PATCH 0/6] Add Microchip MCP25XXFD CAN driver
  2020-06-22 13:26                     ` Kurt Van Dijck
@ 2020-06-22 13:41                       ` Marc Kleine-Budde
  0 siblings, 0 replies; 26+ messages in thread
From: Marc Kleine-Budde @ 2020-06-22 13:41 UTC (permalink / raw)
  To: Manivannan Sadhasivam, wg, kernel, linux-can, netdev, linux-kernel

On 6/22/20 3:26 PM, Kurt Van Dijck wrote:
> On ma, 22 jun 2020 14:54:15 +0200, Marc Kleine-Budde wrote:
>> On 6/22/20 2:43 PM, Kurt Van Dijck wrote:
>>> I get RX-0: FIFO overflows in listen-only mode (back-to-back burst of
>>> the single other node).
>>
>> Single other node? Who's ACKing the CAN frames?
> 
> hence the back-to-back burst.

Just wanted to be sure if I understood correctly. Nice testcase btw!

>>> The SPI peripheral does not use DMA :-(.
>>
>> The SPI messages are quite small, so DMA wont help either. Getting rid of the
>> IRQ and polling for completion is the way to go.
>>
>>> Do you have, by accident, some freescale SPI fixes lying around?
>>
>> nope
>>
>>> It's not the biggest problem on my side, but is proves the system not
>>> being guarded against load.
>>
>> Do you have freq scaling activated?
> 
> Not yet.
> 
> The device tree needs upgrading ... grrr

Without freq scaling the imx core is supposed to run at full speed, which is
better for bursty SPI traffic than starting clocked down into a burst....

Marc

-- 
Pengutronix e.K.                 | Marc Kleine-Budde           |
Embedded Linux                   | https://www.pengutronix.de  |
Vertretung West/Dortmund         | Phone: +49-231-2826-924     |
Amtsgericht Hildesheim, HRA 2686 | Fax:   +49-5121-206917-5555 |

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

end of thread, other threads:[~2020-06-22 13:41 UTC | newest]

Thread overview: 26+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-06-10  7:44 [PATCH 0/6] Add Microchip MCP25XXFD CAN driver Manivannan Sadhasivam
2020-06-10  7:44 ` [PATCH 1/6] dt-bindings: can: Document devicetree bindings for MCP25XXFD Manivannan Sadhasivam
2020-06-10  7:44 ` [PATCH 2/6] can: mcp25xxfd: Add Microchip MCP25XXFD CAN-FD driver infrastructure Manivannan Sadhasivam
2020-06-10  7:44 ` [PATCH 3/6] can: mcp25xxfd: Add support for CAN reception Manivannan Sadhasivam
2020-06-10  7:44 ` [PATCH 4/6] can: mcp25xxfd: Add CAN transmission support Manivannan Sadhasivam
2020-06-10  7:44 ` [PATCH 5/6] can: mcp25xxfd: Optimize TEF read by avoiding unnecessary SPI transfers Manivannan Sadhasivam
2020-06-10  7:44 ` [PATCH 6/6] MAINTAINERS: Add entry for Microchip MCP25XXFD CAN network driver Manivannan Sadhasivam
2020-06-11 16:26 ` [PATCH 0/6] Add Microchip MCP25XXFD CAN driver Marc Kleine-Budde
2020-06-11 20:30   ` Marc Kleine-Budde
2020-06-12 11:27     ` Marc Kleine-Budde
2020-06-17 16:59   ` Kurt Van Dijck
2020-06-17 22:36     ` Marc Kleine-Budde
2020-06-18 12:30       ` Kurt Van Dijck
2020-06-18 12:35         ` Marc Kleine-Budde
2020-06-22 10:25           ` Kurt Van Dijck
2020-06-22 10:55             ` Marc Kleine-Budde
2020-06-22 12:30               ` Kurt Van Dijck
2020-06-22 12:43                 ` Kurt Van Dijck
2020-06-22 12:54                   ` Marc Kleine-Budde
2020-06-22 13:26                     ` Kurt Van Dijck
2020-06-22 13:41                       ` Marc Kleine-Budde
2020-06-18  8:55   ` Manivannan Sadhasivam
2020-06-20  2:43     ` Manivannan Sadhasivam
2020-06-22 11:47     ` Marc Kleine-Budde
2020-06-18 12:06   ` Manivannan Sadhasivam
2020-06-22 11:41     ` Marc Kleine-Budde

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).