From: kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org
To: Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>,
Marc Kleine-Budde <mkl-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>,
Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>,
Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>,
linux-can-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
Subject: [PATCH 2/2] can: mcp2517fd: Add Microchip mcp2517fd CAN FD driver
Date: Fri, 24 Nov 2017 18:35:09 +0000 [thread overview]
Message-ID: <20171124183509.12810-3-kernel@martin.sperl.org> (raw)
In-Reply-To: <20171124183509.12810-1-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
From: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
This patch adds support for the Microchip mcp2517fd CAN-FD controller.
The mcp2517fd is capable of transmitting and receiving standard data
frames, extended data frames, remote frames and Can-FD frames.
The mcp2517fd interfaces with the host over SPI.
Datasheet:
* http://ww1.microchip.com/downloads/en/DeviceDoc/20005688A.pdf
Reference manual:
* http://ww1.microchip.com/downloads/en/DeviceDoc/20005678A.pdf
Signed-off-by: Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
---
drivers/net/can/spi/Kconfig | 6 +
drivers/net/can/spi/Makefile | 1 +
drivers/net/can/spi/mcp2517fd.c | 3733 +++++++++++++++++++++++++++++++++++++++
3 files changed, 3740 insertions(+)
create mode 100644 drivers/net/can/spi/mcp2517fd.c
diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig
index 8f2e0dd7b756..03c0176ccfbf 100644
--- a/drivers/net/can/spi/Kconfig
+++ b/drivers/net/can/spi/Kconfig
@@ -13,4 +13,10 @@ config CAN_MCP251X
---help---
Driver for the Microchip MCP251x SPI CAN controllers.
+config CAN_MCP2517FD
+ tristate "Microchip MCP2517FD SPI CAN controllers"
+ depends on HAS_DMA
+ ---help---
+ Driver for the Microchip MCP2517FD SPI CAN controllers.
+
endmenu
diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile
index f59fa3731073..7a4ed1998661 100644
--- a/drivers/net/can/spi/Makefile
+++ b/drivers/net/can/spi/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_CAN_HI311X) += hi311x.o
obj-$(CONFIG_CAN_MCP251X) += mcp251x.o
+obj-$(CONFIG_CAN_MCP2517FD) += mcp2517fd.o
diff --git a/drivers/net/can/spi/mcp2517fd.c b/drivers/net/can/spi/mcp2517fd.c
new file mode 100644
index 000000000000..11a3f7e7daa8
--- /dev/null
+++ b/drivers/net/can/spi/mcp2517fd.c
@@ -0,0 +1,3733 @@
+/*
+ * CAN bus driver for Microchip 2517FD CAN Controller with SPI Interface
+ *
+ * Copyright 2017 Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
+ *
+ * Based on Microchip MCP251x CAN controller driver written by
+ * David Vrabel, Copyright 2006 Arcom Control Systems Ltd.
+ *
+ */
+
+#include <linux/can/core.h>
+#include <linux/can/dev.h>
+#include <linux/can/led.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/freezer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+#include <linux/spi/spi.h>
+#include <linux/uaccess.h>
+#include <linux/regulator/consumer.h>
+
+#define DEVICE_NAME "mcp2517fd"
+
+/* device description and rational:
+ *
+ * the mcp2517fd is a CanFD controller that also supports can2.0 only
+ * modes.
+ * It is connected via spi to the host and requires at minimum a single
+ * irq line in addition to the SPI lines - it is not mentioned explicitly
+ * in the documentation but in principle SPI 3-wire should be possible.
+ *
+ * The clock connected is typically 4MHz, 20MHz or 40MHz.
+ * For the 4MHz clock the controller contains 10x PLL circuitry.
+ *
+ * The controller itself has 2KB or ECC-SRAM for data.
+ * It also has 32 FIFOs (of up to 32 CAN-frames).
+ * There are 4 Fifo types which can get configured:
+ * * TEF - Transmission Event Fifo - which consumes FIFO 0
+ * even if it is not configured
+ * * Tansmission Queue - for up to 32 Frames.
+ * this queue reorders CAN frames to get transmitted following the
+ * typical CAN dominant/recessive rules on the can bus itself.
+ * This FIFO is optional.
+ * * TX FIFO: generic TX fifos that can contain arbitrary data
+ * and which come with a configurable priority for transmission
+ * It is also possible to have the Controller automatically trigger
+ * a transfer when a Filter Rule for a RTR frame matches.
+ * Each of these fifos in principle can get configured for distinct
+ * dlc sizes (8 thru 64 bytes)
+ * * RX FIFO: generic RX fifo which is filled via filter-rules.
+ * Each of these fifos in principle can get configured for distinct
+ * dlc sizes (8 thru 64 bytes)
+ * Unfortunately there is no filter rule that would allow triggering
+ * on different frame sizes, so for all practical purposes the
+ * RX fifos have to be of the same size (unless one wants to experience
+ * lost data).
+ * When a Can Frame is transmitted fromthe TX Queue or an individual
+ * TX FIFO then a small TEF Frame can get added to the TEF FIFO queue
+ * to log the Transmission of the frame - this includes ID, Flags
+ * (including a custom identifier/index) .
+ *
+ * The controller provides an optional free running counter with a divider
+ * for timestamping of RX frames as well as for TEF entries.
+ *
+ * Driver Implementation details and rational:
+ * * The whole driver has been designed to give best performance
+ * and as little packet loss as possible with 1MHZ Can frames with DLC=0
+ * on small/slow devices like the Raspberry Pi 1
+ * * This means that some optimizations for full duplex communication
+ * have been implemented to avoid CPU introduced latencies
+ * (especially for spi_write_then_read cases) - this only applies to
+ * 4 wire SPI busses.
+ * * Due to the fact that the TXQ does reorder Can-Frames we do not make
+ * use of it to avoid unexpected behaviour (say when running a
+ * firmware upgrade via Can)
+ * * this means we use individual TX-fifos with a given priority and
+ * we have to wait until all the TX fifos have been transmitted before
+ * we can restart the networking queue to avoid reordering the frames on
+ * the Can bus itself.
+ * Still we can transmit a transmit only Duty-cycle of 66% to 90% on the
+ * Can bus (at 1MHz).
+ * The scaling factors here are:
+ * * Can bus speed - lower Speeds increase Duty-cycle
+ * * SPI Clock Rate - higher speeds increase duty-cycle
+ * * CPU speed + SPI implementation - reduces latencies between transfers
+ * * There is a module parameter that allows the modification of the
+ * number of tx_fifos, which is by default 7.
+ * * The driver offers some module parameters that allow to control the use
+ * of some optimizations (prefer reading more data than necessary instead
+ * of multiple SPI transfers - the idea here is that this way we may
+ * allow the SPI-controller to use DMA instead of programmed IO to
+ * limit latencies/number of interrupts)
+ * When we have to read multiple RX frames in CanFD mode:
+ * * we allow reading all 64 bytes of payload even if DLC <=8
+ * this mode is used in Can2.0 only mode by default and can not get
+ * disabled (SRAM Reads have to be a multiple of 4 bytes anyway)
+ * * Releasing/freeing the RX queue requires writing of 1 byte per fifo.
+ * unfortunately these 32-bit registers are not ajacent to each other,
+ * so that for 2 consecutive RX Frames instead of writing 1 byte per
+ * fifo (with protocol overhead of 2 bytes - so a total of 6 bytes in
+ * 2 transfers) we transmit 13 bytes (with a protocol overhead of 2 -
+ * so a total of 15 bytes)
+ * This optimization is only enabled by a module parameter.
+ * * we use TEF + time stamping to record the transmitted frames
+ * including their timestamp - we use this to order TX and RX frames
+ * when submitting them to the network stack.
+ * * due to the inability to "filter" based on DLC sizes we have to use
+ * a common FIFO size. This is 8 bytes for Can2.0 and 64 bytes for CanFD.
+ * * the driver tries to detect the Controller only by reading registers,
+ * but there are circumstances (e.g. after a crashed driver) where we
+ * have to "blindly" configure the clock rate to get the controller to
+ * respond correctly.
+ * * There is one situation where the controller will require a full POR
+ * (total power off) to recover from a bad Clock configuration.
+ * This happens when the wrong clock is configured in the device tree
+ * (say 4MHz are configured, while 20 or 40MHz are used)
+ * in such a situation the driver tries to enable the PLL, which will
+ * never synchronize and the controller becomes unresponsive to further
+ * spi requests until a POR.
+ */
+
+#define MCP2517FD_OST_DELAY_MS 3
+#define MCP2517FD_MIN_CLOCK_FREQUENCY 1000000
+#define MCP2517FD_MAX_CLOCK_FREQUENCY 40000000
+#define MCP2517FD_PLL_MULTIPLIER 10
+#define MCP2517FD_AUTO_PLL_MAX_CLOCK_FREQUENCY \
+ (MCP2517FD_MAX_CLOCK_FREQUENCY / MCP2517FD_PLL_MULTIPLIER)
+#define MCP2517FD_SCLK_DIVIDER 2
+
+#define MCP2517FD_OSC_POLLING_JIFFIES (HZ / 2)
+
+#define TX_ECHO_SKB_MAX 32
+
+#define INSTRUCTION_RESET 0x0000
+#define INSTRUCTION_READ 0x3000
+#define INSTRUCTION_WRITE 0x2000
+#define INSTRUCTION_READ_CRC 0xB000
+#define INSTRUCTION_WRITE_CRC 0xA000
+#define INSTRUCTION_WRITE_SAVE 0xC000
+
+#define ADDRESS_MASK 0x0fff
+
+#define MCP2517FD_SFR_BASE(x) (0xE00 + (x))
+#define MCP2517FD_OSC MCP2517FD_SFR_BASE(0x00)
+# define MCP2517FD_OSC_PLLEN BIT(0)
+# define MCP2517FD_OSC_OSCDIS BIT(2)
+# define MCP2517FD_OSC_SCLKDIV BIT(4)
+# define MCP2517FD_OSC_CLKODIV_BITS 2
+# define MCP2517FD_OSC_CLKODIV_SHIFT 5
+# define MCP2517FD_OSC_CLKODIV_MASK \
+ GENMASK(MCP2517FD_OSC_CLKODIV_SHIFT \
+ + MCP2517FD_OSC_CLKODIV_BITS - 1, \
+ MCP2517FD_OSC_CLKODIV_SHIFT)
+# define MCP2517FD_OSC_CLKODIV_10 3
+# define MCP2517FD_OSC_CLKODIV_4 2
+# define MCP2517FD_OSC_CLKODIV_2 1
+# define MCP2517FD_OSC_CLKODIV_1 0
+# define MCP2517FD_OSC_PLLRDY BIT(8)
+# define MCP2517FD_OSC_OSCRDY BIT(10)
+# define MCP2517FD_OSC_SCLKRDY BIT(12)
+#define MCP2517FD_IOCON MCP2517FD_SFR_BASE(0x04)
+# define MCP2517FD_IOCON_TRIS0 BIT(0)
+# define MCP2517FD_IOCON_TRIS1 BIT(1)
+# define MCP2517FD_IOCON_XSTBYEN BIT(6)
+# define MCP2517FD_IOCON_LAT0 BIT(8)
+# define MCP2517FD_IOCON_LAT1 BIT(9)
+# define MCP2517FD_IOCON_GPIO0 BIT(16)
+# define MCP2517FD_IOCON_GPIO1 BIT(17)
+# define MCP2517FD_IOCON_PM0 BIT(24)
+# define MCP2517FD_IOCON_PM1 BIT(25)
+# define MCP2517FD_IOCON_TXCANOD BIT(28)
+# define MCP2517FD_IOCON_SOF BIT(29)
+# define MCP2517FD_IOCON_INTOD BIT(29)
+#define MCP2517FD_CRC MCP2517FD_SFR_BASE(0x08)
+# define MCP2517FD_CRC_MASK GENMASK(15, 0)
+# define MCP2517FD_CRC_CRCERRIE BIT(16)
+# define MCP2517FD_CRC_FERRIE BIT(17)
+# define MCP2517FD_CRC_CRCERRIF BIT(24)
+# define MCP2517FD_CRC_FERRIF BIT(25)
+#define MCP2517FD_ECCCON MCP2517FD_SFR_BASE(0x0C)
+# define MCP2517FD_ECCCON_ECCEN BIT(0)
+# define MCP2517FD_ECCCON_SECIE BIT(1)
+# define MCP2517FD_ECCCON_DEDIE BIT(2)
+# define MCP2517FD_ECCCON_PARITY_BITS 6
+# define MCP2517FD_ECCCON_PARITY_SHIFT 8
+# define MCP2517FD_ECCCON_PARITY_MASK \
+ GENMASK(MCP2517FD_ECCCON_PARITY_SHIFT \
+ + MCP2517FD_ECCCON_PARITY_BITS - 1, \
+ MCP2517FD_ECCCON_PARITY_SHIFT)
+#define MCP2517FD_ECCSTAT MCP2517FD_SFR_BASE(0x10)
+# define MCP2517FD_ECCSTAT_SECIF BIT(1)
+# define MCP2517FD_ECCSTAT_DEDIF BIT(2)
+# define MCP2517FD_ECCSTAT_ERRADDR_SHIFT 8
+# define MCP2517FD_ECCSTAT_ERRADDR_MASK \
+ GENMASK(MCP2517FD_ECCSTAT_ERRADDR_SHIFT + 11, \
+ MCP2517FD_ECCSTAT_ERRADDR_SHIFT)
+
+#define CAN_SFR_BASE(x) (0x000 + (x))
+#define CAN_CON CAN_SFR_BASE(0x00)
+# define CAN_CON_DNCNT_BITS 5
+# define CAN_CON_DNCNT_SHIFT 0
+# define CAN_CON_DNCNT_MASK \
+ GENMASK(CAN_CON_DNCNT_SHIFT + CAN_CON_DNCNT_BITS - 1, \
+ CAN_CON_DNCNT_SHIFT)
+# define CAN_CON_ISOCRCEN BIT(5)
+# define CAN_CON_PXEDIS BIT(6)
+# define CAN_CON_WAKFIL BIT(8)
+# define CAN_CON_WFT_BITS 2
+# define CAN_CON_WFT_SHIFT 9
+# define CAN_CON_WFT_MASK \
+ GENMASK(CAN_CON_WFT_SHIFT + CAN_CON_WFT_BITS - 1, \
+ CAN_CON_WFT_SHIFT)
+# define CAN_CON_BUSY BIT(11)
+# define CAN_CON_BRSDIS BIT(12)
+# define CAN_CON_RTXAT BIT(16)
+# define CAN_CON_ESIGM BIT(17)
+# define CAN_CON_SERR2LOM BIT(18)
+# define CAN_CON_STEF BIT(19)
+# define CAN_CON_TXQEN BIT(20)
+# define CAN_CON_OPMODE_BITS 3
+# define CAN_CON_OPMOD_SHIFT 21
+# define CAN_CON_OPMOD_MASK \
+ GENMASK(CAN_CON_OPMOD_SHIFT + CAN_CON_OPMODE_BITS - 1, \
+ CAN_CON_OPMOD_SHIFT)
+# define CAN_CON_REQOP_BITS 3
+# define CAN_CON_REQOP_SHIFT 24
+# define CAN_CON_REQOP_MASK \
+ GENMASK(CAN_CON_REQOP_SHIFT + CAN_CON_REQOP_BITS - 1, \
+ CAN_CON_REQOP_SHIFT)
+# define CAN_CON_MODE_MIXED 0
+# define CAN_CON_MODE_SLEEP 1
+# define CAN_CON_MODE_INTERNAL_LOOPBACK 2
+# define CAN_CON_MODE_LISTENONLY 3
+# define CAN_CON_MODE_CONFIG 4
+# define CAN_CON_MODE_EXTERNAL_LOOPBACK 5
+# define CAN_CON_MODE_CAN2_0 6
+# define CAN_CON_MODE_RESTRICTED 7
+# define CAN_CON_ABAT BIT(27)
+# define CAN_CON_TXBWS_BITS 3
+# define CAN_CON_TXBWS_SHIFT 28
+# define CAN_CON_TXBWS_MASK \
+ GENMASK(CAN_CON_TXBWS_SHIFT + CAN_CON_TXBWS_BITS - 1, \
+ CAN_CON_TXBWS_SHIFT)
+# define CAN_CON_DEFAULT \
+ (CAN_CON_ISOCRCEN | \
+ CAN_CON_PXEDIS | \
+ CAN_CON_WAKFIL | \
+ (3 << CAN_CON_WFT_SHIFT) | \
+ CAN_CON_STEF | \
+ CAN_CON_TXQEN | \
+ (CAN_CON_MODE_CONFIG << CAN_CON_OPMOD_SHIFT) | \
+ (CAN_CON_MODE_CONFIG << CAN_CON_REQOP_SHIFT))
+# define CAN_CON_DEFAULT_MASK \
+ (CAN_CON_DNCNT_MASK | \
+ CAN_CON_ISOCRCEN | \
+ CAN_CON_PXEDIS | \
+ CAN_CON_WAKFIL | \
+ CAN_CON_WFT_MASK | \
+ CAN_CON_BRSDIS | \
+ CAN_CON_RTXAT | \
+ CAN_CON_ESIGM | \
+ CAN_CON_SERR2LOM | \
+ CAN_CON_STEF | \
+ CAN_CON_TXQEN | \
+ CAN_CON_OPMOD_MASK | \
+ CAN_CON_REQOP_MASK | \
+ CAN_CON_ABAT | \
+ CAN_CON_TXBWS_MASK)
+#define CAN_NBTCFG CAN_SFR_BASE(0x04)
+# define CAN_NBTCFG_SJW_BITS 7
+# define CAN_NBTCFG_SJW_SHIFT 0
+# define CAN_NBTCFG_SJW_MASK \
+ GENMASK(CAN_NBTCFG_SJW_SHIFT + CAN_NBTCFG_SJW_BITS - 1, \
+ CAN_NBTCFG_SJW_SHIFT)
+# define CAN_NBTCFG_TSEG2_BITS 7
+# define CAN_NBTCFG_TSEG2_SHIFT 8
+# define CAN_NBTCFG_TSEG2_MASK \
+ GENMASK(CAN_NBTCFG_TSEG2_SHIFT + CAN_NBTCFG_TSEG2_BITS - 1, \
+ CAN_NBTCFG_TSEG2_SHIFT)
+# define CAN_NBTCFG_TSEG1_BITS 8
+# define CAN_NBTCFG_TSEG1_SHIFT 16
+# define CAN_NBTCFG_TSEG1_MASK \
+ GENMASK(CAN_NBTCFG_TSEG1_SHIFT + CAN_NBTCFG_TSEG1_BITS - 1, \
+ CAN_NBTCFG_TSEG1_SHIFT)
+# define CAN_NBTCFG_BRP_BITS 8
+# define CAN_NBTCFG_BRP_SHIFT 24
+# define CAN_NBTCFG_BRP_MASK \
+ GENMASK(CAN_NBTCFG_BRP_SHIFT + CAN_NBTCFG_BRP_BITS - 1, \
+ CAN_NBTCFG_BRP_SHIFT)
+#define CAN_DBTCFG CAN_SFR_BASE(0x08)
+# define CAN_DBTCFG_SJW_BITS 4
+# define CAN_DBTCFG_SJW_SHIFT 0
+# define CAN_DBTCFG_SJW_MASK \
+ GENMASK(CAN_DBTCFG_SJW_SHIFT + CAN_DBTCFG_SJW_BITS - 1, \
+ CAN_DBTCFG_SJW_SHIFT)
+# define CAN_DBTCFG_TSEG2_BITS 4
+# define CAN_DBTCFG_TSEG2_SHIFT 8
+# define CAN_DBTCFG_TSEG2_MASK \
+ GENMASK(CAN_DBTCFG_TSEG2_SHIFT + CAN_DBTCFG_TSEG2_BITS - 1, \
+ CAN_DBTCFG_TSEG2_SHIFT)
+# define CAN_DBTCFG_TSEG1_BITS 5
+# define CAN_DBTCFG_TSEG1_SHIFT 16
+# define CAN_DBTCFG_TSEG1_MASK \
+ GENMASK(CAN_DBTCFG_TSEG1_SHIFT + CAN_DBTCFG_TSEG1_BITS - 1, \
+ CAN_DBTCFG_TSEG1_SHIFT)
+# define CAN_DBTCFG_BRP_BITS 8
+# define CAN_DBTCFG_BRP_SHIFT 24
+# define CAN_DBTCFG_BRP_MASK \
+ GENMASK(CAN_DBTCFG_BRP_SHIFT + CAN_DBTCFG_BRP_BITS - 1, \
+ CAN_DBTCFG_BRP_SHIFT)
+#define CAN_TDC CAN_SFR_BASE(0x0C)
+# define CAN_TDC_TDCV_BITS 5
+# define CAN_TDC_TDCV_SHIFT 0
+# define CAN_TDC_TDCV_MASK \
+ GENMASK(CAN_TDC_TDCV_SHIFT + CAN_TDC_TDCV_BITS - 1, \
+ CAN_TDC_TDCV_SHIFT)
+# define CAN_TDC_TDCO_BITS 5
+# define CAN_TDC_TDCO_SHIFT 8
+# define CAN_TDC_TDCO_MASK \
+ GENMASK(CAN_TDC_TDCO_SHIFT + CAN_TDC_TDCO_BITS - 1, \
+ CAN_TDC_TDCO_SHIFT)
+# define CAN_TDC_TDCMOD_BITS 2
+# define CAN_TDC_TDCMOD_SHIFT 16
+# define CAN_TDC_TDCMOD_MASK \
+ GENMASK(CAN_TDC_TDCMOD_SHIFT + CAN_TDC_TDCMOD_BITS - 1, \
+ CAN_TDC_TDCMOD_SHIFT)
+# define CAN_TDC_SID11EN BIT(24)
+# define CAN_TDC_EDGFLTEN BIT(25)
+#define CAN_TBC CAN_SFR_BASE(0x10)
+#define CAN_TSCON CAN_SFR_BASE(0x14)
+# define CAN_TSCON_TBCPRE_BITS 10
+# define CAN_TSCON_TBCPRE_SHIFT 0
+# define CAN_TSCON_TBCPRE_MASK \
+ GENMASK(CAN_TSCON_TBCPRE_SHIFT + CAN_TSCON_TBCPRE_BITS - 1, \
+ CAN_TSCON_TBCPRE_SHIFT)
+# define CAN_TSCON_TBCEN BIT(16)
+# define CAN_TSCON_TSEOF BIT(17)
+# define CAN_TSCON_TSRES BIT(18)
+#define CAN_VEC CAN_SFR_BASE(0x18)
+# define CAN_VEC_ICODE_BITS 7
+# define CAN_VEC_ICODE_SHIFT 0
+# define CAN_VEC_ICODE_MASK \
+ GENMASK(CAN_VEC_ICODE_SHIFT + CAN_VEC_ICODE_BITS - 1, \
+ CAN_VEC_ICODE_SHIFT)
+# define CAN_VEC_FILHIT_BITS 5
+# define CAN_VEC_FILHIT_SHIFT 8
+# define CAN_VEC_FILHIT_MASK \
+ GENMASK(CAN_VEC_FILHIT_SHIFT + CAN_VEC_FILHIT_BITS - 1, \
+ CAN_VEC_FILHIT_SHIFT)
+# define CAN_VEC_TXCODE_BITS 7
+# define CAN_VEC_TXCODE_SHIFT 16
+# define CAN_VEC_TXCODE_MASK \
+ GENMASK(CAN_VEC_TXCODE_SHIFT + CAN_VEC_TXCODE_BITS - 1, \
+ CAN_VEC_TXCODE_SHIFT)
+# define CAN_VEC_RXCODE_BITS 7
+# define CAN_VEC_RXCODE_SHIFT 24
+# define CAN_VEC_RXCODE_MASK \
+ GENMASK(CAN_VEC_RXCODE_SHIFT + CAN_VEC_RXCODE_BITS - 1, \
+ CAN_VEC_RXCODE_SHIFT)
+#define CAN_INT CAN_SFR_BASE(0x1C)
+# define CAN_INT_IF_SHIFT 0
+# define CAN_INT_TXIF BIT(0)
+# define CAN_INT_RXIF BIT(1)
+# define CAN_INT_TBCIF BIT(2)
+# define CAN_INT_MODIF BIT(3)
+# define CAN_INT_TEFIF BIT(4)
+# define CAN_INT_ECCIF BIT(8)
+# define CAN_INT_SPICRCIF BIT(9)
+# define CAN_INT_TXATIF BIT(10)
+# define CAN_INT_RXOVIF BIT(11)
+# define CAN_INT_SERRIF BIT(12)
+# define CAN_INT_CERRIF BIT(13)
+# define CAN_INT_WAKIF BIT(14)
+# define CAN_INT_IVMIF BIT(15)
+# define CAN_INT_IF_MASK \
+ (CAN_INT_TXIF | \
+ CAN_INT_RXIF | \
+ CAN_INT_TBCIF | \
+ CAN_INT_MODIF | \
+ CAN_INT_TEFIF | \
+ CAN_INT_ECCIF | \
+ CAN_INT_SPICRCIF | \
+ CAN_INT_TXATIF | \
+ CAN_INT_RXOVIF | \
+ CAN_INT_CERRIF | \
+ CAN_INT_SERRIF | \
+ CAN_INT_WAKEIF | \
+ CAN_INT_IVMIF)
+# define CAN_INT_IE_SHIFT 16
+# define CAN_INT_TXIE (CAN_INT_TXIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_RXIE (CAN_INT_RXIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_TBCIE (CAN_INT_TBCIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_MODIE (CAN_INT_MODIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_TEFIE (CAN_INT_TEFIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_ECCIE (CAN_INT_ECCIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_SPICRCIE \
+ (CAN_INT_SPICRCIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_TXATIE (CAN_INT_TXATIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_RXOVIE (CAN_INT_RXOVIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_CERRIE (CAN_INT_CERRIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_SERRIE (CAN_INT_SERRIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_WAKIE (CAN_INT_WAKIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_IVMIE (CAN_INT_IVMIF << CAN_INT_IE_SHIFT)
+# define CAN_INT_IE_MASK \
+ (CAN_INT_TXIE | \
+ CAN_INT_RXIE | \
+ CAN_INT_TBCIE | \
+ CAN_INT_MODIE | \
+ CAN_INT_TEFIE | \
+ CAN_INT_ECCIE | \
+ CAN_INT_SPICRCIE | \
+ CAN_INT_TXATIE | \
+ CAN_INT_RXOVIE | \
+ CAN_INT_CERRIE | \
+ CAN_INT_SERRIE | \
+ CAN_INT_WAKEIE | \
+ CAN_INT_IVMIE)
+#define CAN_RXIF CAN_SFR_BASE(0x20)
+#define CAN_TXIF CAN_SFR_BASE(0x24)
+#define CAN_RXOVIF CAN_SFR_BASE(0x28)
+#define CAN_TXATIF CAN_SFR_BASE(0x2C)
+#define CAN_TXREQ CAN_SFR_BASE(0x30)
+#define CAN_TREC CAN_SFR_BASE(0x34)
+# define CAN_TREC_REC_BITS 8
+# define CAN_TREC_REC_SHIFT 0
+# define CAN_TREC_REC_MASK \
+ GENMASK(CAN_TREC_REC_SHIFT + CAN_TREC_REC_BITS - 1, \
+ CAN_TREC_REC_SHIFT)
+# define CAN_TREC_TEC_BITS 8
+# define CAN_TREC_TEC_SHIFT 8
+# define CAN_TREC_TEC_MASK \
+ GENMASK(CAN_TREC_TEC_SHIFT + CAN_TREC_TEC_BITS - 1, \
+ CAN_TREC_TEC_SHIFT)
+# define CAN_TREC_EWARN BIT(16)
+# define CAN_TREC_RXWARN BIT(17)
+# define CAN_TREC_TXWARN BIT(18)
+# define CAN_TREC_RXBP BIT(19)
+# define CAN_TREC_TXBP BIT(20)
+# define CAN_TREC_TXBO BIT(21)
+#define CAN_BDIAG0 CAN_SFR_BASE(0x38)
+# define CAN_BDIAG0_NRERRCNT_BITS 8
+# define CAN_BDIAG0_NRERRCNT_SHIFT 0
+# define CAN_BDIAG0_NRERRCNT_MASK \
+ GENMASK(CAN_BDIAG0_NRERRCNT_SHIFT + CAN_BDIAG0_NRERRCNT_BITS - 1, \
+ CAN_BDIAG0_NRERRCNT_SHIFT)
+# define CAN_BDIAG0_NTERRCNT_BITS 8
+# define CAN_BDIAG0_NTERRCNT_SHIFT 8
+# define CAN_BDIAG0_NTERRCNT_MASK \
+ GENMASK(CAN_BDIAG0_NTERRCNT_SHIFT + CAN_BDIAG0_NTERRCNT_BITS - 1, \
+ CAN_BDIAG0_NTERRCNT_SHIFT)
+# define CAN_BDIAG0_DRERRCNT_BITS 8
+# define CAN_BDIAG0_DRERRCNT_SHIFT 16
+# define CAN_BDIAG0_DRERRCNT_MASK \
+ GENMASK(CAN_BDIAG0_DRERRCNT_SHIFT + CAN_BDIAG0_DRERRCNT_BITS - 1, \
+ CAN_BDIAG0_DRERRCNT_SHIFT)
+# define CAN_BDIAG0_DTERRCNT_BITS 8
+# define CAN_BDIAG0_DTERRCNT_SHIFT 24
+# define CAN_BDIAG0_DTERRCNT_MASK \
+ GENMASK(CAN_BDIAG0_DTERRCNT_SHIFT + CAN_BDIAG0_DTERRCNT_BITS - 1, \
+ CAN_BDIAG0_DTERRCNT_SHIFT)
+#define CAN_BDIAG1 CAN_SFR_BASE(0x3C)
+# define CAN_BDIAG1_EFMSGCNT_BITS 16
+# define CAN_BDIAG1_EFMSGCNT_SHIFT 0
+# define CAN_BDIAG1_EFMSGCNT_MASK \
+ GENMASK(CAN_BDIAG1_EFMSGCNT_SHIFT + CAN_BDIAG1_EFMSGCNT_BITS - 1, \
+ CAN_BDIAG1_EFMSGCNT_SHIFT)
+# define CAN_BDIAG1_NBIT0ERR BIT(16)
+# define CAN_BDIAG1_NBIT1ERR BIT(17)
+# define CAN_BDIAG1_NACKERR BIT(18)
+# define CAN_BDIAG1_NSTUFERR BIT(19)
+# define CAN_BDIAG1_NFORMERR BIT(20)
+# define CAN_BDIAG1_NCRCERR BIT(21)
+# define CAN_BDIAG1_TXBOERR BIT(23)
+# define CAN_BDIAG1_DBIT0ERR BIT(24)
+# define CAN_BDIAG1_DBIT1ERR BIT(25)
+# define CAN_BDIAG1_DFORMERR BIT(27)
+# define CAN_BDIAG1_DSTUFERR BIT(28)
+# define CAN_BDIAG1_DCRCERR BIT(29)
+# define CAN_BDIAG1_ESI BIT(30)
+# define CAN_BDIAG1_DLCMM BIT(31)
+#define CAN_TEFCON CAN_SFR_BASE(0x40)
+# define CAN_TEFCON_TEFNEIE BIT(0)
+# define CAN_TEFCON_TEFHIE BIT(1)
+# define CAN_TEFCON_TEFFIE BIT(2)
+# define CAN_TEFCON_TEFOVIE BIT(3)
+# define CAN_TEFCON_TEFTSEN BIT(5)
+# define CAN_TEFCON_UINC BIT(8)
+# define CAN_TEFCON_FRESET BIT(10)
+# define CAN_TEFCON_FSIZE_BITS 5
+# define CAN_TEFCON_FSIZE_SHIFT 24
+# define CAN_TEFCON_FSIZE_MASK \
+ GENMASK(CAN_TEFCON_FSIZE_SHIFT + CAN_TEFCON_FSIZE_BITS - 1, \
+ CAN_TEFCON_FSIZE_SHIFT)
+#define CAN_TEFSTA CAN_SFR_BASE(0x44)
+# define CAN_TEFSTA_TEFNEIF BIT(0)
+# define CAN_TEFSTA_TEFHIF BIT(1)
+# define CAN_TEFSTA_TEFFIF BIT(2)
+# define CAN_TEFSTA_TEVOVIF BIT(3)
+#define CAN_TEFUA CAN_SFR_BASE(0x48)
+#define CAN_RESERVED CAN_SFR_BASE(0x4C)
+#define CAN_TXQCON CAN_SFR_BASE(0x50)
+# define CAN_TXQCON_TXQNIE BIT(0)
+# define CAN_TXQCON_TXQEIE BIT(2)
+# define CAN_TXQCON_TXATIE BIT(4)
+# define CAN_TXQCON_TXEN BIT(7)
+# define CAN_TXQCON_UINC BIT(8)
+# define CAN_TXQCON_TXREQ BIT(9)
+# define CAN_TXQCON_FRESET BIT(10)
+# define CAN_TXQCON_TXPRI_BITS 5
+# define CAN_TXQCON_TXPRI_SHIFT 16
+# define CAN_TXQCON_TXPRI_MASK \
+ GENMASK(CAN_TXQCON_TXPRI_SHIFT + CAN_TXQCON_TXPRI_BITS - 1, \
+ CAN_TXQCON_TXPRI_SHIFT)
+# define CAN_TXQCON_TXAT_BITS 2
+# define CAN_TXQCON_TXAT_SHIFT 21
+# define CAN_TXQCON_TXAT_MASK \
+ GENMASK(CAN_TXQCON_TXAT_SHIFT + CAN_TXQCON_TXAT_BITS - 1, \
+ CAN_TXQCON_TXAT_SHIFT)
+# define CAN_TXQCON_FSIZE_BITS 5
+# define CAN_TXQCON_FSIZE_SHIFT 24
+# define CAN_TXQCON_FSIZE_MASK \
+ GENMASK(CAN_TXQCON_FSIZE_SHIFT + CAN_TXQCON_FSIZE_BITS - 1, \
+ CAN_TXQCON_FSIZE_SHIFT)
+# define CAN_TXQCON_PLSIZE_BITS 3
+# define CAN_TXQCON_PLSIZE_SHIFT 29
+# define CAN_TXQCON_PLSIZE_MASK \
+ GENMASK(CAN_TXQCON_PLSIZE_SHIFT + CAN_TXQCON_PLSIZE_BITS - 1, \
+ CAN_TXQCON_PLSIZE_SHIFT)
+# define CAN_TXQCON_PLSIZE_8 0
+# define CAN_TXQCON_PLSIZE_12 1
+# define CAN_TXQCON_PLSIZE_16 2
+# define CAN_TXQCON_PLSIZE_20 3
+# define CAN_TXQCON_PLSIZE_24 4
+# define CAN_TXQCON_PLSIZE_32 5
+# define CAN_TXQCON_PLSIZE_48 6
+# define CAN_TXQCON_PLSIZE_64 7
+
+#define CAN_TXQSTA CAN_SFR_BASE(0x54)
+# define CAN_TXQSTA_TXQNIF BIT(0)
+# define CAN_TXQSTA_TXQEIF BIT(2)
+# define CAN_TXQSTA_TXATIF BIT(4)
+# define CAN_TXQSTA_TXERR BIT(5)
+# define CAN_TXQSTA_TXLARB BIT(6)
+# define CAN_TXQSTA_TXABT BIT(7)
+# define CAN_TXQSTA_TXQCI_BITS 5
+# define CAN_TXQSTA_TXQCI_SHIFT 8
+# define CAN_TXQSTA_TXQCI_MASK \
+ GENMASK(CAN_TXQSTA_TXQCI_SHIFT + CAN_TXQSTA_TXQCI_BITS - 1, \
+ CAN_TXQSTA_TXQCI_SHIFT)
+
+#define CAN_TXQUA CAN_SFR_BASE(0x58)
+#define CAN_FIFOCON(x) CAN_SFR_BASE(0x5C + 12 * (x - 1))
+#define CAN_FIFOCON_TFNRFNIE BIT(0)
+#define CAN_FIFOCON_TFHRFHIE BIT(1)
+#define CAN_FIFOCON_TFERFFIE BIT(2)
+#define CAN_FIFOCON_RXOVIE BIT(3)
+#define CAN_FIFOCON_TXATIE BIT(4)
+#define CAN_FIFOCON_RXTSEN BIT(5)
+#define CAN_FIFOCON_RTREN BIT(6)
+#define CAN_FIFOCON_TXEN BIT(7)
+#define CAN_FIFOCON_UINC BIT(8)
+#define CAN_FIFOCON_TXREQ BIT(9)
+#define CAN_FIFOCON_FRESET BIT(10)
+# define CAN_FIFOCON_TXPRI_BITS 5
+# define CAN_FIFOCON_TXPRI_SHIFT 16
+# define CAN_FIFOCON_TXPRI_MASK \
+ GENMASK(CAN_FIFOCON_TXPRI_SHIFT + CAN_FIFOCON_TXPRI_BITS - 1, \
+ CAN_FIFOCON_TXPRI_SHIFT)
+# define CAN_FIFOCON_TXAT_BITS 2
+# define CAN_FIFOCON_TXAT_SHIFT 21
+# define CAN_FIFOCON_TXAT_MASK \
+ GENMASK(CAN_FIFOCON_TXAT_SHIFT + CAN_FIFOCON_TXAT_BITS - 1, \
+ CAN_FIFOCON_TXAT_SHIFT)
+# define CAN_FIFOCON_TXAT_ONE_SHOT 0
+# define CAN_FIFOCON_TXAT_THREE_SHOT 1
+# define CAN_FIFOCON_TXAT_UNLIMITED 2
+# define CAN_FIFOCON_FSIZE_BITS 5
+# define CAN_FIFOCON_FSIZE_SHIFT 24
+# define CAN_FIFOCON_FSIZE_MASK \
+ GENMASK(CAN_FIFOCON_FSIZE_SHIFT + CAN_FIFOCON_FSIZE_BITS - 1, \
+ CAN_FIFOCON_FSIZE_SHIFT)
+# define CAN_FIFOCON_PLSIZE_BITS 3
+# define CAN_FIFOCON_PLSIZE_SHIFT 29
+# define CAN_FIFOCON_PLSIZE_MASK \
+ GENMASK(CAN_FIFOCON_PLSIZE_SHIFT + CAN_FIFOCON_PLSIZE_BITS - 1, \
+ CAN_FIFOCON_PLSIZE_SHIFT)
+#define CAN_FIFOSTA(x) CAN_SFR_BASE(0x60 + 12 * (x - 1))
+# define CAN_FIFOSTA_TFNRFNIF BIT(0)
+# define CAN_FIFOSTA_TFHRFHIF BIT(1)
+# define CAN_FIFOSTA_TFERFFIF BIT(2)
+# define CAN_FIFOSTA_RXOVIF BIT(3)
+# define CAN_FIFOSTA_TXATIF BIT(4)
+# define CAN_FIFOSTA_RXTSEN BIT(5)
+# define CAN_FIFOSTA_RTREN BIT(6)
+# define CAN_FIFOSTA_TXEN BIT(7)
+# define CAN_FIFOSTA_FIFOCI_BITS 5
+# define CAN_FIFOSTA_FIFOCI_SHIFT 8
+# define CAN_FIFOSTA_FIFOCI_MASK \
+ GENMASK(CAN_FIFOSTA_FIFOCI_SHIFT + CAN_FIFOSTA_FIFOCI_BITS - 1, \
+ CAN_FIFOSTA_FIFOCI_SHIFT)
+#define CAN_FIFOUA(x) CAN_SFR_BASE(0x64 + 12 * (x - 1))
+#define CAN_FLTCON(x) CAN_SFR_BASE(0x1D0 + (x & 0x1c))
+# define CAN_FILCON_SHIFT(x) ((x & 3) * 8)
+# define CAN_FILCON_BITS(x) 4
+# define CAN_FILCON_MASK(x) \
+ GENMASK(CAN_FILCON_SHIFT(x) + CAN_FILCON_BITS(x) - 1, \
+ CAN_FILCON_SHIFT(x))
+# define CAN_FIFOCON_FLTEN(x) BIT(7 + CAN_FILCON_SHIFT(x))
+#define CAN_FLTOBJ(x) CAN_SFR_BASE(0x1F0 + 8 * x)
+# define CAN_FILOBJ_SID_BITS 11
+# define CAN_FILOBJ_SID_SHIFT 0
+# define CAN_FILOBJ_SID_MASK \
+ GENMASK(CAN_FILOBJ_SID_SHIFT + CAN_FILOBJ_SID_BITS - 1, \
+ CAN_FILOBJ_SID_SHIFT)
+# define CAN_FILOBJ_EID_BITS 18
+# define CAN_FILOBJ_EID_SHIFT 12
+# define CAN_FILOBJ_EID_MASK \
+ GENMASK(CAN_FILOBJ_EID_SHIFT + CAN_FILOBJ_EID_BITS - 1, \
+ CAN_FILOBJ_EID_SHIFT)
+# define CAN_FILOBJ_SID11 BIT(29)
+# define CAN_FILOBJ_EXIDE BIT(30)
+#define CAN_FLTMASK(x) CAN_SFR_BASE(0x1F4 + 8 * x)
+# define CAN_FILMASK_MSID_BITS 11
+# define CAN_FILMASK_MSID_SHIFT 0
+# define CAN_FILMASK_MSID_MASK \
+ GENMASK(CAN_FILMASK_MSID_SHIFT + CAN_FILMASK_MSID_BITS - 1, \
+ CAN_FILMASK_MSID_SHIFT)
+# define CAN_FILMASK_MEID_BITS 18
+# define CAN_FILMASK_MEID_SHIFT 12
+# define CAN_FILMASK_MEID_MASK \
+ GENMASK(CAN_FILMASK_MEID_SHIFT + CAN_FILMASK_MEID_BITS - 1, \
+ CAN_FILMASK_MEID_SHIFT)
+# define CAN_FILMASK_MSID11 BIT(29)
+# define CAN_FILMASK_MIDE BIT(30)
+
+#define CAN_OBJ_ID_SID_BITS 11
+#define CAN_OBJ_ID_SID_SHIFT 0
+#define CAN_OBJ_ID_SID_MASK \
+ GENMASK(CAN_OBJ_ID_SID_SHIFT + CAN_OBJ_ID_SID_BITS - 1, \
+ CAN_OBJ_ID_SID_SHIFT)
+#define CAN_OBJ_ID_EID_BITS 18
+#define CAN_OBJ_ID_EID_SHIFT 11
+#define CAN_OBJ_ID_EID_MASK \
+ GENMASK(CAN_OBJ_ID_EID_SHIFT + CAN_OBJ_ID_EID_BITS - 1, \
+ CAN_OBJ_ID_EID_SHIFT)
+#define CAN_OBJ_ID_SID_BIT11 BIT(29)
+
+#define CAN_OBJ_FLAGS_DLC_BITS 4
+#define CAN_OBJ_FLAGS_DLC_SHIFT 0
+#define CAN_OBJ_FLAGS_DLC_MASK \
+ GENMASK(CAN_OBJ_FLAGS_DLC_SHIFT + CAN_OBJ_FLAGS_DLC_BITS - 1, \
+ CAN_OBJ_FLAGS_DLC_SHIFT)
+#define CAN_OBJ_FLAGS_IDE BIT(4)
+#define CAN_OBJ_FLAGS_RTR BIT(5)
+#define CAN_OBJ_FLAGS_BRS BIT(6)
+#define CAN_OBJ_FLAGS_FDF BIT(7)
+#define CAN_OBJ_FLAGS_ESI BIT(8)
+#define CAN_OBJ_FLAGS_SEQ_BITS 7
+#define CAN_OBJ_FLAGS_SEQ_SHIFT 9
+#define CAN_OBJ_FLAGS_SEQ_MASK \
+ GENMASK(CAN_OBJ_FLAGS_SEQ_SHIFT + CAN_OBJ_FLAGS_SEQ_BITS - 1, \
+ CAN_OBJ_FLAGS_SEQ_SHIFT)
+#define CAN_OBJ_FLAGS_FILHIT_BITS 11
+#define CAN_OBJ_FLAGS_FILHIT_SHIFT 5
+#define CAN_OBJ_FLAGS_FILHIT_MASK \
+ GENMASK(CAN_FLAGS_FILHIT_SHIFT + CAN_FLAGS_FILHIT_BITS - 1, \
+ CAN_FLAGS_FILHIT_SHIFT)
+
+#define CAN_OBJ_FLAGS_CUSTOM_ISTEF BIT(31)
+
+#define MCP2517FD_BUFFER_TXRX_SIZE 2048
+
+static const char * const mcp2517fd_mode_names[] = {
+ [CAN_CON_MODE_MIXED] = "can2.0+canfd",
+ [CAN_CON_MODE_SLEEP] = "sleep",
+ [CAN_CON_MODE_INTERNAL_LOOPBACK] = "internal loopback",
+ [CAN_CON_MODE_LISTENONLY] = "listen only",
+ [CAN_CON_MODE_CONFIG] = "config",
+ [CAN_CON_MODE_EXTERNAL_LOOPBACK] = "external loopback",
+ [CAN_CON_MODE_CAN2_0] = "can2.0",
+ [CAN_CON_MODE_RESTRICTED] = "restricted"
+};
+
+struct mcp2517fd_obj {
+ u32 id;
+ u32 flags;
+};
+
+struct mcp2517fd_obj_tx {
+ struct mcp2517fd_obj header;
+ u32 data[];
+};
+
+static void mcp2517fd_obj_to_le(struct mcp2517fd_obj *obj)
+{
+ obj->id = cpu_to_le32(obj->id);
+ obj->flags = cpu_to_le32(obj->flags);
+}
+
+struct mcp2517fd_obj_ts {
+ u32 id;
+ u32 flags;
+ u32 ts;
+};
+
+struct mcp2517fd_obj_tef {
+ struct mcp2517fd_obj_ts header;
+};
+
+struct mcp2517fd_obj_rx {
+ struct mcp2517fd_obj_ts header;
+ u8 data[];
+};
+
+static void mcp2517fd_obj_ts_from_le(struct mcp2517fd_obj_ts *obj)
+{
+ obj->id = le32_to_cpu(obj->id);
+ obj->flags = le32_to_cpu(obj->flags);
+ obj->ts = le32_to_cpu(obj->ts);
+}
+
+#define FIFO_DATA(x) (0x400 + (x))
+#define FIFO_DATA_SIZE 0x800
+
+static const struct can_bittiming_const mcp2517fd_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 mcp2517fd_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,
+};
+
+enum mcp2517fd_model {
+ CAN_MCP2517FD = 0x2517,
+};
+
+enum mcp2517fd_gpio_mode {
+ gpio_mode_int = 0,
+ gpio_mode_standby = MCP2517FD_IOCON_XSTBYEN,
+ gpio_mode_out_low = MCP2517FD_IOCON_PM0,
+ gpio_mode_out_high = MCP2517FD_IOCON_PM0 | MCP2517FD_IOCON_LAT0,
+ gpio_mode_in = MCP2517FD_IOCON_PM0 | MCP2517FD_IOCON_TRIS0
+};
+
+struct mcp2517fd_trigger_tx_message {
+ struct spi_message msg;
+ struct spi_transfer fill_xfer;
+ struct spi_transfer trigger_xfer;
+ int fifo;
+ char fill_cmd[2];
+ char fill_obj[sizeof(struct mcp2517fd_obj_tx)];
+ char fill_data[64];
+ char trigger_cmd[2];
+ char trigger_data;
+};
+
+struct mcp2517fd_read_fifo_info {
+ struct mcp2517fd_obj_ts *rxb[32];
+ int rx_count;
+ u32 tsmin;
+ u32 tsmax;
+};
+
+struct mcp2517fd_priv {
+ struct can_priv can;
+ struct net_device *net;
+ struct spi_device *spi;
+ struct regulator *power;
+ struct regulator *transceiver;
+ struct clk *clk;
+
+ struct dentry *debugfs_dir;
+
+ /* the actual model of the mcp2517fd */
+ enum mcp2517fd_model model;
+
+ struct {
+ /* clock configuration */
+ bool clock_pll;
+ bool clock_div2;
+ int clock_odiv;
+
+ /* GPIO configuration */
+ enum mcp2517fd_gpio_mode gpio0_mode;
+ enum mcp2517fd_gpio_mode gpio1_mode;
+ bool gpio_opendrain;
+ bool txcan_opendrain;
+ bool int_opendrain;
+ } config;
+
+ /* the distinct spi_speeds to use for spi communication */
+ u32 spi_setup_speed_hz;
+ u32 spi_speed_hz;
+
+ /* fifo info */
+ struct {
+ /* define payload size and mode */
+ int payload_size;
+ u32 payload_mode;
+
+ /* TEF addresses - start, end and current */
+ u32 tef_address_start;
+ u32 tef_address_end;
+ u32 tef_address;
+
+ /* address in mcp2517fd-Fifo RAM of each fifo */
+ u32 fifo_address[32];
+
+ /* infos on tx-fifos */
+ u32 tx_fifos;
+ u32 tx_fifo_start;
+ u32 tx_fifo_mask; /* bitmask of which fifo is a tx fifo */
+ u32 tx_submitted_mask;
+ u32 tx_pending_mask;
+ u32 tx_processed_mask;
+
+ /* info on rx_fifos */
+ u32 rx_fifos;
+ u32 rx_fifo_depth;
+ u32 rx_fifo_start;
+ u32 rx_fifo_mask; /* bitmask of which fifo is a rx fifo */
+
+ /* memory image of FIFO RAM on mcp2517fd */
+ u8 fifo_data[MCP2517FD_BUFFER_TXRX_SIZE];
+
+ } fifos;
+
+ /* structure with active fifos that need to get fed to the system */
+ struct mcp2517fd_read_fifo_info queued_fifos;
+
+ /* statistics */
+ struct {
+ /* number of calls to the irq handler */
+ u64 irq_calls;
+ /* number of loops inside the irq handler */
+ u64 irq_loops;
+
+ /* interrupt handler state and statistics */
+ u32 irq_state;
+#define IRQ_STATE_NEVER_RUN 0
+#define IRQ_STATE_RUNNING 1
+#define IRQ_STATE_HANDLED 2
+ /* stats on number of rx overflows */
+ u64 rx_overflow;
+ /* statistics of FIFO usage */
+ u64 fifo_usage[32];
+ } stats;
+
+ /* the current status of the mcp2517fd */
+ 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;
+ /* ASSERT(CAN_TREC + 4 == CAN_BDIAG0) */
+ u32 bdiag0;
+ /* ASSERT(CAN_BDIAG0 + 4 == CAN_BDIAG1) */
+ u32 bdiag1;
+ } status;
+
+ /* configuration registers */
+ struct {
+ u32 osc;
+ u32 ecccon;
+ u32 con;
+ u32 iocon;
+ u32 tdc;
+ u32 tscon;
+ u32 tefcon;
+ u32 nbtcfg;
+ u32 dbtcfg;
+ } regs;
+
+ /* interrupt handler signaling */
+ int force_quit;
+ int after_suspend;
+#define AFTER_SUSPEND_UP 1
+#define AFTER_SUSPEND_DOWN 2
+#define AFTER_SUSPEND_POWER 4
+#define AFTER_SUSPEND_RESTART 8
+ int restart_tx;
+
+ /* interrupt flags during irq handling */
+ u32 int_clear_mask;
+ u32 int_clear_value;
+ u32 bdiag1_clear_mask;
+ u32 bdiag1_clear_value;
+
+ /* composit error id and dataduring irq handling */
+ u32 can_err_id;
+ u32 can_err_data[8];
+
+ /* the current mode */
+ u32 active_can_mode;
+ u32 new_state;
+
+ /* status of the tx_queue enabled/disabled */
+ u32 tx_queue_status;
+
+ /* spi-tx/rx buffers for efficient transfers
+ * used during setup and irq
+ */
+ u8 spi_tx[MCP2517FD_BUFFER_TXRX_SIZE];
+ u8 spi_rx[MCP2517FD_BUFFER_TXRX_SIZE];
+
+ /* structure for transmit fifo spi_messages */
+ struct mcp2517fd_trigger_tx_message *spi_transmit_fifos;
+};
+
+/* module parameters */
+bool use_bulk_release_fifos;
+module_param(use_bulk_release_fifos, bool, 0664);
+MODULE_PARM_DESC(use_bulk_release_fifos,
+ "Use code that favours longer spi transfers over multiple transfers");
+bool use_complete_fdfifo_read;
+module_param(use_complete_fdfifo_read, bool, 0664);
+MODULE_PARM_DESC(use_complete_fdfifo_read,
+ "Use code that favours longer spi transfers over multiple transfers for fd can");
+unsigned int tx_fifos;
+module_param(tx_fifos, uint, 0664);
+MODULE_PARM_DESC(tx_fifos,
+ "Number of tx-fifos to configure\n");
+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");
+
+/* spi sync helper */
+
+/* wrapper arround spi_sync, that sets speed_hz */
+static int mcp2517fd_sync_transfer(struct spi_device *spi,
+ struct spi_transfer *xfer,
+ unsigned int xfers,
+ int speed_hz)
+{
+ int i;
+
+ for (i = 0; i < xfers; i++)
+ xfer[i].speed_hz = speed_hz;
+
+ return spi_sync_transfer(spi, xfer, xfers);
+}
+
+/* an optimization of spi_write_then_read that merges the transfers */
+static int mcp2517fd_write_then_read(struct spi_device *spi,
+ const void *tx_buf,
+ unsigned int tx_len,
+ void *rx_buf,
+ unsigned int rx_len,
+ int speed_hz)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct spi_transfer xfer[2];
+ int ret;
+
+ memset(xfer, 0, sizeof(xfer));
+
+ /* when using a halfduplex controller or to big for buffer */
+ if ((spi->master->flags & SPI_MASTER_HALF_DUPLEX) ||
+ (tx_len + rx_len > sizeof(priv->spi_tx))) {
+ xfer[0].tx_buf = tx_buf;
+ xfer[0].len = tx_len;
+
+ xfer[1].rx_buf = rx_buf;
+ xfer[1].len = rx_len;
+
+ return mcp2517fd_sync_transfer(spi, xfer, 2, speed_hz);
+ }
+
+ /* full duplex optimization */
+ xfer[0].tx_buf = priv->spi_tx;
+ xfer[0].rx_buf = priv->spi_rx;
+ xfer[0].len = tx_len + rx_len;
+
+ /* copy and clean */
+ memcpy(priv->spi_tx, tx_buf, tx_len);
+ memset(priv->spi_tx + tx_len, 0, rx_len);
+
+ ret = mcp2517fd_sync_transfer(spi, xfer, 1, speed_hz);
+ if (ret)
+ return ret;
+
+ memcpy(rx_buf, xfer[0].rx_buf + tx_len, rx_len);
+
+ return 0;
+}
+
+/* simple spi_write wrapper with speed_hz */
+static int mcp2517fd_write(struct spi_device *spi,
+ const void *tx_buf,
+ unsigned int tx_len,
+ int speed_hz)
+{
+ struct spi_transfer xfer;
+
+ memset(&xfer, 0, sizeof(xfer));
+ xfer.tx_buf = tx_buf;
+ xfer.len = tx_len;
+
+ return mcp2517fd_sync_transfer(spi, &xfer, 1, speed_hz);
+}
+
+/* spi_sync wrapper similar to spi_write_then_read that optimizes transfers */
+static int mcp2517fd_write_then_write(struct spi_device *spi,
+ const void *tx_buf,
+ unsigned int tx_len,
+ const void *tx2_buf,
+ unsigned int tx2_len,
+ int speed_hz)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct spi_transfer xfer;
+
+ if (tx_len + tx2_len > MCP2517FD_BUFFER_TXRX_SIZE)
+ return -EINVAL;
+
+ memset(&xfer, 0, sizeof(xfer));
+ xfer.len = tx_len + tx2_len;
+ xfer.tx_buf = priv->spi_tx;
+
+ memcpy(priv->spi_tx, tx_buf, tx_len);
+ memcpy(priv->spi_tx + tx_len, tx2_buf, tx2_len);
+
+ return mcp2517fd_sync_transfer(spi, &xfer, 1, speed_hz);
+}
+
+/* mcp2517fd spi command/protocol helper */
+
+static void mcp2517fd_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 mcp2517fd_cmd_reset(struct spi_device *spi, u32 speed_hz)
+{
+ u8 cmd[2];
+
+ mcp2517fd_calc_cmd_addr(INSTRUCTION_RESET, 0, cmd);
+
+ /* write the reset command */
+ return mcp2517fd_write(spi, cmd, 2, speed_hz);
+}
+
+/* read multiple bytes, transform some registers */
+static int mcp2517fd_cmd_readn(struct spi_device *spi, u32 reg,
+ void *data, int n, u32 speed_hz)
+{
+ u8 cmd[2];
+ int ret;
+
+ mcp2517fd_calc_cmd_addr(INSTRUCTION_READ, reg, cmd);
+
+ ret = mcp2517fd_write_then_read(spi, &cmd, 2, data, n, speed_hz);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mcp2517fd_convert_to_cpu(u32 *data, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ data[i] = le32_to_cpu(data[i]);
+
+ return 0;
+}
+
+/* read a register, but we are only interrested in a few bytes */
+static int mcp2517fd_cmd_read_mask(struct spi_device *spi, u32 reg,
+ u32 *data, u32 mask, u32 speed_hz)
+{
+ int first_byte, last_byte, len_byte;
+ int ret;
+
+ /* check that at least one bit is set */
+ if (!mask)
+ return -EINVAL;
+
+ /* calculate first and last byte used */
+ first_byte = (ffs(mask) - 1) >> 3;
+ last_byte = (fls(mask) - 1) >> 3;
+ len_byte = last_byte - first_byte + 1;
+
+ /* do a partial read */
+ *data = 0;
+ ret = mcp2517fd_cmd_readn(spi, reg,
+ ((void *)data + first_byte), len_byte,
+ speed_hz);
+ if (ret)
+ return ret;
+
+ return mcp2517fd_convert_to_cpu(data, 1);
+}
+
+static int mcp2517fd_cmd_read(struct spi_device *spi, u32 reg, u32 *data,
+ u32 speed_hz)
+{
+ return mcp2517fd_cmd_read_mask(spi, reg, data, -1, speed_hz);
+}
+
+/* read a register, but we are only interrested in a few bytes */
+static int mcp2517fd_cmd_write_mask(struct spi_device *spi, u32 reg,
+ u32 data, u32 mask, u32 speed_hz)
+{
+ int first_byte, last_byte, len_byte;
+ u8 cmd[2];
+
+ /* check that at least one bit is set */
+ if (!mask)
+ return -EINVAL;
+
+ /* calculate first and last byte used */
+ first_byte = (ffs(mask) - 1) >> 3;
+ last_byte = (fls(mask) - 1) >> 3;
+ len_byte = last_byte - first_byte + 1;
+
+ /* prepare buffer */
+ mcp2517fd_calc_cmd_addr(INSTRUCTION_WRITE, reg + first_byte, cmd);
+ data = cpu_to_le32(data);
+
+ return mcp2517fd_write_then_write(spi,
+ cmd, sizeof(cmd),
+ ((void *)&data + first_byte),
+ len_byte,
+ speed_hz);
+}
+
+static int mcp2517fd_cmd_write(struct spi_device *spi, u32 reg, u32 data,
+ u32 speed_hz)
+{
+ return mcp2517fd_cmd_write_mask(spi, reg, data, -1, speed_hz);
+}
+
+static int mcp2517fd_cmd_writen(struct spi_device *spi, u32 reg,
+ void *data, int n, u32 speed_hz)
+{
+ u8 cmd[2];
+ int ret;
+
+ mcp2517fd_calc_cmd_addr(INSTRUCTION_WRITE, reg, cmd);
+
+ ret = mcp2517fd_write_then_write(spi, &cmd, 2, data, n, speed_hz);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* 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 void mcp2517fd_canid_to_mcpid(u32 can_id, u32 *id, u32 *flags)
+{
+ 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;
+ }
+
+ *flags |= (can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
+}
+
+static void mcp2517fd_mcpid_to_canid(u32 mcpid, u32 mcpflags, u32 *id)
+{
+ u32 sid = (mcpid & CAN_OBJ_ID_SID_MASK) >> CAN_OBJ_ID_SID_SHIFT;
+ u32 eid = (mcpid & CAN_OBJ_ID_EID_MASK) >> CAN_OBJ_ID_EID_SHIFT;
+
+ if (mcpflags & CAN_OBJ_FLAGS_IDE) {
+ *id = (eid << CAN_EFF_EID_SHIFT) |
+ (sid << CAN_EFF_SID_SHIFT) |
+ CAN_EFF_FLAG;
+ } else {
+ *id = sid;
+ }
+
+ *id |= (mcpflags & CAN_OBJ_FLAGS_RTR) ? CAN_RTR_FLAG : 0;
+}
+
+/* CAN transmit related*/
+
+static void mcp2517fd_mark_tx_pending(void *context)
+{
+ struct mcp2517fd_trigger_tx_message *txm = context;
+ struct spi_device *spi = txm->msg.spi;
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+
+ /* only here or in the irq handler this value is changed,
+ * so there is no race condition and it does not require locking
+ * serialization happens via spi_pump_message
+ */
+ priv->fifos.tx_pending_mask |= BIT(txm->fifo);
+}
+
+static int mcp2517fd_fill_spi_transmit_fifos(struct mcp2517fd_priv *priv)
+{
+ struct mcp2517fd_trigger_tx_message *txm;
+ int i, fifo;
+ const u32 trigger = CAN_FIFOCON_TXREQ | CAN_FIFOCON_UINC;
+ const int first_byte = (ffs(trigger) - 1) >> 3;
+
+ priv->spi_transmit_fifos = kcalloc(
+ priv->fifos.tx_fifos,
+ sizeof(*priv->spi_transmit_fifos),
+ GFP_KERNEL | GFP_DMA);
+ if (!priv->spi_transmit_fifos)
+ return -ENOMEM;
+
+ for (i = 0; i < priv->fifos.tx_fifos; i++) {
+ fifo = priv->fifos.tx_fifo_start + i;
+ txm = &priv->spi_transmit_fifos[i];
+ /* prepare the message */
+ spi_message_init(&txm->msg);
+ txm->msg.complete = mcp2517fd_mark_tx_pending;
+ txm->msg.context = txm;
+ txm->fifo = fifo;
+ /* the payload itself */
+ txm->fill_xfer.speed_hz = priv->spi_speed_hz;
+ txm->fill_xfer.tx_buf = txm->fill_cmd;
+ txm->fill_xfer.len = 2;
+ txm->fill_xfer.cs_change = true;
+ mcp2517fd_calc_cmd_addr(
+ INSTRUCTION_WRITE,
+ FIFO_DATA(priv->fifos.fifo_address[fifo]),
+ txm->fill_cmd);
+ spi_message_add_tail(&txm->fill_xfer, &txm->msg);
+ /* the trigger command */
+ txm->trigger_xfer.speed_hz = priv->spi_speed_hz;
+ txm->trigger_xfer.tx_buf = txm->trigger_cmd;
+ txm->trigger_xfer.len = 3;
+ mcp2517fd_calc_cmd_addr(INSTRUCTION_WRITE,
+ CAN_FIFOCON(fifo) + first_byte,
+ txm->trigger_cmd);
+ txm->trigger_data = trigger >> (8 * first_byte);
+ spi_message_add_tail(&txm->trigger_xfer, &txm->msg);
+ }
+
+ return 0;
+}
+
+static int mcp2517fd_transmit_message_common(
+ struct spi_device *spi, int fifo,
+ struct mcp2517fd_obj_tx *obj, int len, u8 *data)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct mcp2517fd_trigger_tx_message *txm =
+ &priv->spi_transmit_fifos[fifo - priv->fifos.tx_fifo_start];
+ int ret;
+
+ /* add fifo as seq */
+ obj->header.flags |= fifo << CAN_OBJ_FLAGS_SEQ_SHIFT;
+
+ /* transform to le32 */
+ mcp2517fd_obj_to_le(&obj->header);
+
+ /* fill in details */
+ memcpy(txm->fill_obj, obj, sizeof(struct mcp2517fd_obj_tx));
+ memset(txm->fill_data, 0, priv->fifos.payload_size);
+ memcpy(txm->fill_data, data, len);
+
+ /* transfers to FIFO RAM has to be multiple of 4 */
+ txm->fill_xfer.len =
+ 2 + sizeof(struct mcp2517fd_obj_tx) + ALIGN(len, 4);
+
+ /* and transmit asyncroniously */
+ ret = spi_async(spi, &txm->msg);
+ if (ret)
+ return NETDEV_TX_BUSY;
+
+ return NETDEV_TX_OK;
+}
+
+static int mcp2517fd_transmit_fdmessage(struct spi_device *spi, int fifo,
+ struct canfd_frame *frame)
+{
+ struct mcp2517fd_obj_tx obj;
+ int dlc = can_len2dlc(frame->len);
+ u32 flags;
+
+ frame->len = can_dlc2len(dlc);
+
+ mcp2517fd_canid_to_mcpid(frame->can_id, &obj.header.id, &flags);
+
+ flags |= dlc << CAN_OBJ_FLAGS_DLC_SHIFT;
+ flags |= (frame->can_id & CAN_EFF_FLAG) ? CAN_OBJ_FLAGS_IDE : 0;
+ flags |= (frame->can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
+ flags |= (frame->flags & CANFD_BRS) ? CAN_OBJ_FLAGS_BRS : 0;
+ flags |= (frame->flags & CANFD_ESI) ? CAN_OBJ_FLAGS_ESI : 0;
+ flags |= CAN_OBJ_FLAGS_FDF;
+
+ obj.header.flags = flags;
+
+ return mcp2517fd_transmit_message_common(
+ spi, fifo, &obj, frame->len, frame->data);
+}
+
+static int mcp2517fd_transmit_message(struct spi_device *spi, int fifo,
+ struct can_frame *frame)
+{
+ struct mcp2517fd_obj_tx obj;
+ u32 flags;
+
+ if (frame->can_dlc > 8)
+ frame->can_dlc = 8;
+
+ mcp2517fd_canid_to_mcpid(frame->can_id, &obj.header.id, &flags);
+
+ flags |= frame->can_dlc << CAN_OBJ_FLAGS_DLC_SHIFT;
+ flags |= (frame->can_id & CAN_EFF_FLAG) ? CAN_OBJ_FLAGS_IDE : 0;
+ flags |= (frame->can_id & CAN_RTR_FLAG) ? CAN_OBJ_FLAGS_RTR : 0;
+
+ obj.header.flags = flags;
+
+ return mcp2517fd_transmit_message_common(
+ spi, fifo, &obj, frame->can_dlc, frame->data);
+}
+
+static netdev_tx_t mcp2517fd_start_xmit(struct sk_buff *skb,
+ struct net_device *net)
+{
+ struct mcp2517fd_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ u32 pending_mask;
+ int fifo;
+ int ret;
+
+ if (can_dropped_invalid_skb(net, skb))
+ return NETDEV_TX_OK;
+
+ if (priv->can.state == CAN_STATE_BUS_OFF) {
+ priv->tx_queue_status = 0;
+ netif_stop_queue(priv->net);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* get effective mask */
+ pending_mask = priv->fifos.tx_pending_mask |
+ priv->fifos.tx_submitted_mask;
+
+ /* decide on fifo to assign */
+ if (pending_mask)
+ fifo = ffs(pending_mask) - 2;
+ else
+ fifo = priv->fifos.tx_fifo_start + priv->fifos.tx_fifos - 1;
+
+ /* handle error - this should not happen... */
+ if (fifo < priv->fifos.tx_fifo_start) {
+ dev_err(&spi->dev,
+ "reached tx-fifo %i, which is not valid\n",
+ fifo);
+ return NETDEV_TX_BUSY;
+ }
+
+ /* if we are the last one, then stop the queue */
+ if (fifo == priv->fifos.tx_fifo_start) {
+ priv->tx_queue_status = 0;
+ netif_stop_queue(priv->net);
+ }
+
+ /* mark as submitted */
+ priv->fifos.tx_submitted_mask |= BIT(fifo);
+ priv->stats.fifo_usage[fifo]++;
+
+ /* now process it for real */
+ if (can_is_canfd_skb(skb))
+ ret = mcp2517fd_transmit_fdmessage(
+ spi, fifo, (struct canfd_frame *)skb->data);
+ else
+ ret = mcp2517fd_transmit_message(
+ spi, fifo, (struct can_frame *)skb->data);
+
+ /* keep it for reference until the message really got transmitted */
+ if (ret == NETDEV_TX_OK)
+ can_put_echo_skb(skb, priv->net, fifo);
+
+ return ret;
+}
+
+/* CAN RX Related */
+
+static int mcp2517fd_can_transform_rx_fd(struct spi_device *spi,
+ struct mcp2517fd_obj_rx *rx)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct canfd_frame *frame;
+ struct sk_buff *skb;
+ u32 flags = rx->header.flags;
+
+ /* allocate the skb buffer */
+ skb = alloc_canfd_skb(priv->net, &frame);
+ if (!skb) {
+ dev_err(&spi->dev, "cannot allocate RX skb\n");
+ priv->net->stats.rx_dropped++;
+ return -ENOMEM;
+ }
+
+ mcp2517fd_mcpid_to_canid(rx->header.id, flags, &frame->can_id);
+ frame->flags |= (flags & CAN_OBJ_FLAGS_BRS) ? CANFD_BRS : 0;
+ frame->flags |= (flags & CAN_OBJ_FLAGS_ESI) ? CANFD_ESI : 0;
+
+ frame->len = can_dlc2len((flags & CAN_OBJ_FLAGS_DLC_MASK)
+ >> CAN_OBJ_FLAGS_DLC_SHIFT);
+
+ memcpy(frame->data, rx->data, frame->len);
+
+ priv->net->stats.rx_packets++;
+ priv->net->stats.rx_bytes += frame->len;
+
+ can_led_event(priv->net, CAN_LED_EVENT_RX);
+
+ netif_rx_ni(skb);
+
+ return 0;
+}
+
+static int mcp2517fd_can_transform_rx_normal(struct spi_device *spi,
+ struct mcp2517fd_obj_rx *rx)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct sk_buff *skb;
+ struct can_frame *frame;
+ u32 flags = rx->header.flags;
+ int len;
+
+ /* allocate the skb buffer */
+ skb = alloc_can_skb(priv->net, &frame);
+ if (!skb) {
+ dev_err(&spi->dev, "cannot allocate RX skb\n");
+ priv->net->stats.rx_dropped++;
+ return -ENOMEM;
+ }
+
+ mcp2517fd_mcpid_to_canid(rx->header.id, flags, &frame->can_id);
+
+ frame->can_dlc = (flags & CAN_OBJ_FLAGS_DLC_MASK)
+ >> CAN_OBJ_FLAGS_DLC_SHIFT;
+
+ len = can_dlc2len(frame->can_dlc);
+
+ memcpy(frame->data, rx->data, len);
+
+ priv->net->stats.rx_packets++;
+ priv->net->stats.rx_bytes += len;
+
+ can_led_event(priv->net, CAN_LED_EVENT_RX);
+
+ netif_rx_ni(skb);
+
+ return 0;
+}
+
+static int mcp2517fd_process_queued_rx(struct spi_device *spi,
+ struct mcp2517fd_obj_ts *obj)
+{
+ struct mcp2517fd_obj_rx *rx = container_of(
+ obj, struct mcp2517fd_obj_rx, header);
+
+ if (obj->flags & CAN_OBJ_FLAGS_FDF)
+ return mcp2517fd_can_transform_rx_fd(
+ spi, rx);
+ else
+ return mcp2517fd_can_transform_rx_normal(
+ spi, rx);
+}
+
+static int mcp2517fd_normal_release_fifos(struct spi_device *spi,
+ int start, int end)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int ret;
+
+ /* release each fifo in a separate transfer */
+ for (; start < end ; start++) {
+ ret = mcp2517fd_cmd_write_mask(
+ spi, CAN_FIFOCON(start),
+ CAN_FIFOCON_UINC,
+ CAN_FIFOCON_UINC,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* unfortunately the CAN_FIFOCON are not directly consecutive
+ * so the optimization of "clearing all in one spi_transfer"
+ * would produce an overhead of 11 unnecessary bytes/fifo
+ * - transferring 14 (2 cmd + 12 data) bytes
+ * instead of just 3 (2 + 1).
+ * On some slower systems this may still be beneficial,
+ * but it is not good enough for the generic case.
+ * On a Raspberry Pi CM the timings for clearing 3 fifos
+ * (at 12.5MHz SPI clock speed) are:
+ * * normal:
+ * * 3 spi transfers
+ * * 9 bytes total
+ * * 36.74us from first CS low to last CS high
+ * * individual CS: 9.14us, 5.74us and 5.16us
+ * * 77.02us from CS up of fifo transfer to last release CS up
+ * * bulk:
+ * * 1 spi transfer
+ * * 27 bytes total
+ * * 29.06us CS Low
+ * * 78.28us from CS up of fifo transfer to last release CS up
+ * this obviously varies with SPI_clock speed
+ * - the slower the clock the less efficient the optimization.
+ * similarly the faster the CPU (and bigger the code cache) the
+ * less effcient the optimization - the above case is border line.
+ */
+
+#define FIFOCON_SPACING (CAN_FIFOCON(1) - CAN_FIFOCON(0))
+#define FIFOCON_SPACINGW (FIFOCON_SPACING / sizeof(u32))
+
+static int mcp2517fd_bulk_release_fifos(struct spi_device *spi,
+ int start, int end)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int i;
+ int ret;
+
+ /* calculate start address and length */
+ int fifos = end - start;
+ int first_byte = (ffs(CAN_FIFOCON_UINC) - 1) >> 3;
+ int addr = CAN_FIFOCON(start);
+ int len = 1 + (fifos - 1) * FIFOCON_SPACING;
+
+ /* the worsted case buffer */
+ u32 buf[32 * FIFOCON_SPACINGW], base;
+
+ base = (priv->fifos.payload_mode << CAN_FIFOCON_PLSIZE_SHIFT) |
+ ((priv->fifos.rx_fifo_depth - 1) << CAN_FIFOCON_FSIZE_SHIFT) |
+ CAN_FIFOCON_RXTSEN | /* RX timestamps */
+ CAN_FIFOCON_UINC |
+ CAN_FIFOCON_TFERFFIE | /* FIFO Full */
+ CAN_FIFOCON_TFHRFHIE | /* FIFO Half Full*/
+ CAN_FIFOCON_TFNRFNIE; /* FIFO not empty */
+
+ memset(buf, 0, sizeof(buf));
+ for (i = 0; i < end - start ; i++) {
+ if (i == priv->fifos.rx_fifos - 1)
+ base |= CAN_FIFOCON_RXOVIE;
+ buf[FIFOCON_SPACINGW * i] = cpu_to_le32(base);
+ }
+
+ ret = mcp2517fd_cmd_writen(spi, addr + first_byte,
+ (u8 *)buf + first_byte,
+ len,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/* queued FIFO handling for release to system */
+
+static void mcp2517fd_clear_queued_fifos(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+
+ /* prepare rfi - mostly used for sorting */
+ priv->queued_fifos.tsmin = -1;
+ priv->queued_fifos.tsmax = 0;
+ priv->queued_fifos.rx_count = 0;
+}
+
+static void mcp2517fd_addto_queued_fifos(struct spi_device *spi,
+ struct mcp2517fd_obj_ts *obj)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct mcp2517fd_read_fifo_info *rfi = &priv->queued_fifos;
+
+ /* add pointer to queued array-list */
+ rfi->rxb[rfi->rx_count] = obj;
+ rfi->rx_count++;
+
+ /* and get tsmin/tsmax */
+ if (rfi->tsmin > obj->ts)
+ rfi->tsmin = obj->ts;
+ if (rfi->tsmax > obj->ts)
+ rfi->tsmax = obj->ts;
+}
+
+static int mcp2517fd_process_queued_tef(struct spi_device *spi,
+ struct mcp2517fd_obj_ts *obj)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct mcp2517fd_obj_tef *tef = container_of(
+ obj, struct mcp2517fd_obj_tef, header);
+ int dlc = (obj->flags & CAN_OBJ_FLAGS_DLC_MASK)
+ >> CAN_OBJ_FLAGS_DLC_SHIFT;
+ int fifo = (tef->header.flags & CAN_OBJ_FLAGS_SEQ_MASK) >>
+ CAN_OBJ_FLAGS_SEQ_SHIFT;
+
+ /* update counters */
+ priv->net->stats.tx_packets++;
+ priv->net->stats.tx_bytes += can_dlc2len(dlc);
+
+ /* release it */
+ can_get_echo_skb(priv->net, fifo);
+
+ can_led_event(priv->net, CAN_LED_EVENT_TX);
+
+ return 0;
+}
+
+static int mcp2517fd_compare_obj_ts(const void *a, const void *b)
+{
+ const struct mcp2517fd_obj_ts * const *rxa = a;
+ const struct mcp2517fd_obj_ts * const *rxb = b;
+ /* using signed here to handle rollover correctly */
+ s32 ats = (*rxa)->ts;
+ s32 bts = (*rxb)->ts;
+
+ if (ats < bts)
+ return -1;
+ if (ats > bts)
+ return 1;
+ return 0;
+}
+
+static int mcp2517fd_process_queued_fifos(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct mcp2517fd_read_fifo_info *rfi = &priv->queued_fifos;
+ int i;
+ int ret;
+
+ /* sort the fifos (rx and TEF) by receive timestamp */
+ sort(rfi->rxb, rfi->rx_count, sizeof(struct mcp2517fd_obj_ts *),
+ mcp2517fd_compare_obj_ts, NULL);
+
+ /* process the recived fifos */
+ for (i = 0; i < rfi->rx_count ; i++) {
+ if (rfi->rxb[i]->flags & CAN_OBJ_FLAGS_CUSTOM_ISTEF) {
+ ret = mcp2517fd_process_queued_tef(
+ spi, rfi->rxb[i]);
+ } else {
+ ret = mcp2517fd_process_queued_rx(
+ spi, rfi->rxb[i]);
+ }
+ if (ret)
+ return ret;
+ }
+
+ /* clear queued fifos */
+ mcp2517fd_clear_queued_fifos(spi);
+
+ return 0;
+}
+
+static int mcp2517fd_transform_rx(struct spi_device *spi,
+ struct mcp2517fd_obj_rx *rx)
+{
+ int dlc;
+
+ /* transform the data to system byte order */
+ mcp2517fd_obj_ts_from_le(&rx->header);
+
+ /* add the object to the list */
+ mcp2517fd_addto_queued_fifos(spi, &rx->header);
+
+ /* calc length and return it */
+ dlc = (rx->header.flags & CAN_OBJ_FLAGS_DLC_MASK)
+ >> CAN_OBJ_FLAGS_DLC_SHIFT;
+ return can_dlc2len(dlc);
+}
+
+/* read_fifo implementations
+ *
+ * read_fifos is a simple implementation, that:
+ * * loops all fifos
+ * * read header + some data-bytes (8)
+ * * read rest of data-bytes bytes
+ * * release fifo
+ * for 3 can frames dlc<=8 to read here we have:
+ * * 6 spi transfers
+ * * 75 bytes (= 3 * (2 + 12 + 8) bytes + 3 * 3 bytes)
+ * for 3 canfd frames dlc>8 to read here we have:
+ * * 9 spi transfers
+ * * 81 (= 3 * (2 + 12 + 8 + 2) bytes + 3 * 3 bytes) + 3 * extra payload
+ * this only transfers the required size of bytes on the spi bus.
+ *
+ * bulk_read_fifos is an optimization that is most practical for
+ * Can2.0 busses, but may also be practical for CanFD busses that
+ * have a high average payload data size.
+ *
+ * It will read all of the fifo data in a single spi_transfer:
+ * * read all fifos in one go (as long as these are ajacent to each other)
+ * * loop all fifos
+ * * release fifo
+ * for 3 can2.0 frames to read here we have:
+ * * 4 spi transfers
+ * * 71 bytes (= 2 + 3 * (12 + 8) bytes + 3 * 3 bytes)
+ * for 3 canfd frames to read here we have:
+ * * 4 spi transfers
+ * * 230 bytes (= 2 + 3 * (12 + 64) bytes)
+ * obviously this reads way too many bytes for framesizes <=32 bytes,
+ * but it avoids the overhead on the CPU side and may even trigger
+ * DMA transfers due to the high byte count, which release CPU cycles.
+ *
+ * This optimization will also be efficient for cases where a high
+ * percentage of canFD frames has a dlc-size > 8.
+ * This mode is used for Can2.0 configured busses.
+ *
+ * For now this option can get forced for CanFD via a module parameter.
+ * In the future there may be some heuristics that could trigger a usage
+ * of this mode as well in some circumstances.
+ *
+ * Note: there is a second optimization for release fifo as well,
+ * but it is not as efficient as this optimization for the
+ * non-CanFD case - see mcp2517fd_bulk_release_fifos
+ */
+
+static int mcp2517fd_read_fifos(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int fifo_header_size = sizeof(struct mcp2517fd_obj_rx);
+ int fifo_min_payload_size = 8;
+ int fifo_min_size = fifo_header_size + fifo_min_payload_size;
+ int fifo_max_payload_size =
+ ((priv->can.ctrlmode & CAN_CTRLMODE_FD) ? 64 : 8);
+ u32 mask = priv->status.rxif;
+ struct mcp2517fd_obj_rx *rx;
+ int i, len;
+ int ret;
+
+ /* read all the "open" segments in big chunks */
+ for (i = priv->fifos.rx_fifo_start;
+ i < priv->fifos.rx_fifo_start + priv->fifos.rx_fifos;
+ i++) {
+ if (!(mask & BIT(i)))
+ continue;
+ /* the fifo to fill */
+ rx = (struct mcp2517fd_obj_rx *)
+ (priv->fifos.fifo_data + priv->fifos.fifo_address[i]);
+ /* read the minimal payload */
+ ret = mcp2517fd_cmd_readn(
+ spi, FIFO_DATA(priv->fifos.fifo_address[i]),
+ rx,
+ fifo_min_size,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ /* process fifo stats and get length */
+ len = min_t(int, mcp2517fd_transform_rx(spi, rx),
+ fifo_max_payload_size);
+
+ /* read extra payload if needed */
+ if (len > fifo_min_payload_size) {
+ ret = mcp2517fd_cmd_readn(
+ spi,
+ FIFO_DATA(priv->fifos.fifo_address[i] +
+ fifo_min_size),
+ &rx->data[fifo_min_payload_size],
+ len - fifo_min_payload_size,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ }
+ /* release fifo */
+ ret = mcp2517fd_normal_release_fifos(spi, i, i + 1);
+ if (ret)
+ return ret;
+ /* increment fifo_usage */
+ priv->stats.fifo_usage[i]++;
+ }
+
+ return 0;
+}
+
+static int mcp2517fd_bulk_read_fifos(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int fifo_header_size = sizeof(struct mcp2517fd_obj_rx);
+ int fifo_max_payload_size = priv->fifos.payload_size;
+ int fifo_max_size = fifo_header_size + fifo_max_payload_size;
+ u32 mask = priv->status.rxif;
+ u32 rx_fifo_end = priv->fifos.rx_fifo_start +
+ priv->fifos.rx_fifos;
+ struct mcp2517fd_obj_rx *rx;
+ int i, j;
+ int ret;
+
+ /* read all the "open" segments in big chunks */
+ for (i = priv->fifos.rx_fifo_start; i < rx_fifo_end; i++) {
+ if (mask & BIT(i)) {
+ /* find the last set bit in sequence */
+ for (j = i;
+ (j < rx_fifo_end) && (mask & BIT(j));
+ j++) {
+ /* clear the mask */
+ mask &= ~BIT(j);
+ }
+
+ /* now we got start and end, so read the range */
+ ret = mcp2517fd_cmd_readn(
+ spi,
+ FIFO_DATA(priv->fifos.fifo_address[i]),
+ priv->fifos.fifo_data +
+ priv->fifos.fifo_address[i],
+ (j - i) * fifo_max_size,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ /* clear all the fifos in range */
+ if (use_bulk_release_fifos)
+ ret = mcp2517fd_bulk_release_fifos(spi,
+ i, j);
+ else
+ ret = mcp2517fd_normal_release_fifos(spi,
+ i, j);
+ if (ret)
+ return ret;
+
+ /* preprocess data */
+ for (; i < j ; i++) {
+ /* store the fifo to process */
+ rx = (struct mcp2517fd_obj_rx *)(
+ priv->fifos.fifo_data +
+ priv->fifos.fifo_address[i]);
+ /* process fifo stats */
+ mcp2517fd_transform_rx(spi, rx);
+ /* increment usage */
+ priv->stats.fifo_usage[i]++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int mcp2517fd_can_ist_handle_rxif(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ u32 mask = priv->status.rxif;
+ int ret;
+
+ if (!mask)
+ return 0;
+
+ /* read all the fifos - for non-fd case use bulk read optimization */
+ if (((priv->can.ctrlmode & CAN_CTRLMODE_FD) == 0) ||
+ use_complete_fdfifo_read)
+ ret = mcp2517fd_bulk_read_fifos(spi);
+ else
+ ret = mcp2517fd_read_fifos(spi);
+
+ return 0;
+}
+
+static int mcp2517fd_can_ist_handle_tefif(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct mcp2517fd_obj_tef *tef;
+ u32 pending = priv->fifos.tx_pending_mask &
+ (~priv->fifos.tx_processed_mask);
+ int i, count, fifo;
+ int ret;
+
+ /* calculate the number of fifos that have been processed */
+ count = hweight_long(pending);
+ count -= hweight_long(priv->status.txreq);
+ if (count <= 0) {
+ dev_err(&spi->dev,
+ "handle_tefif: unexpected count = %i\n",
+ count);
+ return -EINVAL;
+ }
+
+ /* now clear TEF for each */
+ /* TODO: optimize for BULK reads, as we (hopefully) know COUNT */
+ for (i = 0; i < count; i++) {
+ /* calc address in address space */
+ tef = (struct mcp2517fd_obj_tef *)(priv->fifos.fifo_data +
+ priv->fifos.tef_address);
+ /* read all the object data */
+ ret = mcp2517fd_cmd_readn(spi,
+ FIFO_DATA(priv->fifos.tef_address),
+ tef, sizeof(*tef),
+ priv->spi_speed_hz);
+
+ /* increment the counter to read next */
+ ret = mcp2517fd_cmd_write_mask(spi,
+ CAN_TEFCON,
+ CAN_TEFCON_UINC,
+ CAN_TEFCON_UINC,
+ priv->spi_speed_hz);
+
+ /* transform the data to system byte order */
+ mcp2517fd_obj_ts_from_le(&tef->header);
+
+ fifo = (tef->header.flags & CAN_OBJ_FLAGS_SEQ_MASK) >>
+ CAN_OBJ_FLAGS_SEQ_SHIFT;
+
+ /* submit to queue */
+ tef->header.flags |= CAN_OBJ_FLAGS_CUSTOM_ISTEF;
+ mcp2517fd_addto_queued_fifos(spi, &tef->header);
+
+ /* increment tef */
+ priv->fifos.tef_address += sizeof(*tef);
+ if (priv->fifos.tef_address > priv->fifos.tef_address_end)
+ priv->fifos.tef_address = priv->fifos.tef_address_start;
+
+ /* and set mask */
+ priv->fifos.tx_processed_mask |= BIT(fifo);
+
+ if (fifo == priv->fifos.tx_fifo_start)
+ priv->tx_queue_status = 2;
+ }
+
+ return 0;
+}
+
+static void mcp2517fd_error_skb(struct net_device *net)
+{
+ struct mcp2517fd_priv *priv = netdev_priv(net);
+ struct sk_buff *skb;
+ struct can_frame *frame;
+
+ skb = alloc_can_err_skb(net, &frame);
+ if (skb) {
+ frame->can_id = priv->can_err_id;
+ memcpy(frame->data, priv->can_err_data, 8);
+ netif_rx_ni(skb);
+ } else {
+ netdev_err(net, "cannot allocate error skb\n");
+ }
+}
+
+static int mcp2517fd_can_ist_handle_rxovif(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ u32 mask = priv->status.rxovif;
+ int i;
+ int ret;
+
+ /* clear all fifos that have an overflow bit set */
+ for (i = 0; i < 32; i++) {
+ if (mask & BIT(i)) {
+ ret = mcp2517fd_cmd_write_mask(spi,
+ CAN_FIFOSTA(i),
+ 0,
+ CAN_FIFOSTA_RXOVIF,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ /* update statistics */
+ priv->net->stats.rx_over_errors++;
+ priv->net->stats.rx_errors++;
+ priv->stats.rx_overflow++;
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_RX_OVERFLOW;
+ }
+ }
+
+ return 0;
+}
+
+static int mcp2517fd_can_ist_handle_modif(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int mode;
+ int ret;
+
+ /* mask interrupt for clearing */
+ priv->int_clear_mask |= CAN_INT_MODIF;
+
+ /* read the mode bit */
+ ret = mcp2517fd_cmd_read_mask(spi,
+ CAN_CON,
+ &priv->regs.con,
+ CAN_CON_OPMOD_MASK,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ mode = (priv->regs.con & CAN_CON_OPMOD_MASK) >>
+ CAN_CON_OPMOD_SHIFT;
+
+ /* this mostly happens on initialization */
+ if (mode == priv->active_can_mode) {
+ dev_err(&spi->dev,
+ "Controller switched to already active mode: %s(%u)\n",
+ mcp2517fd_mode_names[mode], mode);
+ return 0;
+ }
+
+ /* these we need to handle correctly */
+ dev_err(&spi->dev,
+ "Controller switched from mode %s(%u) to %s(%u)\n",
+ mcp2517fd_mode_names[priv->active_can_mode],
+ priv->active_can_mode,
+ mcp2517fd_mode_names[mode], mode);
+
+ /* finally assign the mode as currently active */
+ priv->active_can_mode = mode;
+ return 0;
+}
+
+static int mcp2517fd_can_ist_handle_cerrif(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+
+ /* in principle we could also delay reading bdiag registers
+ * until we get here - it would add some extra delay in the
+ * error case, but be slightly faster in the "normal" case.
+ * slightly faster would be saving 8 bytes of spi transfer.
+ */
+
+ dev_err_ratelimited(&spi->dev, "CAN Bus error\n");
+ priv->can_err_id |= CAN_ERR_BUSERROR;
+ priv->int_clear_mask |= CAN_INT_CERRIF;
+
+ if (priv->status.bdiag1 &
+ (CAN_BDIAG1_DBIT0ERR | CAN_BDIAG1_NBIT0ERR)) {
+ priv->can_err_id |= CAN_ERR_BUSERROR;
+ priv->can_err_data[2] |= CAN_ERR_PROT_BIT0;
+ priv->bdiag1_clear_mask |= CAN_BDIAG1_DBIT0ERR |
+ CAN_BDIAG1_NBIT0ERR;
+ }
+ if (priv->status.bdiag1 &
+ (CAN_BDIAG1_DBIT1ERR | CAN_BDIAG1_NBIT1ERR)) {
+ priv->can_err_id |= CAN_ERR_BUSERROR;
+ priv->can_err_data[2] |= CAN_ERR_PROT_BIT1;
+ priv->bdiag1_clear_mask |= CAN_BDIAG1_DBIT1ERR |
+ CAN_BDIAG1_NBIT1ERR;
+ }
+ if (priv->status.bdiag1 &
+ (CAN_BDIAG1_DSTUFERR | CAN_BDIAG1_NSTUFERR)) {
+ priv->can_err_id |= CAN_ERR_BUSERROR;
+ priv->can_err_data[2] |= CAN_ERR_PROT_STUFF;
+ priv->bdiag1_clear_mask |= CAN_BDIAG1_DSTUFERR |
+ CAN_BDIAG1_NSTUFERR;
+ }
+ if (priv->status.bdiag1 &
+ (CAN_BDIAG1_DFORMERR | CAN_BDIAG1_NFORMERR)) {
+ priv->can_err_id |= CAN_ERR_BUSERROR;
+ priv->can_err_data[2] |= CAN_ERR_PROT_FORM;
+ priv->bdiag1_clear_mask |= CAN_BDIAG1_DFORMERR |
+ CAN_BDIAG1_NFORMERR;
+ }
+ if (priv->status.bdiag1 & CAN_BDIAG1_NACKERR) {
+ priv->can_err_id |= CAN_ERR_ACK;
+ priv->bdiag1_clear_mask |= CAN_BDIAG1_NACKERR;
+ }
+
+ return 0;
+}
+
+static int mcp2517fd_can_ist_handle_eccif(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int ret;
+ u32 val;
+ u32 addr;
+
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_UNSPEC;
+ priv->int_clear_mask |= CAN_INT_ECCIF;
+
+ /* read ECC status register */
+ ret = mcp2517fd_cmd_read(spi, MCP2517FD_ECCSTAT, &val,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ addr = (val & MCP2517FD_ECCSTAT_ERRADDR_MASK) >>
+ MCP2517FD_ECCSTAT_ERRADDR_SHIFT;
+
+ dev_err_ratelimited(&spi->dev,
+ "ECC %s bit error at %03x\n",
+ (val & MCP2517FD_ECCSTAT_DEDIF) ?
+ "double" : "single",
+ addr);
+
+ return mcp2517fd_cmd_write(spi, MCP2517FD_ECCSTAT, 0,
+ priv->spi_speed_hz);
+}
+
+static int mcp2517fd_can_ist_handle_serrif(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+
+ /* 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
+ */
+
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_UNSPEC;
+ priv->int_clear_mask |= CAN_INT_SERRIF;
+
+ /* a mode change or ecc error would indicate TX MAB Undeflow */
+ if (priv->status.intf & (CAN_INT_MODIF | CAN_INT_ECCIF)) {
+ dev_warn_ratelimited(&spi->dev, "TX MAB underflow\n");
+ priv->net->stats.tx_fifo_errors++;
+ priv->net->stats.tx_errors++;
+ } else {
+ dev_warn_ratelimited(&spi->dev, "RX MAB overflow\n");
+ priv->net->stats.rx_dropped++;
+ priv->net->stats.rx_errors++;
+ }
+ return 0;
+}
+
+static int mcp2517fd_disable_interrupts(struct spi_device *spi,
+ u32 speed_hz)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+
+ priv->status.intf = 0;
+ return mcp2517fd_cmd_write(spi, CAN_INT, 0, speed_hz);
+}
+
+static int mcp2517fd_enable_interrupts(struct spi_device *spi,
+ u32 speed_hz)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+
+ priv->status.intf = CAN_INT_TEFIE |
+ CAN_INT_RXIE |
+ CAN_INT_MODIE |
+ CAN_INT_SERRIE |
+ CAN_INT_IVMIE |
+ CAN_INT_CERRIE |
+ CAN_INT_ECCIE;
+ return mcp2517fd_cmd_write(spi, CAN_INT,
+ priv->status.intf,
+ speed_hz);
+}
+
+static int mcp2517fd_hw_wake(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ u32 waitfor = MCP2517FD_OSC_OSCRDY;
+ u32 mask = waitfor | MCP2517FD_OSC_OSCDIS;
+ unsigned long timeout;
+ int ret;
+
+ if (priv->active_can_mode != CAN_CON_MODE_SLEEP)
+ return 0;
+
+ /* write clock */
+ ret = mcp2517fd_cmd_write(
+ spi, MCP2517FD_OSC, priv->regs.osc,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* wait for synced pll/osc/sclk */
+ timeout = jiffies + MCP2517FD_OSC_POLLING_JIFFIES;
+ while (time_before_eq(jiffies, timeout)) {
+ ret = mcp2517fd_cmd_read(spi, MCP2517FD_OSC,
+ &priv->regs.osc,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ if ((priv->regs.osc & mask) == waitfor) {
+ priv->active_can_mode = CAN_CON_MODE_CONFIG;
+ return 0;
+ }
+ }
+
+ dev_err(&spi->dev,
+ "Clock did not enable within the timeout period\n");
+ return -ETIMEDOUT;
+}
+
+static void mcp2517fd_hw_sleep(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+
+ /* disable interrupts */
+ mcp2517fd_disable_interrupts(spi, priv->spi_setup_speed_hz);
+
+ priv->active_can_mode = CAN_CON_MODE_SLEEP;
+ priv->regs.con = (priv->regs.con & ~CAN_CON_REQOP_MASK) |
+ (priv->active_can_mode << CAN_CON_REQOP_SHIFT);
+ mcp2517fd_cmd_write(spi, CAN_CON,
+ priv->regs.con,
+ priv->spi_setup_speed_hz);
+}
+
+static int mcp2517fd_can_ist_handle_status(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int ret;
+
+ /* interrupt clearing info */
+ priv->int_clear_value = 0;
+ priv->int_clear_mask = 0;
+ priv->bdiag1_clear_value = 0;
+ priv->bdiag1_clear_mask = 0;
+ priv->can_err_id = 0;
+ memset(priv->can_err_data, 0, 8);
+
+ /* state changes */
+ priv->new_state = priv->can.state;
+
+ /* clear queued fifos */
+ mcp2517fd_clear_queued_fifos(spi);
+
+ /* handle the rx */
+ if (priv->status.intf & CAN_INT_RXIF) {
+ ret = mcp2517fd_can_ist_handle_rxif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* handle the tef */
+ if (priv->status.intf & CAN_INT_TEFIF) {
+ ret = mcp2517fd_can_ist_handle_tefif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* process the queued fifos */
+ ret = mcp2517fd_process_queued_fifos(spi);
+
+ /* restart the tx queue if needed */
+ if (priv->tx_queue_status == 2) {
+ /* nothing should be left pending /in flight now... */
+ priv->fifos.tx_pending_mask = 0;
+ priv->fifos.tx_submitted_mask = 0;
+ priv->fifos.tx_processed_mask = 0;
+ priv->tx_queue_status = 1;
+ /* wake queue now */
+ netif_wake_queue(priv->net);
+ }
+
+ /* handle error interrupt flags */
+ if (priv->status.rxovif) {
+ ret = mcp2517fd_can_ist_handle_rxovif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* mode change erros */
+ if (priv->status.intf & CAN_INT_MODIF) {
+ ret = mcp2517fd_can_ist_handle_modif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* sram ECC error interrupt */
+ if (priv->status.intf & CAN_INT_ECCIF) {
+ ret = mcp2517fd_can_ist_handle_eccif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* system error interrupt*/
+ if (priv->status.intf & CAN_INT_SERRIF) {
+ ret = mcp2517fd_can_ist_handle_serrif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* message format interrupt */
+ if (priv->status.intf & CAN_INT_IVMIF) {
+ priv->can_err_id |= CAN_ERR_PROT;
+ priv->can_err_data[2] |= CAN_ERR_PROT_FORM;
+ priv->int_clear_mask |= CAN_INT_IVMIF;
+ priv->net->stats.rx_frame_errors++;
+ priv->net->stats.rx_errors++;
+ }
+
+ /* handle bus errors in more detail */
+ if (priv->status.intf & CAN_INT_CERRIF) {
+ ret = mcp2517fd_can_ist_handle_cerrif(spi);
+ if (ret)
+ return ret;
+ }
+
+ /* Error counter handling */
+ if (priv->status.trec & CAN_TREC_TXWARN) {
+ priv->new_state = CAN_STATE_ERROR_WARNING;
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_TX_WARNING;
+ }
+ if (priv->status.trec & CAN_TREC_RXWARN) {
+ priv->new_state = CAN_STATE_ERROR_WARNING;
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_RX_WARNING;
+ }
+ if (priv->status.trec & CAN_TREC_TXBP) {
+ priv->new_state = CAN_STATE_ERROR_PASSIVE;
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_TX_PASSIVE;
+ }
+ if (priv->status.trec & CAN_TREC_RXBP) {
+ priv->new_state = CAN_STATE_ERROR_PASSIVE;
+ priv->can_err_id |= CAN_ERR_CRTL;
+ priv->can_err_data[1] |= CAN_ERR_CRTL_RX_PASSIVE;
+ }
+ if (priv->status.trec & CAN_TREC_TXBO) {
+ priv->new_state = CAN_STATE_BUS_OFF;
+ priv->can_err_id |= CAN_ERR_BUSOFF;
+ }
+
+ /* based on the last state state check the new state */
+ switch (priv->can.state) {
+ case CAN_STATE_ERROR_ACTIVE:
+ if (priv->new_state >= CAN_STATE_ERROR_WARNING &&
+ priv->new_state <= CAN_STATE_BUS_OFF)
+ priv->can.can_stats.error_warning++;
+ /* fallthrough */
+ case CAN_STATE_ERROR_WARNING:
+ if (priv->new_state >= CAN_STATE_ERROR_PASSIVE &&
+ priv->new_state <= CAN_STATE_BUS_OFF)
+ priv->can.can_stats.error_passive++;
+ break;
+ default:
+ break;
+ }
+ priv->can.state = priv->new_state;
+
+ /* and send error packet */
+ if (priv->can_err_id)
+ mcp2517fd_error_skb(priv->net);
+
+ /* handle BUS OFF */
+ if (priv->can.state == CAN_STATE_BUS_OFF) {
+ if (priv->can.restart_ms == 0) {
+ netif_stop_queue(priv->net);
+ priv->force_quit = 1;
+ priv->can.can_stats.bus_off++;
+ can_bus_off(priv->net);
+ mcp2517fd_hw_sleep(spi);
+ }
+ }
+
+ /* clear int flags */
+ if (priv->int_clear_mask) {
+ ret = mcp2517fd_cmd_write_mask(spi,
+ CAN_INT,
+ priv->int_clear_value,
+ priv->int_clear_mask,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ }
+ if (priv->bdiag1_clear_mask) {
+ ret = mcp2517fd_cmd_write_mask(spi,
+ CAN_BDIAG1,
+ priv->bdiag1_clear_value,
+ priv->bdiag1_clear_mask,
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static irqreturn_t mcp2517fd_can_ist(int irq, void *dev_id)
+{
+ struct mcp2517fd_priv *priv = dev_id;
+ struct spi_device *spi = priv->spi;
+ int ret;
+
+ priv->stats.irq_calls++;
+ priv->stats.irq_state = IRQ_STATE_RUNNING;
+
+ while (!priv->force_quit) {
+ /* count irq loops */
+ priv->stats.irq_loops++;
+
+ /* read interrupt status flags */
+ ret = mcp2517fd_cmd_readn(spi, CAN_INT,
+ &priv->status,
+ sizeof(priv->status),
+ priv->spi_speed_hz);
+ if (ret)
+ return ret;
+
+ /* only act if the mask is applied */
+ if ((priv->status.intf &
+ (priv->status.intf >> CAN_INT_IE_SHIFT)) == 0)
+ break;
+
+ /* handle the status */
+ ret = mcp2517fd_can_ist_handle_status(spi);
+ if (ret)
+ return ret;
+ }
+
+ priv->stats.irq_state = IRQ_STATE_HANDLED;
+
+ return IRQ_HANDLED;
+}
+
+static int mcp2517fd_get_berr_counter(const struct net_device *net,
+ struct can_berr_counter *bec)
+{
+ struct mcp2517fd_priv *priv = netdev_priv(net);
+
+ bec->txerr = (priv->status.trec & CAN_TREC_TEC_MASK) >>
+ CAN_TREC_TEC_SHIFT;
+ bec->rxerr = (priv->status.trec & CAN_TREC_REC_MASK) >>
+ CAN_TREC_REC_SHIFT;
+
+ return 0;
+}
+
+static int mcp2517fd_power_enable(struct regulator *reg, int enable)
+{
+ if (IS_ERR_OR_NULL(reg))
+ return 0;
+
+ if (enable)
+ return regulator_enable(reg);
+ else
+ return regulator_disable(reg);
+}
+
+static int mcp2517fd_do_set_mode(struct net_device *net, enum can_mode mode)
+{
+ switch (mode) {
+ case CAN_MODE_START:
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int mcp2517fd_do_set_nominal_bittiming(struct net_device *net)
+{
+ struct mcp2517fd_priv *priv = netdev_priv(net);
+ struct can_bittiming *bt = &priv->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 */
+ priv->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 mcp2517fd_cmd_write(spi, CAN_NBTCFG,
+ priv->regs.nbtcfg,
+ priv->spi_setup_speed_hz);
+}
+
+static int mcp2517fd_do_set_data_bittiming(struct net_device *net)
+{
+ struct mcp2517fd_priv *priv = netdev_priv(net);
+ struct can_bittiming *bt = &priv->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;
+
+ /* calculate nominal bit timing */
+ priv->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 mcp2517fd_cmd_write(spi, CAN_DBTCFG,
+ priv->regs.dbtcfg,
+ priv->spi_setup_speed_hz);
+}
+
+static int mcp2517fd_hw_probe(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ u32 val;
+ int ret;
+
+ /* Wait for oscillator startup timer after power up */
+ mdelay(MCP2517FD_OST_DELAY_MS);
+
+ /* send a "blind" reset, hoping we are in Config mode */
+ mcp2517fd_cmd_reset(spi, priv->spi_setup_speed_hz);
+
+ /* Wait for oscillator startup again */
+ mdelay(MCP2517FD_OST_DELAY_MS);
+
+ /* check clock register that the clock is ready or disabled */
+ ret = mcp2517fd_cmd_read(spi, MCP2517FD_OSC, &val,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* there can only be one... */
+ switch (val & (MCP2517FD_OSC_OSCRDY | MCP2517FD_OSC_OSCDIS)) {
+ case MCP2517FD_OSC_OSCRDY: /* either the clock is ready */
+ break;
+ case MCP2517FD_OSC_OSCDIS: /* or the clock is disabled */
+ /* wakeup sleeping system */
+ ret = mcp2517fd_hw_wake(spi);
+ if (ret)
+ return ret;
+ /* send a reset, hoping we are now in Config mode */
+ mcp2517fd_cmd_reset(spi, priv->spi_setup_speed_hz);
+
+ /* Wait for oscillator startup again */
+ mdelay(MCP2517FD_OST_DELAY_MS);
+ break;
+ default:
+ /* otherwise there is no valid device (or in strange state)
+ *
+ * if PLL is enabled but not ready, then there may be
+ * something "fishy"
+ * this happened during driver development
+ * (enabling pll, when when on wrong clock), so best warn
+ * about such a possibility
+ */
+ if ((val & (MCP2517FD_OSC_PLLEN | MCP2517FD_OSC_PLLRDY))
+ == MCP2517FD_OSC_PLLEN)
+ dev_err(&spi->dev,
+ "mcp2517fd may be in a strange state - a power disconnect may be required\n");
+
+ return -ENODEV;
+ }
+
+ /* check if we are in config mode already*/
+
+ /* read CON register and match */
+ ret = mcp2517fd_cmd_read(spi, CAN_CON, &val,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* apply mask and check */
+ if ((val & CAN_CON_DEFAULT_MASK) == CAN_CON_DEFAULT)
+ return 0;
+
+ /* as per datasheet a reset only works in Config Mode
+ * so as we have in principle no knowledge of the current
+ * mode that the controller is in we have no safe way
+ * to detect the device correctly
+ * hence we need to "blindly" put the controller into
+ * config mode.
+ * on the "save" side, the OSC reg has to be valid already,
+ * so there is a chance we got the controller...
+ */
+
+ /* blindly force it into config mode */
+ ret = mcp2517fd_cmd_write(spi, CAN_CON, CAN_CON_DEFAULT,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* delay some time */
+ mdelay(MCP2517FD_OST_DELAY_MS);
+
+ /* reset can controller */
+ mcp2517fd_cmd_reset(spi, priv->spi_setup_speed_hz);
+
+ /* delay some time */
+ mdelay(MCP2517FD_OST_DELAY_MS);
+
+ /* read CON register and match a final time */
+ ret = mcp2517fd_cmd_read(spi, CAN_CON, &val,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* just allow dumping the register if we ever get here */
+ dev_dbg(&spi->dev, "read CAN_CON = 0x%08x\n", val);
+
+ /* apply mask and check */
+ if ((val & CAN_CON_DEFAULT_MASK) != CAN_CON_DEFAULT)
+ return -ENODEV;
+
+ /* just in case: disable interrupts on controller */
+ return mcp2517fd_disable_interrupts(spi,
+ priv->spi_setup_speed_hz);
+}
+
+static int mcp2517fd_set_normal_mode(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int ret;
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
+ priv->active_can_mode = CAN_CON_MODE_EXTERNAL_LOOPBACK;
+ else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+ priv->active_can_mode = CAN_CON_MODE_LISTENONLY;
+ else if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
+ priv->active_can_mode = CAN_CON_MODE_MIXED;
+ else
+ priv->active_can_mode = CAN_CON_MODE_CAN2_0;
+
+ /* set mode to normal */
+ priv->regs.con = (priv->regs.con & ~CAN_CON_REQOP_MASK) |
+ (priv->active_can_mode << CAN_CON_REQOP_SHIFT);
+
+ ret = mcp2517fd_cmd_write(spi, CAN_CON,
+ priv->regs.con,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+ return 0;
+}
+
+static int mcp2517fd_setup_osc(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ int val = ((priv->config.clock_pll) ? MCP2517FD_OSC_PLLEN : 0)
+ | ((priv->config.clock_div2) ? MCP2517FD_OSC_SCLKDIV : 0);
+ int waitfor = ((priv->config.clock_pll) ? MCP2517FD_OSC_PLLRDY : 0)
+ | ((priv->config.clock_div2) ? MCP2517FD_OSC_SCLKRDY : 0)
+ | MCP2517FD_OSC_OSCRDY;
+ int ret;
+ unsigned long timeout;
+
+ /* manage clock_out divider */
+ switch (priv->config.clock_odiv) {
+ case 10:
+ val |= (MCP2517FD_OSC_CLKODIV_10)
+ << MCP2517FD_OSC_CLKODIV_SHIFT;
+ break;
+ case 4:
+ val |= (MCP2517FD_OSC_CLKODIV_4)
+ << MCP2517FD_OSC_CLKODIV_SHIFT;
+ break;
+ case 2:
+ val |= (MCP2517FD_OSC_CLKODIV_2)
+ << MCP2517FD_OSC_CLKODIV_SHIFT;
+ break;
+ case 1:
+ val |= (MCP2517FD_OSC_CLKODIV_1)
+ << MCP2517FD_OSC_CLKODIV_SHIFT;
+ break;
+ case 0:
+ /* this means implicitly SOF output */
+ val |= (MCP2517FD_OSC_CLKODIV_10)
+ << MCP2517FD_OSC_CLKODIV_SHIFT;
+ break;
+ default:
+ dev_err(&spi->dev,
+ "Unsupported output clock divider %i\n",
+ priv->config.clock_odiv);
+ return -EINVAL;
+ }
+
+ /* write clock */
+ ret = mcp2517fd_cmd_write(spi, MCP2517FD_OSC, val,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* wait for synced pll/osc/sclk */
+ timeout = jiffies + MCP2517FD_OSC_POLLING_JIFFIES;
+ while (time_before_eq(jiffies, timeout)) {
+ ret = mcp2517fd_cmd_read(spi, MCP2517FD_OSC,
+ &priv->regs.osc,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ if ((priv->regs.osc & waitfor) == waitfor)
+ return 0;
+ }
+
+ dev_err(&spi->dev,
+ "Clock did not lock within the timeout period\n");
+
+ /* we timed out */
+ return -ENODEV;
+}
+
+static int mcp2517fd_setup_fifo(struct net_device *net,
+ struct mcp2517fd_priv *priv,
+ struct spi_device *spi)
+{
+ u32 con_val = priv->regs.con & (~CAN_CON_REQOP_MASK);
+ u32 val, available_memory, tx_memory_used;
+ int ret;
+ int i, fifo;
+
+ /* clear all filter */
+ for (i = 0; i < 32; i++) {
+ ret = mcp2517fd_cmd_write(spi, CAN_FLTOBJ(i), 0,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ ret = mcp2517fd_cmd_write(spi, CAN_FLTMASK(i), 0,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ ret = mcp2517fd_cmd_write_mask(
+ spi, CAN_FLTCON(i), 0,
+ CAN_FILCON_MASK(i),
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ }
+
+ /* decide on TEF, tx and rx FIFOS */
+ switch (net->mtu) {
+ case CAN_MTU:
+ /* note: if we have INT1 connected to a GPIO
+ * then we could handle this differently and more
+ * efficiently
+ */
+
+ /* mtu is 8 */
+ priv->fifos.payload_size = 8;
+ priv->fifos.payload_mode = CAN_TXQCON_PLSIZE_8;
+
+ /* 7 tx fifos starting at fifo 1 */
+ priv->fifos.tx_fifos = 7;
+
+ /* 24 rx fifos with 1 buffers/fifo */
+ priv->fifos.rx_fifo_depth = 1;
+
+ break;
+ case CANFD_MTU:
+ /* wish there was a way to have hw filters
+ * that can separate based on length ...
+ */
+ /* MTU is 64 */
+ priv->fifos.payload_size = 64;
+ priv->fifos.payload_mode = CAN_TXQCON_PLSIZE_64;
+
+ /* 7 tx fifos */
+ priv->fifos.tx_fifos = 7;
+
+ /* 19 rx fifos with 1 buffer/fifo */
+ priv->fifos.rx_fifo_depth = 1;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* if defined as a module modify the number of tx_fifos */
+ if (tx_fifos) {
+ dev_info(&spi->dev,
+ "Using %i tx-fifos as per module parameter\n",
+ tx_fifos);
+ priv->fifos.tx_fifos = tx_fifos;
+ }
+
+ /* check range - we need 1 RX-fifo and one tef-fifo, hence 30 */
+ if (priv->fifos.tx_fifos > 30) {
+ dev_err(&spi->dev,
+ "There is an absolute maximum of 30 tx-fifos\n");
+ return -EINVAL;
+ }
+
+ tx_memory_used = priv->fifos.tx_fifos * (
+ sizeof(struct mcp2517fd_obj_tef) +
+ sizeof(struct mcp2517fd_obj_tx) +
+ priv->fifos.payload_size
+ );
+ /* check that we are not exceeding memory limits with 1 RX buffer */
+ if (tx_memory_used + (sizeof(struct mcp2517fd_obj_rx) +
+ priv->fifos.payload_size) > MCP2517FD_BUFFER_TXRX_SIZE) {
+ dev_err(&spi->dev,
+ "Configured %i tx-fifos exceeds available memory already\n",
+ priv->fifos.tx_fifos);
+ return -EINVAL;
+ }
+
+ /* calculate possible amount of RX fifos */
+ available_memory = MCP2517FD_BUFFER_TXRX_SIZE - tx_memory_used;
+
+ priv->fifos.rx_fifos = available_memory /
+ (sizeof(struct mcp2517fd_obj_rx) +
+ priv->fifos.payload_size) /
+ priv->fifos.rx_fifo_depth;
+
+ /* we only support 31 FIFOS in total (TEF = FIFO0),
+ * so modify rx accordingly
+ */
+ if (priv->fifos.tx_fifos + priv->fifos.rx_fifos > 31)
+ priv->fifos.rx_fifos = 31 - priv->fifos.tx_fifos;
+
+ /* calculate rx/tx fifo start */
+ priv->fifos.rx_fifo_start = 1;
+ priv->fifos.tx_fifo_start =
+ priv->fifos.rx_fifo_start + priv->fifos.rx_fifos;
+
+ /* set up TEF SIZE to the number of tx_fifos and IRQ */
+ priv->regs.tefcon = CAN_TEFCON_FRESET |
+ CAN_TEFCON_TEFNEIE |
+ CAN_TEFCON_TEFTSEN |
+ ((priv->fifos.tx_fifos - 1) << CAN_TEFCON_FSIZE_SHIFT),
+
+ ret = mcp2517fd_cmd_write(
+ spi, CAN_TEFCON,
+ priv->regs.tefcon,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* set up tx fifos */
+ val = CAN_FIFOCON_TXEN |
+ CAN_FIFOCON_FRESET | /* reset FIFO */
+ (priv->fifos.payload_mode << CAN_FIFOCON_PLSIZE_SHIFT) |
+ (0 << CAN_FIFOCON_FSIZE_SHIFT); /* 1 FIFO only */
+
+ if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+ val |= CAN_FIFOCON_TXAT_ONE_SHOT <<
+ CAN_FIFOCON_TXAT_SHIFT;
+ else
+ val |= CAN_FIFOCON_TXAT_UNLIMITED <<
+ CAN_FIFOCON_TXAT_SHIFT;
+
+ for (i = 0; i < priv->fifos.tx_fifos; i++) {
+ fifo = priv->fifos.tx_fifo_start + i;
+ ret = mcp2517fd_cmd_write(
+ spi, CAN_FIFOCON(fifo),
+ val | (fifo << CAN_FIFOCON_TXPRI_SHIFT),
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ priv->fifos.tx_fifo_mask |= BIT(fifo);
+ }
+
+ /* now set up RX FIFO */
+ for (i = 0; i < priv->fifos.rx_fifos; i++) {
+ fifo = priv->fifos.rx_fifo_start + i;
+ /* prepare the fifo itself */
+ ret = mcp2517fd_cmd_write(
+ spi, CAN_FIFOCON(fifo),
+ (priv->fifos.payload_mode <<
+ CAN_FIFOCON_PLSIZE_SHIFT) |
+ ((priv->fifos.rx_fifo_depth - 1) <<
+ CAN_FIFOCON_FSIZE_SHIFT) |
+ CAN_FIFOCON_RXTSEN | /* RX timestamps */
+ CAN_FIFOCON_FRESET | /* reset FIFO */
+ CAN_FIFOCON_TFERFFIE | /* FIFO Full */
+ CAN_FIFOCON_TFHRFHIE | /* FIFO Half Full*/
+ CAN_FIFOCON_TFNRFNIE | /* FIFO not empty */
+ /* on the last fifo add overflow flag */
+ ((i == priv->fifos.rx_fifos - 1) ?
+ CAN_FIFOCON_RXOVIE : 0),
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ /* prepare the rx filter config: filter i directs to fifo
+ * FLTMSK and FLTOBJ are 0 already, so they match everything
+ */
+ ret = mcp2517fd_cmd_write_mask(
+ spi, CAN_FLTCON(i),
+ CAN_FIFOCON_FLTEN(i) | (fifo << CAN_FILCON_SHIFT(i)),
+ CAN_FIFOCON_FLTEN(i) | CAN_FILCON_MASK(i),
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ priv->fifos.rx_fifo_mask |= BIT(fifo);
+ }
+
+ /* we need to move out of CONFIG mode shortly to get the addresses */
+ ret = mcp2517fd_cmd_write(
+ spi, CAN_CON, con_val |
+ (CAN_CON_MODE_INTERNAL_LOOPBACK << CAN_CON_REQOP_SHIFT),
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* for the TEF fifo */
+ ret = mcp2517fd_cmd_read(spi, CAN_TEFUA, &val,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ priv->fifos.tef_address = val;
+ priv->fifos.tef_address_start = val;
+ priv->fifos.tef_address_end = priv->fifos.tef_address_start +
+ (priv->fifos.tx_fifos) * sizeof(struct mcp2517fd_obj_tef) -
+ 1;
+
+ /* get all the relevant addresses for the transmit fifos */
+ for (i = 0; i < priv->fifos.tx_fifos; i++) {
+ fifo = priv->fifos.tx_fifo_start + i;
+ ret = mcp2517fd_cmd_read(spi, CAN_FIFOUA(fifo),
+ &val, priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ priv->fifos.fifo_address[fifo] = val;
+ }
+
+ /* and prepare the spi_messages */
+ ret = mcp2517fd_fill_spi_transmit_fifos(priv);
+ if (ret)
+ return ret;
+
+ /* get all the relevant addresses for the rx fifos */
+ for (i = 0; i < priv->fifos.rx_fifos; i++) {
+ fifo = priv->fifos.rx_fifo_start + i;
+ ret = mcp2517fd_cmd_read(spi, CAN_FIFOUA(fifo),
+ &val, priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ priv->fifos.fifo_address[fifo] = val;
+ }
+
+ /* now get back into config mode */
+ ret = mcp2517fd_cmd_write(
+ spi, CAN_CON, con_val |
+ (CAN_CON_MODE_CONFIG << CAN_CON_REQOP_SHIFT),
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int mcp2517fd_setup(struct net_device *net,
+ struct mcp2517fd_priv *priv,
+ struct spi_device *spi)
+{
+ u32 val;
+ int ret;
+
+ /* set up pll/clock if required */
+ ret = mcp2517fd_setup_osc(spi);
+ if (ret)
+ return ret;
+
+ /* set up RAM ECC (but for now without interrupts) */
+ priv->regs.ecccon = MCP2517FD_ECCCON_ECCEN;
+ ret = mcp2517fd_cmd_write(spi, MCP2517FD_ECCCON,
+ priv->regs.ecccon,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* GPIO handling - could expose this as gpios*/
+ val = 0; /* PUSHPULL INT , TXCAN PUSH/PULL, no Standby */
+
+ /* SOF/CLOCKOUT pin 3 */
+ if (priv->config.clock_odiv < 0)
+ val |= MCP2517FD_IOCON_SOF;
+ /* GPIO0 - pin 9 */
+ switch (priv->config.gpio0_mode) {
+ case gpio_mode_standby:
+ case gpio_mode_int: /* asserted low on TXIF */
+ case gpio_mode_out_low:
+ case gpio_mode_out_high:
+ case gpio_mode_in:
+ val |= priv->config.gpio0_mode;
+ break;
+ default: /* GPIO IN */
+ dev_err(&spi->dev,
+ "GPIO1 does not support mode %08x\n",
+ priv->config.gpio0_mode);
+ return -EINVAL;
+ }
+ /* GPIO1 - pin 8 */
+ switch (priv->config.gpio1_mode) {
+ case gpio_mode_standby:
+ dev_err(&spi->dev,
+ "GPIO1 does not support transceiver standby\n");
+ return -EINVAL;
+ case gpio_mode_int: /* asserted low on RXIF */
+ case gpio_mode_out_low:
+ case gpio_mode_out_high:
+ case gpio_mode_in:
+ val |= priv->config.gpio1_mode << 1;
+ break;
+ default:
+ dev_err(&spi->dev,
+ "GPIO1 does not support mode %08x\n",
+ priv->config.gpio0_mode);
+ return -EINVAL;
+ }
+ /* INT/GPIO pins as open drain */
+ if (priv->config.gpio_opendrain)
+ val |= MCP2517FD_IOCON_INTOD;
+ if (priv->config.txcan_opendrain)
+ val |= MCP2517FD_IOCON_TXCANOD; /* OpenDrain TXCAN */
+ if (priv->config.int_opendrain)
+ val |= MCP2517FD_IOCON_INTOD; /* OpenDrain INT pins */
+
+ priv->regs.iocon = val;
+ ret = mcp2517fd_cmd_write(spi, MCP2517FD_IOCON, val,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* set up Transmitter Delay compensation */
+ priv->regs.tdc = CAN_TDC_EDGFLTEN;
+ ret = mcp2517fd_cmd_write(spi, CAN_TDC, priv->regs.tdc,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* time stamp control register - 1ns resolution, but disabled */
+ ret = mcp2517fd_cmd_write(spi, CAN_TBC, 0,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+ priv->regs.tscon = CAN_TSCON_TBCEN |
+ ((priv->can.clock.freq / 1000000)
+ << CAN_TSCON_TBCPRE_SHIFT);
+ ret = mcp2517fd_cmd_write(spi, CAN_TSCON,
+ priv->regs.tscon,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ return ret;
+
+ /* setup value of con_register */
+ priv->regs.con = CAN_CON_STEF /* enable TEF */;
+ /* transmissin bandwidth sharing bits */
+ if (bw_sharing_log2bits < 12)
+ bw_sharing_log2bits = 12;
+ priv->regs.con |= bw_sharing_log2bits << CAN_CON_TXBWS_SHIFT;
+ /* non iso FD mode */
+ if (!(priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO))
+ priv->regs.con |= CAN_CON_ISOCRCEN;
+ /* one shot */
+ if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
+ priv->regs.con |= CAN_CON_RTXAT;
+
+ /* setup fifos - this also puts the system into sleep mode */
+ return mcp2517fd_setup_fifo(net, priv, spi);
+}
+
+static int mcp2517fd_open(struct net_device *net)
+{
+ struct mcp2517fd_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+ int ret;
+
+ ret = open_candev(net);
+ if (ret) {
+ dev_err(&spi->dev, "unable to set initial baudrate!\n");
+ return ret;
+ }
+
+ mcp2517fd_power_enable(priv->transceiver, 1);
+
+ priv->force_quit = 0;
+
+ priv->stats.irq_state = 0;
+ priv->stats.irq_calls = 0;
+ priv->stats.irq_loops = 0;
+
+ priv->force_quit = 0;
+ ret = request_threaded_irq(spi->irq, NULL,
+ mcp2517fd_can_ist,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ DEVICE_NAME, priv);
+ if (ret) {
+ dev_err(&spi->dev, "failed to acquire irq %d - %i\n",
+ spi->irq, ret);
+ mcp2517fd_power_enable(priv->transceiver, 0);
+ close_candev(net);
+ return ret;
+ }
+
+ /* wake from sleep if necessary */
+ ret = mcp2517fd_hw_wake(spi);
+ if (ret)
+ goto open_clean;
+
+ ret = mcp2517fd_hw_probe(spi);
+ if (ret) {
+ dev_err(&spi->dev,
+ "HW Probe failed, but was working earlier!\n");
+ goto open_clean;
+ }
+
+ ret = mcp2517fd_setup(net, priv, spi);
+ if (ret)
+ goto open_clean;
+
+ mcp2517fd_do_set_nominal_bittiming(net);
+ mcp2517fd_do_set_data_bittiming(net);
+
+ ret = mcp2517fd_set_normal_mode(spi);
+ if (ret)
+ goto open_clean;
+
+ /* only now enable the interrupt on the controller */
+ ret = mcp2517fd_enable_interrupts(spi,
+ priv->spi_setup_speed_hz);
+ if (ret)
+ goto open_clean;
+
+ can_led_event(net, CAN_LED_EVENT_OPEN);
+
+ priv->tx_queue_status = 1;
+ netif_wake_queue(net);
+
+ return 0;
+
+open_clean:
+ mcp2517fd_disable_interrupts(spi, priv->spi_setup_speed_hz);
+ free_irq(spi->irq, priv);
+ mcp2517fd_hw_sleep(spi);
+ mcp2517fd_power_enable(priv->transceiver, 0);
+ close_candev(net);
+
+ return ret;
+}
+
+static void mcp2517fd_clean(struct net_device *net)
+{
+ struct mcp2517fd_priv *priv = netdev_priv(net);
+ int i;
+
+ for (i = 0; i < priv->fifos.tx_fifos; i++) {
+ if (priv->fifos.tx_pending_mask & BIT(i)) {
+ can_free_echo_skb(priv->net, 0);
+ priv->net->stats.tx_errors++;
+ }
+ }
+
+ priv->fifos.tx_pending_mask = 0;
+}
+
+static int mcp2517fd_stop(struct net_device *net)
+{
+ struct mcp2517fd_priv *priv = netdev_priv(net);
+ struct spi_device *spi = priv->spi;
+
+ close_candev(net);
+
+ kfree(priv->spi_transmit_fifos);
+ priv->spi_transmit_fifos = NULL;
+
+ priv->force_quit = 1;
+ free_irq(spi->irq, priv);
+
+ /* Disable and clear pending interrupts */
+ mcp2517fd_disable_interrupts(spi, priv->spi_setup_speed_hz);
+
+ mcp2517fd_clean(net);
+
+ mcp2517fd_hw_sleep(spi);
+
+ mcp2517fd_power_enable(priv->transceiver, 0);
+
+ priv->can.state = CAN_STATE_STOPPED;
+
+ can_led_event(net, CAN_LED_EVENT_STOP);
+
+ return 0;
+}
+
+static const struct net_device_ops mcp2517fd_netdev_ops = {
+ .ndo_open = mcp2517fd_open,
+ .ndo_stop = mcp2517fd_stop,
+ .ndo_start_xmit = mcp2517fd_start_xmit,
+ .ndo_change_mtu = can_change_mtu,
+};
+
+static const struct of_device_id mcp2517fd_of_match[] = {
+ {
+ .compatible = "microchip,mcp2517fd",
+ .data = (void *)CAN_MCP2517FD,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mcp2517fd_of_match);
+
+static const struct spi_device_id mcp2517fd_id_table[] = {
+ {
+ .name = "mcp2517fd",
+ .driver_data = (kernel_ulong_t)CAN_MCP2517FD,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, mcp2517fd_id_table);
+
+static void mcp2517fd_debugfs_add(struct mcp2517fd_priv *priv)
+{
+#if defined(CONFIG_DEBUG_FS)
+ struct dentry *root, *fifousage, *fifoaddr, *rx, *tx, *status, *regs;
+ char name[32];
+ int i;
+
+ /* create the net device name */
+ snprintf(name, sizeof(name), DEVICE_NAME "-%s", priv->net->name);
+ priv->debugfs_dir = debugfs_create_dir(name, NULL);
+ root = priv->debugfs_dir;
+
+ rx = debugfs_create_dir("rx", root);
+ tx = debugfs_create_dir("tx", root);
+ fifousage = debugfs_create_dir("fifo_usage", root);
+ fifoaddr = debugfs_create_dir("fifo_address", root);
+ status = debugfs_create_dir("status", root);
+ regs = debugfs_create_dir("regs", root);
+
+ /* add spi speed info */
+ debugfs_create_u32("spi_setup_speed_hz", 0444, root,
+ &priv->spi_setup_speed_hz);
+ debugfs_create_u32("spi_speed_hz", 0444, root,
+ &priv->spi_speed_hz);
+
+ /* add irq state info */
+ debugfs_create_u64("irq_calls", 0444, root, &priv->stats.irq_calls);
+ debugfs_create_u64("irq_loops", 0444, root, &priv->stats.irq_loops);
+ debugfs_create_u32("irq_state", 0444, root, &priv->stats.irq_state);
+
+ /* export the status structure */
+ debugfs_create_x32("intf", 0444, status, &priv->status.intf);
+ debugfs_create_x32("rx_if", 0444, status, &priv->status.rxif);
+ debugfs_create_x32("tx_if", 0444, status, &priv->status.txif);
+ debugfs_create_x32("rx_ovif", 0444, status, &priv->status.rxovif);
+ debugfs_create_x32("tx_atif", 0444, status, &priv->status.txatif);
+ debugfs_create_x32("tx_req", 0444, status, &priv->status.txreq);
+ debugfs_create_x32("trec", 0444, status, &priv->status.trec);
+ debugfs_create_x32("bdiag0", 0444, status, &priv->status.bdiag0);
+ debugfs_create_x32("bdiag1", 0444, status, &priv->status.bdiag1);
+
+ /* some configuration registers */
+ debugfs_create_x32("con", 0444, regs, &priv->regs.con);
+ debugfs_create_x32("ecccon", 0444, regs, &priv->regs.ecccon);
+ debugfs_create_x32("osc", 0444, regs, &priv->regs.osc);
+ debugfs_create_x32("iocon", 0444, regs, &priv->regs.iocon);
+ debugfs_create_x32("tdc", 0444, regs, &priv->regs.tdc);
+ debugfs_create_x32("tscon", 0444, regs, &priv->regs.tscon);
+ debugfs_create_x32("nbtcfg", 0444, regs, &priv->regs.nbtcfg);
+ debugfs_create_x32("dbtcfg", 0444, regs, &priv->regs.dbtcfg);
+
+ /* information on fifos */
+ debugfs_create_u32("fifo_start", 0444, rx,
+ &priv->fifos.rx_fifo_start);
+ debugfs_create_u32("fifo_count", 0444, rx,
+ &priv->fifos.rx_fifos);
+ debugfs_create_x32("fifo_mask", 0444, rx,
+ &priv->fifos.rx_fifo_mask);
+ debugfs_create_u64("rx_overflow", 0444, rx,
+ &priv->stats.rx_overflow);
+
+ debugfs_create_u32("fifo_start", 0444, tx,
+ &priv->fifos.tx_fifo_start);
+ debugfs_create_u32("fifo_count", 0444, tx,
+ &priv->fifos.tx_fifos);
+ debugfs_create_x32("fifo_mask", 0444, tx,
+ &priv->fifos.tx_fifo_mask);
+ debugfs_create_x32("fifo_pending", 0444, tx,
+ &priv->fifos.tx_pending_mask);
+ debugfs_create_x32("fifo_submitted", 0444, tx,
+ &priv->fifos.tx_submitted_mask);
+ debugfs_create_x32("fifo_processed", 0444, tx,
+ &priv->fifos.tx_processed_mask);
+ debugfs_create_u32("queue_status", 0444, tx,
+ &priv->tx_queue_status);
+
+ debugfs_create_u32("fifo_max_payload_size", 0444, root,
+ &priv->fifos.payload_size);
+
+ /* statistics on fifo buffer usage */
+ for (i = 1; i < 32; i++) {
+ snprintf(name, sizeof(name), "%02i", i);
+ debugfs_create_u64(name, 0444, fifousage,
+ &priv->stats.fifo_usage[i]);
+ debugfs_create_u32(name, 0444, fifoaddr,
+ &priv->fifos.fifo_address[i]);
+ }
+
+#endif
+}
+
+static void mcp2517fd_debugfs_remove(struct mcp2517fd_priv *priv)
+{
+#if defined(CONFIG_DEBUG_FS)
+ debugfs_remove_recursive(priv->debugfs_dir);
+#endif
+}
+
+int mcp2517fd_of_parse(struct mcp2517fd_priv *priv)
+{
+#ifdef CONFIG_OF_DYNAMIC
+ struct spi_device *spi = priv->spi;
+ const struct device_node *np = spi->dev.of_node;
+ u32 val;
+ int ret;
+
+ ret = of_property_read_u32_index(np, "microchip,clock_div",
+ 0, &val);
+ if (!ret) {
+ switch (val) {
+ case 1:
+ priv->config.clock_div2 = false;
+ break;
+ case 2:
+ priv->config.clock_div2 = true;
+ break;
+ default:
+ dev_err(&spi->dev,
+ "Invalid value in device tree for microchip,clock_div: %u - valid_values: 1, 2\n",
+ val);
+ return -EINVAL;
+ }
+ }
+
+ ret = of_property_read_u32_index(np, "microchip,clock_out_div",
+ 0, &val);
+ if (!ret) {
+ switch (val) {
+ case 0:
+ case 1:
+ case 2:
+ case 4:
+ case 10:
+ priv->config.clock_odiv = val;
+ break;
+ default:
+ dev_err(&spi->dev,
+ "Invalid value in device tree for microchip,clock_out_div: %u - valid values: 0, 1, 2, 4, 10\n",
+ val);
+ return -EINVAL;
+ }
+ }
+
+ ret = of_property_read_u32_index(np, "microchip,gpio0_mode",
+ 0, &val);
+ if (!ret) {
+ switch (val) {
+ case 0:
+ priv->config.gpio0_mode = gpio_mode_in;
+ break;
+ case 1:
+ priv->config.gpio0_mode = gpio_mode_int;
+ break;
+ case 2:
+ priv->config.gpio0_mode = gpio_mode_out_low;
+ break;
+ case 3:
+ priv->config.gpio0_mode = gpio_mode_out_high;
+ break;
+ case 4:
+ priv->config.gpio0_mode = gpio_mode_standby;
+ break;
+ default:
+ dev_err(&spi->dev,
+ "Invalid value in device tree for microchip,gpio0_mode: %u - valid values: 0, 1, 2, 3, 4\n",
+ val);
+ return -EINVAL;
+ }
+ } else {
+ priv->config.gpio0_mode = gpio_mode_in;
+ }
+
+ ret = of_property_read_u32_index(np, "microchip,gpio1_mode",
+ 0, &val);
+ if (!ret) {
+ switch (val) {
+ case 0:
+ priv->config.gpio1_mode = gpio_mode_in;
+ break;
+ case 1:
+ priv->config.gpio1_mode = gpio_mode_int;
+ break;
+ case 2:
+ priv->config.gpio1_mode = gpio_mode_out_low;
+ break;
+ case 3:
+ priv->config.gpio1_mode = gpio_mode_out_high;
+ break;
+ default:
+ dev_err(&spi->dev,
+ "Invalid value in device tree for microchip,gpio1_mode: %u - valif_values: 0, 1, 2, 3, 4\n",
+ val);
+ return -EINVAL;
+ }
+ } else {
+ priv->config.gpio1_mode = gpio_mode_in;
+ }
+
+ priv->config.gpio_opendrain = of_property_read_bool(
+ np, "microchip,gpio_opendrain");
+
+ priv->config.txcan_opendrain = of_property_read_bool(
+ np, "microchip,txcan_opendrain");
+
+ priv->config.int_opendrain = of_property_read_bool(
+ np, "microchip,int_opendrain");
+#endif
+ return 0;
+}
+
+static int mcp2517fd_can_probe(struct spi_device *spi)
+{
+ const struct of_device_id *of_id =
+ of_match_device(mcp2517fd_of_match, &spi->dev);
+ struct net_device *net;
+ struct mcp2517fd_priv *priv;
+ struct clk *clk;
+ int ret, freq;
+
+ /* as irq_create_fwspec_mapping() can return 0, check for it */
+ if (spi->irq <= 0) {
+ dev_err(&spi->dev, "no valid irq line defined: irq = %i\n",
+ spi->irq);
+ return -EINVAL;
+ }
+
+ clk = devm_clk_get(&spi->dev, NULL);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+ freq = clk_get_rate(clk);
+
+ if (freq < MCP2517FD_MIN_CLOCK_FREQUENCY ||
+ freq > MCP2517FD_MAX_CLOCK_FREQUENCY) {
+ dev_err(&spi->dev,
+ "Clock frequency %i is not in range\n", freq);
+ return -ERANGE;
+ }
+
+ /* Allocate can/net device */
+ net = alloc_candev(sizeof(*priv), TX_ECHO_SKB_MAX);
+ if (!net)
+ return -ENOMEM;
+
+ if (!IS_ERR(clk)) {
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto out_free;
+ }
+
+ net->netdev_ops = &mcp2517fd_netdev_ops;
+ net->flags |= IFF_ECHO;
+
+ priv = netdev_priv(net);
+ priv->can.bittiming_const = &mcp2517fd_nominal_bittiming_const;
+ priv->can.do_set_bittiming = &mcp2517fd_do_set_nominal_bittiming;
+ priv->can.data_bittiming_const = &mcp2517fd_data_bittiming_const;
+ priv->can.do_set_data_bittiming = &mcp2517fd_do_set_data_bittiming;
+ priv->can.do_set_mode = mcp2517fd_do_set_mode;
+ priv->can.do_get_berr_counter = mcp2517fd_get_berr_counter;
+
+ priv->can.ctrlmode_supported =
+ CAN_CTRLMODE_FD |
+ CAN_CTRLMODE_LOOPBACK |
+ CAN_CTRLMODE_LISTENONLY |
+ CAN_CTRLMODE_BERR_REPORTING |
+ CAN_CTRLMODE_FD_NON_ISO |
+ CAN_CTRLMODE_ONE_SHOT;
+
+ if (of_id)
+ priv->model = (enum mcp2517fd_model)of_id->data;
+ else
+ priv->model = spi_get_device_id(spi)->driver_data;
+
+ priv->spi = spi;
+ priv->net = net;
+ priv->clk = clk;
+
+ spi_set_drvdata(spi, priv);
+
+ /* set up gpio modes as GPIO INT */
+ priv->config.gpio0_mode = gpio_mode_int;
+ priv->config.gpio1_mode = gpio_mode_int;
+ /* all by default as push/pull */
+ priv->config.gpio_opendrain = false;
+ priv->config.txcan_opendrain = false;
+ priv->config.int_opendrain = false;
+ /* do not use the SCK clock divider of 2 */
+ priv->config.clock_div2 = false;
+ /* clock output is divided by 10 */
+ priv->config.clock_odiv = 10;
+
+ /* as a first guess we assume we are in CAN_CON_MODE_SLEEP
+ * this is how we leave the controller when removing ourselves
+ */
+ priv->active_can_mode = CAN_CON_MODE_SLEEP;
+
+ /* if we have a clock that is smaller then 4MHz, then enable the pll */
+ priv->config.clock_pll =
+ (freq <= MCP2517FD_AUTO_PLL_MAX_CLOCK_FREQUENCY);
+
+ /* check in device tree for overrrides */
+ ret = mcp2517fd_of_parse(priv);
+ if (ret)
+ return ret;
+
+ /* decide on real can clock rate */
+ priv->can.clock.freq = freq;
+ if (priv->config.clock_pll) {
+ priv->can.clock.freq *= MCP2517FD_PLL_MULTIPLIER;
+ if (priv->can.clock.freq > MCP2517FD_MAX_CLOCK_FREQUENCY) {
+ dev_err(&spi->dev,
+ "PLL clock frequency %i would exceed limit\n",
+ priv->can.clock.freq
+ );
+ return -EINVAL;
+ }
+ }
+ if (priv->config.clock_div2)
+ priv->can.clock.freq /= MCP2517FD_SCLK_DIVIDER;
+
+ /* calclculate the clock frequencies to use */
+ priv->spi_setup_speed_hz = freq / 2;
+ priv->spi_speed_hz = priv->can.clock.freq / 2;
+ if (priv->config.clock_div2) {
+ priv->spi_setup_speed_hz /= MCP2517FD_SCLK_DIVIDER;
+ priv->spi_speed_hz /= MCP2517FD_SCLK_DIVIDER;
+ }
+
+ if (spi->max_speed_hz) {
+ priv->spi_setup_speed_hz = min_t(int,
+ priv->spi_setup_speed_hz,
+ spi->max_speed_hz);
+ priv->spi_speed_hz = min_t(int,
+ priv->spi_speed_hz,
+ spi->max_speed_hz);
+ }
+
+ /* Configure the SPI bus */
+ spi->bits_per_word = 8;
+ ret = spi_setup(spi);
+ if (ret)
+ goto out_clk;
+
+ priv->power = devm_regulator_get_optional(&spi->dev, "vdd");
+ priv->transceiver = devm_regulator_get_optional(&spi->dev, "xceiver");
+ if ((PTR_ERR(priv->power) == -EPROBE_DEFER) ||
+ (PTR_ERR(priv->transceiver) == -EPROBE_DEFER)) {
+ ret = -EPROBE_DEFER;
+ goto out_clk;
+ }
+
+ ret = mcp2517fd_power_enable(priv->power, 1);
+ if (ret)
+ goto out_clk;
+
+ SET_NETDEV_DEV(net, &spi->dev);
+
+ ret = mcp2517fd_hw_probe(spi);
+ /* on error retry a second time */
+ if (ret == -ENODEV) {
+ ret = mcp2517fd_hw_probe(spi);
+ if (!ret)
+ dev_info(&spi->dev,
+ "found device only during retry\n");
+ }
+ if (ret) {
+ if (ret == -ENODEV)
+ dev_err(&spi->dev,
+ "Cannot initialize MCP%x. Wrong wiring?\n",
+ priv->model);
+ goto error_probe;
+ }
+
+ mcp2517fd_hw_sleep(spi);
+
+ ret = register_candev(net);
+ if (ret)
+ goto error_probe;
+
+ /* register debugfs */
+ mcp2517fd_debugfs_add(priv);
+
+ devm_can_led_init(net);
+
+ netdev_info(net, "MCP%x successfully initialized.\n", priv->model);
+ return 0;
+
+error_probe:
+ mcp2517fd_power_enable(priv->power, 0);
+
+out_clk:
+ if (!IS_ERR(clk))
+ clk_disable_unprepare(clk);
+
+out_free:
+ free_candev(net);
+ dev_err(&spi->dev, "Probe failed, err=%d\n", -ret);
+ return ret;
+}
+
+static int mcp2517fd_can_remove(struct spi_device *spi)
+{
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+
+ mcp2517fd_debugfs_remove(priv);
+
+ unregister_candev(net);
+
+ mcp2517fd_power_enable(priv->power, 0);
+
+ if (!IS_ERR(priv->clk))
+ clk_disable_unprepare(priv->clk);
+
+ free_candev(net);
+
+ return 0;
+}
+
+static int __maybe_unused mcp2517fd_can_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+ struct net_device *net = priv->net;
+
+ priv->force_quit = 1;
+ disable_irq(spi->irq);
+
+ if (netif_running(net)) {
+ netif_device_detach(net);
+
+ mcp2517fd_hw_sleep(spi);
+ mcp2517fd_power_enable(priv->transceiver, 0);
+ priv->after_suspend = AFTER_SUSPEND_UP;
+ } else {
+ priv->after_suspend = AFTER_SUSPEND_DOWN;
+ }
+
+ if (!IS_ERR_OR_NULL(priv->power)) {
+ regulator_disable(priv->power);
+ priv->after_suspend |= AFTER_SUSPEND_POWER;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused mcp2517fd_can_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct mcp2517fd_priv *priv = spi_get_drvdata(spi);
+
+ if (priv->after_suspend & AFTER_SUSPEND_POWER)
+ mcp2517fd_power_enable(priv->power, 1);
+
+ if (priv->after_suspend & AFTER_SUSPEND_UP)
+ mcp2517fd_power_enable(priv->transceiver, 1);
+ else
+ priv->after_suspend = 0;
+
+ priv->force_quit = 0;
+
+ enable_irq(spi->irq);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mcp2517fd_can_pm_ops, mcp2517fd_can_suspend,
+ mcp2517fd_can_resume);
+
+static struct spi_driver mcp2517fd_can_driver = {
+ .driver = {
+ .name = DEVICE_NAME,
+ .of_match_table = mcp2517fd_of_match,
+ .pm = &mcp2517fd_can_pm_ops,
+ },
+ .id_table = mcp2517fd_id_table,
+ .probe = mcp2517fd_can_probe,
+ .remove = mcp2517fd_can_remove,
+};
+module_spi_driver(mcp2517fd_can_driver);
+
+MODULE_AUTHOR("Martin Sperl <kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>");
+MODULE_DESCRIPTION("Microchip 2517FD CAN driver");
+MODULE_LICENSE("GPL v2");
--
2.11.0
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
next prev parent reply other threads:[~2017-11-24 18:35 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-11-24 18:35 [PATCH 0/2] Microchip mcp2517fd can controller driver kernel-TqfNSX0MhmxHKSADF0wUEw
[not found] ` <20171124183509.12810-1-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
2017-11-24 18:35 ` [PATCH 1/2] dt-binding: can: mcp2517fd: document device tree bindings kernel-TqfNSX0MhmxHKSADF0wUEw
[not found] ` <20171124183509.12810-2-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
2017-11-26 22:28 ` Rob Herring
2017-11-29 11:55 ` kernel
[not found] ` <6EBDD798-8632-4F42-A138-369BCD36DF68-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
2017-11-29 20:35 ` Patrick Menschel
2017-11-30 7:24 ` kernel
[not found] ` <612BB6CD-5330-40B8-A854-FD065E0A3331-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
2017-11-30 16:44 ` Patrick Menschel
[not found] ` <6aea8071-dc21-4ba7-2b2f-5af41b5755a5-1KBjaw7Xf1+zQB+pC5nmwQ@public.gmane.org>
2017-11-30 16:58 ` kernel-TqfNSX0MhmxHKSADF0wUEw
2017-11-30 17:49 ` Patrick Menschel
[not found] ` <1ac487f7-17e3-c8a0-0f99-8138fe867373-1KBjaw7Xf1+zQB+pC5nmwQ@public.gmane.org>
2017-12-05 10:26 ` Martin Sperl
[not found] ` <d79ee9eb-8b36-0086-8d76-ec6bca224fe5@posteo.de>
[not found] ` <d79ee9eb-8b36-0086-8d76-ec6bca224fe5-1KBjaw7Xf1+zQB+pC5nmwQ@public.gmane.org>
2017-12-17 14:34 ` kernel-TqfNSX0MhmxHKSADF0wUEw
[not found] ` <4E2DB518-A148-46CE-8267-73D292991BD2-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
2017-12-22 15:50 ` Patrick Menschel
2017-11-24 18:35 ` kernel-TqfNSX0MhmxHKSADF0wUEw [this message]
[not found] ` <20171124183509.12810-3-kernel-TqfNSX0MhmxHKSADF0wUEw@public.gmane.org>
2017-11-30 13:04 ` [PATCH 2/2] can: mcp2517fd: Add Microchip mcp2517fd CAN FD driver Marc Kleine-Budde
[not found] ` <ff920dd7-4535-dcaa-27f9-57844ce66c7b-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>
2017-12-03 18:34 ` kernel-TqfNSX0MhmxHKSADF0wUEw
2017-11-25 12:03 ` [PATCH 0/2] Microchip mcp2517fd can controller driver Oliver Hartkopp
2017-11-25 14:47 ` kernel
2017-11-26 16:18 ` Wolfgang Grandegger
2017-11-26 18:29 ` kernel
2017-11-26 19:05 ` Wolfgang Grandegger
2017-11-26 19:53 ` kernel
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=20171124183509.12810-3-kernel@martin.sperl.org \
--to=kernel-tqfnsx0mhmxhksadf0wuew@public.gmane.org \
--cc=devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=linux-can-u79uwXL29TY76Z2rM5mHXA@public.gmane.org \
--cc=mark.rutland-5wv7dgnIgG8@public.gmane.org \
--cc=mkl-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org \
--cc=robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org \
--cc=wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org \
/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).