4576 lines
126 KiB
C
4576 lines
126 KiB
C
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
||
|
|
/* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||
|
|
*
|
||
|
|
* CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface.
|
||
|
|
*
|
||
|
|
*/
|
||
|
|
|
||
|
|
#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/gpio/driver.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 "mcp25xxfd"
|
||
|
|
|
||
|
|
/* device description and rational:
|
||
|
|
*
|
||
|
|
* the mcp25xxfd is a CanFD controller that also supports can2.0 only
|
||
|
|
* modes.
|
||
|
|
* It is connected via spi to the host and requires at minimum a single
|
||
|
|
* irq line in addition to the SPI lines - it is not mentioned explicitly
|
||
|
|
* in the documentation but in principle SPI 3-wire should be possible.
|
||
|
|
*
|
||
|
|
* The clock connected is typically 4MHz, 20MHz or 40MHz.
|
||
|
|
* For the 4MHz clock the controller contains 10x PLL circuitry.
|
||
|
|
*
|
||
|
|
* The controller itself has 2KB or ECC-SRAM for data.
|
||
|
|
* It also has 32 FIFOs (of up to 32 CAN-frames).
|
||
|
|
* There are 4 Fifo types which can get configured:
|
||
|
|
* * TEF - Transmission Event Fifo - which consumes FIFO 0
|
||
|
|
* even if it is not configured
|
||
|
|
* * Tansmission Queue - for up to 32 Frames.
|
||
|
|
* this queue reorders CAN frames to get transmitted following the
|
||
|
|
* typical CAN dominant/recessive rules on the can bus itself.
|
||
|
|
* This FIFO is optional.
|
||
|
|
* * TX FIFO: generic TX fifos that can contain arbitrary data
|
||
|
|
* and which come with a configurable priority for transmission
|
||
|
|
* It is also possible to have the Controller automatically trigger
|
||
|
|
* a transfer when a Filter Rule for a RTR frame matches.
|
||
|
|
* Each of these fifos in principle can get configured for distinct
|
||
|
|
* dlc sizes (8 thru 64 bytes)
|
||
|
|
* * RX FIFO: generic RX fifo which is filled via filter-rules.
|
||
|
|
* Each of these fifos in principle can get configured for distinct
|
||
|
|
* dlc sizes (8 thru 64 bytes)
|
||
|
|
* Unfortunately there is no filter rule that would allow triggering
|
||
|
|
* on different frame sizes, so for all practical purposes the
|
||
|
|
* RX fifos have to be of the same size (unless one wants to experience
|
||
|
|
* lost data).
|
||
|
|
* When a Can Frame is transmitted fromthe TX Queue or an individual
|
||
|
|
* TX FIFO then a small TEF Frame can get added to the TEF FIFO queue
|
||
|
|
* to log the Transmission of the frame - this includes ID, Flags
|
||
|
|
* (including a custom identifier/index) .
|
||
|
|
*
|
||
|
|
* The controller provides an optional free running counter with a divider
|
||
|
|
* for timestamping of RX frames as well as for TEF entries.
|
||
|
|
*
|
||
|
|
* Driver Implementation details and rational:
|
||
|
|
* * The whole driver has been designed to give best performance
|
||
|
|
* and as little packet loss as possible with 1MHZ Can frames with DLC=0
|
||
|
|
* on small/slow devices like the Raspberry Pi 1
|
||
|
|
* * This means that some optimizations for full duplex communication
|
||
|
|
* have been implemented to avoid CPU introduced latencies
|
||
|
|
* (especially for spi_write_then_read cases) - this only applies to
|
||
|
|
* 4 wire SPI busses.
|
||
|
|
* * Due to the fact that the TXQ does reorder Can-Frames we do not make
|
||
|
|
* use of it to avoid unexpected behaviour (say when running a
|
||
|
|
* firmware upgrade via Can)
|
||
|
|
* * this means we use individual TX-fifos with a given priority and
|
||
|
|
* we have to wait until all the TX fifos have been transmitted before
|
||
|
|
* we can restart the networking queue to avoid reordering the frames on
|
||
|
|
* the Can bus itself.
|
||
|
|
* Still we can transmit a transmit only Duty-cycle of 66% to 90% on the
|
||
|
|
* Can bus (at 1MHz).
|
||
|
|
* The scaling factors here are:
|
||
|
|
* * Can bus speed - lower Speeds increase Duty-cycle
|
||
|
|
* * SPI Clock Rate - higher speeds increase duty-cycle
|
||
|
|
* * CPU speed + SPI implementation - reduces latencies between transfers
|
||
|
|
* * There is a module parameter that allows the modification of the
|
||
|
|
* number of tx_fifos, which is by default 7.
|
||
|
|
* * The driver offers some module parameters that allow to control the use
|
||
|
|
* of some optimizations (prefer reading more data than necessary instead
|
||
|
|
* of multiple SPI transfers - the idea here is that this way we may
|
||
|
|
* allow the SPI-controller to use DMA instead of programmed IO to
|
||
|
|
* limit latencies/number of interrupts)
|
||
|
|
* When we have to read multiple RX frames in CanFD mode:
|
||
|
|
* * we allow reading all 64 bytes of payload even if DLC <=8
|
||
|
|
* this mode is used in Can2.0 only mode by default and can not get
|
||
|
|
* disabled (SRAM Reads have to be a multiple of 4 bytes anyway)
|
||
|
|
* * Releasing/freeing the RX queue requires writing of 1 byte per fifo.
|
||
|
|
* unfortunately these 32-bit registers are not ajacent to each other,
|
||
|
|
* so that for 2 consecutive RX Frames instead of writing 1 byte per
|
||
|
|
* fifo (with protocol overhead of 2 bytes - so a total of 6 bytes in
|
||
|
|
* 2 transfers) we transmit 13 bytes (with a protocol overhead of 2 -
|
||
|
|
* so a total of 15 bytes)
|
||
|
|
* This optimization is only enabled by a module parameter.
|
||
|
|
* * we use TEF + time stamping to record the transmitted frames
|
||
|
|
* including their timestamp - we use this to order TX and RX frames
|
||
|
|
* when submitting them to the network stack.
|
||
|
|
* * due to the inability to "filter" based on DLC sizes we have to use
|
||
|
|
* a common FIFO size. This is 8 bytes for Can2.0 and 64 bytes for CanFD.
|
||
|
|
* * the driver tries to detect the Controller only by reading registers,
|
||
|
|
* but there are circumstances (e.g. after a crashed driver) where we
|
||
|
|
* have to "blindly" configure the clock rate to get the controller to
|
||
|
|
* respond correctly.
|
||
|
|
* * There is one situation where the controller will require a full POR
|
||
|
|
* (total power off) to recover from a bad Clock configuration.
|
||
|
|
* This happens when the wrong clock is configured in the device tree
|
||
|
|
* (say 4MHz are configured, while 20 or 40MHz are used)
|
||
|
|
* in such a situation the driver tries to enable the PLL, which will
|
||
|
|
* never synchronize and the controller becomes unresponsive to further
|
||
|
|
* spi requests until a POR.
|
||
|
|
*/
|
||
|
|
|
||
|
|
#define MCP25XXFD_OST_DELAY_MS 3
|
||
|
|
#define MCP25XXFD_MIN_CLOCK_FREQUENCY 1000000
|
||
|
|
#define MCP25XXFD_MAX_CLOCK_FREQUENCY 40000000
|
||
|
|
#define MCP25XXFD_PLL_MULTIPLIER 10
|
||
|
|
#define MCP25XXFD_AUTO_PLL_MAX_CLOCK_FREQUENCY \
|
||
|
|
(MCP25XXFD_MAX_CLOCK_FREQUENCY / MCP25XXFD_PLL_MULTIPLIER)
|
||
|
|
#define MCP25XXFD_SCLK_DIVIDER 2
|
||
|
|
|
||
|
|
#define MCP25XXFD_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 MCP25XXFD_SFR_BASE(x) (0xE00 + (x))
|
||
|
|
#define MCP25XXFD_OSC MCP25XXFD_SFR_BASE(0x00)
|
||
|
|
# define MCP25XXFD_OSC_PLLEN BIT(0)
|
||
|
|
# define MCP25XXFD_OSC_OSCDIS BIT(2)
|
||
|
|
# define MCP25XXFD_OSC_SCLKDIV BIT(4)
|
||
|
|
# define MCP25XXFD_OSC_CLKODIV_BITS 2
|
||
|
|
# define MCP25XXFD_OSC_CLKODIV_SHIFT 5
|
||
|
|
# define MCP25XXFD_OSC_CLKODIV_MASK \
|
||
|
|
GENMASK(MCP25XXFD_OSC_CLKODIV_SHIFT \
|
||
|
|
+ MCP25XXFD_OSC_CLKODIV_BITS - 1, \
|
||
|
|
MCP25XXFD_OSC_CLKODIV_SHIFT)
|
||
|
|
# define MCP25XXFD_OSC_CLKODIV_10 3
|
||
|
|
# define MCP25XXFD_OSC_CLKODIV_4 2
|
||
|
|
# define MCP25XXFD_OSC_CLKODIV_2 1
|
||
|
|
# define MCP25XXFD_OSC_CLKODIV_1 0
|
||
|
|
# define MCP25XXFD_OSC_PLLRDY BIT(8)
|
||
|
|
# define MCP25XXFD_OSC_OSCRDY BIT(10)
|
||
|
|
# define MCP25XXFD_OSC_SCLKRDY BIT(12)
|
||
|
|
#define MCP25XXFD_IOCON MCP25XXFD_SFR_BASE(0x04)
|
||
|
|
# define MCP25XXFD_IOCON_TRIS0 BIT(0)
|
||
|
|
# define MCP25XXFD_IOCON_TRIS1 BIT(1)
|
||
|
|
# define MCP25XXFD_IOCON_XSTBYEN BIT(6)
|
||
|
|
# define MCP25XXFD_IOCON_LAT0 BIT(8)
|
||
|
|
# define MCP25XXFD_IOCON_LAT1 BIT(9)
|
||
|
|
# define MCP25XXFD_IOCON_GPIO0 BIT(16)
|
||
|
|
# define MCP25XXFD_IOCON_GPIO1 BIT(17)
|
||
|
|
# define MCP25XXFD_IOCON_PM0 BIT(24)
|
||
|
|
# define MCP25XXFD_IOCON_PM1 BIT(25)
|
||
|
|
# define MCP25XXFD_IOCON_TXCANOD BIT(28)
|
||
|
|
# define MCP25XXFD_IOCON_SOF BIT(29)
|
||
|
|
# define MCP25XXFD_IOCON_INTOD BIT(29)
|
||
|
|
#define MCP25XXFD_CRC MCP25XXFD_SFR_BASE(0x08)
|
||
|
|
# define MCP25XXFD_CRC_MASK GENMASK(15, 0)
|
||
|
|
# define MCP25XXFD_CRC_CRCERRIE BIT(16)
|
||
|
|
# define MCP25XXFD_CRC_FERRIE BIT(17)
|
||
|
|
# define MCP25XXFD_CRC_CRCERRIF BIT(24)
|
||
|
|
# define MCP25XXFD_CRC_FERRIF BIT(25)
|
||
|
|
#define MCP25XXFD_ECCCON MCP25XXFD_SFR_BASE(0x0C)
|
||
|
|
# define MCP25XXFD_ECCCON_ECCEN BIT(0)
|
||
|
|
# define MCP25XXFD_ECCCON_SECIE BIT(1)
|
||
|
|
# define MCP25XXFD_ECCCON_DEDIE BIT(2)
|
||
|
|
# define MCP25XXFD_ECCCON_PARITY_BITS 6
|
||
|
|
# define MCP25XXFD_ECCCON_PARITY_SHIFT 8
|
||
|
|
# define MCP25XXFD_ECCCON_PARITY_MASK \
|
||
|
|
GENMASK(MCP25XXFD_ECCCON_PARITY_SHIFT \
|
||
|
|
+ MCP25XXFD_ECCCON_PARITY_BITS - 1, \
|
||
|
|
MCP25XXFD_ECCCON_PARITY_SHIFT)
|
||
|
|
#define MCP25XXFD_ECCSTAT MCP25XXFD_SFR_BASE(0x10)
|
||
|
|
# define MCP25XXFD_ECCSTAT_SECIF BIT(1)
|
||
|
|
# define MCP25XXFD_ECCSTAT_DEDIF BIT(2)
|
||
|
|
# define MCP25XXFD_ECCSTAT_ERRADDR_SHIFT 8
|
||
|
|
# define MCP25XXFD_ECCSTAT_ERRADDR_MASK \
|
||
|
|
GENMASK(MCP25XXFD_ECCSTAT_ERRADDR_SHIFT + 11, \
|
||
|
|
MCP25XXFD_ECCSTAT_ERRADDR_SHIFT)
|
||
|
|
|
||
|
|
#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_TDCMOD_DISABLED 0
|
||
|
|
# define CAN_TDC_TDCMOD_MANUAL 1
|
||
|
|
# define CAN_TDC_TDCMOD_AUTO 2
|
||
|
|
# 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_TXERR BIT(5)
|
||
|
|
# define CAN_FIFOSTA_TXLARB BIT(6)
|
||
|
|
# define CAN_FIFOSTA_TXABT 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) CAN_FILCON_BITS_
|
||
|
|
# define CAN_FILCON_BITS_ 4
|
||
|
|
/* avoid macro reuse warning, so do not use GENMASK as above */
|
||
|
|
# define CAN_FILCON_MASK(x) \
|
||
|
|
(GENMASK(CAN_FILCON_BITS_ - 1, 0) << 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 MCP25XXFD_BUFFER_TXRX_SIZE 2048
|
||
|
|
|
||
|
|
#define MCP25xxFD_SPI_TRANSFER_BUFFER_MAX_COUNT 2
|
||
|
|
#define SPI_TRANSFER_BUFFER_REPLACE 0
|
||
|
|
#define SPI_TRANSFER_BUFFER_REPLACE_BACK 1
|
||
|
|
|
||
|
|
static const char * const mcp25xxfd_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 mcp25xxfd_obj {
|
||
|
|
u32 id;
|
||
|
|
u32 flags;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct mcp25xxfd_obj_tx {
|
||
|
|
struct mcp25xxfd_obj header;
|
||
|
|
u32 data[];
|
||
|
|
};
|
||
|
|
|
||
|
|
static void mcp25xxfd_obj_to_le(struct mcp25xxfd_obj *obj)
|
||
|
|
{
|
||
|
|
obj->id = cpu_to_le32(obj->id);
|
||
|
|
obj->flags = cpu_to_le32(obj->flags);
|
||
|
|
}
|
||
|
|
|
||
|
|
struct mcp25xxfd_obj_ts {
|
||
|
|
u32 id;
|
||
|
|
u32 flags;
|
||
|
|
u32 ts;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct mcp25xxfd_obj_tef {
|
||
|
|
struct mcp25xxfd_obj_ts header;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct mcp25xxfd_obj_rx {
|
||
|
|
struct mcp25xxfd_obj_ts header;
|
||
|
|
u8 data[];
|
||
|
|
};
|
||
|
|
|
||
|
|
static void mcp25xxfd_obj_ts_from_le(struct mcp25xxfd_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 mcp25xxfd_nominal_bittiming_const = {
|
||
|
|
.name = DEVICE_NAME,
|
||
|
|
.tseg1_min = 2,
|
||
|
|
.tseg1_max = BIT(CAN_NBTCFG_TSEG1_BITS),
|
||
|
|
.tseg2_min = 1,
|
||
|
|
.tseg2_max = BIT(CAN_NBTCFG_TSEG2_BITS),
|
||
|
|
.sjw_max = BIT(CAN_NBTCFG_SJW_BITS),
|
||
|
|
.brp_min = 1,
|
||
|
|
.brp_max = BIT(CAN_NBTCFG_BRP_BITS),
|
||
|
|
.brp_inc = 1,
|
||
|
|
};
|
||
|
|
|
||
|
|
static const struct can_bittiming_const mcp25xxfd_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 mcp25xxfd_model {
|
||
|
|
CAN_MCP2518FD = 0x2518,
|
||
|
|
};
|
||
|
|
|
||
|
|
enum mcp25xxfd_gpio_mode {
|
||
|
|
gpio_mode_int = 0,
|
||
|
|
gpio_mode_standby = MCP25XXFD_IOCON_XSTBYEN,
|
||
|
|
gpio_mode_out_low = MCP25XXFD_IOCON_PM0,
|
||
|
|
gpio_mode_out_high = MCP25XXFD_IOCON_PM0 | MCP25XXFD_IOCON_LAT0,
|
||
|
|
gpio_mode_in = MCP25XXFD_IOCON_PM0 | MCP25XXFD_IOCON_TRIS0
|
||
|
|
};
|
||
|
|
|
||
|
|
struct mcp25xxfd_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 mcp25xxfd_obj_tx)];
|
||
|
|
char fill_data[64];
|
||
|
|
char trigger_cmd[2];
|
||
|
|
char trigger_data;
|
||
|
|
char trigger_buff[64];
|
||
|
|
};
|
||
|
|
|
||
|
|
struct mcp25xxfd_read_fifo_info {
|
||
|
|
struct mcp25xxfd_obj_ts *rxb[32];
|
||
|
|
int rx_count;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct mcp25xxfd_priv {
|
||
|
|
struct can_priv can;
|
||
|
|
struct net_device *net;
|
||
|
|
struct spi_device *spi;
|
||
|
|
struct regulator *power;
|
||
|
|
struct regulator *transceiver;
|
||
|
|
struct clk *clk;
|
||
|
|
|
||
|
|
struct mutex clk_user_lock; /* lock for enabling/disabling the clock */
|
||
|
|
int clk_user_mask;
|
||
|
|
#define MCP25XXFD_CLK_USER_CAN BIT(0)
|
||
|
|
#define MCP25XXFD_CLK_USER_GPIO0 BIT(1)
|
||
|
|
#define MCP25XXFD_CLK_USER_GPIO1 BIT(2)
|
||
|
|
|
||
|
|
struct dentry *debugfs_dir;
|
||
|
|
|
||
|
|
#ifdef CONFIG_GPIOLIB
|
||
|
|
struct gpio_chip gpio;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* the actual model of the mcp25xxfd */
|
||
|
|
enum mcp25xxfd_model model;
|
||
|
|
|
||
|
|
struct {
|
||
|
|
/* clock configuration */
|
||
|
|
bool clock_pll;
|
||
|
|
bool clock_div2;
|
||
|
|
int clock_odiv;
|
||
|
|
|
||
|
|
/* GPIO configuration */
|
||
|
|
bool gpio_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_fifos;
|
||
|
|
u32 tef_address_start;
|
||
|
|
u32 tef_address_end;
|
||
|
|
u32 tef_address;
|
||
|
|
|
||
|
|
/* address in mcp25xxfd-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_pending_mask_in_irq;
|
||
|
|
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 mcp25xxfd */
|
||
|
|
u8 fifo_data[MCP25XXFD_BUFFER_TXRX_SIZE];
|
||
|
|
|
||
|
|
} fifos;
|
||
|
|
|
||
|
|
/* structure with active fifos that need to get fed to the system */
|
||
|
|
struct mcp25xxfd_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];
|
||
|
|
|
||
|
|
/* message abort counter */
|
||
|
|
u64 rx_mab;
|
||
|
|
u64 tx_mab;
|
||
|
|
|
||
|
|
/* message counter fd */
|
||
|
|
u64 rx_fd_count;
|
||
|
|
u64 tx_fd_count;
|
||
|
|
|
||
|
|
/* message counter fd bit rate switch */
|
||
|
|
u64 rx_brs_count;
|
||
|
|
u64 tx_brs_count;
|
||
|
|
|
||
|
|
/* interrupt counter */
|
||
|
|
u64 int_ivm_count;
|
||
|
|
u64 int_wake_count;
|
||
|
|
u64 int_cerr_count;
|
||
|
|
u64 int_serr_count;
|
||
|
|
u64 int_rxov_count;
|
||
|
|
u64 int_txat_count;
|
||
|
|
u64 int_spicrc_count;
|
||
|
|
u64 int_ecc_count;
|
||
|
|
u64 int_tef_count;
|
||
|
|
u64 int_mod_count;
|
||
|
|
u64 int_tbc_count;
|
||
|
|
u64 int_rx_count;
|
||
|
|
u64 int_tx_count;
|
||
|
|
|
||
|
|
/* dlc statistics */
|
||
|
|
u64 rx_dlc_usage[16];
|
||
|
|
u64 tx_dlc_usage[16];
|
||
|
|
} stats;
|
||
|
|
|
||
|
|
/* the current status of the mcp25xxfd */
|
||
|
|
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 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;
|
||
|
|
#define TX_QUEUE_STATUS_INIT 0
|
||
|
|
#define TX_QUEUE_STATUS_RUNNING 1
|
||
|
|
#define TX_QUEUE_STATUS_NEEDS_START 2
|
||
|
|
#define TX_QUEUE_STATUS_STOPPED 3
|
||
|
|
|
||
|
|
/* spi-tx/rx buffers for efficient transfers
|
||
|
|
* used during setup and irq
|
||
|
|
*/
|
||
|
|
struct mutex spi_rxtx_lock;
|
||
|
|
u8 spi_tx[MCP25XXFD_BUFFER_TXRX_SIZE];
|
||
|
|
u8 spi_rx[MCP25XXFD_BUFFER_TXRX_SIZE];
|
||
|
|
|
||
|
|
/* structure for transmit fifo spi_messages */
|
||
|
|
struct mcp25xxfd_trigger_tx_message *spi_transmit_fifos;
|
||
|
|
};
|
||
|
|
|
||
|
|
struct mcp25xxfd_spi_transfer_buffer {
|
||
|
|
u8 *spi_tx_kzalloc[MCP25xxFD_SPI_TRANSFER_BUFFER_MAX_COUNT];
|
||
|
|
u8 *spi_rx_kzalloc[MCP25xxFD_SPI_TRANSFER_BUFFER_MAX_COUNT];
|
||
|
|
u8 const *spi_tx_hold[MCP25xxFD_SPI_TRANSFER_BUFFER_MAX_COUNT];
|
||
|
|
u8 *spi_rx_hold[MCP25xxFD_SPI_TRANSFER_BUFFER_MAX_COUNT];
|
||
|
|
};
|
||
|
|
|
||
|
|
/* module parameters */
|
||
|
|
bool use_bulk_release_fifos;
|
||
|
|
bool use_complete_fdfifo_read;
|
||
|
|
unsigned int tx_fifos;
|
||
|
|
unsigned int bw_sharing_log2bits;
|
||
|
|
bool three_shot;
|
||
|
|
struct mcp25xxfd_spi_transfer_buffer spi_transfer_buffer;
|
||
|
|
unsigned int message_transmit_interval;
|
||
|
|
|
||
|
|
/* replace transfer memory with kzalloc */
|
||
|
|
static void area_replace(struct spi_transfer *xfer,
|
||
|
|
struct mcp25xxfd_spi_transfer_buffer *spi_transfer_buffer_area,
|
||
|
|
int place, int stage)
|
||
|
|
{
|
||
|
|
if (stage == SPI_TRANSFER_BUFFER_REPLACE) {
|
||
|
|
(*spi_transfer_buffer_area).spi_tx_hold[place] = xfer[place].tx_buf;
|
||
|
|
(*spi_transfer_buffer_area).spi_rx_hold[place] = xfer[place].rx_buf;
|
||
|
|
xfer[place].tx_buf = (*spi_transfer_buffer_area).spi_tx_kzalloc[place];
|
||
|
|
xfer[place].rx_buf = (*spi_transfer_buffer_area).spi_rx_kzalloc[place];
|
||
|
|
} else if (stage == SPI_TRANSFER_BUFFER_REPLACE_BACK) {
|
||
|
|
xfer[place].tx_buf = (*spi_transfer_buffer_area).spi_tx_hold[place];
|
||
|
|
xfer[place].rx_buf = (*spi_transfer_buffer_area).spi_rx_hold[place];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* spi sync helper */
|
||
|
|
|
||
|
|
/* wrapper arround spi_sync, that sets speed_hz and use kzalloc to transfer */
|
||
|
|
static int mcp25xxfd_sync_transfer(struct spi_device *spi,
|
||
|
|
struct spi_transfer *xfer,
|
||
|
|
unsigned int xfers,
|
||
|
|
int speed_hz)
|
||
|
|
{
|
||
|
|
int i, ret;
|
||
|
|
|
||
|
|
for (i = 0; i < xfers; i++) {
|
||
|
|
xfer[i].speed_hz = speed_hz;
|
||
|
|
if (xfer[i].tx_buf)
|
||
|
|
memcpy(spi_transfer_buffer.spi_tx_kzalloc[i], xfer[i].tx_buf, xfer[i].len);
|
||
|
|
area_replace(xfer, &spi_transfer_buffer, i, SPI_TRANSFER_BUFFER_REPLACE);
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = spi_sync_transfer(spi, xfer, xfers);
|
||
|
|
|
||
|
|
for (i = 0; i < xfers; i++) {
|
||
|
|
if (spi_transfer_buffer.spi_rx_hold[i])
|
||
|
|
memcpy(spi_transfer_buffer.spi_rx_hold[i], xfer[i].rx_buf, xfer[i].len);
|
||
|
|
area_replace(xfer, &spi_transfer_buffer, i, SPI_TRANSFER_BUFFER_REPLACE_BACK);
|
||
|
|
}
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* an optimization of spi_write_then_read that merges the transfers */
|
||
|
|
static int mcp25xxfd_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 mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
struct spi_transfer xfer[2];
|
||
|
|
u8 single_reg_data_tx[6];
|
||
|
|
u8 single_reg_data_rx[6];
|
||
|
|
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 mcp25xxfd_sync_transfer(spi, xfer, 2, speed_hz);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* full duplex optimization */
|
||
|
|
xfer[0].len = tx_len + rx_len;
|
||
|
|
if (xfer[0].len > sizeof(single_reg_data_tx)) {
|
||
|
|
mutex_lock(&priv->spi_rxtx_lock);
|
||
|
|
xfer[0].tx_buf = priv->spi_tx;
|
||
|
|
xfer[0].rx_buf = priv->spi_rx;
|
||
|
|
} else {
|
||
|
|
xfer[0].tx_buf = single_reg_data_tx;
|
||
|
|
xfer[0].rx_buf = single_reg_data_rx;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* copy and clean */
|
||
|
|
memcpy((u8 *)xfer[0].tx_buf, tx_buf, tx_len);
|
||
|
|
memset((u8 *)xfer[0].tx_buf + tx_len, 0, rx_len);
|
||
|
|
|
||
|
|
ret = mcp25xxfd_sync_transfer(spi, xfer, 1, speed_hz);
|
||
|
|
if (!ret)
|
||
|
|
memcpy(rx_buf, xfer[0].rx_buf + tx_len, rx_len);
|
||
|
|
|
||
|
|
if (xfer[0].len > sizeof(single_reg_data_tx))
|
||
|
|
mutex_unlock(&priv->spi_rxtx_lock);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* simple spi_write wrapper with speed_hz */
|
||
|
|
static int mcp25xxfd_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 mcp25xxfd_sync_transfer(spi, &xfer, 1, speed_hz);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* spi_sync wrapper similar to spi_write_then_read that optimizes transfers */
|
||
|
|
static int mcp25xxfd_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 mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
struct spi_transfer xfer;
|
||
|
|
u8 single_reg_data[6];
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
if (tx_len + tx2_len > MCP25XXFD_BUFFER_TXRX_SIZE)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
memset(&xfer, 0, sizeof(xfer));
|
||
|
|
|
||
|
|
xfer.len = tx_len + tx2_len;
|
||
|
|
if (xfer.len > sizeof(single_reg_data)) {
|
||
|
|
mutex_lock(&priv->spi_rxtx_lock);
|
||
|
|
xfer.tx_buf = priv->spi_tx;
|
||
|
|
} else {
|
||
|
|
xfer.tx_buf = single_reg_data;
|
||
|
|
}
|
||
|
|
|
||
|
|
memcpy((u8 *)xfer.tx_buf, tx_buf, tx_len);
|
||
|
|
memcpy((u8 *)xfer.tx_buf + tx_len, tx2_buf, tx2_len);
|
||
|
|
|
||
|
|
ret = mcp25xxfd_sync_transfer(spi, &xfer, 1, speed_hz);
|
||
|
|
|
||
|
|
if (xfer.len > sizeof(single_reg_data))
|
||
|
|
mutex_unlock(&priv->spi_rxtx_lock);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* mcp25xxfd spi command/protocol helper */
|
||
|
|
|
||
|
|
static void mcp25xxfd_calc_cmd_addr(u16 cmd, u16 addr, u8 *data)
|
||
|
|
{
|
||
|
|
cmd = cmd | (addr & ADDRESS_MASK);
|
||
|
|
|
||
|
|
data[0] = (cmd >> 8) & 0xff;
|
||
|
|
data[1] = (cmd >> 0) & 0xff;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_cmd_reset(struct spi_device *spi, u32 speed_hz)
|
||
|
|
{
|
||
|
|
u8 cmd[2];
|
||
|
|
|
||
|
|
mcp25xxfd_calc_cmd_addr(INSTRUCTION_RESET, 0, cmd);
|
||
|
|
|
||
|
|
/* write the reset command */
|
||
|
|
return mcp25xxfd_write(spi, cmd, 2, speed_hz);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* read multiple bytes, transform some registers */
|
||
|
|
static int mcp25xxfd_cmd_readn(struct spi_device *spi, u32 reg,
|
||
|
|
void *data, int n, u32 speed_hz)
|
||
|
|
{
|
||
|
|
u8 cmd[2];
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
mcp25xxfd_calc_cmd_addr(INSTRUCTION_READ, reg, cmd);
|
||
|
|
|
||
|
|
ret = mcp25xxfd_write_then_read(spi, &cmd, 2, data, n, speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_convert_to_cpu(u32 *data, int n)
|
||
|
|
{
|
||
|
|
int i;
|
||
|
|
|
||
|
|
for (i = 0; i < n; i++)
|
||
|
|
data[i] = le32_to_cpu(data[i]);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_first_byte(u32 mask)
|
||
|
|
{
|
||
|
|
return (mask & 0x0000ffff) ?
|
||
|
|
((mask & 0x000000ff) ? 0 : 1) :
|
||
|
|
((mask & 0x00ff0000) ? 2 : 3);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_last_byte(u32 mask)
|
||
|
|
{
|
||
|
|
return (mask & 0xffff0000) ?
|
||
|
|
((mask & 0xff000000) ? 3 : 2) :
|
||
|
|
((mask & 0x0000ff00) ? 1 : 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* read a register, but we are only interrested in a few bytes */
|
||
|
|
static int mcp25xxfd_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 = mcp25xxfd_first_byte(mask);
|
||
|
|
last_byte = mcp25xxfd_last_byte(mask);
|
||
|
|
len_byte = last_byte - first_byte + 1;
|
||
|
|
|
||
|
|
/* do a partial read */
|
||
|
|
*data = 0;
|
||
|
|
ret = mcp25xxfd_cmd_readn(spi, reg + first_byte,
|
||
|
|
((void *)data + first_byte), len_byte,
|
||
|
|
speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
return mcp25xxfd_convert_to_cpu(data, 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_cmd_read(struct spi_device *spi, u32 reg, u32 *data,
|
||
|
|
u32 speed_hz)
|
||
|
|
{
|
||
|
|
return mcp25xxfd_cmd_read_mask(spi, reg, data, -1, speed_hz);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* read a register, but we are only interrested in a few bytes */
|
||
|
|
static int mcp25xxfd_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 = mcp25xxfd_first_byte(mask);
|
||
|
|
last_byte = mcp25xxfd_last_byte(mask);
|
||
|
|
len_byte = last_byte - first_byte + 1;
|
||
|
|
|
||
|
|
/* prepare buffer */
|
||
|
|
mcp25xxfd_calc_cmd_addr(INSTRUCTION_WRITE, reg + first_byte, cmd);
|
||
|
|
data = cpu_to_le32(data);
|
||
|
|
|
||
|
|
return mcp25xxfd_write_then_write(spi,
|
||
|
|
cmd, sizeof(cmd),
|
||
|
|
((void *)&data + first_byte),
|
||
|
|
len_byte,
|
||
|
|
speed_hz);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_cmd_write(struct spi_device *spi, u32 reg, u32 data,
|
||
|
|
u32 speed_hz)
|
||
|
|
{
|
||
|
|
return mcp25xxfd_cmd_write_mask(spi, reg, data, -1, speed_hz);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_cmd_writen(struct spi_device *spi, u32 reg,
|
||
|
|
void *data, int n, u32 speed_hz)
|
||
|
|
{
|
||
|
|
u8 cmd[2];
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
mcp25xxfd_calc_cmd_addr(INSTRUCTION_WRITE, reg, cmd);
|
||
|
|
|
||
|
|
ret = mcp25xxfd_write_then_write(spi, &cmd, 2, data, n, speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_clean_sram(struct spi_device *spi, u32 speed_hz)
|
||
|
|
{
|
||
|
|
u8 buffer[256];
|
||
|
|
int i;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
memset(buffer, 0, sizeof(buffer));
|
||
|
|
|
||
|
|
for (i = 0; i < FIFO_DATA_SIZE; i += sizeof(buffer)) {
|
||
|
|
ret = mcp25xxfd_cmd_writen(spi, FIFO_DATA(i),
|
||
|
|
buffer, sizeof(buffer),
|
||
|
|
speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* mcp25xxfd opmode helper functions */
|
||
|
|
|
||
|
|
static int mcp25xxfd_get_opmode(struct spi_device *spi,
|
||
|
|
int *mode,
|
||
|
|
int speed_hz)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* read the mode */
|
||
|
|
ret = mcp25xxfd_cmd_read_mask(spi,
|
||
|
|
CAN_CON,
|
||
|
|
&priv->regs.con,
|
||
|
|
CAN_CON_OPMOD_MASK,
|
||
|
|
speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
/* calculate the mode */
|
||
|
|
*mode = (priv->regs.con & CAN_CON_OPMOD_MASK) >>
|
||
|
|
CAN_CON_OPMOD_SHIFT;
|
||
|
|
|
||
|
|
/* and assign to active mode as well */
|
||
|
|
priv->active_can_mode = *mode;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_set_opmode(struct spi_device *spi, int mode,
|
||
|
|
int speed_hz)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
u32 val = priv->regs.con & ~CAN_CON_REQOP_MASK;
|
||
|
|
|
||
|
|
/* regs.con also contains the effective register */
|
||
|
|
priv->regs.con = val |
|
||
|
|
(mode << CAN_CON_REQOP_SHIFT) |
|
||
|
|
(mode << CAN_CON_OPMOD_SHIFT);
|
||
|
|
priv->active_can_mode = mode;
|
||
|
|
|
||
|
|
/* if the opmode is sleep then the oscilator will be disabled
|
||
|
|
* and also not ready
|
||
|
|
*/
|
||
|
|
if (mode == CAN_CON_MODE_SLEEP) {
|
||
|
|
priv->regs.osc &= ~(MCP25XXFD_OSC_OSCRDY |
|
||
|
|
MCP25XXFD_OSC_PLLRDY |
|
||
|
|
MCP25XXFD_OSC_SCLKRDY);
|
||
|
|
priv->regs.osc |= MCP25XXFD_OSC_OSCDIS;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* but only write the relevant section */
|
||
|
|
return mcp25xxfd_cmd_write_mask(spi, CAN_CON,
|
||
|
|
priv->regs.con,
|
||
|
|
CAN_CON_REQOP_MASK,
|
||
|
|
speed_hz);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_set_normal_opmode(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
int mode;
|
||
|
|
|
||
|
|
if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK)
|
||
|
|
mode = CAN_CON_MODE_EXTERNAL_LOOPBACK;
|
||
|
|
else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
|
||
|
|
mode = CAN_CON_MODE_LISTENONLY;
|
||
|
|
else if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
|
||
|
|
mode = CAN_CON_MODE_MIXED;
|
||
|
|
else
|
||
|
|
mode = CAN_CON_MODE_CAN2_0;
|
||
|
|
|
||
|
|
return mcp25xxfd_set_opmode(spi, mode, priv->spi_setup_speed_hz);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* clock helper */
|
||
|
|
static int mcp25xxfd_wake_from_sleep(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
u32 waitfor = MCP25XXFD_OSC_OSCRDY;
|
||
|
|
u32 mask = waitfor | MCP25XXFD_OSC_OSCDIS;
|
||
|
|
unsigned long timeout;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* write clock with OSCDIS cleared*/
|
||
|
|
priv->regs.osc &= ~MCP25XXFD_OSC_OSCDIS;
|
||
|
|
ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_OSC,
|
||
|
|
priv->regs.osc,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* wait for synced pll/osc/sclk */
|
||
|
|
timeout = jiffies + MCP25XXFD_OSC_POLLING_JIFFIES;
|
||
|
|
while (time_before_eq(jiffies, timeout)) {
|
||
|
|
ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_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;
|
||
|
|
}
|
||
|
|
/* wait some time */
|
||
|
|
msleep(100);
|
||
|
|
}
|
||
|
|
|
||
|
|
dev_err(&spi->dev,
|
||
|
|
"Clock did not enable within the timeout period\n");
|
||
|
|
return -ETIMEDOUT;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_hw_check_clock(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
u32 val;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* read the osc register and check if it matches
|
||
|
|
* what we have on record
|
||
|
|
*/
|
||
|
|
ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_OSC,
|
||
|
|
&val,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
if (val == priv->regs.osc)
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
/* ignore all those ready bits on second try */
|
||
|
|
if ((val & 0xff) == (priv->regs.osc & 0xff)) {
|
||
|
|
dev_info(&spi->dev,
|
||
|
|
"The oscillator register value %08x does not match what we expect: %08x - it is still reasonable, but please investigate\n",
|
||
|
|
val, priv->regs.osc);
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
dev_err(&spi->dev,
|
||
|
|
"The oscillator register value %08x does not match what we expect: %08x\n",
|
||
|
|
val, priv->regs.osc);
|
||
|
|
|
||
|
|
return -ENODEV;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_start_clock(struct spi_device *spi, int requestor_mask)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
int ret = 0;
|
||
|
|
|
||
|
|
mutex_lock(&priv->clk_user_lock);
|
||
|
|
|
||
|
|
priv->clk_user_mask |= requestor_mask;
|
||
|
|
|
||
|
|
if (priv->clk_user_mask != requestor_mask)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
/* check that the controller clock register
|
||
|
|
* is what it is supposed to be
|
||
|
|
*/
|
||
|
|
ret = mcp25xxfd_hw_check_clock(spi);
|
||
|
|
if (ret)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
/* and we start the clock */
|
||
|
|
if (!IS_ERR(priv->clk))
|
||
|
|
ret = clk_prepare_enable(priv->clk);
|
||
|
|
|
||
|
|
/* we wake from sleep */
|
||
|
|
if (priv->active_can_mode == CAN_CON_MODE_SLEEP)
|
||
|
|
ret = mcp25xxfd_wake_from_sleep(spi);
|
||
|
|
|
||
|
|
out:
|
||
|
|
mutex_unlock(&priv->clk_user_lock);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_stop_clock(struct spi_device *spi, int requestor_mask)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
|
||
|
|
mutex_lock(&priv->clk_user_lock);
|
||
|
|
|
||
|
|
priv->clk_user_mask &= ~requestor_mask;
|
||
|
|
|
||
|
|
if (!priv->clk_user_mask)
|
||
|
|
goto out;
|
||
|
|
|
||
|
|
/* put us into sleep mode */
|
||
|
|
mcp25xxfd_set_opmode(spi, CAN_CON_MODE_SLEEP,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
|
||
|
|
/* and we stop the clock */
|
||
|
|
if (!IS_ERR(priv->clk))
|
||
|
|
clk_disable_unprepare(priv->clk);
|
||
|
|
|
||
|
|
out:
|
||
|
|
mutex_unlock(&priv->clk_user_lock);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* mcp25xxfd GPIO helper functions */
|
||
|
|
#ifdef CONFIG_GPIOLIB
|
||
|
|
|
||
|
|
enum mcp25xxfd_gpio_pins {
|
||
|
|
MCP25XXFD_GPIO_GPIO0 = 0,
|
||
|
|
MCP25XXFD_GPIO_GPIO1 = 1,
|
||
|
|
};
|
||
|
|
|
||
|
|
static int mcp25xxfd_gpio_request(struct gpio_chip *chip,
|
||
|
|
unsigned int offset)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
|
||
|
|
int clock_requestor = offset ?
|
||
|
|
MCP25XXFD_CLK_USER_GPIO1 : MCP25XXFD_CLK_USER_GPIO0;
|
||
|
|
|
||
|
|
/* only handle gpio 0/1 */
|
||
|
|
if (offset > 1)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
mcp25xxfd_start_clock(priv->spi, clock_requestor);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void mcp25xxfd_gpio_free(struct gpio_chip *chip,
|
||
|
|
unsigned int offset)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
|
||
|
|
int clock_requestor = offset ?
|
||
|
|
MCP25XXFD_CLK_USER_GPIO1 : MCP25XXFD_CLK_USER_GPIO0;
|
||
|
|
|
||
|
|
/* only handle gpio 0/1 */
|
||
|
|
if (offset > 1)
|
||
|
|
return;
|
||
|
|
|
||
|
|
mcp25xxfd_stop_clock(priv->spi, clock_requestor);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
|
||
|
|
u32 mask = (offset) ? MCP25XXFD_IOCON_GPIO1 : MCP25XXFD_IOCON_GPIO0;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* only handle gpio 0/1 */
|
||
|
|
if (offset > 1)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
/* read the relevant gpio Latch */
|
||
|
|
ret = mcp25xxfd_cmd_read_mask(priv->spi, MCP25XXFD_IOCON,
|
||
|
|
&priv->regs.iocon, mask,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* return the match */
|
||
|
|
return priv->regs.iocon & mask;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void mcp25xxfd_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||
|
|
int value)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
|
||
|
|
u32 mask = (offset) ? MCP25XXFD_IOCON_LAT1 : MCP25XXFD_IOCON_LAT0;
|
||
|
|
|
||
|
|
/* only handle gpio 0/1 */
|
||
|
|
if (offset > 1)
|
||
|
|
return;
|
||
|
|
|
||
|
|
/* update in memory representation with the corresponding value */
|
||
|
|
if (value)
|
||
|
|
priv->regs.iocon |= mask;
|
||
|
|
else
|
||
|
|
priv->regs.iocon &= ~mask;
|
||
|
|
|
||
|
|
mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_IOCON,
|
||
|
|
priv->regs.iocon, mask,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_gpio_direction_input(struct gpio_chip *chip,
|
||
|
|
unsigned int offset)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
|
||
|
|
u32 mask_tri = (offset) ?
|
||
|
|
MCP25XXFD_IOCON_TRIS1 : MCP25XXFD_IOCON_TRIS0;
|
||
|
|
u32 mask_stby = (offset) ?
|
||
|
|
0 : MCP25XXFD_IOCON_XSTBYEN;
|
||
|
|
u32 mask_pm = (offset) ?
|
||
|
|
MCP25XXFD_IOCON_PM1 : MCP25XXFD_IOCON_PM0;
|
||
|
|
|
||
|
|
/* only handle gpio 0/1 */
|
||
|
|
if (offset > 1)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
/* set the mask */
|
||
|
|
priv->regs.iocon |= mask_tri | mask_pm;
|
||
|
|
|
||
|
|
/* clear stby */
|
||
|
|
priv->regs.iocon &= ~mask_stby;
|
||
|
|
|
||
|
|
return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_IOCON,
|
||
|
|
priv->regs.iocon,
|
||
|
|
mask_tri | mask_stby | mask_pm,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_gpio_direction_output(struct gpio_chip *chip,
|
||
|
|
unsigned int offset, int value)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = gpiochip_get_data(chip);
|
||
|
|
u32 mask_tri = (offset) ?
|
||
|
|
MCP25XXFD_IOCON_TRIS1 : MCP25XXFD_IOCON_TRIS0;
|
||
|
|
u32 mask_lat = (offset) ?
|
||
|
|
MCP25XXFD_IOCON_LAT1 : MCP25XXFD_IOCON_LAT0;
|
||
|
|
u32 mask_pm = (offset) ?
|
||
|
|
MCP25XXFD_IOCON_PM1 : MCP25XXFD_IOCON_PM0;
|
||
|
|
u32 mask_stby = (offset) ?
|
||
|
|
0 : MCP25XXFD_IOCON_XSTBYEN;
|
||
|
|
|
||
|
|
/* only handle gpio 0/1 */
|
||
|
|
if (offset > 1)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
/* clear the tristate bit and also clear stby */
|
||
|
|
priv->regs.iocon &= ~(mask_tri | mask_stby);
|
||
|
|
|
||
|
|
/* set GPIO mode */
|
||
|
|
priv->regs.iocon |= mask_pm;
|
||
|
|
|
||
|
|
/* set the value */
|
||
|
|
if (value)
|
||
|
|
priv->regs.iocon |= mask_lat;
|
||
|
|
else
|
||
|
|
priv->regs.iocon &= ~mask_lat;
|
||
|
|
|
||
|
|
return mcp25xxfd_cmd_write_mask(priv->spi, MCP25XXFD_IOCON,
|
||
|
|
priv->regs.iocon,
|
||
|
|
mask_tri | mask_lat |
|
||
|
|
mask_pm | mask_stby,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_gpio_setup(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
|
||
|
|
/* gpiochip only handles GPIO0 and GPIO1 */
|
||
|
|
priv->gpio.owner = THIS_MODULE;
|
||
|
|
priv->gpio.parent = &spi->dev;
|
||
|
|
priv->gpio.label = dev_name(&spi->dev);
|
||
|
|
priv->gpio.direction_input = mcp25xxfd_gpio_direction_input;
|
||
|
|
priv->gpio.get = mcp25xxfd_gpio_get;
|
||
|
|
priv->gpio.direction_output = mcp25xxfd_gpio_direction_output;
|
||
|
|
priv->gpio.set = mcp25xxfd_gpio_set;
|
||
|
|
priv->gpio.request = mcp25xxfd_gpio_request;
|
||
|
|
priv->gpio.free = mcp25xxfd_gpio_free;
|
||
|
|
priv->gpio.base = -1;
|
||
|
|
priv->gpio.ngpio = 2;
|
||
|
|
priv->gpio.can_sleep = 1;
|
||
|
|
|
||
|
|
return devm_gpiochip_add_data(&spi->dev, &priv->gpio, priv);
|
||
|
|
}
|
||
|
|
|
||
|
|
#else
|
||
|
|
|
||
|
|
static int mcp25xxfd_gpio_setup(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif
|
||
|
|
|
||
|
|
/* 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 mcp25xxfd_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 mcp25xxfd_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;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void __mcp25xxfd_stop_queue(struct net_device *net,
|
||
|
|
unsigned int id)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = netdev_priv(net);
|
||
|
|
|
||
|
|
if (priv->tx_queue_status >= TX_QUEUE_STATUS_STOPPED)
|
||
|
|
dev_warn(&priv->spi->dev,
|
||
|
|
"tx-queue is already stopped by: %i\n",
|
||
|
|
priv->tx_queue_status);
|
||
|
|
|
||
|
|
priv->tx_queue_status = id ? id : TX_QUEUE_STATUS_STOPPED;
|
||
|
|
netif_stop_queue(priv->net);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* helper to identify who is stopping the queue by line number */
|
||
|
|
#define mcp25xxfd_stop_queue(spi) \
|
||
|
|
__mcp25xxfd_stop_queue(spi, __LINE__)
|
||
|
|
|
||
|
|
static void mcp25xxfd_wake_queue(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
|
||
|
|
/* 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 = TX_QUEUE_STATUS_RUNNING;
|
||
|
|
|
||
|
|
/* wake queue now */
|
||
|
|
netif_wake_queue(priv->net);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* CAN transmit related*/
|
||
|
|
|
||
|
|
static void mcp25xxfd_mark_tx_pending(void *context)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_trigger_tx_message *txm = context;
|
||
|
|
struct spi_device *spi = txm->msg.spi;
|
||
|
|
struct mcp25xxfd_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);
|
||
|
|
udelay(message_transmit_interval);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_fill_spi_transmit_fifos(struct mcp25xxfd_priv *priv)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_trigger_tx_message *txm;
|
||
|
|
int i, fifo;
|
||
|
|
const u32 trigger = CAN_FIFOCON_TXREQ | CAN_FIFOCON_UINC;
|
||
|
|
const int first_byte = mcp25xxfd_first_byte(trigger);
|
||
|
|
u32 fifo_address;
|
||
|
|
|
||
|
|
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];
|
||
|
|
fifo_address = priv->fifos.fifo_address[fifo];
|
||
|
|
/* prepare the message */
|
||
|
|
spi_message_init(&txm->msg);
|
||
|
|
txm->msg.complete = mcp25xxfd_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;
|
||
|
|
mcp25xxfd_calc_cmd_addr(INSTRUCTION_WRITE,
|
||
|
|
FIFO_DATA(fifo_address),
|
||
|
|
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;
|
||
|
|
mcp25xxfd_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 mcp25xxfd_transmit_message_common(struct spi_device *spi,
|
||
|
|
int fifo,
|
||
|
|
struct mcp25xxfd_obj_tx *obj,
|
||
|
|
int len,
|
||
|
|
u8 *data)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
struct mcp25xxfd_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 */
|
||
|
|
mcp25xxfd_obj_to_le(&obj->header);
|
||
|
|
|
||
|
|
/* fill in details */
|
||
|
|
memcpy(txm->fill_obj, obj, sizeof(struct mcp25xxfd_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 mcp25xxfd_obj_tx) + ALIGN(len, 4);
|
||
|
|
txm->trigger_xfer.len = txm->fill_xfer.len;
|
||
|
|
|
||
|
|
/* and transmit asyncroniously */
|
||
|
|
ret = spi_async(spi, &txm->msg);
|
||
|
|
if (ret)
|
||
|
|
return NETDEV_TX_BUSY;
|
||
|
|
|
||
|
|
return NETDEV_TX_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_transmit_fdmessage(struct spi_device *spi, int fifo,
|
||
|
|
struct canfd_frame *frame)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
struct mcp25xxfd_obj_tx obj;
|
||
|
|
int dlc = can_fd_len2dlc(frame->len);
|
||
|
|
u32 flags;
|
||
|
|
|
||
|
|
frame->len = can_fd_dlc2len(dlc);
|
||
|
|
|
||
|
|
mcp25xxfd_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;
|
||
|
|
if (frame->flags & CANFD_BRS) {
|
||
|
|
flags |= CAN_OBJ_FLAGS_BRS;
|
||
|
|
priv->stats.tx_brs_count++;
|
||
|
|
}
|
||
|
|
flags |= (frame->flags & CANFD_ESI) ? CAN_OBJ_FLAGS_ESI : 0;
|
||
|
|
flags |= CAN_OBJ_FLAGS_FDF;
|
||
|
|
|
||
|
|
priv->stats.tx_fd_count++;
|
||
|
|
priv->stats.tx_dlc_usage[dlc]++;
|
||
|
|
|
||
|
|
obj.header.flags = flags;
|
||
|
|
|
||
|
|
return mcp25xxfd_transmit_message_common(spi, fifo, &obj,
|
||
|
|
frame->len, frame->data);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_transmit_message(struct spi_device *spi, int fifo,
|
||
|
|
struct can_frame *frame)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
struct mcp25xxfd_obj_tx obj;
|
||
|
|
u32 flags;
|
||
|
|
|
||
|
|
if (frame->can_dlc > 8)
|
||
|
|
frame->can_dlc = 8;
|
||
|
|
|
||
|
|
priv->stats.tx_dlc_usage[frame->can_dlc]++;
|
||
|
|
|
||
|
|
mcp25xxfd_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 mcp25xxfd_transmit_message_common(spi, fifo, &obj,
|
||
|
|
frame->can_dlc, frame->data);
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool mcp25xxfd_is_last_txfifo(struct spi_device *spi,
|
||
|
|
int fifo)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
|
||
|
|
return (fifo ==
|
||
|
|
(priv->fifos.tx_fifo_start + priv->fifos.tx_fifos - 1));
|
||
|
|
}
|
||
|
|
|
||
|
|
static netdev_tx_t mcp25xxfd_start_xmit(struct sk_buff *skb,
|
||
|
|
struct net_device *net)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_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) {
|
||
|
|
mcp25xxfd_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 = fls(pending_mask);
|
||
|
|
else
|
||
|
|
fifo = priv->fifos.tx_fifo_start;
|
||
|
|
|
||
|
|
/* handle error - this should not happen... */
|
||
|
|
if (fifo >= priv->fifos.tx_fifo_start + priv->fifos.tx_fifos) {
|
||
|
|
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 (mcp25xxfd_is_last_txfifo(spi, fifo))
|
||
|
|
mcp25xxfd_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 = mcp25xxfd_transmit_fdmessage(spi, fifo,
|
||
|
|
(struct canfd_frame *)
|
||
|
|
skb->data);
|
||
|
|
else
|
||
|
|
ret = mcp25xxfd_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, 0);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* CAN RX Related */
|
||
|
|
|
||
|
|
static int mcp25xxfd_can_transform_rx_fd(struct spi_device *spi,
|
||
|
|
struct mcp25xxfd_obj_rx *rx)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
struct canfd_frame *frame;
|
||
|
|
struct sk_buff *skb;
|
||
|
|
u32 flags = rx->header.flags;
|
||
|
|
int dlc;
|
||
|
|
|
||
|
|
/* 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;
|
||
|
|
}
|
||
|
|
|
||
|
|
mcp25xxfd_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;
|
||
|
|
|
||
|
|
dlc = (flags & CAN_OBJ_FLAGS_DLC_MASK) >> CAN_OBJ_FLAGS_DLC_SHIFT;
|
||
|
|
if (dlc > 15)
|
||
|
|
dlc = 15;
|
||
|
|
|
||
|
|
frame->len = can_fd_dlc2len(dlc);
|
||
|
|
|
||
|
|
memcpy(frame->data, rx->data, frame->len);
|
||
|
|
|
||
|
|
priv->stats.rx_fd_count++;
|
||
|
|
priv->net->stats.rx_packets++;
|
||
|
|
priv->net->stats.rx_bytes += frame->len;
|
||
|
|
if (rx->header.flags & CAN_OBJ_FLAGS_BRS)
|
||
|
|
priv->stats.rx_brs_count++;
|
||
|
|
priv->stats.rx_dlc_usage[dlc]++;
|
||
|
|
|
||
|
|
can_led_event(priv->net, CAN_LED_EVENT_RX);
|
||
|
|
|
||
|
|
netif_rx_ni(skb);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_can_transform_rx_normal(struct spi_device *spi,
|
||
|
|
struct mcp25xxfd_obj_rx *rx)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
struct sk_buff *skb;
|
||
|
|
struct can_frame *frame;
|
||
|
|
u32 flags = rx->header.flags;
|
||
|
|
int len;
|
||
|
|
int dlc;
|
||
|
|
|
||
|
|
/* 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;
|
||
|
|
}
|
||
|
|
|
||
|
|
mcp25xxfd_mcpid_to_canid(rx->header.id, flags, &frame->can_id);
|
||
|
|
|
||
|
|
dlc = (flags & CAN_OBJ_FLAGS_DLC_MASK) >> CAN_OBJ_FLAGS_DLC_SHIFT;
|
||
|
|
if (dlc > 15)
|
||
|
|
dlc = 15;
|
||
|
|
|
||
|
|
frame->can_dlc = dlc;
|
||
|
|
|
||
|
|
len = can_fd_dlc2len(frame->can_dlc);
|
||
|
|
|
||
|
|
memcpy(frame->data, rx->data, len);
|
||
|
|
|
||
|
|
priv->net->stats.rx_packets++;
|
||
|
|
priv->net->stats.rx_bytes += len;
|
||
|
|
priv->stats.rx_dlc_usage[dlc]++;
|
||
|
|
|
||
|
|
can_led_event(priv->net, CAN_LED_EVENT_RX);
|
||
|
|
|
||
|
|
netif_rx_ni(skb);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_process_queued_rx(struct spi_device *spi,
|
||
|
|
struct mcp25xxfd_obj_ts *obj)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_obj_rx *rx = container_of(obj,
|
||
|
|
struct mcp25xxfd_obj_rx,
|
||
|
|
header);
|
||
|
|
|
||
|
|
if (obj->flags & CAN_OBJ_FLAGS_FDF)
|
||
|
|
return mcp25xxfd_can_transform_rx_fd(spi, rx);
|
||
|
|
else
|
||
|
|
return mcp25xxfd_can_transform_rx_normal(spi, rx);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_normal_release_fifos(struct spi_device *spi,
|
||
|
|
int start, int end)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* release each fifo in a separate transfer */
|
||
|
|
for (; start < end ; start++) {
|
||
|
|
ret = mcp25xxfd_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 mcp25xxfd_bulk_release_fifos(struct spi_device *spi,
|
||
|
|
int start, int end)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
int i;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* calculate start address and length */
|
||
|
|
int fifos = end - start;
|
||
|
|
int first_byte = mcp25xxfd_first_byte(CAN_FIFOCON_UINC);
|
||
|
|
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 = mcp25xxfd_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 mcp25xxfd_clear_queued_fifos(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
|
||
|
|
/* prepare rfi - mostly used for sorting */
|
||
|
|
priv->queued_fifos.rx_count = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void mcp25xxfd_addto_queued_fifos(struct spi_device *spi,
|
||
|
|
struct mcp25xxfd_obj_ts *obj)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
struct mcp25xxfd_read_fifo_info *rfi = &priv->queued_fifos;
|
||
|
|
|
||
|
|
/* timestamps must ignore the highest byte, so we shift it,
|
||
|
|
* so that it still compares correctly
|
||
|
|
*/
|
||
|
|
obj->ts <<= 8;
|
||
|
|
|
||
|
|
/* add pointer to queued array-list */
|
||
|
|
rfi->rxb[rfi->rx_count] = obj;
|
||
|
|
rfi->rx_count++;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_process_queued_tef(struct spi_device *spi,
|
||
|
|
struct mcp25xxfd_obj_ts *obj)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
struct mcp25xxfd_obj_tef *tef = container_of(obj,
|
||
|
|
struct mcp25xxfd_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;
|
||
|
|
|
||
|
|
if (dlc > 15)
|
||
|
|
dlc = 15;
|
||
|
|
|
||
|
|
/* update counters */
|
||
|
|
priv->net->stats.tx_packets++;
|
||
|
|
priv->net->stats.tx_bytes += can_fd_dlc2len(dlc);
|
||
|
|
if (obj->flags & CAN_OBJ_FLAGS_FDF)
|
||
|
|
priv->stats.tx_fd_count++;
|
||
|
|
if (obj->flags & CAN_OBJ_FLAGS_BRS)
|
||
|
|
priv->stats.tx_brs_count++;
|
||
|
|
priv->stats.tx_dlc_usage[dlc]++;
|
||
|
|
|
||
|
|
/* release it */
|
||
|
|
can_get_echo_skb(priv->net, fifo, NULL);
|
||
|
|
|
||
|
|
can_led_event(priv->net, CAN_LED_EVENT_TX);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_compare_obj_ts(const void *a, const void *b)
|
||
|
|
{
|
||
|
|
const struct mcp25xxfd_obj_ts * const *rxa = a;
|
||
|
|
const struct mcp25xxfd_obj_ts * const *rxb = b;
|
||
|
|
/* using signed here to handle rollover correctly */
|
||
|
|
s32 ats = (*rxa)->ts;
|
||
|
|
s32 bts = (*rxb)->ts;
|
||
|
|
|
||
|
|
if (ats < bts)
|
||
|
|
return -EINVAL;
|
||
|
|
if (ats > bts)
|
||
|
|
return 1;
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_process_queued_fifos(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
struct mcp25xxfd_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 mcp25xxfd_obj_ts *),
|
||
|
|
mcp25xxfd_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 = mcp25xxfd_process_queued_tef(spi, rfi->rxb[i]);
|
||
|
|
else
|
||
|
|
ret = mcp25xxfd_process_queued_rx(spi, rfi->rxb[i]);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* clear queued fifos */
|
||
|
|
mcp25xxfd_clear_queued_fifos(spi);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_transform_rx(struct spi_device *spi,
|
||
|
|
struct mcp25xxfd_obj_rx *rx)
|
||
|
|
{
|
||
|
|
int dlc;
|
||
|
|
|
||
|
|
/* transform the data to system byte order */
|
||
|
|
mcp25xxfd_obj_ts_from_le(&rx->header);
|
||
|
|
|
||
|
|
/* add the object to the list */
|
||
|
|
mcp25xxfd_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_fd_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
|
||
|
|
* * 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 mcp25xxfd_bulk_release_fifos
|
||
|
|
*/
|
||
|
|
|
||
|
|
static int mcp25xxfd_read_fifos(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
int fifo_header_size = sizeof(struct mcp25xxfd_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 mcp25xxfd_obj_rx *rx;
|
||
|
|
int i, len;
|
||
|
|
int ret;
|
||
|
|
u32 fifo_address;
|
||
|
|
u8 *data;
|
||
|
|
|
||
|
|
/* read all the "open" segments in big chunks */
|
||
|
|
for (i = priv->fifos.rx_fifo_start + priv->fifos.rx_fifos - 1;
|
||
|
|
i >= priv->fifos.rx_fifo_start;
|
||
|
|
i--) {
|
||
|
|
if (!(mask & BIT(i)))
|
||
|
|
continue;
|
||
|
|
/* the fifo to fill */
|
||
|
|
rx = (struct mcp25xxfd_obj_rx *)
|
||
|
|
(priv->fifos.fifo_data + priv->fifos.fifo_address[i]);
|
||
|
|
/* read the minimal payload */
|
||
|
|
fifo_address = priv->fifos.fifo_address[i];
|
||
|
|
ret = mcp25xxfd_cmd_readn(spi,
|
||
|
|
FIFO_DATA(fifo_address),
|
||
|
|
rx,
|
||
|
|
fifo_min_size,
|
||
|
|
priv->spi_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
/* process fifo stats and get length */
|
||
|
|
len = min_t(int, mcp25xxfd_transform_rx(spi, rx),
|
||
|
|
fifo_max_payload_size);
|
||
|
|
|
||
|
|
/* read extra payload if needed */
|
||
|
|
if (len > fifo_min_payload_size) {
|
||
|
|
data = &rx->data[fifo_min_payload_size];
|
||
|
|
ret = mcp25xxfd_cmd_readn(spi,
|
||
|
|
FIFO_DATA(fifo_address +
|
||
|
|
fifo_min_size),
|
||
|
|
data,
|
||
|
|
len - fifo_min_payload_size,
|
||
|
|
priv->spi_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
/* release fifo */
|
||
|
|
ret = mcp25xxfd_normal_release_fifos(spi, i, i + 1);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
/* increment fifo_usage */
|
||
|
|
priv->stats.fifo_usage[i]++;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_bulk_read_fifo_range(struct spi_device *spi,
|
||
|
|
int start, int end)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
const int fifo_header_size = sizeof(struct mcp25xxfd_obj_rx);
|
||
|
|
const int fifo_max_payload_size = priv->fifos.payload_size;
|
||
|
|
const int fifo_max_size = fifo_header_size + fifo_max_payload_size;
|
||
|
|
struct mcp25xxfd_obj_rx *rx;
|
||
|
|
int i;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* now we got start and end, so read the range */
|
||
|
|
ret = mcp25xxfd_cmd_readn(spi,
|
||
|
|
FIFO_DATA(priv->fifos.fifo_address[start]),
|
||
|
|
priv->fifos.fifo_data +
|
||
|
|
priv->fifos.fifo_address[start],
|
||
|
|
(end - start) * fifo_max_size,
|
||
|
|
priv->spi_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* clear all the fifos in range */
|
||
|
|
if (use_bulk_release_fifos)
|
||
|
|
ret = mcp25xxfd_bulk_release_fifos(spi, start, end);
|
||
|
|
else
|
||
|
|
ret = mcp25xxfd_normal_release_fifos(spi, start, end);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* preprocess data */
|
||
|
|
for (i = start; i < end ; i++) {
|
||
|
|
/* store the fifo to process */
|
||
|
|
rx = (struct mcp25xxfd_obj_rx *)
|
||
|
|
(priv->fifos.fifo_data + priv->fifos.fifo_address[i]);
|
||
|
|
/* process fifo stats */
|
||
|
|
mcp25xxfd_transform_rx(spi, rx);
|
||
|
|
/* increment usage */
|
||
|
|
priv->stats.fifo_usage[i]++;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_bulk_read_fifos(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
u32 mask = priv->status.rxif;
|
||
|
|
int i, start, end;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* find blocks of set bits top down */
|
||
|
|
for (i = priv->fifos.rx_fifo_start + priv->fifos.rx_fifos - 1;
|
||
|
|
mask && (i >= priv->fifos.rx_fifo_start);
|
||
|
|
i--) {
|
||
|
|
/* if the bit is 0 then continue loop to find a 1 */
|
||
|
|
if ((mask & BIT(i)) == 0)
|
||
|
|
continue;
|
||
|
|
|
||
|
|
/* so we found a non-0 bit - this is start and end */
|
||
|
|
start = i;
|
||
|
|
end = i;
|
||
|
|
|
||
|
|
/* find the first bit set */
|
||
|
|
for (; mask & BIT(i); i--) {
|
||
|
|
mask &= ~BIT(i);
|
||
|
|
start = i;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* now process that range */
|
||
|
|
ret = mcp25xxfd_bulk_read_fifo_range(spi, start, end + 1);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_can_ist_handle_rxif(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_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 = mcp25xxfd_bulk_read_fifos(spi);
|
||
|
|
else
|
||
|
|
ret = mcp25xxfd_read_fifos(spi);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void mcp25xxfd_mark_tx_processed(struct spi_device *spi,
|
||
|
|
int fifo)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
|
||
|
|
/* set mask */
|
||
|
|
priv->fifos.tx_processed_mask |= BIT(fifo);
|
||
|
|
|
||
|
|
/* check if we should reenable the TX-queue */
|
||
|
|
if (mcp25xxfd_is_last_txfifo(spi, fifo))
|
||
|
|
priv->tx_queue_status = TX_QUEUE_STATUS_NEEDS_START;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_can_ist_handle_tefif_handle_single(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
struct mcp25xxfd_obj_tef *tef;
|
||
|
|
int fifo;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* calc address in address space */
|
||
|
|
tef = (struct mcp25xxfd_obj_tef *)(priv->fifos.fifo_data +
|
||
|
|
priv->fifos.tef_address);
|
||
|
|
|
||
|
|
/* read all the object data */
|
||
|
|
ret = mcp25xxfd_cmd_readn(spi,
|
||
|
|
FIFO_DATA(priv->fifos.tef_address),
|
||
|
|
tef,
|
||
|
|
/* we do not read the last byte of the ts
|
||
|
|
* to avoid MAB issiues
|
||
|
|
*/
|
||
|
|
sizeof(*tef) - 1,
|
||
|
|
priv->spi_speed_hz);
|
||
|
|
/* increment the counter to read next */
|
||
|
|
ret = mcp25xxfd_cmd_write_mask(spi,
|
||
|
|
CAN_TEFCON,
|
||
|
|
CAN_TEFCON_UINC,
|
||
|
|
CAN_TEFCON_UINC,
|
||
|
|
priv->spi_speed_hz);
|
||
|
|
|
||
|
|
/* transform the data to system byte order */
|
||
|
|
mcp25xxfd_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;
|
||
|
|
mcp25xxfd_addto_queued_fifos(spi, &tef->header);
|
||
|
|
|
||
|
|
/* increment tef_address with rollover */
|
||
|
|
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 mark as processed right now */
|
||
|
|
mcp25xxfd_mark_tx_processed(spi, fifo);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_can_ist_handle_tefif_conservative(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
u32 val[2];
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
while (1) {
|
||
|
|
/* get the current TEFSTA and TEFUA */
|
||
|
|
ret = mcp25xxfd_cmd_readn(priv->spi,
|
||
|
|
CAN_TEFSTA,
|
||
|
|
val,
|
||
|
|
8,
|
||
|
|
priv->spi_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
mcp25xxfd_convert_to_cpu(val, 2);
|
||
|
|
|
||
|
|
/* check for interrupt flags */
|
||
|
|
if (!(val[0] & CAN_TEFSTA_TEFNEIF))
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
if (priv->fifos.tef_address != val[1]) {
|
||
|
|
dev_err(&spi->dev,
|
||
|
|
"TEF Address mismatch - read: %04x calculated: %04x\n",
|
||
|
|
val[1], priv->fifos.tef_address);
|
||
|
|
priv->fifos.tef_address = val[1];
|
||
|
|
}
|
||
|
|
|
||
|
|
ret = mcp25xxfd_can_ist_handle_tefif_handle_single(spi);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_can_ist_handle_tefif_count(struct spi_device *spi,
|
||
|
|
int count)
|
||
|
|
{
|
||
|
|
int i;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* now clear TEF for each */
|
||
|
|
/* TODO: optimize for BULK reads, as we (hopefully) know COUNT */
|
||
|
|
for (i = 0; i < count; i++) {
|
||
|
|
/* handle a single TEF */
|
||
|
|
ret = mcp25xxfd_can_ist_handle_tefif_handle_single(spi);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_can_ist_handle_tefif(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
u32 pending = priv->fifos.tx_pending_mask_in_irq &
|
||
|
|
(~priv->fifos.tx_processed_mask);
|
||
|
|
int count;
|
||
|
|
|
||
|
|
/* calculate the number of fifos that have been processed */
|
||
|
|
count = hweight_long(pending);
|
||
|
|
count -= hweight_long(priv->status.txreq & pending);
|
||
|
|
|
||
|
|
/* in case of unexpected results handle "safely" */
|
||
|
|
if (count <= 0)
|
||
|
|
return mcp25xxfd_can_ist_handle_tefif_conservative(spi);
|
||
|
|
|
||
|
|
return mcp25xxfd_can_ist_handle_tefif_count(spi, count);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_can_ist_handle_txatif_fifo(struct spi_device *spi,
|
||
|
|
int fifo)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
u32 val;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* read fifo status */
|
||
|
|
ret = mcp25xxfd_cmd_read(spi,
|
||
|
|
CAN_FIFOSTA(fifo),
|
||
|
|
&val,
|
||
|
|
priv->spi_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* clear the relevant interrupt flags */
|
||
|
|
ret = mcp25xxfd_cmd_write_mask(spi,
|
||
|
|
CAN_FIFOSTA(fifo),
|
||
|
|
0,
|
||
|
|
CAN_FIFOSTA_TXABT |
|
||
|
|
CAN_FIFOSTA_TXLARB |
|
||
|
|
CAN_FIFOSTA_TXERR |
|
||
|
|
CAN_FIFOSTA_TXATIF,
|
||
|
|
priv->spi_speed_hz);
|
||
|
|
|
||
|
|
/* for specific cases we could trigger a retransmit
|
||
|
|
* instead of an abort.
|
||
|
|
*/
|
||
|
|
|
||
|
|
/* and we release it from the echo_skb buffer
|
||
|
|
* NOTE: this is one place where packet delivery will not
|
||
|
|
* be ordered, as we do not have any timing information
|
||
|
|
* when this occurred
|
||
|
|
*/
|
||
|
|
can_get_echo_skb(priv->net, fifo, NULL);
|
||
|
|
|
||
|
|
/* but we need to run a bit of cleanup */
|
||
|
|
priv->status.txif &= ~BIT(fifo);
|
||
|
|
priv->net->stats.tx_aborted_errors++;
|
||
|
|
|
||
|
|
/* mark the fifo as processed */
|
||
|
|
mcp25xxfd_mark_tx_processed(spi, fifo);
|
||
|
|
|
||
|
|
/* handle all the known cases accordingly - ignoring FIFO full */
|
||
|
|
val &= CAN_FIFOSTA_TXABT |
|
||
|
|
CAN_FIFOSTA_TXLARB |
|
||
|
|
CAN_FIFOSTA_TXERR;
|
||
|
|
switch (val) {
|
||
|
|
case CAN_FIFOSTA_TXERR:
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
dev_warn_ratelimited(&spi->dev,
|
||
|
|
"Unknown TX-Fifo abort condition: %08x - stopping tx-queue\n",
|
||
|
|
val);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_can_ist_handle_txatif(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
int i, fifo;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* process all the fifos with that flag set */
|
||
|
|
for (i = 0, fifo = priv->fifos.tx_fifo_start;
|
||
|
|
i < priv->fifos.tx_fifos; i++, fifo++) {
|
||
|
|
if (priv->status.txatif & BIT(fifo)) {
|
||
|
|
ret = mcp25xxfd_can_ist_handle_txatif_fifo(spi, fifo);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void mcp25xxfd_error_skb(struct net_device *net)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_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 mcp25xxfd_can_ist_handle_rxovif(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_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 = mcp25xxfd_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 mcp25xxfd_can_ist_handle_modif(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
int omode = priv->active_can_mode;
|
||
|
|
int mode;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* Note that this irq does not get triggered in all situations
|
||
|
|
* for example SERRIF will move to RESTICTED or LISTENONLY
|
||
|
|
* but MODIF will not be raised!
|
||
|
|
*/
|
||
|
|
|
||
|
|
/* get the mode */
|
||
|
|
ret = mcp25xxfd_get_opmode(spi, &mode, priv->spi_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* if we are restricted, then return to "normal" mode */
|
||
|
|
if (mode == CAN_CON_MODE_RESTRICTED)
|
||
|
|
return mcp25xxfd_set_normal_opmode(spi);
|
||
|
|
|
||
|
|
/* the controller itself will transition to sleep, so we ignore it */
|
||
|
|
if (mode == CAN_CON_MODE_SLEEP)
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
/* switches to the same mode as before are also ignored
|
||
|
|
* - this typically happens if the driver is shortly
|
||
|
|
* switching to a different mode and then returning to the
|
||
|
|
* original mode
|
||
|
|
*/
|
||
|
|
if (mode == omode)
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
/* these we need to handle correctly, so warn and give context */
|
||
|
|
dev_warn(&spi->dev,
|
||
|
|
"Controller unexpectedly switched from mode %s(%u) to %s(%u)\n",
|
||
|
|
mcp25xxfd_mode_names[omode], omode,
|
||
|
|
mcp25xxfd_mode_names[mode], mode);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_can_ist_handle_cerrif(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_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;
|
||
|
|
|
||
|
|
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 mcp25xxfd_can_ist_handle_eccif(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_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;
|
||
|
|
|
||
|
|
/* read ECC status register */
|
||
|
|
ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_ECCSTAT, &val,
|
||
|
|
priv->spi_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
addr = (val & MCP25XXFD_ECCSTAT_ERRADDR_MASK) >>
|
||
|
|
MCP25XXFD_ECCSTAT_ERRADDR_SHIFT;
|
||
|
|
|
||
|
|
dev_err_ratelimited(&spi->dev,
|
||
|
|
"ECC %s bit error at %03x\n",
|
||
|
|
(val & MCP25XXFD_ECCSTAT_DEDIF) ?
|
||
|
|
"double" : "single",
|
||
|
|
addr);
|
||
|
|
|
||
|
|
return mcp25xxfd_cmd_write(spi, MCP25XXFD_ECCSTAT, 0,
|
||
|
|
priv->spi_speed_hz);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_can_ist_handle_serrif_txmab(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
|
||
|
|
priv->net->stats.tx_fifo_errors++;
|
||
|
|
priv->net->stats.tx_errors++;
|
||
|
|
priv->stats.tx_mab++;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_can_ist_handle_serrif_rxmab(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
|
||
|
|
priv->net->stats.rx_dropped++;
|
||
|
|
priv->net->stats.rx_errors++;
|
||
|
|
priv->stats.rx_mab++;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_can_ist_handle_serrif(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
u32 clear;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* clear some interrupts immediately,
|
||
|
|
* so that we get notified if they happen again
|
||
|
|
*/
|
||
|
|
clear = CAN_INT_SERRIF | CAN_INT_MODIF | CAN_INT_IVMIF;
|
||
|
|
ret = mcp25xxfd_cmd_write_mask(spi, CAN_INT,
|
||
|
|
priv->status.intf & (~clear),
|
||
|
|
clear,
|
||
|
|
priv->spi_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* 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;
|
||
|
|
|
||
|
|
/* a mode change + invalid message would indicate
|
||
|
|
* TX MAB Underflow
|
||
|
|
*/
|
||
|
|
if ((priv->status.intf & CAN_INT_MODIF) &&
|
||
|
|
(priv->status.intf & CAN_INT_IVMIF)) {
|
||
|
|
return mcp25xxfd_can_ist_handle_serrif_txmab(spi);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* for RX there is only the RXIF an indicator
|
||
|
|
* - surprizingly RX-MAB does not change mode or anything
|
||
|
|
*/
|
||
|
|
if (priv->status.intf & CAN_INT_RXIF)
|
||
|
|
return mcp25xxfd_can_ist_handle_serrif_rxmab(spi);
|
||
|
|
|
||
|
|
/* the final case */
|
||
|
|
dev_warn_ratelimited(&spi->dev,
|
||
|
|
"unidentified system error - intf = %08x\n",
|
||
|
|
priv->status.intf);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_can_ist_handle_ivmif(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
|
||
|
|
/* if we have a systemerror as well, then ignore it */
|
||
|
|
if (priv->status.intf & CAN_INT_SERRIF)
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
/* otherwise it is an RX issue, so account for it here */
|
||
|
|
priv->can_err_id |= CAN_ERR_PROT;
|
||
|
|
priv->can_err_data[2] |= CAN_ERR_PROT_FORM;
|
||
|
|
priv->net->stats.rx_frame_errors++;
|
||
|
|
priv->net->stats.rx_errors++;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_disable_interrupts(struct spi_device *spi,
|
||
|
|
u32 speed_hz)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
|
||
|
|
priv->status.intf = 0;
|
||
|
|
return mcp25xxfd_cmd_write(spi, CAN_INT, 0, speed_hz);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_enable_interrupts(struct spi_device *spi,
|
||
|
|
u32 speed_hz)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_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_RXOVIE |
|
||
|
|
CAN_INT_ECCIE;
|
||
|
|
return mcp25xxfd_cmd_write(spi, CAN_INT,
|
||
|
|
priv->status.intf,
|
||
|
|
speed_hz);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_hw_wake(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
return mcp25xxfd_start_clock(spi, MCP25XXFD_CLK_USER_CAN);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void mcp25xxfd_hw_sleep(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
|
||
|
|
/* disable interrupts */
|
||
|
|
mcp25xxfd_disable_interrupts(spi, priv->spi_setup_speed_hz);
|
||
|
|
|
||
|
|
/* stop the clocks */
|
||
|
|
mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_can_ist_handle_status(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
const u32 clear_irq = CAN_INT_TBCIF |
|
||
|
|
CAN_INT_MODIF |
|
||
|
|
CAN_INT_SERRIF |
|
||
|
|
CAN_INT_CERRIF |
|
||
|
|
CAN_INT_WAKIF |
|
||
|
|
CAN_INT_IVMIF;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* clear all the interrupts asap */
|
||
|
|
ret = mcp25xxfd_cmd_write_mask(spi, CAN_INT,
|
||
|
|
priv->status.intf & (~clear_irq),
|
||
|
|
clear_irq,
|
||
|
|
priv->spi_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* interrupt clearing info */
|
||
|
|
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 */
|
||
|
|
mcp25xxfd_clear_queued_fifos(spi);
|
||
|
|
|
||
|
|
/* system error interrupt needs to get handled first
|
||
|
|
* to get us out of restricted mode
|
||
|
|
*/
|
||
|
|
if (priv->status.intf & CAN_INT_SERRIF) {
|
||
|
|
priv->stats.int_serr_count++;
|
||
|
|
ret = mcp25xxfd_can_ist_handle_serrif(spi);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* mode change interrupt */
|
||
|
|
if (priv->status.intf & CAN_INT_MODIF) {
|
||
|
|
priv->stats.int_mod_count++;
|
||
|
|
ret = mcp25xxfd_can_ist_handle_modif(spi);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* handle the rx */
|
||
|
|
if (priv->status.intf & CAN_INT_RXIF) {
|
||
|
|
priv->stats.int_rx_count++;
|
||
|
|
ret = mcp25xxfd_can_ist_handle_rxif(spi);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* handle aborted TX FIFOs */
|
||
|
|
if (priv->status.txatif) {
|
||
|
|
priv->stats.int_txat_count++;
|
||
|
|
ret = mcp25xxfd_can_ist_handle_txatif(spi);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* handle the tef */
|
||
|
|
if (priv->status.intf & CAN_INT_TEFIF) {
|
||
|
|
priv->stats.int_tef_count++;
|
||
|
|
ret = mcp25xxfd_can_ist_handle_tefif(spi);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* process the queued fifos */
|
||
|
|
ret = mcp25xxfd_process_queued_fifos(spi);
|
||
|
|
|
||
|
|
/* handle error interrupt flags */
|
||
|
|
if (priv->status.rxovif) {
|
||
|
|
priv->stats.int_rxov_count++;
|
||
|
|
ret = mcp25xxfd_can_ist_handle_rxovif(spi);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* sram ECC error interrupt */
|
||
|
|
if (priv->status.intf & CAN_INT_ECCIF) {
|
||
|
|
priv->stats.int_ecc_count++;
|
||
|
|
ret = mcp25xxfd_can_ist_handle_eccif(spi);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* message format interrupt */
|
||
|
|
if (priv->status.intf & CAN_INT_IVMIF) {
|
||
|
|
priv->stats.int_ivm_count++;
|
||
|
|
ret = mcp25xxfd_can_ist_handle_ivmif(spi);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* handle bus errors in more detail */
|
||
|
|
if (priv->status.intf & CAN_INT_CERRIF) {
|
||
|
|
priv->stats.int_cerr_count++;
|
||
|
|
ret = mcp25xxfd_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 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++;
|
||
|
|
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)
|
||
|
|
mcp25xxfd_error_skb(priv->net);
|
||
|
|
|
||
|
|
/* handle BUS OFF */
|
||
|
|
if (priv->can.state == CAN_STATE_BUS_OFF) {
|
||
|
|
if (priv->can.restart_ms == 0) {
|
||
|
|
mcp25xxfd_stop_queue(priv->net);
|
||
|
|
priv->force_quit = 1;
|
||
|
|
priv->can.can_stats.bus_off++;
|
||
|
|
can_bus_off(priv->net);
|
||
|
|
mcp25xxfd_hw_sleep(spi);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
/* restart the tx queue if needed */
|
||
|
|
if (priv->fifos.tx_processed_mask == priv->fifos.tx_fifo_mask)
|
||
|
|
mcp25xxfd_wake_queue(spi);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* clear bdiag flags */
|
||
|
|
if (priv->bdiag1_clear_mask) {
|
||
|
|
ret = mcp25xxfd_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 mcp25xxfd_can_ist(int irq, void *dev_id)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_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++;
|
||
|
|
|
||
|
|
/* copy pending to in_irq - any
|
||
|
|
* updates that happen asyncronously
|
||
|
|
* are not taken into account here
|
||
|
|
*/
|
||
|
|
priv->fifos.tx_pending_mask_in_irq =
|
||
|
|
priv->fifos.tx_pending_mask;
|
||
|
|
|
||
|
|
/* read interrupt status flags */
|
||
|
|
ret = mcp25xxfd_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 = mcp25xxfd_can_ist_handle_status(spi);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
return IRQ_HANDLED;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_get_berr_counter(const struct net_device *net,
|
||
|
|
struct can_berr_counter *bec)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_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 mcp25xxfd_power_enable(struct regulator *reg, int enable)
|
||
|
|
{
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_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 mcp25xxfd_do_set_nominal_bittiming(struct net_device *net)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_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 mcp25xxfd_cmd_write(spi, CAN_NBTCFG,
|
||
|
|
priv->regs.nbtcfg,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_do_set_data_bittiming(struct net_device *net)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_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;
|
||
|
|
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* set up Transmitter delay compensation */
|
||
|
|
if (!priv->regs.tdc)
|
||
|
|
priv->regs.tdc = CAN_TDC_EDGFLTEN |
|
||
|
|
(CAN_TDC_TDCMOD_AUTO << CAN_TDC_TDCMOD_SHIFT);
|
||
|
|
ret = mcp25xxfd_cmd_write(spi, CAN_TDC, priv->regs.tdc,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* 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 mcp25xxfd_cmd_write(spi, CAN_DBTCFG,
|
||
|
|
priv->regs.dbtcfg,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_hw_probe(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* Wait for oscillator startup timer after power up */
|
||
|
|
msleep(MCP25XXFD_OST_DELAY_MS);
|
||
|
|
|
||
|
|
/* send a "blind" reset, hoping we are in Config mode */
|
||
|
|
mcp25xxfd_cmd_reset(spi, priv->spi_setup_speed_hz);
|
||
|
|
|
||
|
|
/* Wait for oscillator startup again */
|
||
|
|
msleep(MCP25XXFD_OST_DELAY_MS);
|
||
|
|
|
||
|
|
/* check clock register that the clock is ready or disabled */
|
||
|
|
ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_OSC,
|
||
|
|
&priv->regs.osc,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* there can only be one... */
|
||
|
|
switch (priv->regs.osc &
|
||
|
|
(MCP25XXFD_OSC_OSCRDY | MCP25XXFD_OSC_OSCDIS)) {
|
||
|
|
case MCP25XXFD_OSC_OSCRDY: /* either the clock is ready */
|
||
|
|
break;
|
||
|
|
case MCP25XXFD_OSC_OSCDIS: /* or the clock is disabled */
|
||
|
|
/* wakeup sleeping system */
|
||
|
|
ret = mcp25xxfd_wake_from_sleep(spi);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
/* send a reset, hoping we are now in Config mode */
|
||
|
|
mcp25xxfd_cmd_reset(spi, priv->spi_setup_speed_hz);
|
||
|
|
|
||
|
|
/* Wait for oscillator startup again */
|
||
|
|
msleep(MCP25XXFD_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 on wrong clock), so best warn
|
||
|
|
* about such a possibility
|
||
|
|
*/
|
||
|
|
if ((priv->regs.osc &
|
||
|
|
(MCP25XXFD_OSC_PLLEN | MCP25XXFD_OSC_PLLRDY))
|
||
|
|
== MCP25XXFD_OSC_PLLEN)
|
||
|
|
dev_err(&spi->dev,
|
||
|
|
"mcp25xxfd 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 = mcp25xxfd_cmd_read(spi, CAN_CON,
|
||
|
|
&priv->regs.con,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* apply mask and check */
|
||
|
|
if ((priv->regs.con & CAN_CON_DEFAULT_MASK) == CAN_CON_DEFAULT) {
|
||
|
|
priv->active_can_mode = CAN_CON_MODE_CONFIG;
|
||
|
|
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 */
|
||
|
|
priv->regs.con = CAN_CON_DEFAULT;
|
||
|
|
priv->active_can_mode = CAN_CON_MODE_CONFIG;
|
||
|
|
ret = mcp25xxfd_cmd_write(spi, CAN_CON, CAN_CON_DEFAULT,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* delay some time */
|
||
|
|
msleep(MCP25XXFD_OST_DELAY_MS);
|
||
|
|
|
||
|
|
/* reset can controller */
|
||
|
|
mcp25xxfd_cmd_reset(spi, priv->spi_setup_speed_hz);
|
||
|
|
|
||
|
|
/* delay some time */
|
||
|
|
msleep(MCP25XXFD_OST_DELAY_MS);
|
||
|
|
|
||
|
|
/* read CON register and match a final time */
|
||
|
|
ret = mcp25xxfd_cmd_read(spi, CAN_CON,
|
||
|
|
&priv->regs.con,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* apply mask and check */
|
||
|
|
if ((priv->regs.con & CAN_CON_DEFAULT_MASK) != CAN_CON_DEFAULT)
|
||
|
|
return -ENODEV;
|
||
|
|
|
||
|
|
/* just in case: disable interrupts on controller */
|
||
|
|
return mcp25xxfd_disable_interrupts(spi,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_setup_osc(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
int val = ((priv->config.clock_pll) ? MCP25XXFD_OSC_PLLEN : 0)
|
||
|
|
| ((priv->config.clock_div2) ? MCP25XXFD_OSC_SCLKDIV : 0);
|
||
|
|
int waitfor = ((priv->config.clock_pll) ? MCP25XXFD_OSC_PLLRDY : 0)
|
||
|
|
| ((priv->config.clock_div2) ? MCP25XXFD_OSC_SCLKRDY : 0)
|
||
|
|
| MCP25XXFD_OSC_OSCRDY;
|
||
|
|
int ret;
|
||
|
|
unsigned long timeout;
|
||
|
|
|
||
|
|
/* manage clock_out divider */
|
||
|
|
switch (priv->config.clock_odiv) {
|
||
|
|
case 10:
|
||
|
|
val |= (MCP25XXFD_OSC_CLKODIV_10)
|
||
|
|
<< MCP25XXFD_OSC_CLKODIV_SHIFT;
|
||
|
|
break;
|
||
|
|
case 4:
|
||
|
|
val |= (MCP25XXFD_OSC_CLKODIV_4)
|
||
|
|
<< MCP25XXFD_OSC_CLKODIV_SHIFT;
|
||
|
|
break;
|
||
|
|
case 2:
|
||
|
|
val |= (MCP25XXFD_OSC_CLKODIV_2)
|
||
|
|
<< MCP25XXFD_OSC_CLKODIV_SHIFT;
|
||
|
|
break;
|
||
|
|
case 1:
|
||
|
|
val |= (MCP25XXFD_OSC_CLKODIV_1)
|
||
|
|
<< MCP25XXFD_OSC_CLKODIV_SHIFT;
|
||
|
|
break;
|
||
|
|
case 0:
|
||
|
|
/* this means implicitly SOF output */
|
||
|
|
val |= (MCP25XXFD_OSC_CLKODIV_10)
|
||
|
|
<< MCP25XXFD_OSC_CLKODIV_SHIFT;
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
dev_err(&spi->dev,
|
||
|
|
"Unsupported output clock divider %i\n",
|
||
|
|
priv->config.clock_odiv);
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* write clock */
|
||
|
|
ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_OSC, val,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* wait for synced pll/osc/sclk */
|
||
|
|
timeout = jiffies + MCP25XXFD_OSC_POLLING_JIFFIES;
|
||
|
|
while (time_before_eq(jiffies, timeout)) {
|
||
|
|
ret = mcp25xxfd_cmd_read(spi, MCP25XXFD_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 mcp25xxfd_setup_fifo(struct net_device *net,
|
||
|
|
struct mcp25xxfd_priv *priv,
|
||
|
|
struct spi_device *spi)
|
||
|
|
{
|
||
|
|
u32 val, available_memory, tx_memory_used;
|
||
|
|
int ret;
|
||
|
|
int i, fifo;
|
||
|
|
|
||
|
|
/* clear all filter */
|
||
|
|
for (i = 0; i < 32; i++) {
|
||
|
|
ret = mcp25xxfd_cmd_write(spi, CAN_FLTOBJ(i), 0,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
ret = mcp25xxfd_cmd_write(spi, CAN_FLTMASK(i), 0,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
ret = mcp25xxfd_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 mcp25xxfd_obj_tef) +
|
||
|
|
sizeof(struct mcp25xxfd_obj_tx) +
|
||
|
|
priv->fifos.payload_size);
|
||
|
|
/* check that we are not exceeding memory limits with 1 RX buffer */
|
||
|
|
if (tx_memory_used + (sizeof(struct mcp25xxfd_obj_rx) +
|
||
|
|
priv->fifos.payload_size) > MCP25XXFD_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 = MCP25XXFD_BUFFER_TXRX_SIZE - tx_memory_used;
|
||
|
|
|
||
|
|
priv->fifos.rx_fifos = available_memory /
|
||
|
|
(sizeof(struct mcp25xxfd_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 effective memory used */
|
||
|
|
available_memory -= priv->fifos.rx_fifos *
|
||
|
|
(sizeof(struct mcp25xxfd_obj_rx) +
|
||
|
|
priv->fifos.payload_size) *
|
||
|
|
priv->fifos.rx_fifo_depth;
|
||
|
|
|
||
|
|
/* calcluate tef size */
|
||
|
|
priv->fifos.tef_fifos = priv->fifos.tx_fifos;
|
||
|
|
fifo = available_memory / sizeof(struct mcp25xxfd_obj_tef);
|
||
|
|
if (fifo > 0) {
|
||
|
|
priv->fifos.tef_fifos += fifo;
|
||
|
|
if (priv->fifos.tef_fifos > 32)
|
||
|
|
priv->fifos.tef_fifos = 32;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* 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.tef_fifos - 1) << CAN_TEFCON_FSIZE_SHIFT);
|
||
|
|
|
||
|
|
ret = mcp25xxfd_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_TXATIE | /* show up txatie flags in txatif reg */
|
||
|
|
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)
|
||
|
|
if (three_shot)
|
||
|
|
val |= CAN_FIFOCON_TXAT_THREE_SHOT <<
|
||
|
|
CAN_FIFOCON_TXAT_SHIFT;
|
||
|
|
else
|
||
|
|
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 = mcp25xxfd_cmd_write(spi, CAN_FIFOCON(fifo),
|
||
|
|
/* the prioriy needs to be inverted
|
||
|
|
* we need to run from lowest to
|
||
|
|
* highest to avoid MAB errors
|
||
|
|
*/
|
||
|
|
val | ((31 - 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,
|
||
|
|
fifo = priv->fifos.rx_fifo_start + priv->fifos.rx_fifos - 1;
|
||
|
|
i < priv->fifos.rx_fifos; i++, fifo--) {
|
||
|
|
/* prepare the fifo itself */
|
||
|
|
ret = mcp25xxfd_cmd_write(spi, CAN_FIFOCON(fifo),
|
||
|
|
(priv->fifos.payload_mode <<
|
||
|
|
CAN_FIFOCON_PLSIZE_SHIFT) |
|
||
|
|
((priv->fifos.rx_fifo_depth - 1) <<
|
||
|
|
CAN_FIFOCON_FSIZE_SHIFT) |
|
||
|
|
/* RX timestamps: */
|
||
|
|
CAN_FIFOCON_RXTSEN |
|
||
|
|
/* reset FIFO: */
|
||
|
|
CAN_FIFOCON_FRESET |
|
||
|
|
/* FIFO Full: */
|
||
|
|
CAN_FIFOCON_TFERFFIE |
|
||
|
|
/* FIFO Half Full: */
|
||
|
|
CAN_FIFOCON_TFHRFHIE |
|
||
|
|
/* FIFO not empty: */
|
||
|
|
CAN_FIFOCON_TFNRFNIE |
|
||
|
|
/* on 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 = mcp25xxfd_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 = mcp25xxfd_set_opmode(spi, CAN_CON_MODE_INTERNAL_LOOPBACK,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* for the TEF fifo */
|
||
|
|
ret = mcp25xxfd_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.tef_fifos * sizeof(struct mcp25xxfd_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 = mcp25xxfd_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 = mcp25xxfd_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 = mcp25xxfd_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 = mcp25xxfd_set_opmode(spi, CAN_CON_MODE_CONFIG,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_setup(struct net_device *net,
|
||
|
|
struct mcp25xxfd_priv *priv,
|
||
|
|
struct spi_device *spi)
|
||
|
|
{
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
/* set up pll/clock if required */
|
||
|
|
ret = mcp25xxfd_setup_osc(spi);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* set up RAM ECC */
|
||
|
|
priv->regs.ecccon = MCP25XXFD_ECCCON_ECCEN |
|
||
|
|
MCP25XXFD_ECCCON_SECIE |
|
||
|
|
MCP25XXFD_ECCCON_DEDIE;
|
||
|
|
ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_ECCCON,
|
||
|
|
priv->regs.ecccon,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* clean SRAM now that we have ECC enabled
|
||
|
|
* only this case it is clear that all RAM cels have
|
||
|
|
* valid ECC bits
|
||
|
|
*/
|
||
|
|
ret = mcp25xxfd_clean_sram(spi, priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* time stamp control register - 1ns resolution, but disabled */
|
||
|
|
ret = mcp25xxfd_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 = mcp25xxfd_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 */;
|
||
|
|
|
||
|
|
/* transmission bandwidth sharing bits */
|
||
|
|
if (bw_sharing_log2bits > 12)
|
||
|
|
bw_sharing_log2bits = 12;
|
||
|
|
priv->regs.con |= bw_sharing_log2bits << CAN_CON_TXBWS_SHIFT;
|
||
|
|
/* non iso FD mode */
|
||
|
|
if (!(priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO))
|
||
|
|
priv->regs.con |= CAN_CON_ISOCRCEN;
|
||
|
|
/* one shot */
|
||
|
|
if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT)
|
||
|
|
priv->regs.con |= CAN_CON_RTXAT;
|
||
|
|
|
||
|
|
/* and put us into default mode = CONFIG */
|
||
|
|
priv->regs.con |= (CAN_CON_MODE_CONFIG << CAN_CON_REQOP_SHIFT) |
|
||
|
|
(CAN_CON_MODE_CONFIG << CAN_CON_OPMOD_SHIFT);
|
||
|
|
/* apply it now - later we will only switch opsmodes... */
|
||
|
|
ret = mcp25xxfd_cmd_write(spi, CAN_CON,
|
||
|
|
priv->regs.con,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
|
||
|
|
/* setup fifos - this also puts the system into sleep mode */
|
||
|
|
return mcp25xxfd_setup_fifo(net, priv, spi);
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_open(struct net_device *net)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = netdev_priv(net);
|
||
|
|
struct spi_device *spi = priv->spi;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
//pr_err("mcp25xxfd_open start\n");
|
||
|
|
ret = open_candev(net);
|
||
|
|
if (ret) {
|
||
|
|
dev_err(&spi->dev, "unable to set initial baudrate!\n");
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
mcp25xxfd_gpio_direction_output(&priv->gpio, 0, 0);
|
||
|
|
|
||
|
|
mcp25xxfd_power_enable(priv->transceiver, 1);
|
||
|
|
|
||
|
|
priv->force_quit = 0;
|
||
|
|
|
||
|
|
/* clear those statistics */
|
||
|
|
memset(&priv->stats, 0, sizeof(priv->stats));
|
||
|
|
|
||
|
|
ret = request_threaded_irq(spi->irq, NULL,
|
||
|
|
mcp25xxfd_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);
|
||
|
|
mcp25xxfd_power_enable(priv->transceiver, 0);
|
||
|
|
close_candev(net);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* wake from sleep if necessary */
|
||
|
|
ret = mcp25xxfd_hw_wake(spi);
|
||
|
|
if (ret)
|
||
|
|
goto open_clean;
|
||
|
|
|
||
|
|
ret = mcp25xxfd_setup(net, priv, spi);
|
||
|
|
if (ret)
|
||
|
|
goto open_clean;
|
||
|
|
|
||
|
|
mcp25xxfd_do_set_nominal_bittiming(net);
|
||
|
|
mcp25xxfd_do_set_data_bittiming(net);
|
||
|
|
|
||
|
|
ret = mcp25xxfd_set_normal_opmode(spi);
|
||
|
|
if (ret)
|
||
|
|
goto open_clean;
|
||
|
|
/* setting up default state */
|
||
|
|
priv->can.state = CAN_STATE_ERROR_ACTIVE;
|
||
|
|
|
||
|
|
/* only now enable the interrupt on the controller */
|
||
|
|
ret = mcp25xxfd_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 = TX_QUEUE_STATUS_RUNNING;
|
||
|
|
netif_wake_queue(net);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
open_clean:
|
||
|
|
mcp25xxfd_disable_interrupts(spi, priv->spi_setup_speed_hz);
|
||
|
|
free_irq(spi->irq, priv);
|
||
|
|
mcp25xxfd_hw_sleep(spi);
|
||
|
|
mcp25xxfd_power_enable(priv->transceiver, 0);
|
||
|
|
close_candev(net);
|
||
|
|
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void mcp25xxfd_clean(struct net_device *net)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_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, NULL);
|
||
|
|
priv->net->stats.tx_errors++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
priv->fifos.tx_pending_mask = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_stop(struct net_device *net)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = netdev_priv(net);
|
||
|
|
struct spi_device *spi = priv->spi;
|
||
|
|
|
||
|
|
close_candev(net);
|
||
|
|
mcp25xxfd_gpio_direction_output(&priv->gpio, 0, 1);
|
||
|
|
kfree(priv->spi_transmit_fifos);
|
||
|
|
priv->spi_transmit_fifos = NULL;
|
||
|
|
|
||
|
|
priv->force_quit = 1;
|
||
|
|
free_irq(spi->irq, priv);
|
||
|
|
|
||
|
|
/* Disable and clear pending interrupts */
|
||
|
|
mcp25xxfd_disable_interrupts(spi, priv->spi_setup_speed_hz);
|
||
|
|
|
||
|
|
mcp25xxfd_clean(net);
|
||
|
|
|
||
|
|
mcp25xxfd_hw_sleep(spi);
|
||
|
|
|
||
|
|
mcp25xxfd_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 mcp25xxfd_netdev_ops = {
|
||
|
|
.ndo_open = mcp25xxfd_open,
|
||
|
|
.ndo_stop = mcp25xxfd_stop,
|
||
|
|
.ndo_start_xmit = mcp25xxfd_start_xmit,
|
||
|
|
.ndo_change_mtu = can_change_mtu,
|
||
|
|
};
|
||
|
|
|
||
|
|
static const struct of_device_id mcp25xxfd_of_match[] = {
|
||
|
|
{
|
||
|
|
.compatible = "microchip,mcp2518fd",
|
||
|
|
.data = (void *)CAN_MCP2518FD,
|
||
|
|
},
|
||
|
|
{ }
|
||
|
|
};
|
||
|
|
MODULE_DEVICE_TABLE(of, mcp25xxfd_of_match);
|
||
|
|
|
||
|
|
static const struct spi_device_id mcp25xxfd_id_table[] = {
|
||
|
|
{
|
||
|
|
.name = "mcp2518fd",
|
||
|
|
.driver_data = (kernel_ulong_t)CAN_MCP2518FD,
|
||
|
|
},
|
||
|
|
{ }
|
||
|
|
};
|
||
|
|
MODULE_DEVICE_TABLE(spi, mcp25xxfd_id_table);
|
||
|
|
|
||
|
|
#if defined(CONFIG_DEBUG_FS)
|
||
|
|
static int mcp25xxfd_dump_regs(struct seq_file *file, void *offset)
|
||
|
|
{
|
||
|
|
struct spi_device *spi = file->private;
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
u32 data[CAN_TXQUA - CAN_CON + 4];
|
||
|
|
int i;
|
||
|
|
int count;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
count = (CAN_TXQUA - CAN_CON) / 4 + 1;
|
||
|
|
ret = mcp25xxfd_cmd_readn(spi, CAN_CON, data, 4 * count,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
mcp25xxfd_convert_to_cpu((u32 *)data, 4 * count);
|
||
|
|
|
||
|
|
for (i = 0; i < count; i++) {
|
||
|
|
seq_printf(file, "Reg 0x%03x = 0x%08x\n",
|
||
|
|
CAN_CON + 4 * i,
|
||
|
|
((u32 *)data)[i]);
|
||
|
|
}
|
||
|
|
|
||
|
|
count = (MCP25XXFD_ECCSTAT - MCP25XXFD_OSC) / 4 + 1;
|
||
|
|
ret = mcp25xxfd_cmd_readn(spi, MCP25XXFD_OSC, data, 4 * count,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
mcp25xxfd_convert_to_cpu((u32 *)data, 4 * count);
|
||
|
|
|
||
|
|
for (i = 0; i < count; i++) {
|
||
|
|
seq_printf(file, "Reg 0x%03x = 0x%08x\n",
|
||
|
|
MCP25XXFD_OSC + 4 * i,
|
||
|
|
((u32 *)data)[i]);
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void mcp25xxfd_debugfs_add(struct mcp25xxfd_priv *priv)
|
||
|
|
{
|
||
|
|
struct dentry *root, *fifousage, *fifoaddr, *rx, *tx, *status,
|
||
|
|
*regs, *stats, *rxdlc, *txdlc;
|
||
|
|
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);
|
||
|
|
fifoaddr = debugfs_create_dir("fifo_address", root);
|
||
|
|
status = debugfs_create_dir("status", root);
|
||
|
|
regs = debugfs_create_dir("regs", root);
|
||
|
|
stats = debugfs_create_dir("stats", root);
|
||
|
|
fifousage = debugfs_create_dir("fifo_usage", stats);
|
||
|
|
rxdlc = debugfs_create_dir("rx_dlc_usage", stats);
|
||
|
|
txdlc = debugfs_create_dir("tx_dlc_usage", stats);
|
||
|
|
|
||
|
|
/* 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_u32("irq_state", 0444, root, &priv->stats.irq_state);
|
||
|
|
|
||
|
|
/* for the clock user mask */
|
||
|
|
debugfs_create_u32("clk_user_mask", 0444, root, &priv->clk_user_mask);
|
||
|
|
|
||
|
|
/* add fd statistics */
|
||
|
|
debugfs_create_u64("rx_fd_frames", 0444, stats,
|
||
|
|
&priv->stats.rx_fd_count);
|
||
|
|
debugfs_create_u64("tx_fd_frames", 0444, stats,
|
||
|
|
&priv->stats.tx_fd_count);
|
||
|
|
debugfs_create_u64("rx_brs_frames", 0444, stats,
|
||
|
|
&priv->stats.rx_brs_count);
|
||
|
|
debugfs_create_u64("tx_brs_frames", 0444, stats,
|
||
|
|
&priv->stats.tx_brs_count);
|
||
|
|
|
||
|
|
/* 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", 0774, 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_u64("rx_mab", 0444, stats,
|
||
|
|
&priv->stats.rx_mab);
|
||
|
|
|
||
|
|
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_u64("tx_mab", 0444, stats,
|
||
|
|
&priv->stats.tx_mab);
|
||
|
|
|
||
|
|
debugfs_create_u32("tef_count", 0444, tx,
|
||
|
|
&priv->fifos.tef_fifos);
|
||
|
|
|
||
|
|
debugfs_create_u32("fifo_max_payload_size", 0444, root,
|
||
|
|
&priv->fifos.payload_size);
|
||
|
|
|
||
|
|
/* interrupt statistics */
|
||
|
|
debugfs_create_u64("int", 0444, stats,
|
||
|
|
&priv->stats.irq_calls);
|
||
|
|
debugfs_create_u64("int_loops", 0444, stats,
|
||
|
|
&priv->stats.irq_loops);
|
||
|
|
debugfs_create_u64("int_ivm", 0444, stats,
|
||
|
|
&priv->stats.int_ivm_count);
|
||
|
|
debugfs_create_u64("int_wake", 0444, stats,
|
||
|
|
&priv->stats.int_wake_count);
|
||
|
|
debugfs_create_u64("int_cerr", 0444, stats,
|
||
|
|
&priv->stats.int_cerr_count);
|
||
|
|
debugfs_create_u64("int_serr", 0444, stats,
|
||
|
|
&priv->stats.int_serr_count);
|
||
|
|
debugfs_create_u64("int_rxov", 0444, stats,
|
||
|
|
&priv->stats.int_rxov_count);
|
||
|
|
debugfs_create_u64("int_txat", 0444, stats,
|
||
|
|
&priv->stats.int_txat_count);
|
||
|
|
debugfs_create_u64("int_spicrc", 0444, stats,
|
||
|
|
&priv->stats.int_spicrc_count);
|
||
|
|
debugfs_create_u64("int_ecc", 0444, stats,
|
||
|
|
&priv->stats.int_ecc_count);
|
||
|
|
debugfs_create_u64("int_tef", 0444, stats,
|
||
|
|
&priv->stats.int_tef_count);
|
||
|
|
debugfs_create_u64("int_mod", 0444, stats,
|
||
|
|
&priv->stats.int_mod_count);
|
||
|
|
debugfs_create_u64("int_tbc", 0444, stats,
|
||
|
|
&priv->stats.int_tbc_count);
|
||
|
|
debugfs_create_u64("int_rx", 0444, stats,
|
||
|
|
&priv->stats.int_rx_count);
|
||
|
|
debugfs_create_u64("int_tx", 0444, stats,
|
||
|
|
&priv->stats.int_tx_count);
|
||
|
|
|
||
|
|
/* dlc statistics */
|
||
|
|
for (i = 0; i < 16; i++) {
|
||
|
|
snprintf(name, sizeof(name), "%02i", i);
|
||
|
|
debugfs_create_u64(name, 0444, rxdlc,
|
||
|
|
&priv->stats.rx_dlc_usage[i]);
|
||
|
|
debugfs_create_u64(name, 0444, txdlc,
|
||
|
|
&priv->stats.tx_dlc_usage[i]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* statistics on fifo buffer usage and address */
|
||
|
|
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]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* dump the controller registers themselves */
|
||
|
|
debugfs_create_devm_seqfile(&priv->spi->dev, "reg_dump",
|
||
|
|
root, mcp25xxfd_dump_regs);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void mcp25xxfd_debugfs_remove(struct mcp25xxfd_priv *priv)
|
||
|
|
{
|
||
|
|
debugfs_remove_recursive(priv->debugfs_dir);
|
||
|
|
}
|
||
|
|
|
||
|
|
#else
|
||
|
|
static void mcp25xxfd_debugfs_add(struct mcp25xxfd_priv *priv)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
|
||
|
|
static void mcp25xxfd_debugfs_remove(struct mcp25xxfd_priv *priv)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
#ifdef CONFIG_OF_DYNAMIC
|
||
|
|
int mcp25xxfd_of_parse(struct mcp25xxfd_priv *priv)
|
||
|
|
{
|
||
|
|
struct spi_device *spi = priv->spi;
|
||
|
|
const struct device_node *np = spi->dev.of_node;
|
||
|
|
u32 val;
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
priv->config.clock_div2 =
|
||
|
|
of_property_read_bool(np, "microchip,clock-div2");
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
priv->config.gpio_opendrain =
|
||
|
|
of_property_read_bool(np, "gpio-open-drain");
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
#else
|
||
|
|
int mcp25xxfd_of_parse(struct mcp25xxfd_priv *priv)
|
||
|
|
{
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
|
||
|
|
static int mcp25xxfd_can_probe(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
const struct of_device_id *of_id =
|
||
|
|
of_match_device(mcp25xxfd_of_match, &spi->dev);
|
||
|
|
struct net_device *net;
|
||
|
|
struct mcp25xxfd_priv *priv;
|
||
|
|
struct clk *clk;
|
||
|
|
int ret, freq, i;
|
||
|
|
|
||
|
|
for (i = 0; i < MCP25xxFD_SPI_TRANSFER_BUFFER_MAX_COUNT; i++) {
|
||
|
|
spi_transfer_buffer.spi_tx_kzalloc[i] =
|
||
|
|
kzalloc(MCP25XXFD_BUFFER_TXRX_SIZE, GFP_KERNEL);
|
||
|
|
spi_transfer_buffer.spi_rx_kzalloc[i] =
|
||
|
|
kzalloc(MCP25XXFD_BUFFER_TXRX_SIZE, GFP_KERNEL);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Because use spi_async to transmit message,
|
||
|
|
* sometimes too fast message transfer can cause kernel panic.
|
||
|
|
* Appropriate time intervals can be added
|
||
|
|
* between message transmissions as required by the platform.
|
||
|
|
*/
|
||
|
|
if (of_property_read_u32(spi->dev.of_node, "message-transmit-interval",
|
||
|
|
&message_transmit_interval))
|
||
|
|
message_transmit_interval = 0;
|
||
|
|
|
||
|
|
/* 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)) {
|
||
|
|
dev_err(&spi->dev,
|
||
|
|
"Can't get Clock source\n");
|
||
|
|
return PTR_ERR(clk);
|
||
|
|
}
|
||
|
|
freq = clk_get_rate(clk);
|
||
|
|
if (freq < MCP25XXFD_MIN_CLOCK_FREQUENCY ||
|
||
|
|
freq > MCP25XXFD_MAX_CLOCK_FREQUENCY) {
|
||
|
|
dev_err(&spi->dev,
|
||
|
|
"Clock frequency %i is not in range [%i:%i]\n",
|
||
|
|
freq,
|
||
|
|
MCP25XXFD_MIN_CLOCK_FREQUENCY,
|
||
|
|
MCP25XXFD_MAX_CLOCK_FREQUENCY);
|
||
|
|
return -ERANGE;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Allocate can/net device */
|
||
|
|
net = alloc_candev(sizeof(*priv), TX_ECHO_SKB_MAX);
|
||
|
|
if (!net)
|
||
|
|
return -ENOMEM;
|
||
|
|
|
||
|
|
net->netdev_ops = &mcp25xxfd_netdev_ops;
|
||
|
|
net->flags |= IFF_ECHO;
|
||
|
|
|
||
|
|
priv = netdev_priv(net);
|
||
|
|
priv->can.bittiming_const = &mcp25xxfd_nominal_bittiming_const;
|
||
|
|
priv->can.do_set_bittiming = &mcp25xxfd_do_set_nominal_bittiming;
|
||
|
|
priv->can.data_bittiming_const = &mcp25xxfd_data_bittiming_const;
|
||
|
|
priv->can.do_set_data_bittiming = &mcp25xxfd_do_set_data_bittiming;
|
||
|
|
priv->can.do_set_mode = mcp25xxfd_do_set_mode;
|
||
|
|
priv->can.do_get_berr_counter = mcp25xxfd_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 mcp25xxfd_model)of_id->data;
|
||
|
|
else
|
||
|
|
priv->model = spi_get_device_id(spi)->driver_data;
|
||
|
|
|
||
|
|
spi_set_drvdata(spi, priv);
|
||
|
|
priv->spi = spi;
|
||
|
|
priv->net = net;
|
||
|
|
priv->clk = clk;
|
||
|
|
|
||
|
|
priv->clk_user_mask = MCP25XXFD_CLK_USER_CAN;
|
||
|
|
|
||
|
|
mutex_init(&priv->clk_user_lock);
|
||
|
|
mutex_init(&priv->spi_rxtx_lock);
|
||
|
|
|
||
|
|
/* enable the clock and mark as enabled */
|
||
|
|
priv->clk_user_mask = MCP25XXFD_CLK_USER_CAN;
|
||
|
|
ret = clk_prepare_enable(clk);
|
||
|
|
if (ret)
|
||
|
|
goto out_free;
|
||
|
|
|
||
|
|
/* Setup GPIO controller */
|
||
|
|
ret = mcp25xxfd_gpio_setup(spi);
|
||
|
|
if (ret)
|
||
|
|
goto out_clk;
|
||
|
|
|
||
|
|
/* all by default as push/pull */
|
||
|
|
priv->config.gpio_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 <= MCP25XXFD_AUTO_PLL_MAX_CLOCK_FREQUENCY);
|
||
|
|
|
||
|
|
/* check in device tree for overrrides */
|
||
|
|
ret = mcp25xxfd_of_parse(priv);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* decide on real can clock rate */
|
||
|
|
priv->can.clock.freq = freq;
|
||
|
|
if (priv->config.clock_pll) {
|
||
|
|
priv->can.clock.freq *= MCP25XXFD_PLL_MULTIPLIER;
|
||
|
|
if (priv->can.clock.freq > MCP25XXFD_MAX_CLOCK_FREQUENCY) {
|
||
|
|
dev_err(&spi->dev,
|
||
|
|
"PLL clock frequency %i would exceed limit\n",
|
||
|
|
priv->can.clock.freq
|
||
|
|
);
|
||
|
|
return -EINVAL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (priv->config.clock_div2)
|
||
|
|
priv->can.clock.freq /= MCP25XXFD_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 /= MCP25XXFD_SCLK_DIVIDER;
|
||
|
|
priv->spi_speed_hz /= MCP25XXFD_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;
|
||
|
|
|
||
|
|
ret = mcp25xxfd_power_enable(priv->power, 1);
|
||
|
|
if (ret)
|
||
|
|
goto out_clk;
|
||
|
|
|
||
|
|
SET_NETDEV_DEV(net, &spi->dev);
|
||
|
|
|
||
|
|
ret = mcp25xxfd_hw_probe(spi);
|
||
|
|
/* on error retry a second time */
|
||
|
|
if (ret == -ENODEV) {
|
||
|
|
ret = mcp25xxfd_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);
|
||
|
|
}
|
||
|
|
|
||
|
|
/* setting up GPIO+INT as PUSHPULL , TXCAN PUSH/PULL, no Standby */
|
||
|
|
priv->regs.iocon = 0;
|
||
|
|
|
||
|
|
/* SOF/CLOCKOUT pin 3 */
|
||
|
|
if (priv->config.clock_odiv < 1)
|
||
|
|
priv->regs.iocon |= MCP25XXFD_IOCON_SOF;
|
||
|
|
|
||
|
|
/* INT/GPIO (probably also clockout) as open drain */
|
||
|
|
if (priv->config.gpio_opendrain)
|
||
|
|
priv->regs.iocon |= MCP25XXFD_IOCON_INTOD;
|
||
|
|
|
||
|
|
ret = mcp25xxfd_cmd_write(spi, MCP25XXFD_IOCON, priv->regs.iocon,
|
||
|
|
priv->spi_setup_speed_hz);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
/* and put controller to sleep */
|
||
|
|
mcp25xxfd_hw_sleep(spi);
|
||
|
|
|
||
|
|
ret = register_candev(net);
|
||
|
|
if (ret)
|
||
|
|
goto error_probe;
|
||
|
|
|
||
|
|
/* register debugfs */
|
||
|
|
mcp25xxfd_debugfs_add(priv);
|
||
|
|
|
||
|
|
devm_can_led_init(net);
|
||
|
|
|
||
|
|
netdev_info(net, "MCP%x successfully initialized.\n", priv->model);
|
||
|
|
return 0;
|
||
|
|
|
||
|
|
error_probe:
|
||
|
|
mcp25xxfd_power_enable(priv->power, 0);
|
||
|
|
|
||
|
|
out_clk:
|
||
|
|
mcp25xxfd_stop_clock(spi, MCP25XXFD_CLK_USER_CAN);
|
||
|
|
|
||
|
|
out_free:
|
||
|
|
free_candev(net);
|
||
|
|
dev_err(&spi->dev, "Probe failed, err=%d\n", -ret);
|
||
|
|
return ret;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int mcp25xxfd_can_remove(struct spi_device *spi)
|
||
|
|
{
|
||
|
|
struct mcp25xxfd_priv *priv = spi_get_drvdata(spi);
|
||
|
|
struct net_device *net = priv->net;
|
||
|
|
int i;
|
||
|
|
|
||
|
|
mcp25xxfd_debugfs_remove(priv);
|
||
|
|
|
||
|
|
unregister_candev(net);
|
||
|
|
|
||
|
|
mcp25xxfd_power_enable(priv->power, 0);
|
||
|
|
|
||
|
|
if (!IS_ERR(priv->clk))
|
||
|
|
clk_disable_unprepare(priv->clk);
|
||
|
|
|
||
|
|
for (i = 0; i < MCP25xxFD_SPI_TRANSFER_BUFFER_MAX_COUNT; i++) {
|
||
|
|
kvfree(spi_transfer_buffer.spi_tx_kzalloc[i]);
|
||
|
|
kvfree(spi_transfer_buffer.spi_rx_kzalloc[i]);
|
||
|
|
}
|
||
|
|
free_candev(net);
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
static int __maybe_unused mcp25xxfd_can_suspend(struct device *dev)
|
||
|
|
{
|
||
|
|
struct spi_device *spi = NULL;
|
||
|
|
struct mcp25xxfd_priv *priv = NULL;
|
||
|
|
struct net_device *net = NULL;
|
||
|
|
|
||
|
|
spi = to_spi_device(dev);
|
||
|
|
if (!spi)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
priv = spi_get_drvdata(spi);
|
||
|
|
net = priv->net;
|
||
|
|
|
||
|
|
priv->force_quit = 1;
|
||
|
|
disable_irq(spi->irq);
|
||
|
|
|
||
|
|
if (netif_running(net)) {
|
||
|
|
netif_device_detach(net);
|
||
|
|
|
||
|
|
mcp25xxfd_hw_sleep(spi);
|
||
|
|
mcp25xxfd_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 mcp25xxfd_can_resume(struct device *dev)
|
||
|
|
{
|
||
|
|
struct spi_device *spi = NULL;
|
||
|
|
struct mcp25xxfd_priv *priv = NULL;
|
||
|
|
|
||
|
|
spi = to_spi_device(dev);
|
||
|
|
if (!spi)
|
||
|
|
return -EINVAL;
|
||
|
|
|
||
|
|
priv = spi_get_drvdata(spi);
|
||
|
|
|
||
|
|
if (priv->after_suspend & AFTER_SUSPEND_POWER)
|
||
|
|
mcp25xxfd_power_enable(priv->power, 1);
|
||
|
|
|
||
|
|
if (priv->after_suspend & AFTER_SUSPEND_UP)
|
||
|
|
mcp25xxfd_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(mcp25xxfd_can_pm_ops, mcp25xxfd_can_suspend,
|
||
|
|
mcp25xxfd_can_resume);
|
||
|
|
|
||
|
|
static struct spi_driver mcp25xxfd_can_driver = {
|
||
|
|
.driver = {
|
||
|
|
.name = DEVICE_NAME,
|
||
|
|
.of_match_table = mcp25xxfd_of_match,
|
||
|
|
.pm = &mcp25xxfd_can_pm_ops,
|
||
|
|
},
|
||
|
|
.probe = mcp25xxfd_can_probe,
|
||
|
|
.remove = mcp25xxfd_can_remove,
|
||
|
|
};
|
||
|
|
|
||
|
|
static int __init mcp25xxfd_can_driver_init(void)
|
||
|
|
{
|
||
|
|
int ret;
|
||
|
|
|
||
|
|
ret = spi_register_driver(&mcp25xxfd_can_driver);
|
||
|
|
if (ret)
|
||
|
|
return ret;
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
module_init(mcp25xxfd_can_driver_init);
|
||
|
|
|
||
|
|
static void __exit mcp25xxfd_can_driver_exit(void)
|
||
|
|
{
|
||
|
|
spi_unregister_driver(&mcp25xxfd_can_driver);
|
||
|
|
}
|
||
|
|
module_exit(mcp25xxfd_can_driver_exit);
|
||
|
|
|
||
|
|
MODULE_DESCRIPTION("Microchip 25XXFD CAN driver");
|
||
|
|
MODULE_LICENSE("GPL v2");
|