Re: [PATCH 2/2] can: mcp2517fd: Add Microchip mcp2517fd CAN FD driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On 11/24/2017 07:35 PM, kernel@xxxxxxxxxxxxxxxx wrote:
> From: Martin Sperl <kernel@xxxxxxxxxxxxxxxx>
> 
> 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.

I've review about the last 1/3 of the driver. See comments inline.

> 
> 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@xxxxxxxxxxxxxxxx>
> ---
>  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@xxxxxxxxxxxxxxxx>
> + *
> + * 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.
> + */
> +

Please use a common prefix, e.g. MCP2517 for all defines.

> +#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)

bool enabel

> +{
> +	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;

Please use if-else and move the | to the end of the line.

> +	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:

Please add comments for fallthrough for all your switch-case.

> +		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;

We have the max() helper function:

	bw_sharing_log2bits = max(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");

No need for the dev_err, open_candev() prints in case of an error.

> +		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;

please move this to the common cleanup at the end of this function

> +	}
> +
> +	/* 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)

Please move the ifdef outside of the function and provide a static
inline void for the else branch.

> +	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

Why does this code depend on 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;
> +	}

Why do you keep the clock running after the device has been probed?
Usually we enable the clock during open().

> +
> +	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);

A if else is probably more readable...and you don't need the comment

> +
> +	/* check in device tree for overrrides */
> +	ret = mcp2517fd_of_parse(priv);
> +	if (ret)
> +		return ret;

You're keeping the clock running.

> +
> +	/* 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;

same here

> +		}
> +	}
> +	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 */

this obvious :)

> +	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@xxxxxxxxxxxxxxxx>");
> +MODULE_DESCRIPTION("Microchip 2517FD CAN driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.11.0
> 


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

Attachment: signature.asc
Description: OpenPGP digital signature


[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux