From: kernel@martin.sperl.org
To: linux-can@vger.kernel.org, devicetree@vger.kernel.org,
Wolfgang Grandegger <wg@grandegger.com>,
Mark Kleine-Budde <mkl@pengutronix.de>,
Rob Herring <robh+dt@kernel.org>,
Mark Rutland <mark.rutland@arm.com>
Cc: Martin Sperl <kernel@martin.sperl.org>,
Sukkin Pang <skp@skpang.co.uk>,
Gerhard Bertelsmann <info@gerhard-bertelsmann.de>,
Wilhelm Leichtfried <Wilhelm.Leichtfried@microchip.com>,
Thomas Kopp <Thomas.Kopp@microchip.com>,
Enrico Scholz <enrico.scholz@sigma-chemnitz.de>,
Brad Hanson <Brad.Hanson@arity.com>,
Teemu Keskinarkaus <teemu.keskinarkaus@crosscontrol.com>
Subject: [PATCH V5 4/4] can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver
Date: Fri, 21 Dec 2018 09:29:50 +0000 [thread overview]
Message-ID: <20181221092950.14192-5-kernel@martin.sperl.org> (raw)
In-Reply-To: <20181221092950.14192-1-kernel@martin.sperl.org>
From: Martin Sperl <kernel@martin.sperl.org>
Add un-optimized Can2.0 and CanFD support.
Optimizations and HW-bug-workarounds are separate patches
On a Rasperry pi 3 it is already able to process Can2.0 Frames
with DLC=0 on a CAN bus with 1MHz. without losing any packets
on the SPI side. Packets still get lost inside the network stack.
As for transmission on a Rpi3 we can saturate the CAN bus at 1MHz
with Can2.0 frames with DLC=0 for the number of configured tx-fifos.
Afterwards we need some time to recover the state before we can
fill in the fifos again.
With 7 tx fifos we can: send those 7 frames in 0.33ms and then we
wait for 0.26ms so that is a 56% duty cycle.
With 24 tx fifos this changes to: 1.19ms for 24 frames and then we
wait for 0.52ms so that is a 70% duty cycle.
Signed-off-by: Martin Sperl <kernel@martin.sperl.org>
---
Changelog:
V1 -> V2: new more generic name based on feedback from microchip
cleanup of code (checkpatch)
address all feedback on code
prepare code for GPIO handling (separate patches)
handle systemerrors in a much better/reliable way
V2 -> V3: added vendor-prefix for gpio-opendrain
V3 -> V4: resend
V4 -> V5: split can driver into a separate patch
code cleanup/rewrite
---
drivers/net/can/spi/mcp25xxfd/Makefile | 4 +
drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h | 36 +
drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c | 52 +-
drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c | 593 ++++++++++++++-
drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h | 275 ++++++-
drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c | 479 ++++++++++++
drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c | 723 ++++++++++++++++++
drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c | 208 ++++++
drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c | 824 +++++++++++++++++++++
9 files changed, 3160 insertions(+), 34 deletions(-)
create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c
create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c
create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c
create mode 100644 drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c
diff --git a/drivers/net/can/spi/mcp25xxfd/Makefile b/drivers/net/can/spi/mcp25xxfd/Makefile
index 5dd7f13674ec..6a27cec424de 100644
--- a/drivers/net/can/spi/mcp25xxfd/Makefile
+++ b/drivers/net/can/spi/mcp25xxfd/Makefile
@@ -4,4 +4,8 @@
obj-$(CONFIG_CAN_MCP25XXFD) += mcp25xxfd.o
mcp25xxfd-objs := mcp25xxfd_base.o
mcp25xxfd-objs += mcp25xxfd_can.o
+mcp25xxfd-objs += mcp25xxfd_can_fifo.o
+mcp25xxfd-objs += mcp25xxfd_can_int.o
+mcp25xxfd-objs += mcp25xxfd_can_rx.o
+mcp25xxfd-objs += mcp25xxfd_can_tx.o
mcp25xxfd-objs += mcp25xxfd_gpio.o
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
index 73817a567219..624602ca898f 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd.h
@@ -107,6 +107,7 @@ enum mcp25xxfd_model {
struct mcp25xxfd_priv {
struct spi_device *spi;
+ struct net_device *net;
struct gpio_chip *gpio;
struct clk *clk;
@@ -154,6 +155,11 @@ struct mcp25xxfd_priv {
u32 ecccon;
} regs;
+ /* flag to see if the irq is enabled */
+ struct {
+ int enabled;
+ } irq;
+
/* debugfs related */
#if defined(CONFIG_DEBUG_FS)
struct dentry *debugfs_dir;
@@ -188,6 +194,28 @@ static inline void mcp25xxfd_convert_from_cpu(u32 *data, int n)
data[i] = cpu_to_le32(data[i]);
}
+static inline void mcp25xxfd_calc_cmd_addr(u16 cmd, u16 addr, u8 *data)
+{
+ cmd = cmd | (addr & ADDRESS_MASK);
+
+ data[0] = (cmd >> 8) & 0xff;
+ data[1] = (cmd >> 0) & 0xff;
+}
+
+static inline int mcp25xxfd_first_byte(u32 mask)
+{
+ return (mask & 0x0000ffff) ?
+ ((mask & 0x000000ff) ? 0 : 1) :
+ ((mask & 0x00ff0000) ? 2 : 3);
+}
+
+static inline int mcp25xxfd_last_byte(u32 mask)
+{
+ return (mask & 0xffff0000) ?
+ ((mask & 0xff000000) ? 3 : 2) :
+ ((mask & 0x0000ff00) ? 1 : 0);
+}
+
int mcp25xxfd_cmd_readn(struct spi_device *spi, u32 reg,
void *data, int n);
int mcp25xxfd_cmd_read_mask(struct spi_device *spi, u32 reg,
@@ -233,11 +261,19 @@ int mcp25xxfd_can_hw_probe(struct spi_device *spi);
/* clearing interrupt flags */
int mcp25xxfd_clear_crc_interrupts(struct spi_device *spi);
int mcp25xxfd_clear_ecc_interrupts(struct spi_device *spi);
+int mcp25xxfd_clear_can_interrupts(struct spi_device *spi);
int mcp25xxfd_clear_interrupts(struct spi_device *spi);
/* enabling interrupts */
int mcp25xxfd_enable_interrupts(struct spi_device *spi, bool enable);
+int mcp25xxfd_enable_can_interrupts(struct spi_device *spi, bool enable);
/* gpiolib support */
int mcp25xxfd_gpio_setup(struct spi_device *spi);
void mcp25xxfd_gpio_remove(struct spi_device *spi);
+
+/* can support */
+int mcp25xxfd_can_setup(struct spi_device *spi);
+void mcp25xxfd_can_remove(struct spi_device *spi);
+int mcp25xxfd_can_suspend(struct spi_device *spi);
+int mcp25xxfd_can_resume(struct spi_device *spi);
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
index b0851d97bcbb..63c69139cfdf 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_base.c
@@ -226,28 +226,6 @@ static int mcp25xxfd_write_then_write(struct spi_device *spi,
/* mcp25xxfd spi command/protocol helper */
-static void mcp25xxfd_calc_cmd_addr(u16 cmd, u16 addr, u8 *data)
-{
- cmd = cmd | (addr & ADDRESS_MASK);
-
- data[0] = (cmd >> 8) & 0xff;
- data[1] = (cmd >> 0) & 0xff;
-}
-
-static int mcp25xxfd_first_byte(u32 mask)
-{
- return (mask & 0x0000ffff) ?
- ((mask & 0x000000ff) ? 0 : 1) :
- ((mask & 0x00ff0000) ? 2 : 3);
-}
-
-static int mcp25xxfd_last_byte(u32 mask)
-{
- return (mask & 0xffff0000) ?
- ((mask & 0xff000000) ? 3 : 2) :
- ((mask & 0x0000ff00) ? 1 : 0);
-}
-
/* read multiple bytes, transform some registers */
int mcp25xxfd_cmd_readn(struct spi_device *spi, u32 reg,
void *data, int n)
@@ -508,6 +486,7 @@ int mcp25xxfd_clear_interrupts(struct spi_device *spi)
{
mcp25xxfd_clear_ecc_interrupts(spi);
mcp25xxfd_clear_crc_interrupts(spi);
+ mcp25xxfd_clear_can_interrupts(spi);
return 0;
}
@@ -542,7 +521,7 @@ static int mcp25xxfd_enable_crc_interrupts(struct spi_device *spi,
int mcp25xxfd_enable_interrupts(struct spi_device *spi,
bool enable)
{
- /* error handling only on enable for this function */
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
int ret = 0;
/* if we enable clear interrupt flags first */
@@ -559,6 +538,21 @@ int mcp25xxfd_enable_interrupts(struct spi_device *spi,
if (enable && ret)
goto out_ecc;
+ ret = mcp25xxfd_enable_can_interrupts(spi, enable);
+ if (enable && ret)
+ goto out_ecc;
+
+ /* enable/disable interrupt as required */
+ if (enable) {
+ if (!priv->irq.enabled)
+ enable_irq(spi->irq);
+ } else {
+ if (priv->irq.enabled)
+ disable_irq(spi->irq);
+ }
+ /* mark enabled/disabled */
+ priv->irq.enabled = enable;
+
/* if we disable interrupts clear interrupt flags last */
if (!enable)
mcp25xxfd_clear_interrupts(spi);
@@ -1110,15 +1104,22 @@ static int mcp25xxfd_probe(struct spi_device *spi)
if (ret)
goto out_debugfs;
+ /* add can (depends on debugfs) */
+ ret = mcp25xxfd_can_setup(spi);
+ if (ret)
+ goto out_gpio;
+
/* and put controller to sleep by stopping the can clock */
ret = mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
if (ret)
- goto out_gpio;
+ goto out_can;
dev_info(&spi->dev,
"MCP%x successfully initialized.\n", priv->model);
return 0;
+out_can:
+ mcp25xxfd_can_remove(spi);
out_gpio:
mcp25xxfd_gpio_remove(spi);
out_debugfs:
@@ -1139,6 +1140,9 @@ static int mcp25xxfd_remove(struct spi_device *spi)
{
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ /* remove can */
+ mcp25xxfd_can_remove(spi);
+
/* remove gpio */
mcp25xxfd_gpio_remove(spi);
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
index e5c7098a09c2..e827edb37da8 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.c
@@ -43,18 +43,160 @@
/* Implementation notes:
*
- * Right now we only use the CAN controller block to put us into deep sleep
- * this means that the oscillator clock is turned off.
- * So this is the only thing that we implement here right now
+ * * the can related portion of the driver is split out into
+ * * basic configuration (mcp25xxfd_can.c)
+ * * can fifo configuration (mcp25xxfd_can_fifo.c)
+ * * can interrupt handling (mcp25xxfd_can_int.c)
+ * * can reception including interrupt handling (mcp25xxfd_can_rx.c)
+ * * can transmission including interrupt handling (mcp25xxfd_can_tx.c)
+ * * the interrupt handler trys to issue the smallest amount of
+ * spi messages to avoid spi stack overhead. for that it is more agressive
+ * when reading registers that are close to each other - e.g:
+ * * if there is one register that is not really needed then it will
+ * still read it to avoid the startup and teardown costs of the
+ * spi stack which results in latencies.
+ * * when in can2.0 mode it will read all 8 data bytes even if DLC = 0
+ * the header on its own is 12 bytes and then there are 2 bytes for
+ * the spi overhead so the effecitve worsted case overhead introduced
+ * is +57%
+ * * due to the reordering inside the controller the Transmission queue
+ * feature is not used (it also makes the "next" frame freed less
+ * predictable which would complicate the code as well.
+ * the only advantage seen is that one could have more transmission
+ * slots in the case of CAN2.0 only.
+ * * transmissions are instead handled by fixed priority fifos that can
+ * get filled and triggered asyncronously.
+ * The biggest drawback is that we are unable to sustain 100% CAN bus
+ * usage for more than the <number of can fifos allocated> frames.
+ * This means that we can have a duty cycle of about 7/8th of the CAN bus
+ * (unless more fifos are reserved for transmission)
+ * The only situation where this would be observable in real live
+ * would be big transfers (for firmware updates or similar)
+ * * this could have been better supported with a better
+ * layout of FIFOCON, so that TXREQ and TXPRIO can be written
+ * in a single transfer without a race condition
+ * * can transmissions are handled by using spi_async for submission and
+ * scheduling. This somewhat impacts interrupt handling as some spi_sync
+ * calls will defer to the spi kthread resulting in some context switches.
+ * which introduces some latencies.
+ * * some special features of the can controller can only get configured
+ * by the use of module parameters (some of which are changeable at
+ * runtime via sysfs) due to the fact that netlink does not support:
+ * * 3-shot submission
+ * * selection of number of fifos
+ * * configuarble delay between transmission of two can frames so that
+ * the can bus is not monopolized by this device.
+ * also some debug settings are controlable via modules.
*/
#include "mcp25xxfd_can.h"
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
-static int mcp25xxfd_can_get_mode(struct spi_device *spi,
- u32 *mode_data)
+/* module parameters */
+unsigned int bw_sharing_log2bits;
+module_param(bw_sharing_log2bits, uint, 0664);
+MODULE_PARM_DESC(bw_sharing_log2bits,
+ "Delay between 2 transmissions in number of arbitration bit times\n");
+/* everything related to bit timing */
+static
+const struct can_bittiming_const mcp25xxfd_can_nominal_bittiming_const = {
+ .name = DEVICE_NAME,
+ .tseg1_min = 2,
+ .tseg1_max = BIT(CAN_NBTCFG_TSEG1_BITS),
+ .tseg2_min = 1,
+ .tseg2_max = BIT(CAN_NBTCFG_TSEG2_BITS),
+ .sjw_max = BIT(CAN_NBTCFG_SJW_BITS),
+ .brp_min = 1,
+ .brp_max = BIT(CAN_NBTCFG_BRP_BITS),
+ .brp_inc = 1,
+};
+
+static
+const struct can_bittiming_const mcp25xxfd_can_data_bittiming_const = {
+ .name = DEVICE_NAME,
+ .tseg1_min = 1,
+ .tseg1_max = BIT(CAN_DBTCFG_TSEG1_BITS),
+ .tseg2_min = 1,
+ .tseg2_max = BIT(CAN_DBTCFG_TSEG2_BITS),
+ .sjw_max = BIT(CAN_DBTCFG_SJW_BITS),
+ .brp_min = 1,
+ .brp_max = BIT(CAN_DBTCFG_BRP_BITS),
+ .brp_inc = 1,
+};
+
+static int mcp25xxfd_can_do_set_nominal_bittiming(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct mcp25xxfd_priv *priv = cpriv->priv;
+ struct can_bittiming *bt = &cpriv->can.bittiming;
+ struct spi_device *spi = priv->spi;
+
+ int sjw = bt->sjw;
+ int pseg2 = bt->phase_seg2;
+ int pseg1 = bt->phase_seg1;
+ int propseg = bt->prop_seg;
+ int brp = bt->brp;
+
+ int tseg1 = propseg + pseg1;
+ int tseg2 = pseg2;
+
+ /* calculate nominal bit timing */
+ cpriv->regs.nbtcfg = ((sjw - 1) << CAN_NBTCFG_SJW_SHIFT) |
+ ((tseg2 - 1) << CAN_NBTCFG_TSEG2_SHIFT) |
+ ((tseg1 - 1) << CAN_NBTCFG_TSEG1_SHIFT) |
+ ((brp - 1) << CAN_NBTCFG_BRP_SHIFT);
+
+ return mcp25xxfd_cmd_write(spi, CAN_NBTCFG,
+ cpriv->regs.nbtcfg);
+}
+
+static int mcp25xxfd_can_do_set_data_bittiming(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct mcp25xxfd_priv *priv = cpriv->priv;
+ struct can_bittiming *bt = &cpriv->can.data_bittiming;
+ struct spi_device *spi = priv->spi;
+
+ int sjw = bt->sjw;
+ int pseg2 = bt->phase_seg2;
+ int pseg1 = bt->phase_seg1;
+ int propseg = bt->prop_seg;
+ int brp = bt->brp;
+
+ int tseg1 = propseg + pseg1;
+ int tseg2 = pseg2;
+
+ int ret;
+
+ /* set up Transmitter delay compensation */
+ if (!cpriv->regs.tdc)
+ cpriv->regs.tdc = CAN_TDC_EDGFLTEN |
+ (CAN_TDC_TDCMOD_AUTO << CAN_TDC_TDCMOD_SHIFT);
+ ret = mcp25xxfd_cmd_write(spi, CAN_TDC, cpriv->regs.tdc);
+ if (ret)
+ return ret;
+
+ /* calculate nominal bit timing */
+ cpriv->regs.dbtcfg = ((sjw - 1) << CAN_DBTCFG_SJW_SHIFT) |
+ ((tseg2 - 1) << CAN_DBTCFG_TSEG2_SHIFT) |
+ ((tseg1 - 1) << CAN_DBTCFG_TSEG1_SHIFT) |
+ ((brp - 1) << CAN_DBTCFG_BRP_SHIFT);
+
+ return mcp25xxfd_cmd_write(spi, CAN_DBTCFG,
+ cpriv->regs.dbtcfg);
+}
+
+/* configuration functions that are used by the base during initialization */
+int mcp25xxfd_can_get_mode(struct spi_device *spi, u32 *mode_data)
{
int ret;
@@ -65,11 +207,11 @@ static int mcp25xxfd_can_get_mode(struct spi_device *spi,
return (*mode_data & CAN_CON_OPMOD_MASK) >> CAN_CON_OPMOD_SHIFT;
}
-int mcp25xxfd_can_switch_mode(struct spi_device *spi,
- u32 *mode_data, int mode)
+int mcp25xxfd_can_switch_mode_nowait(struct spi_device *spi,
+ u32 *mode_data, int mode)
{
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
- int ret, i;
+ int ret;
/* get the current mode/register - if mode_data is -1
* this only happens during initialization phase
@@ -98,7 +240,16 @@ int mcp25xxfd_can_switch_mode(struct spi_device *spi,
}
/* request the mode switch */
- ret = mcp25xxfd_cmd_write(spi, CAN_CON, *mode_data);
+ return mcp25xxfd_cmd_write(spi, CAN_CON, *mode_data);
+}
+
+int mcp25xxfd_can_switch_mode(struct spi_device *spi,
+ u32 *mode_data, int mode)
+{
+ int ret, i;
+
+ /* trigger the mode switch itself */
+ ret = mcp25xxfd_can_switch_mode_nowait(spi, mode_data, mode);
if (ret)
return ret;
@@ -226,3 +377,427 @@ int mcp25xxfd_can_hw_probe(struct spi_device *spi)
/* check that modeswitch is really working */
return mcp25xxfd_can_hw_probe_modeswitch(spi);
}
+
+/* debugfs related */
+#if defined(CONFIG_DEBUG_FS)
+
+static int mcp25xxfd_can_dump_all_regs(struct seq_file *file, void *offset)
+{
+ return mcp25xxfd_dump_regs_range(file, CAN_CON, CAN_FLTMASK(31));
+}
+
+static int mcp25xxfd_can_dump_regs(struct seq_file *file, void *offset)
+{
+ return mcp25xxfd_dump_regs_range(file, CAN_CON, CAN_TXQUA);
+}
+
+static void mcp25xxfd_can_debugfs_add(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(priv->net);
+ struct dentry *root = priv->debugfs_dir;
+ struct dentry *cregs, *cstatus, *cfifotef, *cstats;
+
+ /* export config registers */
+ cregs = debugfs_create_dir("can_regs", root);
+ debugfs_create_x32("con", 0444, cregs, &cpriv->regs.con);
+ debugfs_create_x32("tdc", 0774, cregs, &cpriv->regs.tdc);
+ debugfs_create_x32("tscon", 0444, cregs, &cpriv->regs.tscon);
+ debugfs_create_x32("tefcon", 0444, cregs, &cpriv->regs.tscon);
+ debugfs_create_x32("nbtcfg", 0444, cregs, &cpriv->regs.nbtcfg);
+ debugfs_create_x32("dbtcfg", 0444, cregs, &cpriv->regs.dbtcfg);
+
+ /* export status registers */
+ cstatus = debugfs_create_dir("can_status", root);
+ debugfs_create_x32("intf", 0444, cstatus,
+ &cpriv->status.intf);
+ debugfs_create_x32("rx_if", 0444, cstatus,
+ &cpriv->status.rxif);
+ debugfs_create_x32("tx_if", 0444, cstatus,
+ &cpriv->status.txif);
+ debugfs_create_x32("rx_ovif", 0444, cstatus,
+ &cpriv->status.rxovif);
+ debugfs_create_x32("tx_atif", 0444, cstatus,
+ &cpriv->status.txatif);
+ debugfs_create_x32("tx_req", 0444, cstatus,
+ &cpriv->status.txreq);
+ debugfs_create_x32("trec", 0444, cstatus,
+ &cpriv->status.trec);
+
+ cfifotef = debugfs_create_dir("can_tef", root);
+ debugfs_create_u32("count", 0444, cfifotef,
+ &cpriv->fifos.tef.count);
+ debugfs_create_u32("size", 0444, cfifotef,
+ &cpriv->fifos.tef.size);
+
+ /* dump the controller registers themselves */
+ debugfs_create_devm_seqfile(&priv->spi->dev, "can_regs_live_dump",
+ root, mcp25xxfd_can_dump_regs);
+ debugfs_create_devm_seqfile(&priv->spi->dev, "can_regs_full_live_dump",
+ root, mcp25xxfd_can_dump_all_regs);
+
+ /* and stats */
+ cstats = debugfs_create_dir("can_stats", root);
+# define DEBUGFS_CREATE(name, var) \
+ debugfs_create_u64(name, 0444, cstats, &cpriv->stats.var)
+ DEBUGFS_CREATE("int_calls", irq_calls);
+ DEBUGFS_CREATE("int_loops", irq_loops);
+ DEBUGFS_CREATE("int_system_error", int_serr_count);
+ DEBUGFS_CREATE("int_system_error_tx", int_serr_tx_count);
+ DEBUGFS_CREATE("int_system_error_rx", int_serr_rx_count);
+ DEBUGFS_CREATE("int_mode_switch", int_mod_count);
+ DEBUGFS_CREATE("int_rx", int_rx_count);
+ DEBUGFS_CREATE("int_tx_attempt", int_txat_count);
+ DEBUGFS_CREATE("int_tef", int_tef_count);
+ DEBUGFS_CREATE("int_rx_overflow", int_rxov_count);
+ DEBUGFS_CREATE("int_ecc_error", int_ecc_count);
+ DEBUGFS_CREATE("int_rx_invalid_message", int_ivm_count);
+ DEBUGFS_CREATE("int_crcerror", int_cerr_count);
+
+ DEBUGFS_CREATE("tx_frames_fd", tx_fd_count);
+ DEBUGFS_CREATE("tx_frames_brs", tx_brs_count);
+#undef DEBUGFS_CREATE
+}
+#else
+static void mcp25xxfd_can_debugfs_add(struct spi_device *spi)
+{
+}
+#endif
+
+int mcp25xxfd_clear_can_interrupts(struct spi_device *spi)
+{
+ return mcp25xxfd_cmd_write_mask(spi, CAN_INT, 0, CAN_INT_IF_MASK);
+}
+
+int mcp25xxfd_enable_can_interrupts(struct spi_device *spi, bool enable)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct mcp25xxfd_can_priv *cpriv = priv->net ?
+ netdev_priv(priv->net) : NULL;
+ const u32 mask = CAN_INT_TEFIE |
+ CAN_INT_RXIE |
+ CAN_INT_MODIE |
+ CAN_INT_SERRIE |
+ CAN_INT_IVMIE |
+ CAN_INT_CERRIE |
+ CAN_INT_RXOVIE |
+ CAN_INT_ECCIE;
+ u32 value = cpriv ? cpriv->status.intf : 0;
+
+ /* apply mask and */
+ value &= ~(CAN_INT_IE_MASK);
+ if (enable)
+ value |= mask;
+ /* write back if net is set up */
+ if (cpriv)
+ cpriv->status.intf = value;
+
+ /* and write to int register */
+ return mcp25xxfd_cmd_write_mask(spi, CAN_INT, value, mask);
+}
+
+static int mcp25xxfd_can_config(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct mcp25xxfd_priv *priv = cpriv->priv;
+ struct spi_device *spi = priv->spi;
+ int ret;
+
+ /* setup value of con_register */
+ cpriv->regs.con = CAN_CON_STEF; /* enable TEF, disable TXQUEUE */
+
+ /* transmission bandwidth sharing bits */
+ if (bw_sharing_log2bits > 12)
+ bw_sharing_log2bits = 12;
+ cpriv->regs.con |= bw_sharing_log2bits << CAN_CON_TXBWS_SHIFT;
+
+ /* non iso FD mode */
+ if (!(cpriv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO))
+ cpriv->regs.con |= CAN_CON_ISOCRCEN;
+
+ /* one shot */
+ if (cpriv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+ cpriv->regs.con |= CAN_CON_RTXAT;
+
+ /* apply it now together with a mode switch */
+ ret = mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
+ CAN_CON_MODE_CONFIG);
+ if (ret)
+ return 0;
+
+ /* time stamp control register - 1ns resolution */
+ cpriv->regs.tscon = 0;
+ ret = mcp25xxfd_cmd_write(spi, CAN_TBC, 0);
+ if (ret)
+ return ret;
+
+ cpriv->regs.tscon = CAN_TSCON_TBCEN |
+ ((cpriv->can.clock.freq / 1000000)
+ << CAN_TSCON_TBCPRE_SHIFT);
+ ret = mcp25xxfd_cmd_write(spi, CAN_TSCON, cpriv->regs.tscon);
+ if (ret)
+ return ret;
+
+ /* setup fifos */
+ ret = mcp25xxfd_can_setup_fifos(net);
+ if (ret)
+ return ret;
+
+ /* setup can bittiming now - the do_set_bittiming methods
+ * are not used as they get callled before open
+ */
+ ret = mcp25xxfd_can_do_set_nominal_bittiming(net);
+ if (ret)
+ return ret;
+ ret = mcp25xxfd_can_do_set_data_bittiming(net);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static void mcp25xxfd_can_shutdown(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct mcp25xxfd_priv *priv = cpriv->priv;
+ struct spi_device *spi = priv->spi;
+
+ /* switch us to CONFIG mode - this disables the controller */
+ mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
+ CAN_CON_MODE_CONFIG);
+}
+
+/* all ops */
+
+/* open and stop */
+static int mcp25xxfd_can_open(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct mcp25xxfd_priv *priv = cpriv->priv;
+ struct spi_device *spi = priv->spi;
+ int ret;
+
+ netdev_err(net, "OPEN\n");
+
+ ret = open_candev(net);
+ if (ret) {
+ netdev_err(net, "unable to set initial baudrate!\n");
+ return ret;
+ }
+
+ /* clear those statistics */
+ memset(&cpriv->stats, 0, sizeof(cpriv->stats));
+
+ /* request an IRQ but keep disabled for now */
+ ret = request_threaded_irq(spi->irq, NULL,
+ mcp25xxfd_can_int,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ DEVICE_NAME, priv);
+ if (ret) {
+ dev_err(&spi->dev, "failed to acquire irq %d - %i\n",
+ spi->irq, ret);
+ goto out_candev;
+ }
+ disable_irq(spi->irq);
+ priv->irq.enabled = false;
+
+ /* enable power to the transceiver */
+ ret = mcp25xxfd_power_enable(cpriv->transceiver, 1);
+ if (ret)
+ goto out_irq;
+
+ /* enable clock (so that spi works) */
+ ret = mcp25xxfd_start_clock(spi, MCP25XXFD_CLK_USER_CAN);
+ if (ret)
+ goto out_transceiver;
+
+ /* configure controller for reception */
+ ret = mcp25xxfd_can_config(net);
+ if (ret)
+ goto out_canclock;
+
+ /* setting up state */
+ cpriv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ /* enable interrupts */
+ ret = mcp25xxfd_enable_interrupts(spi, true);
+ if (ret)
+ goto out_canconfig;
+
+ /* switch to active mode */
+ ret = mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
+ (net->mtu == CAN_MTU) ?
+ CAN_CON_MODE_CAN2_0 :
+ CAN_CON_MODE_MIXED);
+ if (ret)
+ goto out_int;
+
+ /* start the tx_queue */
+ mcp25xxfd_can_tx_queue_manage(net, TX_QUEUE_STATE_STARTED);
+
+ return 0;
+
+out_int:
+ mcp25xxfd_enable_interrupts(spi, false);
+out_canconfig:
+ mcp25xxfd_can_release_fifos(net);
+out_canclock:
+ mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
+out_transceiver:
+ mcp25xxfd_power_enable(cpriv->transceiver, 0);
+out_irq:
+ free_irq(spi->irq, priv);
+out_candev:
+ close_candev(net);
+ return ret;
+}
+
+static int mcp25xxfd_can_stop(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct mcp25xxfd_priv *priv = cpriv->priv;
+ struct spi_device *spi = priv->spi;
+
+ netdev_err(net, "STOP\n");
+
+ /* stop transmit queue */
+ mcp25xxfd_can_tx_queue_manage(net, TX_QUEUE_STATE_STOPPED);
+
+ /* release debugfs */
+ mcp25xxfd_can_release_fifos(net);
+
+ /* shutdown the can controller */
+ mcp25xxfd_can_shutdown(net);
+
+ /* disable inerrupts on controller */
+ mcp25xxfd_enable_interrupts(spi, false);
+
+ /* stop the clock */
+ mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
+
+ /* and disable the transceiver */
+ mcp25xxfd_power_enable(cpriv->transceiver, 0);
+
+ /* disable interrupt on host */
+ free_irq(spi->irq, priv);
+ priv->irq.enabled = false;
+
+ /* close the can_decice */
+ close_candev(net);
+
+ return 0;
+}
+
+/* mode setting */
+static int mcp25xxfd_can_do_set_mode(struct net_device *net,
+ enum can_mode mode)
+{
+ switch (mode) {
+ case CAN_MODE_START:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/* binary error counters */
+static int mcp25xxfd_can_get_berr_counter(const struct net_device *net,
+ struct can_berr_counter *bec)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+ bec->txerr = (cpriv->status.trec & CAN_TREC_TEC_MASK) >>
+ CAN_TREC_TEC_SHIFT;
+ bec->rxerr = (cpriv->status.trec & CAN_TREC_REC_MASK) >>
+ CAN_TREC_REC_SHIFT;
+
+ return 0;
+}
+
+static const struct net_device_ops mcp25xxfd_netdev_ops = {
+ .ndo_open = mcp25xxfd_can_open,
+ .ndo_stop = mcp25xxfd_can_stop,
+ .ndo_start_xmit = mcp25xxfd_can_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
+};
+
+/* probe and remove */
+int mcp25xxfd_can_setup(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct mcp25xxfd_can_priv *cpriv;
+ struct net_device *net;
+ struct regulator *transceiver;
+ int ret;
+
+ /* get transceiver power regulator*/
+ transceiver = devm_regulator_get_optional(&spi->dev,
+ "xceiver");
+ if (PTR_ERR(transceiver) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ /* allocate can device */
+ net = alloc_candev(sizeof(*cpriv), TX_ECHO_SKB_MAX);
+ if (!net)
+ return -ENOMEM;
+
+ /* and do some cross-asignments */
+ cpriv = netdev_priv(net);
+ cpriv->priv = priv;
+ priv->net = net;
+ SET_NETDEV_DEV(net, &spi->dev);
+
+ net->netdev_ops = &mcp25xxfd_netdev_ops;
+ net->flags |= IFF_ECHO;
+
+ cpriv->transceiver = transceiver;
+
+ cpriv->can.clock.freq = priv->clock_freq;
+
+ cpriv->can.bittiming_const =
+ &mcp25xxfd_can_nominal_bittiming_const;
+ cpriv->can.data_bittiming_const =
+ &mcp25xxfd_can_data_bittiming_const;
+ /* we are not setting bit-timing methods here as they get
+ * called by the framework before open so the controller is
+ * still in sleep mode, which does not help
+ */
+ cpriv->can.do_set_mode =
+ mcp25xxfd_can_do_set_mode;
+ cpriv->can.do_get_berr_counter =
+ mcp25xxfd_can_get_berr_counter;
+ cpriv->can.ctrlmode_supported =
+ CAN_CTRLMODE_FD |
+ CAN_CTRLMODE_FD_NON_ISO |
+ CAN_CTRLMODE_LOOPBACK |
+ CAN_CTRLMODE_LISTENONLY |
+ CAN_CTRLMODE_BERR_REPORTING |
+ CAN_CTRLMODE_ONE_SHOT;
+
+ ret = register_candev(net);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to register can device\n");
+ goto out;
+ }
+
+ mcp25xxfd_can_debugfs_add(spi);
+
+ return 0;
+out:
+ free_candev(net);
+
+ return ret;
+}
+
+void mcp25xxfd_can_remove(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+
+ if (priv->net) {
+ unregister_candev(priv->net);
+ free_candev(priv->net);
+ priv->net = NULL;
+ }
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
index fe0e93e28b62..ce35518fdb28 100644
--- a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can.h
@@ -2,7 +2,7 @@
/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
*
- * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
+ * Copyright 2018 Martin Sperl <kernel@martin.sperl.org>
*
* Based on Microchip MCP251x CAN controller driver written by
* David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
@@ -10,6 +10,12 @@
#include "mcp25xxfd.h"
#include <linux/bitops.h>
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
#define CAN_SFR_BASE(x) (0x000 + (x))
#define CAN_CON CAN_SFR_BASE(0x00)
@@ -205,6 +211,13 @@
CAN_INT_SERRIF | \
CAN_INT_WAKIF | \
CAN_INT_IVMIF)
+# define CAN_INT_IF_CLEAR_MASK \
+ (CAN_INT_TBCIF | \
+ CAN_INT_MODIF | \
+ CAN_INT_CERRIF | \
+ CAN_INT_SERRIF | \
+ CAN_INT_WAKIF | \
+ CAN_INT_IVMIF)
# define CAN_INT_IE_SHIFT 16
# define CAN_INT_TXIE (CAN_INT_TXIF << CAN_INT_IE_SHIFT)
# define CAN_INT_RXIE (CAN_INT_RXIF << CAN_INT_IE_SHIFT)
@@ -485,3 +498,263 @@
#define CAN_OBJ_FLAGS_FILHIT_MASK \
GENMASK(CAN_FLAGS_FILHIT_SHIFT + CAN_FLAGS_FILHIT_BITS - 1, \
CAN_FLAGS_FILHIT_SHIFT)
+
+#define TX_ECHO_SKB_MAX 32
+
+struct mcp25xxfd_fifo {
+ u32 count;
+ u32 start;
+ u32 increment;
+ u32 size;
+ u32 priority_start;
+ u32 priority_increment;
+ u64 dlc_usage[16];
+ u64 fd_count;
+
+ struct dentry *debugfs_dir;
+};
+
+struct mcp25xxfd_obj_ts {
+ /* using signed here to handle rollover correctly */
+ s32 ts;
+ /* positive fifos are rx, negative tx, 0 is not a valid fifo */
+ s32 fifo;
+};
+
+struct mcp25xxfd_can_priv {
+ /* can_priv has to be the first one to be usable with alloc_candev
+ * which expects struct can_priv to be right at the start of the
+ * priv structure
+ */
+ struct can_priv can;
+ struct mcp25xxfd_priv *priv;
+ struct regulator *transceiver;
+
+ /* the can mode currently active */
+ int mode;
+
+ /* can config registers */
+ struct {
+ u32 con;
+ u32 tdc;
+ u32 tscon;
+ u32 tefcon;
+ u32 nbtcfg;
+ u32 dbtcfg;
+ } regs;
+
+ /* can status registers (mostly) - read in one go
+ * bdiag0 and bdiag1 are optional, but when
+ * berr counters are requested on a regular basis
+ * during high CAN-bus load this would trigger the fact
+ * that spi_sync would get queued for execution in the
+ * spi thread and the spi handler would not get
+ * called inline in the interrupt thread without any
+ * context switches or wakeups...
+ */
+ struct {
+ u32 intf;
+ /* ASSERT(CAN_INT + 4 == CAN_RXIF) */
+ u32 rxif;
+ /* ASSERT(CAN_RXIF + 4 == CAN_TXIF) */
+ u32 txif;
+ /* ASSERT(CAN_TXIF + 4 == CAN_RXOVIF) */
+ u32 rxovif;
+ /* ASSERT(CAN_RXOVIF + 4 == CAN_TXATIF) */
+ u32 txatif;
+ /* ASSERT(CAN_TXATIF + 4 == CAN_TXREQ) */
+ u32 txreq;
+ /* ASSERT(CAN_TXREQ + 4 == CAN_TREC) */
+ u32 trec;
+ } status;
+
+ /* information of fifo setup */
+ struct {
+ /* define payload size and mode */
+ u32 payload_size;
+ u32 payload_mode;
+
+ /* infos on fifo layout */
+ struct {
+ u32 count;
+ u32 size;
+ u32 index;
+ } tef;
+ struct mcp25xxfd_fifo tx;
+ struct mcp25xxfd_fifo rx;
+
+ /* the address and priority of all fifos */
+ struct {
+ u32 control;
+ u32 status;
+ u32 offset;
+ } fifo_reg[32];
+ struct {
+ u32 is_tx;
+ u32 priority;
+ u64 use_count;
+ } fifo_info[32];
+
+ /* queue of can frames that need to get submitted
+ * to the network stack during an interrupt loop in one go
+ * (this gets sorted by timestamp before submission
+ * and contains both rx frames as well tx frames that have
+ * gone over the CAN bus successfully
+ */
+ struct mcp25xxfd_obj_ts submit_queue[32];
+ int submit_queue_count;
+
+ /* the tx queue of spi messages */
+ struct mcp2517fd_tx_spi_message_queue *tx_queue;
+
+ /* the directory entry of the can_fifo debugfs directory */
+ struct dentry *debugfs_dir;
+ } fifos;
+
+ /* statistics */
+ struct {
+ u64 irq_calls;
+ u64 irq_loops;
+
+ u64 int_serr_count;
+ u64 int_serr_rx_count;
+ u64 int_serr_tx_count;
+ u64 int_mod_count;
+ u64 int_rx_count;
+ u64 int_txat_count;
+ u64 int_tef_count;
+ u64 int_rxov_count;
+ u64 int_ecc_count;
+ u64 int_ivm_count;
+ u64 int_cerr_count;
+
+ u64 tx_fd_count;
+ u64 tx_brs_count;
+ } stats;
+
+ /* bus state */
+ struct {
+ u32 state;
+ u32 new_state;
+
+ u32 bdiag[2];
+ } bus;
+
+ /* can merror messages */
+ struct {
+ u32 id;
+ u8 data[8];
+ } error_frame;
+
+ /* a sram equivalent */
+ u8 sram[MCP25XXFD_SRAM_SIZE];
+};
+
+struct mcp25xxfd_obj_tef {
+ u32 id;
+ u32 flags;
+ u32 ts;
+};
+
+struct mcp25xxfd_obj_tx {
+ u32 id;
+ u32 flags;
+ u8 data[];
+};
+
+struct mcp25xxfd_obj_rx {
+ u32 id;
+ u32 flags;
+ u32 ts;
+ u8 data[];
+};
+
+int mcp25xxfd_can_get_mode(struct spi_device *spi, u32 *mode_data);
+int mcp25xxfd_can_switch_mode(struct spi_device *spi, u32 *mode_data,
+ int mode);
+irqreturn_t mcp25xxfd_can_int(int irq, void *dev_id);
+netdev_tx_t mcp25xxfd_can_start_xmit(struct sk_buff *skb,
+ struct net_device *net);
+int mcp25xxfd_can_setup_fifos(struct net_device *net);
+void mcp25xxfd_can_release_fifos(struct net_device *net);
+
+/* ideally these would be defined in uapi/linux/can.h */
+#define CAN_EFF_SID_SHIFT (CAN_EFF_ID_BITS - CAN_SFF_ID_BITS)
+#define CAN_EFF_SID_BITS CAN_SFF_ID_BITS
+#define CAN_EFF_SID_MASK \
+ GENMASK(CAN_EFF_SID_SHIFT + CAN_EFF_SID_BITS - 1, \
+ CAN_EFF_SID_SHIFT)
+#define CAN_EFF_EID_SHIFT 0
+#define CAN_EFF_EID_BITS CAN_EFF_SID_SHIFT
+#define CAN_EFF_EID_MASK \
+ GENMASK(CAN_EFF_EID_SHIFT + CAN_EFF_EID_BITS - 1, \
+ CAN_EFF_EID_SHIFT)
+
+static inline
+void mcp25xxfd_mcpid_to_canid(u32 mcp_id, u32 mcp_flags, u32 *can_id)
+{
+ u32 sid = (mcp_id & CAN_OBJ_ID_SID_MASK) >> CAN_OBJ_ID_SID_SHIFT;
+ u32 eid = (mcp_id & CAN_OBJ_ID_EID_MASK) >> CAN_OBJ_ID_EID_SHIFT;
+
+ /* select normal or extended ids */
+ if (mcp_flags & CAN_OBJ_FLAGS_IDE) {
+ *can_id = (eid << CAN_EFF_EID_SHIFT) |
+ (sid << CAN_EFF_SID_SHIFT) |
+ CAN_EFF_FLAG;
+ } else {
+ *can_id = sid;
+ }
+ /* handle rtr */
+ *can_id |= (mcp_flags & CAN_OBJ_FLAGS_RTR) ? CAN_RTR_FLAG : 0;
+}
+
+static inline
+void mcp25xxfd_canid_to_mcpid(u32 can_id, u32 *id, u32 *flags)
+{
+ /* depending on can_id flag compute extended or standard ids */
+ if (can_id & CAN_EFF_FLAG) {
+ int sid = (can_id & CAN_EFF_SID_MASK) >> CAN_EFF_SID_SHIFT;
+ int eid = (can_id & CAN_EFF_EID_MASK) >> CAN_EFF_EID_SHIFT;
+ *id = (eid << CAN_OBJ_ID_EID_SHIFT) |
+ (sid << CAN_OBJ_ID_SID_SHIFT);
+ *flags = CAN_OBJ_FLAGS_IDE;
+ } else {
+ *id = can_id & CAN_SFF_MASK;
+ *flags = 0;
+ }
+
+ /* Handle RTR */
+ *flags |= (can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
+}
+
+static inline
+void mcp25xxfd_can_queue_frame(struct mcp25xxfd_can_priv *cpriv,
+ s32 fifo, s32 ts)
+{
+ int idx = cpriv->fifos.submit_queue_count;
+
+ cpriv->fifos.submit_queue[idx].fifo = fifo;
+ cpriv->fifos.submit_queue[idx].ts = ts;
+
+ cpriv->fifos.submit_queue_count++;
+}
+
+int mcp25xxfd_can_read_rx_frames(struct spi_device *spi);
+int mcp25xxfd_can_submit_rx_frame(struct spi_device *spi, int fifo);
+int mcp25xxfd_can_submit_tx_frame(struct spi_device *spi, int fifo);
+
+int mcp25xxfd_can_int_handle_txatif(struct spi_device *spi);
+int mcp25xxfd_can_int_handle_tefif(struct spi_device *spi);
+
+int mcp25xxfd_can_tx_queue_alloc(struct net_device *net);
+void mcp25xxfd_can_tx_queue_free(struct net_device *net);
+void mcp25xxfd_can_tx_queue_restart(struct net_device *net);
+void mcp25xxfd_can_tx_queue_manage(struct net_device *net, int state);
+void mcp25xxfd_can_tx_queue_manage_nolock(struct net_device *net, int state);
+# define TX_QUEUE_STATE_STOPPED 0
+# define TX_QUEUE_STATE_STARTED 1
+# define TX_QUEUE_STATE_RUNABLE 2
+# define TX_QUEUE_STATE_RESTART 3
+
+int mcp25xxfd_can_switch_mode_nowait(struct spi_device *spi,
+ u32 *mode_data, int mode);
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c
new file mode 100644
index 000000000000..1a85fc57a955
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_fifo.c
@@ -0,0 +1,479 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ *
+ * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ */
+
+/* here we define and configure the fifo layout */
+
+#include "mcp25xxfd_can.h"
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/spi/spi.h>
+
+/* some module parameters that are currently not configurable via netlink */
+unsigned int tx_fifos;
+module_param(tx_fifos, uint, 0664);
+MODULE_PARM_DESC(tx_fifos, "Number of tx-fifos to configure\n");
+
+bool three_shot;
+module_param(three_shot, bool, 0664);
+MODULE_PARM_DESC(three_shot, "Use 3 shots when one-shot is requested");
+
+#if defined(CONFIG_DEBUG_FS)
+static
+void mcp25xxfd_can_setup_fifo_debugfs_rxtx(struct net_device *net,
+ const char *name,
+ struct mcp25xxfd_fifo *desc)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct mcp25xxfd_priv *priv = cpriv->priv;
+
+ /* remove old debug directory */
+ debugfs_remove_recursive(desc->debugfs_dir);
+
+ /* create new one */
+ desc->debugfs_dir = debugfs_create_dir(name, priv->debugfs_dir);
+
+ /* and fill it */
+ debugfs_create_u32("count", 0444, desc->debugfs_dir,
+ &desc->count);
+ debugfs_create_u32("size", 0444, desc->debugfs_dir,
+ &desc->size);
+ debugfs_create_u32("start", 0444, desc->debugfs_dir,
+ &desc->start);
+ debugfs_create_u32("increment", 0444, desc->debugfs_dir,
+ &desc->increment);
+ debugfs_create_u32("priority_start", 0444, desc->debugfs_dir,
+ &desc->priority_start);
+ debugfs_create_u32("priority_increment", 0444, desc->debugfs_dir,
+ &desc->priority_increment);
+}
+
+static
+void mcp25xxfd_can_setup_fifo_debugfs_link_rxtx(struct mcp25xxfd_fifo *desc,
+ int index, int fifo)
+{
+ char name[4];
+ char link[32];
+
+ snprintf(name, sizeof(name), "%02i", index);
+ snprintf(link, sizeof(link), "../can_fifos/%02i", fifo);
+
+ debugfs_create_symlink(name, desc->debugfs_dir, link);
+}
+
+static
+void mcp25xxfd_can_setup_fifo_debugfs_present_fifo(struct net_device *net,
+ int index)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct dentry *debugfs_dir;
+ char name[4];
+
+ snprintf(name, sizeof(name), "%02i", index);
+ debugfs_dir = debugfs_create_dir(name, cpriv->fifos.debugfs_dir);
+
+ /* and the entries */
+ debugfs_create_u32("is_tx", 0444, debugfs_dir,
+ &cpriv->fifos.fifo_info[index].is_tx);
+ debugfs_create_x32("offset", 0444, debugfs_dir,
+ &cpriv->fifos.fifo_reg[index].offset);
+ debugfs_create_u32("priority", 0444, debugfs_dir,
+ &cpriv->fifos.fifo_info[index].priority);
+ debugfs_create_u64("use_count", 0444, debugfs_dir,
+ &cpriv->fifos.fifo_info[index].use_count);
+}
+
+static
+void mcp25xxfd_can_setup_fifo_debugfs_present_fifos(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct mcp25xxfd_priv *priv = cpriv->priv;
+ int i;
+
+ /* create the directory if necessary */
+ if (!cpriv->fifos.debugfs_dir)
+ cpriv->fifos.debugfs_dir =
+ debugfs_create_dir("can_fifos", priv->debugfs_dir);
+
+ /* now present all fifos
+ * - note that there is no fifo 0 - that is the TX-queue!
+ */
+ for (i = 1; i < 32; i++)
+ mcp25xxfd_can_setup_fifo_debugfs_present_fifo(net, i);
+}
+
+static void mcp25xxfd_can_remove_fifo_debugfs(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+ debugfs_remove_recursive(cpriv->fifos.debugfs_dir);
+ cpriv->fifos.debugfs_dir = NULL;
+ debugfs_remove_recursive(cpriv->fifos.rx.debugfs_dir);
+ cpriv->fifos.rx.debugfs_dir = NULL;
+ debugfs_remove_recursive(cpriv->fifos.tx.debugfs_dir);
+ cpriv->fifos.tx.debugfs_dir = NULL;
+}
+
+#else
+
+static
+void mcp25xxfd_can_setup_fifo_debugfs_rxtx(struct net_device *net,
+ const char *name,
+ struct mcp25xxfd_fifo *desc)
+{
+}
+
+static
+void mcp25xxfd_can_setup_fifo_debugfs_link_rxtx(struct mcp25xxfd_fifo *desc,
+ int index, int fifo)
+{
+}
+
+static
+void mcp25xxfd_can_setup_fifo_debugfs_present_fifos(struct net_device *net)
+{
+}
+
+static void mcp25xxfd_can_remove_fifo_debugfs(struct net_device *net)
+{
+}
+#endif
+
+static int mcp25xxfd_can_get_fifo_address(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct mcp25xxfd_priv *priv = cpriv->priv;
+ struct spi_device *spi = priv->spi;
+ int ret;
+
+ /* write the config to the controller in one go */
+ ret = mcp25xxfd_cmd_write_regs(spi, CAN_FIFOCON(0),
+ &cpriv->fifos.fifo_reg[0].control,
+ sizeof(cpriv->fifos.fifo_reg));
+ if (ret)
+ return ret;
+
+ /* we need to move out of config mode to force address computation */
+ ret = mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
+ CAN_CON_MODE_INTERNAL_LOOPBACK);
+ if (ret)
+ return ret;
+
+ /* and get back into config mode */
+ ret = mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
+ CAN_CON_MODE_CONFIG);
+ if (ret)
+ return ret;
+
+ /* read address and config back in */
+ return mcp25xxfd_cmd_read_regs(spi, CAN_FIFOCON(0),
+ &cpriv->fifos.fifo_reg[0].control,
+ sizeof(cpriv->fifos.fifo_reg));
+}
+
+static int mcp25xxfd_can_setup_fifo_config(struct net_device *net,
+ const char *name,
+ struct mcp25xxfd_fifo *desc,
+ u32 flags,
+ u32 flags_last)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ bool is_tx;
+ u32 val;
+ int index, prio, fifo, count;
+
+ /* present fifo type info */
+ mcp25xxfd_can_setup_fifo_debugfs_rxtx(net, name, desc);
+
+ /* now setup the fifos themselves */
+ for (index = 0,
+ fifo = desc->start,
+ count = desc->count,
+ prio = desc->priority_start;
+ count > 0;
+ index++,
+ fifo += desc->increment,
+ prio += desc->priority_increment,
+ count--) {
+ /* select the effective value */
+ val = (count > 1) ? flags : flags_last;
+
+ /* are we in tx mode */
+ is_tx = flags & CAN_FIFOCON_TXEN;
+ cpriv->fifos.fifo_info[fifo].is_tx = is_tx;
+
+ /* set priority if we are tx */
+ if (is_tx) {
+ cpriv->fifos.fifo_info[fifo].priority = prio;
+ val |= (prio << CAN_FIFOCON_TXPRI_SHIFT);
+ }
+
+ /* setup interface */
+ cpriv->fifos.fifo_reg[fifo].control = val;
+
+ /* and link debugfs fifo */
+ mcp25xxfd_can_setup_fifo_debugfs_link_rxtx(desc, index,
+ fifo);
+ }
+
+ return 0;
+}
+
+static int mcp25xxfd_can_setup_tx_fifo_config(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ u32 tx_flags;
+
+ /* TX Fifo configuration */
+ tx_flags = CAN_FIFOCON_FRESET | /* reset FIFO */
+ CAN_FIFOCON_TXEN | /* this is a TX_FIFO */
+ CAN_FIFOCON_TXATIE | /* show up txatie flags in txatif reg */
+ (cpriv->fifos.payload_mode << CAN_FIFOCON_PLSIZE_SHIFT) |
+ (0 << CAN_FIFOCON_FSIZE_SHIFT); /* 1 FIFO only */
+ if (cpriv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+ if (three_shot)
+ tx_flags |= CAN_FIFOCON_TXAT_THREE_SHOT <<
+ CAN_FIFOCON_TXAT_SHIFT;
+ else
+ tx_flags |= CAN_FIFOCON_TXAT_ONE_SHOT <<
+ CAN_FIFOCON_TXAT_SHIFT;
+ else
+ tx_flags |= CAN_FIFOCON_TXAT_UNLIMITED <<
+ CAN_FIFOCON_TXAT_SHIFT;
+
+ return mcp25xxfd_can_setup_fifo_config(net, "can_fifo_tx",
+ &cpriv->fifos.tx,
+ tx_flags, tx_flags);
+}
+
+static int mcp25xxfd_can_setup_rx_fifo_config(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ u32 rx_flags, rx_flags_last;
+
+ /* RX Fifo configuration */
+ rx_flags = CAN_FIFOCON_FRESET | /* reset FIFO */
+ CAN_FIFOCON_RXTSEN | /* RX timestamps */
+ CAN_FIFOCON_TFERFFIE | /* FIFO Full */
+ CAN_FIFOCON_TFHRFHIE | /* FIFO Half Full*/
+ CAN_FIFOCON_TFNRFNIE | /* FIFO not empty */
+ (cpriv->fifos.payload_mode << CAN_FIFOCON_PLSIZE_SHIFT) |
+ (0 << CAN_FIFOCON_FSIZE_SHIFT); /* 1 FIFO only */
+ rx_flags_last = rx_flags | CAN_FIFOCON_RXOVIE;
+
+ /* configure the fifos */
+ return mcp25xxfd_can_setup_fifo_config(net, "can_fifo_rx",
+ &cpriv->fifos.rx,
+ rx_flags, rx_flags_last);
+}
+
+static int mcp25xxfd_can_clear_rx_filter_masks(struct spi_device *spi)
+{
+ u32 data[2 * 32]; /* 2 registers (match and mask) per filter */
+
+ memset(data, 0, sizeof(data));
+
+ return mcp25xxfd_cmd_write_regs(spi, CAN_FLTOBJ(0),
+ data, sizeof(data));
+}
+
+static int mcp25xxfd_can_setup_rx_filter_config(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct mcp25xxfd_priv *priv = cpriv->priv;
+ struct spi_device *spi = priv->spi;
+ struct mcp25xxfd_fifo *desc = &cpriv->fifos.rx;
+ u8 filter_con[32];
+ int filter, fifo, ret;
+
+ /* present all fifos */
+ mcp25xxfd_can_setup_fifo_debugfs_present_fifos(net);
+
+ /* clear the filter mask - match any frame with every filter */
+ ret = mcp25xxfd_can_clear_rx_filter_masks(spi);
+ if (ret)
+ return ret;
+
+ /* clear the filters and filter mappings for all filters */
+ memset(filter_con, 0, sizeof(filter_con));
+
+ /* and now set up the rx filters */
+ for (filter = 0, fifo = desc->start;
+ filter < desc->count;
+ filter++, fifo += desc->increment) {
+ /* set up filter config - we can use the mask of filter 0 */
+ filter_con[filter] = CAN_FIFOCON_FLTEN(0) |
+ (fifo << CAN_FILCON_SHIFT(0));
+ }
+
+ /* and set up filter control */
+ return mcp25xxfd_cmd_write_regs(spi, CAN_FLTCON(0),
+ (u32 *)filter_con,
+ sizeof(filter_con));
+}
+
+static int mcp25xxfd_can_compute_fifos(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ int tef_memory_used, tx_memory_used, rx_memory_available;
+
+ /* default settings as per MTU/CANFD */
+ switch (net->mtu) {
+ case CAN_MTU:
+ /* mtu is 8 */
+ cpriv->fifos.payload_size = 8;
+ cpriv->fifos.payload_mode = CAN_TXQCON_PLSIZE_8;
+
+ /* 7 tx fifos */
+ cpriv->fifos.tx.count = 7;
+
+ break;
+ case CANFD_MTU:
+ /* wish there was a way to have hw filters
+ * that can separate based on length ...
+ */
+ /* MTU is 64 */
+ cpriv->fifos.payload_size = 64;
+ cpriv->fifos.payload_mode = CAN_TXQCON_PLSIZE_64;
+
+ /* 7 tx fifos */
+ cpriv->fifos.tx.count = 7;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* compute effective sizes */
+ cpriv->fifos.tef.size = sizeof(struct mcp25xxfd_obj_tef);
+ cpriv->fifos.tx.size = sizeof(struct mcp25xxfd_obj_tx) +
+ cpriv->fifos.payload_size;
+ cpriv->fifos.rx.size = sizeof(struct mcp25xxfd_obj_rx) +
+ cpriv->fifos.payload_size;
+
+ /* if defined as a module parameter modify the number of tx_fifos */
+ if (tx_fifos) {
+ netdev_info(net,
+ "Using %i tx-fifos as per module parameter\n",
+ tx_fifos);
+ cpriv->fifos.tx.count = tx_fifos;
+ }
+
+ /* there can be at the most 30 tx fifos (TEF and at least 1 RX fifo */
+ if (cpriv->fifos.tx.count > 30) {
+ netdev_err(net,
+ "There is an absolute maximum of 30 tx-fifos\n");
+ return -EINVAL;
+ }
+
+ /* set tef fifos to the number of tx fifos */
+ cpriv->fifos.tef.count = cpriv->fifos.tx.count;
+
+ /* compute size of the tx fifos and TEF */
+ tx_memory_used = cpriv->fifos.tx.count * cpriv->fifos.tx.size;
+ tef_memory_used = cpriv->fifos.tef.count * cpriv->fifos.tef.size;
+
+ /* calculate evailable memory for RX_fifos */
+ rx_memory_available = MCP25XXFD_BUFFER_TXRX_SIZE -
+ tx_memory_used -
+ tef_memory_used;
+
+ /* we need at least one RX Frame */
+ if (rx_memory_available < cpriv->fifos.rx.size) {
+ netdev_err(net,
+ "Configured %i tx-fifos exceeds available memory already\n",
+ cpriv->fifos.tx.count);
+ return -EINVAL;
+ }
+
+ /* calculate possible amount of RX fifos */
+ cpriv->fifos.rx.count = rx_memory_available / cpriv->fifos.rx.size;
+
+ /* so now calculate effective number of rx-fifos
+ * there are only 31 fifos available in total,
+ * so we need to limit ourselves
+ */
+ if (cpriv->fifos.rx.count + cpriv->fifos.tx.count > 31)
+ cpriv->fifos.rx.count = 31 - cpriv->fifos.tx.count;
+
+ /* define the layout now that we have gotten everything */
+ cpriv->fifos.tx.start = 1;
+ cpriv->fifos.tx.increment = 1;
+ /* highest priority is 31 */
+ cpriv->fifos.tx.priority_start = 31;
+ cpriv->fifos.tx.priority_increment = -1;
+
+ cpriv->fifos.rx.start = 1 + cpriv->fifos.tx.count;
+ cpriv->fifos.rx.increment = 1;
+ /* rx fifos do not have a priority */
+ cpriv->fifos.rx.priority_start = 0;
+ cpriv->fifos.rx.priority_increment = 0;
+
+ return 0;
+}
+
+int mcp25xxfd_can_setup_fifos(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct mcp25xxfd_priv *priv = cpriv->priv;
+ struct spi_device *spi = priv->spi;
+ int ret;
+
+ /* compute fifos counts */
+ ret = mcp25xxfd_can_compute_fifos(net);
+ if (ret)
+ return ret;
+
+ /* configure TEF */
+ if (cpriv->fifos.tef.count)
+ cpriv->regs.tefcon =
+ CAN_TEFCON_FRESET |
+ CAN_TEFCON_TEFNEIE |
+ CAN_TEFCON_TEFTSEN |
+ ((cpriv->fifos.tef.count - 1) <<
+ CAN_TEFCON_FSIZE_SHIFT);
+ else
+ cpriv->regs.tefcon = 0;
+ ret = mcp25xxfd_cmd_write(spi, CAN_TEFCON, cpriv->regs.tefcon);
+ if (ret)
+ return ret;
+
+ /* clear fifo_reg and fifo_info */
+ memset(cpriv->fifos.fifo_reg, 0, sizeof(cpriv->fifos.fifo_reg));
+ memset(cpriv->fifos.fifo_info, 0, sizeof(cpriv->fifos.fifo_info));
+
+ /* configure FIFOS themselves */
+ ret = mcp25xxfd_can_setup_tx_fifo_config(net);
+ if (ret)
+ return ret;
+ ret = mcp25xxfd_can_setup_rx_fifo_config(net);
+ if (ret)
+ return ret;
+
+ /* get fifo addresses */
+ ret = mcp25xxfd_can_get_fifo_address(net);
+ if (ret)
+ return ret;
+
+ /* finally configure RX filters */
+ ret = mcp25xxfd_can_setup_rx_filter_config(net);
+ if (ret)
+ return ret;
+
+ /* setup tx_fifo_queue */
+ return mcp25xxfd_can_tx_queue_alloc(net);
+}
+
+void mcp25xxfd_can_release_fifos(struct net_device *net)
+{
+ mcp25xxfd_can_tx_queue_free(net);
+ mcp25xxfd_can_remove_fifo_debugfs(net);
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c
new file mode 100644
index 000000000000..842c714fbbfe
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_int.c
@@ -0,0 +1,723 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ * implementation of can interrupt handling
+ *
+ * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ */
+
+#include "mcp25xxfd_can.h"
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+
+static void mcp25xxfd_can_error_skb(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct sk_buff *skb;
+ struct can_frame *frame;
+
+ /* allocate error frame */
+ skb = alloc_can_err_skb(net, &frame);
+ if (!skb) {
+ netdev_err(net, "cannot allocate error skb\n");
+ return;
+ }
+
+ /* setup can error frame data */
+ frame->can_id |= cpriv->error_frame.id;
+ memcpy(frame->data, cpriv->error_frame.data, sizeof(frame->data));
+
+ /* and submit it */
+ netif_receive_skb(skb);
+}
+
+static int mcp25xxfd_can_int_clear_int_flags(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ u32 clearable_irq_active = cpriv->status.intf & CAN_INT_IF_CLEAR_MASK;
+ u32 clear_irq = cpriv->status.intf & (~CAN_INT_IF_CLEAR_MASK);
+
+ /* if no clearable flags are set then skip the whole transfer */
+ if (!clearable_irq_active)
+ return 0;
+
+ return mcp25xxfd_cmd_write_mask(spi, CAN_INT,
+ clear_irq, clearable_irq_active);
+}
+
+static int mcp25xxfd_can_ist_handle_serrif_txmab(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+ net->stats.tx_fifo_errors++;
+ net->stats.tx_errors++;
+ cpriv->stats.int_serr_tx_count++;
+
+ /* and switch back into the correct mode */
+ return mcp25xxfd_can_switch_mode_nowait(spi, &cpriv->regs.con,
+ (net->mtu == CAN_MTU) ?
+ CAN_CON_MODE_CAN2_0 :
+ CAN_CON_MODE_MIXED);
+}
+
+static int mcp25xxfd_can_ist_handle_serrif_rxmab(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+ net->stats.rx_dropped++;
+ net->stats.rx_errors++;
+ cpriv->stats.int_serr_rx_count++;
+
+ return 0;
+}
+
+static int mcp25xxfd_can_int_handle_serrif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+ if (!(cpriv->status.intf & CAN_INT_SERRIF))
+ return 0;
+
+ /* increment statistics counter now */
+ cpriv->stats.int_serr_count++;
+
+ /* interrupt flags have been cleared already */
+
+ /* Errors here are:
+ * * Bus Bandwidth Error: when a RX Message Assembly Buffer
+ * is still full when the next message has already arrived
+ * the recived message shall be ignored
+ * * TX MAB Underflow: when a TX Message is invalid
+ * due to ECC errors or TXMAB underflow
+ * in this situatioon the system will transition to
+ * Restricted or Listen Only mode
+ */
+
+ cpriv->error_frame.id |= CAN_ERR_CRTL;
+ cpriv->error_frame.data[1] |= CAN_ERR_CRTL_UNSPEC;
+
+ /* a mode change + invalid message would indicate
+ * TX MAB Underflow
+ */
+ if ((cpriv->status.intf & CAN_INT_MODIF) &&
+ (cpriv->status.intf & CAN_INT_IVMIF)) {
+ return mcp25xxfd_can_ist_handle_serrif_txmab(spi);
+ }
+
+ /* for RX there is only the RXIF an indicator
+ * - surprizingly RX-MAB does not change mode or anything
+ */
+ if (cpriv->status.intf & CAN_INT_RXIF)
+ return mcp25xxfd_can_ist_handle_serrif_rxmab(spi);
+
+ /* the final case */
+ dev_warn_ratelimited(&spi->dev,
+ "unidentified system error - intf = %08x\n",
+ cpriv->status.intf);
+
+ return 0;
+}
+
+static int mcp25xxfd_can_int_handle_modif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ int mode;
+ int ret;
+
+ /* Note that this irq does not get triggered in all situations
+ * for example SERRIF will move to RESTICTED or LISTENONLY
+ * but MODIF will not be raised!
+ */
+
+ if (!(cpriv->status.intf & CAN_INT_MODIF))
+ return 0;
+ cpriv->stats.int_mod_count++;
+
+ /* get the current mode */
+ ret = mcp25xxfd_can_get_mode(spi, &mode);
+ if (ret)
+ return ret;
+ mode = ret;
+
+ /* switches to the same mode as before are ignored
+ * - this typically happens if the driver is shortly
+ * switching to a different mode and then returning to the
+ * original mode
+ */
+ if (mode == cpriv->mode)
+ return 0;
+
+ /* if we are restricted, then return to "normal" mode */
+ if (mode == CAN_CON_MODE_RESTRICTED) {
+ cpriv->mode = mode;
+ return mcp25xxfd_can_switch_mode(spi, &cpriv->regs.con,
+ (net->mtu == CAN_MTU) ?
+ CAN_CON_MODE_CAN2_0 :
+ CAN_CON_MODE_MIXED);
+ }
+
+ /* the controller itself will transition to sleep, so we ignore it */
+ if (mode == CAN_CON_MODE_SLEEP) {
+ cpriv->mode = mode;
+ return 0;
+ }
+
+ /* these we need to handle correctly, so warn and give context */
+ dev_warn(&spi->dev,
+ "Controller unexpectedly switched from mode %u to %u\n",
+ cpriv->mode, mode);
+
+ /* assign the mode as current */
+ cpriv->mode = mode;
+
+ return 0;
+}
+
+static int mcp25xxfd_compare_obj_ts(const void *a, const void *b)
+{
+ s32 ats = ((struct mcp25xxfd_obj_ts *)a)->ts;
+ s32 bts = ((struct mcp25xxfd_obj_ts *)b)->ts;
+
+ if (ats < bts)
+ return -1;
+ if (ats > bts)
+ return 1;
+ return 0;
+}
+
+static int mcp25xxfd_can_submit_frames(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct mcp25xxfd_obj_ts *queue = cpriv->fifos.submit_queue;
+ int count = cpriv->fifos.submit_queue_count;
+ int i, fifo;
+ int ret;
+
+ /* skip processing if the queue count is 0 */
+ if (count == 0)
+ goto out;
+
+ /* sort the fifos (rx and TEF) by receive timestamp */
+ sort(queue, count, sizeof(*queue), mcp25xxfd_compare_obj_ts, NULL);
+
+ /* now submit the fifos */
+ for (i = 0; i < count; i++) {
+ fifo = queue[i].fifo;
+ if (fifo > 0)
+ ret = mcp25xxfd_can_submit_rx_frame(spi, fifo);
+ else
+ ret = mcp25xxfd_can_submit_tx_frame(spi, -fifo);
+ if (ret)
+ return ret;
+ }
+
+ /* if we have received or transmitted something
+ * and the IVMIE is disabled, then enable it
+ * this is mostly to avoid unnecessary interrupts during a
+ * disconnected CAN BUS
+ */
+ if (!(cpriv->status.intf | CAN_INT_IVMIE)) {
+ cpriv->status.intf |= CAN_INT_IVMIE;
+ ret = mcp25xxfd_cmd_write_mask(spi, CAN_INT,
+ cpriv->status.intf,
+ CAN_INT_IVMIE);
+ if (ret)
+ return ret;
+ }
+
+out:
+ /* enable tx_queue if necessary */
+ mcp25xxfd_can_tx_queue_restart(net);
+
+ return 0;
+}
+
+static int mcp25xxfd_can_int_handle_rxif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+ if (!cpriv->status.rxif)
+ return 0;
+
+ cpriv->stats.int_rx_count++;
+
+ /* read all the fifos */
+ return mcp25xxfd_can_read_rx_frames(spi);
+}
+
+static int mcp25xxfd_can_int_handle_rxovif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ int ret, i;
+
+ if (!cpriv->status.rxovif)
+ return 0;
+ cpriv->stats.int_rxov_count++;
+
+ /* clear all fifos that have an overflow bit set */
+ for (i = 0; i < 32; i++) {
+ if (cpriv->status.rxovif & BIT(i)) {
+ /* clear fifo status */
+ ret = mcp25xxfd_cmd_write_mask(spi,
+ CAN_FIFOSTA(i),
+ 0,
+ CAN_FIFOSTA_RXOVIF);
+ if (ret)
+ return ret;
+
+ /* update statistics */
+ net->stats.rx_over_errors++;
+ net->stats.rx_errors++;
+
+ /* and prepare ERROR FRAME */
+ cpriv->error_frame.id |= CAN_ERR_CRTL;
+ cpriv->error_frame.data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+ }
+ }
+
+ return 0;
+}
+
+static int mcp25xxfd_can_int_handle_eccif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+ if (!(cpriv->status.intf & CAN_INT_ECCIF))
+ return 0;
+
+ cpriv->stats.int_ecc_count++;
+
+ /* and prepare ERROR FRAME */
+ cpriv->error_frame.id |= CAN_ERR_CRTL;
+ cpriv->error_frame.data[1] |= CAN_ERR_CRTL_UNSPEC;
+
+ /* delegate to interrupt cleaning */
+ return mcp25xxfd_clear_ecc_interrupts(spi);
+}
+
+static void mcp25xxfd_can_int_handle_ivmif_tx(struct spi_device *spi,
+ u32 *mask)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+ /* check if it is really a known tx error */
+ if ((cpriv->bus.bdiag[1] &
+ (CAN_BDIAG1_DBIT1ERR |
+ CAN_BDIAG1_DBIT0ERR |
+ CAN_BDIAG1_NACKERR |
+ CAN_BDIAG1_NBIT1ERR |
+ CAN_BDIAG1_NBIT0ERR
+ )) == 0)
+ return;
+
+ /* mark it as a protocol error */
+ cpriv->error_frame.id |= CAN_ERR_PROT;
+
+ /* and update statistics */
+ net->stats.tx_errors++;
+
+ /* and handle all the known cases */
+ if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NACKERR) {
+ /* TX-Frame not acknowledged - connected to CAN-bus? */
+ *mask |= CAN_BDIAG1_NACKERR;
+ cpriv->error_frame.data[2] |= CAN_ERR_PROT_TX;
+ net->stats.tx_aborted_errors++;
+ }
+ if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NBIT1ERR) {
+ /* TX-Frame CAN-BUS Level is unexpectedly dominant */
+ *mask |= CAN_BDIAG1_NBIT1ERR;
+ net->stats.tx_carrier_errors++;
+ cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT1;
+ }
+ if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NBIT0ERR) {
+ /* TX-Frame CAN-BUS Level is unexpectedly recessive */
+ *mask |= CAN_BDIAG1_NBIT0ERR;
+ net->stats.tx_carrier_errors++;
+ cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT0;
+ }
+ if (cpriv->bus.bdiag[1] & CAN_BDIAG1_DBIT1ERR) {
+ /* TX-Frame CAN-BUS Level is unexpectedly dominant
+ * during data phase
+ */
+ *mask |= CAN_BDIAG1_DBIT1ERR;
+ net->stats.tx_carrier_errors++;
+ cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT1;
+ }
+ if (cpriv->bus.bdiag[1] & CAN_BDIAG1_DBIT0ERR) {
+ /* TX-Frame CAN-BUS Level is unexpectedly recessive
+ * during data phase
+ */
+ *mask |= CAN_BDIAG1_DBIT0ERR;
+ net->stats.tx_carrier_errors++;
+ cpriv->error_frame.data[2] |= CAN_ERR_PROT_BIT0;
+ }
+}
+
+static void mcp25xxfd_can_int_handle_ivmif_rx(struct spi_device *spi,
+ u32 *mask)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+ /* check if it is really a known tx error */
+ if ((cpriv->bus.bdiag[1] &
+ (CAN_BDIAG1_DCRCERR |
+ CAN_BDIAG1_DSTUFERR |
+ CAN_BDIAG1_DFORMERR |
+ CAN_BDIAG1_NCRCERR |
+ CAN_BDIAG1_NSTUFERR |
+ CAN_BDIAG1_NFORMERR
+ )) == 0)
+ return;
+
+ /* mark it as a protocol error */
+ cpriv->error_frame.id |= CAN_ERR_PROT;
+
+ /* and update statistics */
+ net->stats.rx_errors++;
+
+ /* handle the cases */
+ if (cpriv->bus.bdiag[1] & CAN_BDIAG1_DCRCERR) {
+ /* RX-Frame with bad CRC during data phase */
+ *mask |= CAN_BDIAG1_DCRCERR;
+ net->stats.rx_crc_errors++;
+ cpriv->error_frame.data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
+ }
+ if (cpriv->bus.bdiag[1] & CAN_BDIAG1_DSTUFERR) {
+ /* RX-Frame with bad stuffing during data phase */
+ *mask |= CAN_BDIAG1_DSTUFERR;
+ net->stats.rx_frame_errors++;
+ cpriv->error_frame.data[2] |= CAN_ERR_PROT_STUFF;
+ }
+ if (cpriv->bus.bdiag[1] & CAN_BDIAG1_DFORMERR) {
+ /* RX-Frame with bad format during data phase */
+ *mask |= CAN_BDIAG1_DFORMERR;
+ net->stats.rx_frame_errors++;
+ cpriv->error_frame.data[2] |= CAN_ERR_PROT_FORM;
+ }
+ if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NCRCERR) {
+ /* RX-Frame with bad CRC during data phase */
+ *mask |= CAN_BDIAG1_NCRCERR;
+ net->stats.rx_crc_errors++;
+ cpriv->error_frame.data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ;
+ }
+ if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NSTUFERR) {
+ /* RX-Frame with bad stuffing during data phase */
+ *mask |= CAN_BDIAG1_NSTUFERR;
+ net->stats.rx_frame_errors++;
+ cpriv->error_frame.data[2] |= CAN_ERR_PROT_STUFF;
+ }
+ if (cpriv->bus.bdiag[1] & CAN_BDIAG1_NFORMERR) {
+ /* RX-Frame with bad format during data phase */
+ *mask |= CAN_BDIAG1_NFORMERR;
+ net->stats.rx_frame_errors++;
+ cpriv->error_frame.data[2] |= CAN_ERR_PROT_FORM;
+ }
+}
+
+static int mcp25xxfd_can_int_handle_ivmif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ u32 mask, bdiag1;
+ int ret;
+
+ if (!(cpriv->status.intf & CAN_INT_IVMIF))
+ return 0;
+
+ cpriv->stats.int_ivm_count++;
+
+ /* if we have a systemerror as well,
+ * then ignore it as they correlate
+ */
+ if (cpriv->status.intf & CAN_INT_SERRIF)
+ return 0;
+
+ /* read bus diagnostics */
+ ret = mcp25xxfd_cmd_read_regs(spi, CAN_BDIAG0,
+ cpriv->bus.bdiag,
+ sizeof(cpriv->bus.bdiag));
+ if (ret)
+ return ret;
+
+ /* clear the masks of bits to clear */
+ mask = 0;
+
+ /* check rx and tx errors */
+ mcp25xxfd_can_int_handle_ivmif_tx(spi, &mask);
+ mcp25xxfd_can_int_handle_ivmif_rx(spi, &mask);
+
+ /* clear flags if we have bits masked */
+ if (!mask) {
+ /* the unsupported case, where we are not
+ * clearing any registers
+ */
+ dev_warn_once(&spi->dev,
+ "found IVMIF situation not supported by driver - bdiag = [0x%08x, 0x%08x]",
+ cpriv->bus.bdiag[0], cpriv->bus.bdiag[1]);
+ return -EINVAL;
+ }
+
+ /* clear the bits in bdiag1 */
+ bdiag1 = cpriv->bus.bdiag[1] & (~mask);
+ /* and write it */
+ ret = mcp25xxfd_cmd_write_mask(spi, CAN_BDIAG1, bdiag1, mask);
+ if (ret)
+ return ret;
+
+ /* and clear the interrupt flag until we have received or transmited */
+ cpriv->status.intf &= ~(CAN_INT_IVMIE);
+ return mcp25xxfd_cmd_write_mask(spi, CAN_INT, cpriv->status.intf,
+ CAN_INT_IVMIE);
+}
+
+static int mcp25xxfd_can_int_handle_cerrif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+ if (!(cpriv->status.intf & CAN_INT_CERRIF))
+ return 0;
+
+ /* this interrupt exists primarilly to counter possible
+ * bus off situations more detailed information
+ * can be found and controlled in the TREC register
+ */
+
+ cpriv->stats.int_cerr_count++;
+
+ netdev_warn(net, "CAN Bus error experienced");
+
+ return 0;
+}
+
+static int mcp25xxfd_can_int_error_counters(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+ if (cpriv->status.trec & CAN_TREC_TXWARN) {
+ cpriv->bus.new_state = CAN_STATE_ERROR_WARNING;
+ cpriv->error_frame.id |= CAN_ERR_CRTL;
+ cpriv->error_frame.data[1] |= CAN_ERR_CRTL_TX_WARNING;
+ }
+ if (cpriv->status.trec & CAN_TREC_RXWARN) {
+ cpriv->bus.new_state = CAN_STATE_ERROR_WARNING;
+ cpriv->error_frame.id |= CAN_ERR_CRTL;
+ cpriv->error_frame.data[1] |= CAN_ERR_CRTL_RX_WARNING;
+ }
+ if (cpriv->status.trec & CAN_TREC_TXBP) {
+ cpriv->bus.new_state = CAN_STATE_ERROR_PASSIVE;
+ cpriv->error_frame.id |= CAN_ERR_CRTL;
+ cpriv->error_frame.data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+ }
+ if (cpriv->status.trec & CAN_TREC_RXBP) {
+ cpriv->bus.new_state = CAN_STATE_ERROR_PASSIVE;
+ cpriv->error_frame.id |= CAN_ERR_CRTL;
+ cpriv->error_frame.data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+ }
+ if (cpriv->status.trec & CAN_TREC_TXBO) {
+ cpriv->bus.new_state = CAN_STATE_BUS_OFF;
+ cpriv->error_frame.id |= CAN_ERR_BUSOFF;
+ }
+
+ return 0;
+}
+
+static int mcp25xxfd_can_int_error_handling(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+ /* based on the last state state check the new state */
+ switch (cpriv->can.state) {
+ case CAN_STATE_ERROR_ACTIVE:
+ if (cpriv->bus.new_state >= CAN_STATE_ERROR_WARNING &&
+ cpriv->bus.new_state <= CAN_STATE_BUS_OFF)
+ cpriv->can.can_stats.error_warning++;
+ /* fallthrough */
+ case CAN_STATE_ERROR_WARNING:
+ if (cpriv->bus.new_state >= CAN_STATE_ERROR_PASSIVE &&
+ cpriv->bus.new_state <= CAN_STATE_BUS_OFF)
+ cpriv->can.can_stats.error_passive++;
+ break;
+ default:
+ break;
+ }
+ cpriv->can.state = cpriv->bus.new_state;
+
+ /* and send error packet */
+ if (cpriv->error_frame.id)
+ mcp25xxfd_can_error_skb(spi);
+
+ /* handle BUS OFF */
+ if (cpriv->can.state == CAN_STATE_BUS_OFF) {
+ if (cpriv->can.restart_ms == 0) {
+ cpriv->can.can_stats.bus_off++;
+ can_bus_off(net);
+ }
+ } else {
+ /* restart the tx queue if needed */
+ }
+
+ return 0;
+}
+
+static int mcp25xxfd_can_int_handle_status(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ int ret;
+
+ /* clear all the interrupts asap - we have them on file allready */
+ ret = mcp25xxfd_can_int_clear_int_flags(spi);
+ if (ret)
+ return ret;
+
+ /* set up new state and error frame for this loop */
+ cpriv->bus.new_state = cpriv->bus.state;
+ memset(&cpriv->error_frame, 0, sizeof(cpriv->error_frame));
+
+ /* setup the process queue by clearing the counter */
+ cpriv->fifos.submit_queue_count = 0;
+
+ /* handle interrupts */
+
+ /* system error interrupt needs to get handled first
+ * to get us out of restricted mode
+ */
+ ret = mcp25xxfd_can_int_handle_serrif(spi);
+ if (ret)
+ return ret;
+
+ /* mode change interrupt */
+ ret = mcp25xxfd_can_int_handle_modif(spi);
+ if (ret)
+ return ret;
+
+ /* handle the rx */
+ ret = mcp25xxfd_can_int_handle_rxif(spi);
+ if (ret)
+ return ret;
+ /* handle aborted TX FIFOs */
+ ret = mcp25xxfd_can_int_handle_txatif(spi);
+ if (ret)
+ return ret;
+
+ /* handle the TEF */
+ ret = mcp25xxfd_can_int_handle_tefif(spi);
+ if (ret)
+ return ret;
+
+ /* handle error interrupt flags */
+ ret = mcp25xxfd_can_int_handle_rxovif(spi);
+ if (ret)
+ return ret;
+
+ /* sram ECC error interrupt */
+ ret = mcp25xxfd_can_int_handle_eccif(spi);
+ if (ret)
+ return ret;
+
+ /* message format interrupt */
+ ret = mcp25xxfd_can_int_handle_ivmif(spi);
+ if (ret)
+ return ret;
+
+ /* handle bus errors in more detail */
+ ret = mcp25xxfd_can_int_handle_cerrif(spi);
+ if (ret)
+ return ret;
+
+ /* error counter handling */
+ ret = mcp25xxfd_can_int_error_counters(spi);
+ if (ret)
+ return ret;
+
+ /* error counter handling */
+ ret = mcp25xxfd_can_int_error_handling(spi);
+ if (ret)
+ return ret;
+
+ /* and submit can frames to network stack */
+ ret = mcp25xxfd_can_submit_frames(spi);
+
+ return ret;
+}
+
+irqreturn_t mcp25xxfd_can_int(int irq, void *dev_id)
+{
+ struct mcp25xxfd_priv *priv = dev_id;
+ struct spi_device *spi = priv->spi;
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ int ret;
+
+ /* count interrupt calls */
+ cpriv->stats.irq_calls++;
+
+ /* as long as we should be running */
+ while (1) {
+ /* count irq loops */
+ cpriv->stats.irq_loops++;
+
+ /* read interrupt status flags in bulk */
+ ret = mcp25xxfd_cmd_read_regs(spi, CAN_INT,
+ &cpriv->status.intf,
+ sizeof(cpriv->status));
+ if (ret)
+ return ret;
+
+ /* only act if the IE mask configured has active IF bits
+ * otherwise the Interrupt line should be deasserted already
+ * so we can exit the loop
+ */
+ if (((cpriv->status.intf >> CAN_INT_IE_SHIFT) &
+ cpriv->status.intf) == 0)
+ break;
+
+ /* handle the status */
+ ret = mcp25xxfd_can_int_handle_status(spi);
+ if (ret)
+ return ret;
+ }
+
+ return IRQ_HANDLED;
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c
new file mode 100644
index 000000000000..d5e4e2a18bb7
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_rx.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ * implementation of can transmission
+ *
+ * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ */
+
+#include "mcp25xxfd_can.h"
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+/* module parameters */
+bool do_not_submit_rx;
+module_param(do_not_submit_rx, bool, 0664);
+MODULE_PARM_DESC(do_not_submit_rx,
+ "do not submit rx frames to can stack - used to test performance of the spi layer");
+
+static
+struct sk_buff *mcp25xxfd_can_submit_rx_normal_frame(struct net_device *net,
+ u32 id,
+ u32 dlc, u8 **data)
+{
+ struct can_frame *frame;
+ struct sk_buff *skb;
+
+ /* allocate frame */
+ skb = alloc_can_skb(net, &frame);
+ if (!skb)
+ return NULL;
+
+ /* set id, dlc and flags */
+ frame->can_id = id;
+ frame->can_dlc = dlc;
+
+ /* and set the pointer to data */
+ *data = frame->data;
+
+ return skb;
+}
+
+/* it is almost identical except for the type of the frame... */
+static
+struct sk_buff *mcp25xxfd_can_submit_rx_fd_frame(struct net_device *net,
+ u32 id, u32 flags,
+ u32 len, u8 **data)
+{
+ struct canfd_frame *frame;
+ struct sk_buff *skb;
+
+ /* allocate frame */
+ skb = alloc_canfd_skb(net, &frame);
+ if (!skb)
+ return NULL;
+
+ /* set id, dlc and flags */
+ frame->can_id = id;
+ frame->len = len;
+ frame->flags |= flags;
+
+ /* and set the pointer to data */
+ *data = frame->data;
+
+ return skb;
+}
+
+int mcp25xxfd_can_submit_rx_frame(struct spi_device *spi, int fifo)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ int addr = cpriv->fifos.fifo_reg[fifo].offset;
+ struct mcp25xxfd_obj_rx *rx =
+ (struct mcp25xxfd_obj_rx *)(cpriv->sram + addr);
+ u8 *data = NULL;
+ struct sk_buff *skb;
+ u32 id, dlc, len, flags;
+
+ /* compute the can_id */
+ mcp25xxfd_mcpid_to_canid(rx->id, rx->flags, &id);
+
+ /* and dlc */
+ dlc = (rx->flags & CAN_OBJ_FLAGS_DLC_MASK) >>
+ CAN_OBJ_FLAGS_DLC_SHIFT;
+ len = can_dlc2len(dlc);
+
+ /* update stats */
+ priv->net->stats.rx_packets++;
+ priv->net->stats.rx_bytes += len;
+ cpriv->fifos.rx.dlc_usage[dlc]++;
+ if (rx->flags & CAN_OBJ_FLAGS_FDF)
+ cpriv->fifos.rx.fd_count++;
+
+ /* allocate the skb buffer */
+ if (rx->flags & CAN_OBJ_FLAGS_FDF) {
+ flags = 0;
+
+ flags |= (flags & CAN_OBJ_FLAGS_BRS) ? CANFD_BRS : 0;
+ flags |= (flags & CAN_OBJ_FLAGS_ESI) ? CANFD_ESI : 0;
+ skb = mcp25xxfd_can_submit_rx_fd_frame(net, id, flags,
+ dlc, &data);
+ } else {
+ skb = mcp25xxfd_can_submit_rx_normal_frame(net, id,
+ len, &data);
+ }
+ if (!skb) {
+ dev_err(&spi->dev, "cannot allocate RX skb\n");
+ priv->net->stats.rx_dropped++;
+ return -ENOMEM;
+ }
+
+ /* copy the payload data */
+ memcpy(data, rx->data, len);
+
+ /* and submit the frame */
+ if (do_not_submit_rx)
+ consume_skb(skb);
+ else
+ netif_rx_ni(skb);
+
+ return 0;
+}
+
+static int mcp25xxfd_can_read_rx_frame(struct spi_device *spi,
+ int fifo)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ int addr = cpriv->fifos.fifo_reg[fifo].offset;
+ struct mcp25xxfd_obj_rx *rx =
+ (struct mcp25xxfd_obj_rx *)(cpriv->sram + addr);
+ int len, ret;
+
+ /* we read the header plus 8 data bytes for "standard frames" */
+ ret = mcp25xxfd_cmd_readn(spi, MCP25XXFD_SRAM_ADDR(addr),
+ rx, sizeof(*rx) + 8);
+ if (ret)
+ return ret;
+
+ /* transpose the headers to CPU format*/
+ rx->id = le32_to_cpu(rx->id);
+ rx->flags = le32_to_cpu(rx->flags);
+ rx->ts = le32_to_cpu(rx->ts);
+
+ /* compute len */
+ len = can_dlc2len((rx->flags & CAN_OBJ_FLAGS_DLC_MASK) >>
+ CAN_OBJ_FLAGS_DLC_SHIFT);
+
+ /* read the remaining data for canfd frames */
+ if (net->mtu == CANFD_MTU && len > 8) {
+ /* here the extra portion reading CanFD frames */
+ ret = mcp25xxfd_cmd_readn(spi,
+ MCP25XXFD_SRAM_ADDR(addr) +
+ sizeof(*rx) + 8,
+ &rx->data[8], len - 8);
+ if (ret)
+ return ret;
+ }
+
+ /* clear the rest of the buffer - just to be safe */
+ memset(rx->data + len, 0,
+ ((net->mtu == CANFD_MTU) ? 64 : 8) - len);
+
+ /* increment the statistics counter */
+ cpriv->fifos.fifo_info[fifo].use_count++;
+
+ /* add the fifo to the process queues */
+ mcp25xxfd_can_queue_frame(cpriv, fifo, rx->ts);
+
+ /* and clear the interrupt flag for that fifo */
+ return mcp25xxfd_cmd_write_mask(spi, CAN_FIFOCON(fifo),
+ CAN_FIFOCON_FRESET,
+ CAN_FIFOCON_FRESET);
+}
+
+int mcp25xxfd_can_read_rx_frames(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ int i, fifo;
+ int ret;
+
+ /* we can optimize here */
+ for (i = 0, fifo = cpriv->fifos.rx.start;
+ i < cpriv->fifos.rx.count;
+ i++, fifo += cpriv->fifos.rx.increment) {
+ if (cpriv->status.rxif & BIT(fifo)) {
+ /* read the frame */
+ ret = mcp25xxfd_can_read_rx_frame(spi, fifo);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c
new file mode 100644
index 000000000000..9d53ac04ae10
--- /dev/null
+++ b/drivers/net/can/spi/mcp25xxfd/mcp25xxfd_can_tx.c
@@ -0,0 +1,824 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface
+ * implementation of can transmission
+ *
+ * Copyright 2017 Martin Sperl <kernel@martin.sperl.org>
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ */
+
+#include "mcp25xxfd_can.h"
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/netdevice.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+/* structure of a spi message that is prepared and can get deployed quickly */
+struct mcp2517fd_tx_spi_message {
+ /* the network device this is related to */
+ struct net_device *net;
+ /* the fifo this fills */
+ u32 fifo;
+ /* the xfer to fill in the fifo data */
+ struct {
+ struct spi_message msg;
+ struct spi_transfer xfer;
+ struct {
+ u8 cmd[2];
+ u8 header[sizeof(struct mcp25xxfd_obj_tx)];
+ u8 data[64];
+ } data;
+ } fill_fifo;
+ /* the xfer to enable transmission on the can bus */
+ struct {
+ struct spi_message msg;
+ struct spi_transfer xfer;
+ struct {
+ u8 cmd[2];
+ u8 data;
+ } data;
+ } trigger_fifo;
+};
+
+struct mcp2517fd_tx_spi_message_queue {
+ /* spinlock protecting the bitmaps
+ * as well as state and the skb_echo_* functions
+ */
+ spinlock_t lock;
+ /* bitmap of which fifo is in which stage */
+ u32 idle;
+ u32 in_fill_fifo_transfer;
+ u32 in_trigger_fifo_transfer;
+ u32 in_can_transfer;
+ u32 transferred;
+
+ /* the queue state as seen per controller */
+ int state;
+
+ /* spinlock protecting spi submission order */
+ spinlock_t spi_lock;
+
+ /* map each fifo to a mcp2517fd_tx_spi_message */
+ struct mcp2517fd_tx_spi_message *fifo2message[32];
+
+ /* the individual messages */
+ struct mcp2517fd_tx_spi_message message[];
+};
+
+/* mostly bit manipulations to move between stages */
+static
+struct mcp2517fd_tx_spi_message *mcp25xxfd_can_tx_queue_first_spi_message(
+ struct mcp2517fd_tx_spi_message_queue *queue, u32 *bitmap)
+{
+ u32 first = ffs(*bitmap);
+
+ if (!first)
+ return NULL;
+
+ return queue->fifo2message[first - 1];
+}
+
+static
+struct mcp2517fd_tx_spi_message *mcp25xxfd_can_tx_queue_last_spi_message(
+ struct mcp2517fd_tx_spi_message_queue *queue, u32 *bitmap)
+{
+ u32 last = ffs(*bitmap);
+
+ if (!last)
+ return NULL;
+
+ return queue->fifo2message[last - 1];
+}
+
+static void mcp25xxfd_can_tx_queue_remove_spi_message(u32 *bitmap, int fifo)
+{
+ *bitmap &= ~BIT(fifo);
+}
+
+static void mcp25xxfd_can_tx_queue_add_spi_message(u32 *bitmap, int fifo)
+{
+ *bitmap |= BIT(fifo);
+}
+
+static void mcp25xxfd_can_tx_queue_move_spi_message(u32 *src, u32 *dest,
+ int fifo)
+{
+ mcp25xxfd_can_tx_queue_remove_spi_message(src, fifo);
+ mcp25xxfd_can_tx_queue_add_spi_message(dest, fifo);
+}
+
+static void mcp25xxfd_can_tx_spi_message_fill_fifo_complete(void *context)
+{
+ struct mcp2517fd_tx_spi_message *msg = context;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(msg->net);
+ unsigned long flags;
+
+ /* reset transfer length to without data (DLC = 0) */
+ msg->fill_fifo.xfer.len = sizeof(msg->fill_fifo.data.cmd) +
+ sizeof(msg->fill_fifo.data.header);
+
+ /* we need to hold this lock to protect us from
+ * concurrent access by start_xmit
+ */
+ spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+ /* move to in_trigger_fifo_transfer */
+ mcp25xxfd_can_tx_queue_move_spi_message(
+ &cpriv->fifos.tx_queue->in_fill_fifo_transfer,
+ &cpriv->fifos.tx_queue->in_trigger_fifo_transfer,
+ msg->fifo);
+
+ spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+}
+
+static void mcp25xxfd_can_tx_spi_message_trigger_fifo_complete(void *context)
+{
+ struct mcp2517fd_tx_spi_message *msg = context;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(msg->net);
+ unsigned long flags;
+
+ /* we need to hold this lock to protect us from
+ * concurrent access by the interrupt thread
+ */
+ spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+ /* move to can_transfer */
+ mcp25xxfd_can_tx_queue_move_spi_message(
+ &cpriv->fifos.tx_queue->in_trigger_fifo_transfer,
+ &cpriv->fifos.tx_queue->in_can_transfer,
+ msg->fifo);
+
+ spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+static void mcp25xxfd_can_tx_queue_debugfs(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct dentry *dir = cpriv->fifos.tx.debugfs_dir;
+ struct mcp2517fd_tx_spi_message_queue *queue = cpriv->fifos.tx_queue;
+
+ debugfs_create_u32("netif_queue_state", 0444, dir,
+ &cpriv->fifos.tx_queue->state);
+
+ /* and for each queue stage the states */
+ debugfs_create_x32("fifos_idle", 0444, dir,
+ &queue->idle);
+ debugfs_create_x32("fifos_in_fill_fifo_transfer", 0444, dir,
+ &queue->in_fill_fifo_transfer);
+ debugfs_create_x32("fifos_in_trigger_fifo_transfer", 0444, dir,
+ &queue->in_trigger_fifo_transfer);
+ debugfs_create_x32("fifos_in_can_transfer", 0444, dir,
+ &queue->in_can_transfer);
+ debugfs_create_x32("fifos_transferred", 0444, dir,
+ &queue->transferred);
+}
+#else
+static void mcp25xxfd_can_tx_queue_debugfs(struct net_device *net)
+{
+}
+#endif
+
+static
+void mcp25xxfd_can_tx_message_init(struct net_device *net,
+ struct mcp2517fd_tx_spi_message *msg,
+ int fifo)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct mcp25xxfd_priv *priv = cpriv->priv;
+ const u32 trigger = CAN_FIFOCON_TXREQ | CAN_FIFOCON_UINC;
+ const int first_byte = mcp25xxfd_first_byte(trigger);
+ u32 addr;
+
+ /* and initialize the structure */
+ msg->net = net;
+ msg->fifo = fifo;
+
+ /* init fill_fifo */
+ spi_message_init(&msg->fill_fifo.msg);
+ msg->fill_fifo.msg.complete =
+ mcp25xxfd_can_tx_spi_message_fill_fifo_complete;
+ msg->fill_fifo.msg.context = msg;
+
+ msg->fill_fifo.xfer.speed_hz = priv->spi_use_speed_hz;
+ msg->fill_fifo.xfer.tx_buf = msg->fill_fifo.data.cmd;
+ msg->fill_fifo.xfer.len = sizeof(msg->fill_fifo.data.cmd) +
+ sizeof(msg->fill_fifo.data.header);
+ spi_message_add_tail(&msg->fill_fifo.xfer, &msg->fill_fifo.msg);
+
+ addr = MCP25XXFD_SRAM_ADDR(cpriv->fifos.fifo_reg[fifo].offset);
+ mcp25xxfd_calc_cmd_addr(INSTRUCTION_WRITE,
+ addr, msg->fill_fifo.data.cmd);
+
+ /* init trigger_fifo */
+ spi_message_init(&msg->trigger_fifo.msg);
+ msg->trigger_fifo.msg.complete =
+ mcp25xxfd_can_tx_spi_message_trigger_fifo_complete;
+ msg->trigger_fifo.msg.context = msg;
+
+ msg->trigger_fifo.xfer.speed_hz = priv->spi_use_speed_hz;
+ msg->trigger_fifo.xfer.tx_buf = msg->trigger_fifo.data.cmd;
+ msg->trigger_fifo.xfer.len = sizeof(msg->trigger_fifo.data.cmd) +
+ sizeof(msg->trigger_fifo.data.data);
+ spi_message_add_tail(&msg->trigger_fifo.xfer, &msg->trigger_fifo.msg);
+
+ mcp25xxfd_calc_cmd_addr(INSTRUCTION_WRITE,
+ CAN_FIFOCON(fifo) + first_byte,
+ msg->trigger_fifo.data.cmd);
+ msg->trigger_fifo.data.data = trigger >> (8 * first_byte);
+
+ /* and add to idle tx transfers */
+ mcp25xxfd_can_tx_queue_add_spi_message(&cpriv->fifos.tx_queue->idle,
+ fifo);
+}
+
+int mcp25xxfd_can_tx_queue_alloc(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ size_t size = sizeof(struct mcp2517fd_tx_spi_message_queue) +
+ cpriv->fifos.tx.count *
+ sizeof(struct mcp2517fd_tx_spi_message);
+ struct mcp2517fd_tx_spi_message *msg;
+ int i, fifo;
+
+ /* allocate the fifos as an array */
+ cpriv->fifos.tx_queue = kzalloc(size, GFP_KERNEL);
+ if (!cpriv->fifos.tx_queue)
+ return -ENOMEM;
+
+ /* initialize the tx_queue structure */
+ spin_lock_init(&cpriv->fifos.tx_queue->lock);
+ spin_lock_init(&cpriv->fifos.tx_queue->spi_lock);
+
+ /* initialize the individual spi_message structures */
+ for (i = 0, fifo = cpriv->fifos.tx.start;
+ i < cpriv->fifos.tx.count;
+ i++, fifo += cpriv->fifos.tx.increment) {
+ msg = &cpriv->fifos.tx_queue->message[i];
+ cpriv->fifos.tx_queue->fifo2message[fifo] = msg;
+ mcp25xxfd_can_tx_message_init(net, msg, fifo);
+ }
+
+ mcp25xxfd_can_tx_queue_debugfs(net);
+
+ return 0;
+}
+
+void mcp25xxfd_can_tx_queue_free(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+ /* eventually we may need to wait here
+ * for all transfers to have finished
+ */
+
+ kfree(cpriv->fifos.tx_queue);
+ cpriv->fifos.tx_queue = NULL;
+}
+
+void mcp25xxfd_can_tx_queue_manage_nolock(struct net_device *net, int state)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+
+ /* skip early */
+ if (state == cpriv->fifos.tx_queue->state)
+ return;
+
+ /* start/stop netif_queue if necessary */
+ switch (cpriv->fifos.tx_queue->state) {
+ case TX_QUEUE_STATE_RUNABLE:
+ switch (state) {
+ case TX_QUEUE_STATE_RESTART:
+ case TX_QUEUE_STATE_STARTED:
+ netif_wake_queue(net);
+ cpriv->fifos.tx_queue->state =
+ TX_QUEUE_STATE_STARTED;
+ break;
+ }
+ break;
+ case TX_QUEUE_STATE_STOPPED:
+ switch (state) {
+ case TX_QUEUE_STATE_STARTED:
+ netif_wake_queue(net);
+ cpriv->fifos.tx_queue->state = state;
+ break;
+ }
+ break;
+ case TX_QUEUE_STATE_STARTED:
+ switch (state) {
+ case TX_QUEUE_STATE_RUNABLE:
+ case TX_QUEUE_STATE_STOPPED:
+ netif_stop_queue(net);
+ cpriv->fifos.tx_queue->state = state;
+ break;
+ }
+ break;
+ default:
+ WARN(true, "Unsupported tx_queue state: %i\n",
+ cpriv->fifos.tx_queue->state);
+ break;
+ }
+}
+
+void mcp25xxfd_can_tx_queue_manage(struct net_device *net, int state)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ unsigned long flags;
+
+ spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+ mcp25xxfd_can_tx_queue_manage_nolock(net, state);
+
+ spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+}
+
+void mcp25xxfd_can_tx_queue_restart(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ unsigned long flags;
+ u32 mask;
+
+ spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+ /* only move if there is nothing pending or idle */
+ mask = cpriv->fifos.tx_queue->idle |
+ cpriv->fifos.tx_queue->in_fill_fifo_transfer |
+ cpriv->fifos.tx_queue->in_trigger_fifo_transfer |
+ cpriv->fifos.tx_queue->in_can_transfer;
+ if (mask)
+ goto out;
+
+ /* move all items from transferred to idle */
+ cpriv->fifos.tx_queue->idle |= cpriv->fifos.tx_queue->transferred;
+ cpriv->fifos.tx_queue->transferred = 0;
+
+ /* and enable queue */
+ mcp25xxfd_can_tx_queue_manage_nolock(net, TX_QUEUE_STATE_RESTART);
+out:
+ spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+}
+
+static int mcp25xxfd_can_int_handle_txatif_fifo(struct spi_device *spi,
+ int fifo)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ u32 val;
+ unsigned long flags;
+ int ret;
+
+ /* read fifo status */
+ ret = mcp25xxfd_cmd_read(spi, CAN_FIFOSTA(fifo), &val);
+ if (ret)
+ return ret;
+
+ /* clear the relevant interrupt flags */
+ ret = mcp25xxfd_cmd_write_mask(spi,
+ CAN_FIFOSTA(fifo),
+ 0,
+ CAN_FIFOSTA_TXABT |
+ CAN_FIFOSTA_TXLARB |
+ CAN_FIFOSTA_TXERR |
+ CAN_FIFOSTA_TXATIF);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+ /* for specific cases we probably could trigger a retransmit
+ * instead of an abort.
+ */
+
+ /* and we release it from the echo_skb buffer
+ * NOTE: this is one place where packet delivery will not
+ * be ordered, as we do not have any timing information
+ * when this occurred
+ */
+ can_get_echo_skb(net, fifo);
+
+ mcp25xxfd_can_tx_queue_move_spi_message(
+ &cpriv->fifos.tx_queue->in_can_transfer,
+ &cpriv->fifos.tx_queue->transferred,
+ fifo);
+
+ spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+
+ /* but we need to run a bit of cleanup */
+ cpriv->status.txif &= ~BIT(fifo);
+ net->stats.tx_aborted_errors++;
+
+ /* handle all the known cases accordingly - ignoring FIFO full */
+ val &= CAN_FIFOSTA_TXABT |
+ CAN_FIFOSTA_TXLARB |
+ CAN_FIFOSTA_TXERR;
+ switch (val) {
+ case CAN_FIFOSTA_TXERR:
+ /* this indicates a possible bus error */
+ break;
+ default:
+ dev_warn_ratelimited(&spi->dev,
+ "Unknown TX-Fifo abort condition: %08x - stopping tx-queue\n",
+ val);
+ break;
+ }
+
+ return 0;
+}
+
+int mcp25xxfd_can_int_handle_txatif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ int i, fifo;
+ int ret;
+
+ /* if txatif is unset, then there are no
+ * can frames that have been transmitted
+ * and need to get reingested into the network stack
+ */
+ if (!cpriv->status.txatif)
+ return 0;
+ cpriv->stats.int_txat_count++;
+
+ /* process all the fifos with that flag set */
+ for (i = 0, fifo = cpriv->fifos.tx.start;
+ i < cpriv->fifos.tx.count;
+ i++, fifo += cpriv->fifos.tx.increment) {
+ if (cpriv->status.txatif & BIT(fifo)) {
+ ret = mcp25xxfd_can_int_handle_txatif_fifo(spi,
+ fifo);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* submit the fifo back to the network stack */
+int mcp25xxfd_can_submit_tx_frame(struct spi_device *spi, int fifo)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct mcp25xxfd_obj_tx *tx = (struct mcp25xxfd_obj_tx *)
+ (cpriv->sram + cpriv->fifos.fifo_reg[fifo].offset);
+ int dlc = (tx->flags & CAN_OBJ_FLAGS_DLC_MASK) >>
+ CAN_OBJ_FLAGS_DLC_SHIFT;
+ unsigned long flags;
+
+ /* update counters */
+ net->stats.tx_packets++;
+ cpriv->fifos.tx.dlc_usage[dlc]++;
+ priv->net->stats.tx_bytes += can_dlc2len(dlc);
+ if (tx->flags & CAN_OBJ_FLAGS_FDF)
+ cpriv->stats.tx_fd_count++;
+ if (tx->flags & CAN_OBJ_FLAGS_BRS)
+ cpriv->stats.tx_brs_count++;
+
+ spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+ /* release the echo buffer */
+ can_get_echo_skb(priv->net, fifo);
+
+ /* move from in_can_transfer to transferred */
+ mcp25xxfd_can_tx_queue_move_spi_message(
+ &cpriv->fifos.tx_queue->in_can_transfer,
+ &cpriv->fifos.tx_queue->transferred,
+ fifo);
+
+ spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+
+ return 0;
+}
+
+static int mcp25xxfd_can_int_handle_tefif_fifo(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct mcp25xxfd_obj_tef *tef;
+ u32 tef_offset = cpriv->fifos.tef.index * cpriv->fifos.tef.size;
+ int fifo, ret;
+
+ /* read the next TEF entry to get the transmit timestamp and fifo */
+ tef = (struct mcp25xxfd_obj_tef *)(cpriv->sram + tef_offset);
+ ret = mcp25xxfd_cmd_read_regs(spi, MCP25XXFD_SRAM_ADDR(tef_offset),
+ &tef->id, sizeof(*tef));
+ if (ret)
+ return ret;
+
+ /* now we can schedule the fifo for echo submission */
+ fifo = (tef->flags & CAN_OBJ_FLAGS_SEQ_MASK) >>
+ CAN_OBJ_FLAGS_SEQ_SHIFT;
+ mcp25xxfd_can_queue_frame(cpriv, -fifo, tef->ts);
+
+ /* increment the tef index with wraparround */
+ cpriv->fifos.tef.index++;
+ if (cpriv->fifos.tef.index >= cpriv->fifos.tef.count)
+ cpriv->fifos.tef.index = 0;
+
+ /* finally just increment the TEF pointer */
+ return mcp25xxfd_cmd_write_mask(spi, CAN_TEFCON,
+ CAN_TEFCON_UINC,
+ CAN_TEFCON_UINC);
+}
+
+static
+int mcp25xxfd_can_int_handle_tefif_conservative(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ u32 tefsta;
+ int ret;
+
+ netdev_warn(net,
+ "Something is wrong - we got a TEF interrupt but we were not able to detect a finished fifo\n");
+
+ /* we can assume that there is at least one,
+ * so fake the first read of TEFSTA
+ */
+ tefsta = CAN_TEFSTA_TEFNEIF;
+
+ /* read the tef in an inefficient loop */
+ while (tefsta & CAN_TEFSTA_TEFNEIF) {
+ /* read one tef */
+ ret = mcp25xxfd_can_int_handle_tefif_fifo(spi);
+ if (ret)
+ return ret;
+
+ /* read the TEF status again*/
+ ret = mcp25xxfd_cmd_read_mask(spi, CAN_TEFSTA,
+ &tefsta, CAN_TEFSTA_TEFNEIF);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static
+int mcp25xxfd_can_int_handle_tefif_oportunistic(struct spi_device *spi,
+ u32 finished)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ int i, fifo, ret;
+
+ /* now iterate those */
+ for (i = 0, fifo = cpriv->fifos.tx.start;
+ i < cpriv->fifos.tx.count;
+ i++, fifo += cpriv->fifos.tx.increment) {
+ if (finished & BIT(fifo)) {
+ ret = mcp25xxfd_can_int_handle_tefif_fifo(spi);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int mcp25xxfd_can_int_handle_tefif(struct spi_device *spi)
+{
+ struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ unsigned long flags;
+ u32 finished;
+
+ if (!(cpriv->status.intf & CAN_INT_TEFIF))
+ return 0;
+ cpriv->stats.int_tef_count++;
+
+ spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+ /* compute finished fifos and clear them immediately */
+ finished = (cpriv->fifos.tx_queue->in_can_transfer ^
+ cpriv->status.txreq) &
+ cpriv->fifos.tx_queue->in_can_transfer;
+
+ cpriv->fifos.tx_queue->in_can_transfer &= ~finished;
+ cpriv->fifos.tx_queue->transferred |= finished;
+
+ spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+
+ /* in case of a strange situation run in safe mode */
+ if (!finished)
+ return mcp25xxfd_can_int_handle_tefif_conservative(spi);
+
+ /* otherwise run in oportunistic mode */
+ return mcp25xxfd_can_int_handle_tefif_oportunistic(spi, finished);
+}
+
+static
+void mcp25xxfd_can_tx_fill_fifo_common(struct net_device *net,
+ struct mcp2517fd_tx_spi_message *smsg,
+ struct mcp25xxfd_obj_tx *tx,
+ int dlc, u8 *data)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ int len = can_dlc2len(dlc);
+
+ /* update statistics */
+ cpriv->fifos.tx.dlc_usage[dlc]++;
+ cpriv->fifos.fifo_info[smsg->fifo].use_count++;
+
+ /* add fifo number as seq */
+ tx->flags |= smsg->fifo << CAN_OBJ_FLAGS_SEQ_SHIFT;
+
+ /* copy data to tx->data for future reference */
+ memcpy(tx->data, data, len);
+
+ /* transform header to controller format */
+ mcp25xxfd_convert_from_cpu(&tx->id, sizeof(*tx) / sizeof(u32));
+
+ /* copy header + data to final location - we are not aligned */
+ memcpy(smsg->fill_fifo.data.header, &tx->id, sizeof(*tx) + len);
+
+ /* convert it back to CPU format */
+ mcp25xxfd_convert_to_cpu(&tx->id, sizeof(*tx) / sizeof(u32));
+
+ /* set up size of transfer */
+ smsg->fill_fifo.xfer.len = sizeof(smsg->fill_fifo.data.cmd) +
+ sizeof(smsg->fill_fifo.data.header) + len;
+}
+
+static
+void mcp25xxfd_can_tx_fill_fifo_fd(struct net_device *net,
+ struct canfd_frame *frame,
+ struct mcp2517fd_tx_spi_message *smsg,
+ struct mcp25xxfd_obj_tx *tx)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ int dlc = can_len2dlc(frame->len);
+
+ /* update some statistics */
+ cpriv->stats.tx_fd_count++;
+
+ /* compute can id */
+ mcp25xxfd_canid_to_mcpid(frame->can_id, &tx->id, &tx->flags);
+
+ /* setup flags */
+ tx->flags |= dlc << CAN_OBJ_FLAGS_DLC_SHIFT;
+ tx->flags |= (frame->can_id & CAN_EFF_FLAG) ? CAN_OBJ_FLAGS_IDE : 0;
+ tx->flags |= (frame->can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
+ if (frame->flags & CANFD_BRS) {
+ tx->flags |= CAN_OBJ_FLAGS_BRS;
+ cpriv->stats.tx_brs_count++;
+ }
+ tx->flags |= (frame->flags & CANFD_ESI) ? CAN_OBJ_FLAGS_ESI : 0;
+ tx->flags |= CAN_OBJ_FLAGS_FDF;
+
+ /* and do common processing */
+ mcp25xxfd_can_tx_fill_fifo_common(net, smsg, tx,
+ dlc, frame->data);
+}
+
+static
+void mcp25xxfd_can_tx_fill_fifo(struct net_device *net,
+ struct can_frame *frame,
+ struct mcp2517fd_tx_spi_message *smsg,
+ struct mcp25xxfd_obj_tx *tx)
+{
+ /* set frame to valid dlc */
+ if (frame->can_dlc > 8)
+ frame->can_dlc = 8;
+
+ /* compute can id */
+ mcp25xxfd_canid_to_mcpid(frame->can_id, &tx->id, &tx->flags);
+
+ /* setup flags */
+ tx->flags |= frame->can_dlc << CAN_OBJ_FLAGS_DLC_SHIFT;
+ tx->flags |= (frame->can_id & CAN_EFF_FLAG) ? CAN_OBJ_FLAGS_IDE : 0;
+ tx->flags |= (frame->can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
+
+ /* and do common processing */
+ mcp25xxfd_can_tx_fill_fifo_common(net, smsg, tx,
+ frame->can_dlc, frame->data);
+}
+
+static struct mcp2517fd_tx_spi_message
+*mcp25xxfd_can_tx_queue_get_next_fifo(struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct mcp2517fd_tx_spi_message *smsg;
+ unsigned long flags;
+
+ /* we need to hold this lock to protect us against
+ * concurrent modifications of cpriv->fifos.tx_queue->idle
+ * in the interrupt thread
+ */
+ spin_lock_irqsave(&cpriv->fifos.tx_queue->lock, flags);
+
+ /* get the first entry from idle */
+ if (cpriv->fifos.tx.increment > 0)
+ smsg = mcp25xxfd_can_tx_queue_first_spi_message(
+ cpriv->fifos.tx_queue, &cpriv->fifos.tx_queue->idle);
+ else
+ smsg = mcp25xxfd_can_tx_queue_last_spi_message(
+ cpriv->fifos.tx_queue, &cpriv->fifos.tx_queue->idle);
+ if (!smsg)
+ goto out_busy;
+
+ /* and move the fifo to next stage */
+ mcp25xxfd_can_tx_queue_move_spi_message(
+ &cpriv->fifos.tx_queue->idle,
+ &cpriv->fifos.tx_queue->in_fill_fifo_transfer,
+ smsg->fifo);
+
+ /* if queue is empty then stop the network queue immediately */
+ if (!cpriv->fifos.tx_queue->idle)
+ mcp25xxfd_can_tx_queue_manage_nolock(net,
+ TX_QUEUE_STATE_RUNABLE);
+out_busy:
+ spin_unlock_irqrestore(&cpriv->fifos.tx_queue->lock, flags);
+
+ return smsg;
+}
+
+/* submit the can message to the can-bus */
+netdev_tx_t mcp25xxfd_can_start_xmit(struct sk_buff *skb,
+ struct net_device *net)
+{
+ struct mcp25xxfd_can_priv *cpriv = netdev_priv(net);
+ struct mcp25xxfd_priv *priv = cpriv->priv;
+ struct spi_device *spi = priv->spi;
+ struct mcp2517fd_tx_spi_message *smsg;
+ struct mcp25xxfd_obj_tx *tx;
+ int addr;
+ unsigned long flags;
+ int ret;
+
+ /* invalid skb we can ignore */
+ if (can_dropped_invalid_skb(net, skb))
+ return NETDEV_TX_OK;
+
+ /* acquire lock on spi so that we are are not risking
+ * some reordering of spi messages when we are running
+ * start_xmit in multiple threads (on multiple cores)
+ */
+ spin_lock_irqsave(&cpriv->fifos.tx_queue->spi_lock, flags);
+
+ /* get the fifo message structure to process now */
+ smsg = mcp25xxfd_can_tx_queue_get_next_fifo(net);
+ if (!smsg)
+ goto out_busy;
+
+ /* compute the fifo in sram */
+ addr = cpriv->fifos.fifo_reg[smsg->fifo].offset;
+ tx = (struct mcp25xxfd_obj_tx *)(cpriv->sram + addr);
+
+ /* fill in message from skb->data depending on can2.0 or canfd */
+ if (can_is_canfd_skb(skb))
+ mcp25xxfd_can_tx_fill_fifo_fd(net,
+ (struct canfd_frame *)skb->data,
+ smsg, tx);
+ else
+ mcp25xxfd_can_tx_fill_fifo(net,
+ (struct can_frame *)skb->data,
+ smsg, tx);
+
+ /* submit the two messages asyncronously
+ * the reason why we separate transfers into two spi_messages is:
+ * * because the spi framework (currently) does add a 10us delay
+ * between 2 spi_transfers in a single spi_message when
+ * change_cs is set - 2 consecutive spi messages show a shorter
+ * cs disable phase increasing bus utilization
+ * * this allows the interrupt handler to start spi messages earlier
+ * so reducing latencies a bit and to allow for better concurrency
+ */
+ ret = spi_async(spi, &smsg->fill_fifo.msg);
+ if (ret)
+ goto out_async_failed;
+ ret = spi_async(spi, &smsg->trigger_fifo.msg);
+ if (ret)
+ goto out_async_failed;
+
+ /* unlock the spi bus */
+ spin_unlock_irqrestore(&cpriv->fifos.tx_queue->spi_lock, flags);
+
+ /* keep it for reference until the message really got transmitted */
+ can_put_echo_skb(skb, net, smsg->fifo);
+
+ return NETDEV_TX_OK;
+out_async_failed:
+ netdev_err(net, "spi_async submission of fifo %i failed - %i\n",
+ smsg->fifo, ret);
+
+out_busy:
+ /* stop the queue */
+ mcp25xxfd_can_tx_queue_manage_nolock(net, TX_QUEUE_STATE_STOPPED);
+
+ spin_unlock_irqrestore(&cpriv->fifos.tx_queue->spi_lock, flags);
+
+ return NETDEV_TX_BUSY;
+}
--
2.11.0
next prev parent reply other threads:[~2018-12-21 9:29 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2018-12-21 9:29 [PATCH V5 0/4] Microchip mcp25xxfd can controller driver kernel
2018-12-21 9:29 ` [PATCH V5 1/4] dt-binding: can: mcp25xxfd: document device tree bindings kernel
2018-12-21 9:29 ` [PATCH V5 2/4] can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver basics kernel
2019-01-21 12:31 ` Wolfgang Grandegger
2019-01-21 18:29 ` Martin Sperl
2019-01-21 19:25 ` Wolfgang Grandegger
2018-12-21 9:29 ` [PATCH V5 3/4] can: mcp25xxfd: add gpiolib support for GPIO0/1 (aka. INT0/INT1) kernel
2018-12-21 9:29 ` kernel [this message]
2019-01-21 18:32 ` [PATCH V5 4/4] can: mcp25xxfd: Add Microchip mcp25xxfd CAN FD driver Wolfgang Grandegger
2019-01-10 8:30 ` [PATCH V5 0/4] Microchip mcp25xxfd can controller driver Wolfgang Grandegger
2019-01-10 16:37 ` kernel
2019-01-11 8:21 ` Wolfgang Grandegger
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20181221092950.14192-5-kernel@martin.sperl.org \
--to=kernel@martin.sperl.org \
--cc=Brad.Hanson@arity.com \
--cc=Thomas.Kopp@microchip.com \
--cc=Wilhelm.Leichtfried@microchip.com \
--cc=devicetree@vger.kernel.org \
--cc=enrico.scholz@sigma-chemnitz.de \
--cc=info@gerhard-bertelsmann.de \
--cc=linux-can@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=mkl@pengutronix.de \
--cc=robh+dt@kernel.org \
--cc=skp@skpang.co.uk \
--cc=teemu.keskinarkaus@crosscontrol.com \
--cc=wg@grandegger.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).