* [RESEND PATCH 1/6] dt-bindings: can: Document devicetree bindings for MCP25XXFD
2020-06-10 7:47 [RESEND PATCH 0/6] Add Microchip MCP25XXFD CAN driver Manivannan Sadhasivam
@ 2020-06-10 7:47 ` Manivannan Sadhasivam
2020-06-17 22:16 ` Rob Herring
2020-06-10 7:47 ` [RESEND PATCH 2/6] can: mcp25xxfd: Add Microchip MCP25XXFD CAN-FD driver infrastructure Manivannan Sadhasivam
` (4 subsequent siblings)
5 siblings, 1 reply; 9+ messages in thread
From: Manivannan Sadhasivam @ 2020-06-10 7:47 UTC (permalink / raw)
To: wg, mkl, robh+dt
Cc: kernel, linux-can, netdev, linux-kernel, devicetree,
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 = <®5v0>;
+ xceiver-supply = <®5v0>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+ };
+
+...
--
2.17.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [RESEND PATCH 1/6] dt-bindings: can: Document devicetree bindings for MCP25XXFD
2020-06-10 7:47 ` [RESEND PATCH 1/6] dt-bindings: can: Document devicetree bindings for MCP25XXFD Manivannan Sadhasivam
@ 2020-06-17 22:16 ` Rob Herring
0 siblings, 0 replies; 9+ messages in thread
From: Rob Herring @ 2020-06-17 22:16 UTC (permalink / raw)
To: Manivannan Sadhasivam
Cc: wg, mkl, kernel, linux-can, netdev, linux-kernel, devicetree
On Wed, Jun 10, 2020 at 01:17:06PM +0530, Manivannan Sadhasivam wrote:
> 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
You can drop the 'allOf' now.
> + 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
And '#gpio-cells'?
> + - 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 = <®5v0>;
> + xceiver-supply = <®5v0>;
> + gpio-controller;
> + #gpio-cells = <2>;
> + };
> + };
> +
> +...
> --
> 2.17.1
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [RESEND PATCH 2/6] can: mcp25xxfd: Add Microchip MCP25XXFD CAN-FD driver infrastructure
2020-06-10 7:47 [RESEND PATCH 0/6] Add Microchip MCP25XXFD CAN driver Manivannan Sadhasivam
2020-06-10 7:47 ` [RESEND PATCH 1/6] dt-bindings: can: Document devicetree bindings for MCP25XXFD Manivannan Sadhasivam
@ 2020-06-10 7:47 ` Manivannan Sadhasivam
2020-06-10 14:32 ` kernel test robot
2020-06-10 7:47 ` [RESEND PATCH 3/6] can: mcp25xxfd: Add support for CAN reception Manivannan Sadhasivam
` (3 subsequent siblings)
5 siblings, 1 reply; 9+ messages in thread
From: Manivannan Sadhasivam @ 2020-06-10 7:47 UTC (permalink / raw)
To: wg, mkl, robh+dt
Cc: kernel, linux-can, netdev, linux-kernel, devicetree,
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] 9+ messages in thread
* Re: [RESEND PATCH 2/6] can: mcp25xxfd: Add Microchip MCP25XXFD CAN-FD driver infrastructure
2020-06-10 7:47 ` [RESEND PATCH 2/6] can: mcp25xxfd: Add Microchip MCP25XXFD CAN-FD driver infrastructure Manivannan Sadhasivam
@ 2020-06-10 14:32 ` kernel test robot
0 siblings, 0 replies; 9+ messages in thread
From: kernel test robot @ 2020-06-10 14:32 UTC (permalink / raw)
To: Manivannan Sadhasivam, wg, mkl, robh+dt
Cc: kbuild-all, clang-built-linux, kernel, linux-can, netdev,
linux-kernel, devicetree, Manivannan Sadhasivam
[-- Attachment #1: Type: text/plain, Size: 4700 bytes --]
Hi Manivannan,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on robh/for-next]
[also build test WARNING on linus/master v5.7 next-20200610]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Manivannan-Sadhasivam/Add-Microchip-MCP25XXFD-CAN-driver/20200610-155045
base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next
config: x86_64-allyesconfig (attached as .config)
compiler: clang version 11.0.0 (https://github.com/llvm/llvm-project bc2b70982be8f5250cd0082a7190f8b417bd4dfe)
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# install x86_64 cross compiling tool for clang build
# apt-get install binutils-x86-64-linux-gnu
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64
If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>
All warnings (new ones prefixed by >>, old ones prefixed by <<):
>> drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c:69:11: warning: cast to smaller integer type 'enum mcp25xxfd_model' from 'const void *' [-Wvoid-pointer-to-enum-cast]
model = (enum mcp25xxfd_model)of_id->data;
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 warning generated.
vim +69 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
38
39 static int mcp25xxfd_base_probe(struct spi_device *spi)
40 {
41 const struct of_device_id *of_id =
42 of_match_device(mcp25xxfd_of_match, &spi->dev);
43 struct mcp25xxfd_priv *priv;
44 struct clk *clk;
45 enum mcp25xxfd_model model;
46 u32 freq;
47 int ret;
48
49 /* Check whether valid IRQ line is defined or not */
50 if (spi->irq <= 0) {
51 dev_err(&spi->dev, "no valid irq line defined: irq = %i\n",
52 spi->irq);
53 return -EINVAL;
54 }
55
56 priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
57 if (!priv)
58 return -ENOMEM;
59
60 spi_set_drvdata(spi, priv);
61 priv->spi = spi;
62
63 /* Assign device name */
64 snprintf(priv->device_name, sizeof(priv->device_name),
65 DEVICE_NAME "-%s", dev_name(&priv->spi->dev));
66
67 /* assign model from of or driver_data */
68 if (of_id)
> 69 model = (enum mcp25xxfd_model)of_id->data;
70 else
71 model = spi_get_device_id(spi)->driver_data;
72
73 clk = devm_clk_get(&spi->dev, NULL);
74 if (IS_ERR(clk)) {
75 ret = PTR_ERR(clk);
76 goto out_free;
77 }
78
79 freq = clk_get_rate(clk);
80 if (!(freq == CLOCK_4_MHZ || freq == CLOCK_10_MHZ ||
81 freq == CLOCK_40_MHZ)) {
82 ret = -ERANGE;
83 goto out_free;
84 }
85
86 ret = clk_prepare_enable(clk);
87 if (ret)
88 goto out_free;
89
90 priv->clk = clk;
91 priv->clock_freq = freq;
92
93 /* Configure the SPI bus */
94 spi->bits_per_word = 8;
95
96 /* The frequency of SCK has to be less than or equal to half the
97 * frequency of SYSCLK.
98 */
99 spi->max_speed_hz = freq / 2;
100 ret = spi_setup(spi);
101 if (ret)
102 goto out_clk;
103
104 priv->power = devm_regulator_get(&spi->dev, "vdd");
105 if (IS_ERR(priv->power)) {
106 if (PTR_ERR(priv->power) != -EPROBE_DEFER)
107 dev_err(&spi->dev, "failed to get vdd\n");
108 ret = PTR_ERR(priv->power);
109 goto out_clk;
110 }
111
112 ret = mcp25xxfd_base_power_enable(priv->power, 1);
113 if (ret)
114 goto out_clk;
115
116 /* disable interrupts */
117 ret = mcp25xxfd_int_enable(priv, false);
118 if (ret)
119 goto out_power;
120
121 /* setup ECC for SRAM */
122 ret = mcp25xxfd_ecc_enable(priv);
123 if (ret)
124 goto out_power;
125
126 dev_info(&spi->dev,
127 "MCP%04x successfully initialized.\n", model);
128 return 0;
129
130 out_power:
131 mcp25xxfd_base_power_enable(priv->power, 0);
132 out_clk:
133 clk_disable_unprepare(clk);
134 out_free:
135 dev_err(&spi->dev, "Probe failed, err=%d\n", -ret);
136 return ret;
137 }
138
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 73446 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* [RESEND PATCH 3/6] can: mcp25xxfd: Add support for CAN reception
2020-06-10 7:47 [RESEND PATCH 0/6] Add Microchip MCP25XXFD CAN driver Manivannan Sadhasivam
2020-06-10 7:47 ` [RESEND PATCH 1/6] dt-bindings: can: Document devicetree bindings for MCP25XXFD Manivannan Sadhasivam
2020-06-10 7:47 ` [RESEND PATCH 2/6] can: mcp25xxfd: Add Microchip MCP25XXFD CAN-FD driver infrastructure Manivannan Sadhasivam
@ 2020-06-10 7:47 ` Manivannan Sadhasivam
2020-06-10 7:47 ` [RESEND PATCH 4/6] can: mcp25xxfd: Add CAN transmission support Manivannan Sadhasivam
` (2 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Manivannan Sadhasivam @ 2020-06-10 7:47 UTC (permalink / raw)
To: wg, mkl, robh+dt
Cc: kernel, linux-can, netdev, linux-kernel, devicetree,
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] 9+ messages in thread
* [RESEND PATCH 4/6] can: mcp25xxfd: Add CAN transmission support
2020-06-10 7:47 [RESEND PATCH 0/6] Add Microchip MCP25XXFD CAN driver Manivannan Sadhasivam
` (2 preceding siblings ...)
2020-06-10 7:47 ` [RESEND PATCH 3/6] can: mcp25xxfd: Add support for CAN reception Manivannan Sadhasivam
@ 2020-06-10 7:47 ` Manivannan Sadhasivam
2020-06-10 7:47 ` [RESEND PATCH 5/6] can: mcp25xxfd: Optimize TEF read by avoiding unnecessary SPI transfers Manivannan Sadhasivam
2020-06-10 7:47 ` [RESEND PATCH 6/6] MAINTAINERS: Add entry for Microchip MCP25XXFD CAN network driver Manivannan Sadhasivam
5 siblings, 0 replies; 9+ messages in thread
From: Manivannan Sadhasivam @ 2020-06-10 7:47 UTC (permalink / raw)
To: wg, mkl, robh+dt
Cc: kernel, linux-can, netdev, linux-kernel, devicetree,
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] 9+ messages in thread
* [RESEND PATCH 5/6] can: mcp25xxfd: Optimize TEF read by avoiding unnecessary SPI transfers
2020-06-10 7:47 [RESEND PATCH 0/6] Add Microchip MCP25XXFD CAN driver Manivannan Sadhasivam
` (3 preceding siblings ...)
2020-06-10 7:47 ` [RESEND PATCH 4/6] can: mcp25xxfd: Add CAN transmission support Manivannan Sadhasivam
@ 2020-06-10 7:47 ` Manivannan Sadhasivam
2020-06-10 7:47 ` [RESEND PATCH 6/6] MAINTAINERS: Add entry for Microchip MCP25XXFD CAN network driver Manivannan Sadhasivam
5 siblings, 0 replies; 9+ messages in thread
From: Manivannan Sadhasivam @ 2020-06-10 7:47 UTC (permalink / raw)
To: wg, mkl, robh+dt
Cc: kernel, linux-can, netdev, linux-kernel, devicetree,
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] 9+ messages in thread
* [RESEND PATCH 6/6] MAINTAINERS: Add entry for Microchip MCP25XXFD CAN network driver
2020-06-10 7:47 [RESEND PATCH 0/6] Add Microchip MCP25XXFD CAN driver Manivannan Sadhasivam
` (4 preceding siblings ...)
2020-06-10 7:47 ` [RESEND PATCH 5/6] can: mcp25xxfd: Optimize TEF read by avoiding unnecessary SPI transfers Manivannan Sadhasivam
@ 2020-06-10 7:47 ` Manivannan Sadhasivam
5 siblings, 0 replies; 9+ messages in thread
From: Manivannan Sadhasivam @ 2020-06-10 7:47 UTC (permalink / raw)
To: wg, mkl, robh+dt
Cc: kernel, linux-can, netdev, linux-kernel, devicetree,
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] 9+ messages in thread