diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:27:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:27:49 +0000 |
commit | ace9429bb58fd418f0c81d4c2835699bddf6bde6 (patch) | |
tree | b2d64bc10158fdd5497876388cd68142ca374ed3 /drivers/net/can/spi | |
parent | Initial commit. (diff) | |
download | linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.tar.xz linux-ace9429bb58fd418f0c81d4c2835699bddf6bde6.zip |
Adding upstream version 6.6.15.upstream/6.6.15
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/net/can/spi')
21 files changed, 8640 insertions, 0 deletions
diff --git a/drivers/net/can/spi/Kconfig b/drivers/net/can/spi/Kconfig new file mode 100644 index 0000000000..f454492100 --- /dev/null +++ b/drivers/net/can/spi/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only +menu "CAN SPI interfaces" + depends on SPI + +config CAN_HI311X + tristate "Holt HI311x SPI CAN controllers" + help + Driver for the Holt HI311x SPI CAN controllers. + +config CAN_MCP251X + tristate "Microchip MCP251x and MCP25625 SPI CAN controllers" + help + Driver for the Microchip MCP251x and MCP25625 SPI CAN + controllers. + +source "drivers/net/can/spi/mcp251xfd/Kconfig" + +endmenu diff --git a/drivers/net/can/spi/Makefile b/drivers/net/can/spi/Makefile new file mode 100644 index 0000000000..33e3f60bbc --- /dev/null +++ b/drivers/net/can/spi/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Makefile for the Linux Controller Area Network SPI drivers. +# + + +obj-$(CONFIG_CAN_HI311X) += hi311x.o +obj-$(CONFIG_CAN_MCP251X) += mcp251x.o +obj-y += mcp251xfd/ diff --git a/drivers/net/can/spi/hi311x.c b/drivers/net/can/spi/hi311x.c new file mode 100644 index 0000000000..e1b8533a60 --- /dev/null +++ b/drivers/net/can/spi/hi311x.c @@ -0,0 +1,1033 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* CAN bus driver for Holt HI3110 CAN Controller with SPI Interface + * + * Copyright(C) Timesys Corporation 2016 + * + * Based on Microchip 251x CAN Controller (mcp251x) Linux kernel driver + * Copyright 2009 Christian Pellegrin EVOL S.r.l. + * Copyright 2007 Raymarine UK, Ltd. All Rights Reserved. + * Copyright 2006 Arcom Control Systems Ltd. + * + * Based on CAN bus driver for the CCAN controller written by + * - Sascha Hauer, Marc Kleine-Budde, Pengutronix + * - Simon Kallweit, intefo AG + * Copyright 2007 + */ + +#include <linux/can/core.h> +#include <linux/can/dev.h> +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/ethtool.h> +#include <linux/freezer.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/uaccess.h> + +#define HI3110_MASTER_RESET 0x56 +#define HI3110_READ_CTRL0 0xD2 +#define HI3110_READ_CTRL1 0xD4 +#define HI3110_READ_STATF 0xE2 +#define HI3110_WRITE_CTRL0 0x14 +#define HI3110_WRITE_CTRL1 0x16 +#define HI3110_WRITE_INTE 0x1C +#define HI3110_WRITE_BTR0 0x18 +#define HI3110_WRITE_BTR1 0x1A +#define HI3110_READ_BTR0 0xD6 +#define HI3110_READ_BTR1 0xD8 +#define HI3110_READ_INTF 0xDE +#define HI3110_READ_ERR 0xDC +#define HI3110_READ_FIFO_WOTIME 0x48 +#define HI3110_WRITE_FIFO 0x12 +#define HI3110_READ_MESSTAT 0xDA +#define HI3110_READ_REC 0xEA +#define HI3110_READ_TEC 0xEC + +#define HI3110_CTRL0_MODE_MASK (7 << 5) +#define HI3110_CTRL0_NORMAL_MODE (0 << 5) +#define HI3110_CTRL0_LOOPBACK_MODE (1 << 5) +#define HI3110_CTRL0_MONITOR_MODE (2 << 5) +#define HI3110_CTRL0_SLEEP_MODE (3 << 5) +#define HI3110_CTRL0_INIT_MODE (4 << 5) + +#define HI3110_CTRL1_TXEN BIT(7) + +#define HI3110_INT_RXTMP BIT(7) +#define HI3110_INT_RXFIFO BIT(6) +#define HI3110_INT_TXCPLT BIT(5) +#define HI3110_INT_BUSERR BIT(4) +#define HI3110_INT_MCHG BIT(3) +#define HI3110_INT_WAKEUP BIT(2) +#define HI3110_INT_F1MESS BIT(1) +#define HI3110_INT_F0MESS BIT(0) + +#define HI3110_ERR_BUSOFF BIT(7) +#define HI3110_ERR_TXERRP BIT(6) +#define HI3110_ERR_RXERRP BIT(5) +#define HI3110_ERR_BITERR BIT(4) +#define HI3110_ERR_FRMERR BIT(3) +#define HI3110_ERR_CRCERR BIT(2) +#define HI3110_ERR_ACKERR BIT(1) +#define HI3110_ERR_STUFERR BIT(0) +#define HI3110_ERR_PROTOCOL_MASK (0x1F) +#define HI3110_ERR_PASSIVE_MASK (0x60) + +#define HI3110_STAT_RXFMTY BIT(1) +#define HI3110_STAT_BUSOFF BIT(2) +#define HI3110_STAT_ERRP BIT(3) +#define HI3110_STAT_ERRW BIT(4) +#define HI3110_STAT_TXMTY BIT(7) + +#define HI3110_BTR0_SJW_SHIFT 6 +#define HI3110_BTR0_BRP_SHIFT 0 + +#define HI3110_BTR1_SAMP_3PERBIT (1 << 7) +#define HI3110_BTR1_SAMP_1PERBIT (0 << 7) +#define HI3110_BTR1_TSEG2_SHIFT 4 +#define HI3110_BTR1_TSEG1_SHIFT 0 + +#define HI3110_FIFO_WOTIME_TAG_OFF 0 +#define HI3110_FIFO_WOTIME_ID_OFF 1 +#define HI3110_FIFO_WOTIME_DLC_OFF 5 +#define HI3110_FIFO_WOTIME_DAT_OFF 6 + +#define HI3110_FIFO_WOTIME_TAG_IDE BIT(7) +#define HI3110_FIFO_WOTIME_ID_RTR BIT(0) + +#define HI3110_FIFO_TAG_OFF 0 +#define HI3110_FIFO_ID_OFF 1 +#define HI3110_FIFO_STD_DLC_OFF 3 +#define HI3110_FIFO_STD_DATA_OFF 4 +#define HI3110_FIFO_EXT_DLC_OFF 5 +#define HI3110_FIFO_EXT_DATA_OFF 6 + +#define HI3110_CAN_MAX_DATA_LEN 8 +#define HI3110_RX_BUF_LEN 15 +#define HI3110_TX_STD_BUF_LEN 12 +#define HI3110_TX_EXT_BUF_LEN 14 +#define HI3110_CAN_FRAME_MAX_BITS 128 +#define HI3110_EFF_FLAGS 0x18 /* IDE + SRR */ + +#define HI3110_TX_ECHO_SKB_MAX 1 + +#define HI3110_OST_DELAY_MS (10) + +#define DEVICE_NAME "hi3110" + +static const struct can_bittiming_const hi3110_bittiming_const = { + .name = DEVICE_NAME, + .tseg1_min = 2, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +enum hi3110_model { + CAN_HI3110_HI3110 = 0x3110, +}; + +struct hi3110_priv { + struct can_priv can; + struct net_device *net; + struct spi_device *spi; + enum hi3110_model model; + + struct mutex hi3110_lock; /* SPI device lock */ + + u8 *spi_tx_buf; + u8 *spi_rx_buf; + + struct sk_buff *tx_skb; + + struct workqueue_struct *wq; + struct work_struct tx_work; + struct work_struct restart_work; + + int force_quit; + int after_suspend; +#define HI3110_AFTER_SUSPEND_UP 1 +#define HI3110_AFTER_SUSPEND_DOWN 2 +#define HI3110_AFTER_SUSPEND_POWER 4 +#define HI3110_AFTER_SUSPEND_RESTART 8 + int restart_tx; + bool tx_busy; + + struct regulator *power; + struct regulator *transceiver; + struct clk *clk; +}; + +static void hi3110_clean(struct net_device *net) +{ + struct hi3110_priv *priv = netdev_priv(net); + + if (priv->tx_skb || priv->tx_busy) + net->stats.tx_errors++; + dev_kfree_skb(priv->tx_skb); + if (priv->tx_busy) + can_free_echo_skb(priv->net, 0, NULL); + priv->tx_skb = NULL; + priv->tx_busy = false; +} + +/* Note about handling of error return of hi3110_spi_trans: accessing + * registers via SPI is not really different conceptually than using + * normal I/O assembler instructions, although it's much more + * complicated from a practical POV. So it's not advisable to always + * check the return value of this function. Imagine that every + * read{b,l}, write{b,l} and friends would be bracketed in "if ( < 0) + * error();", it would be a great mess (well there are some situation + * when exception handling C++ like could be useful after all). So we + * just check that transfers are OK at the beginning of our + * conversation with the chip and to avoid doing really nasty things + * (like injecting bogus packets in the network stack). + */ +static int hi3110_spi_trans(struct spi_device *spi, int len) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + struct spi_transfer t = { + .tx_buf = priv->spi_tx_buf, + .rx_buf = priv->spi_rx_buf, + .len = len, + .cs_change = 0, + }; + struct spi_message m; + int ret; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + ret = spi_sync(spi, &m); + + if (ret) + dev_err(&spi->dev, "spi transfer failed: ret = %d\n", ret); + return ret; +} + +static int hi3110_cmd(struct spi_device *spi, u8 command) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + + priv->spi_tx_buf[0] = command; + dev_dbg(&spi->dev, "hi3110_cmd: %02X\n", command); + + return hi3110_spi_trans(spi, 1); +} + +static u8 hi3110_read(struct spi_device *spi, u8 command) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + u8 val = 0; + + priv->spi_tx_buf[0] = command; + hi3110_spi_trans(spi, 2); + val = priv->spi_rx_buf[1]; + + return val; +} + +static void hi3110_write(struct spi_device *spi, u8 reg, u8 val) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + + priv->spi_tx_buf[0] = reg; + priv->spi_tx_buf[1] = val; + hi3110_spi_trans(spi, 2); +} + +static void hi3110_hw_tx_frame(struct spi_device *spi, u8 *buf, int len) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + + priv->spi_tx_buf[0] = HI3110_WRITE_FIFO; + memcpy(priv->spi_tx_buf + 1, buf, len); + hi3110_spi_trans(spi, len + 1); +} + +static void hi3110_hw_tx(struct spi_device *spi, struct can_frame *frame) +{ + u8 buf[HI3110_TX_EXT_BUF_LEN]; + + buf[HI3110_FIFO_TAG_OFF] = 0; + + if (frame->can_id & CAN_EFF_FLAG) { + /* Extended frame */ + buf[HI3110_FIFO_ID_OFF] = (frame->can_id & CAN_EFF_MASK) >> 21; + buf[HI3110_FIFO_ID_OFF + 1] = + (((frame->can_id & CAN_EFF_MASK) >> 13) & 0xe0) | + HI3110_EFF_FLAGS | + (((frame->can_id & CAN_EFF_MASK) >> 15) & 0x07); + buf[HI3110_FIFO_ID_OFF + 2] = + (frame->can_id & CAN_EFF_MASK) >> 7; + buf[HI3110_FIFO_ID_OFF + 3] = + ((frame->can_id & CAN_EFF_MASK) << 1) | + ((frame->can_id & CAN_RTR_FLAG) ? 1 : 0); + + buf[HI3110_FIFO_EXT_DLC_OFF] = frame->len; + + memcpy(buf + HI3110_FIFO_EXT_DATA_OFF, + frame->data, frame->len); + + hi3110_hw_tx_frame(spi, buf, HI3110_TX_EXT_BUF_LEN - + (HI3110_CAN_MAX_DATA_LEN - frame->len)); + } else { + /* Standard frame */ + buf[HI3110_FIFO_ID_OFF] = (frame->can_id & CAN_SFF_MASK) >> 3; + buf[HI3110_FIFO_ID_OFF + 1] = + ((frame->can_id & CAN_SFF_MASK) << 5) | + ((frame->can_id & CAN_RTR_FLAG) ? (1 << 4) : 0); + + buf[HI3110_FIFO_STD_DLC_OFF] = frame->len; + + memcpy(buf + HI3110_FIFO_STD_DATA_OFF, + frame->data, frame->len); + + hi3110_hw_tx_frame(spi, buf, HI3110_TX_STD_BUF_LEN - + (HI3110_CAN_MAX_DATA_LEN - frame->len)); + } +} + +static void hi3110_hw_rx_frame(struct spi_device *spi, u8 *buf) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + + priv->spi_tx_buf[0] = HI3110_READ_FIFO_WOTIME; + hi3110_spi_trans(spi, HI3110_RX_BUF_LEN); + memcpy(buf, priv->spi_rx_buf + 1, HI3110_RX_BUF_LEN - 1); +} + +static void hi3110_hw_rx(struct spi_device *spi) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + struct sk_buff *skb; + struct can_frame *frame; + u8 buf[HI3110_RX_BUF_LEN - 1]; + + skb = alloc_can_skb(priv->net, &frame); + if (!skb) { + priv->net->stats.rx_dropped++; + return; + } + + hi3110_hw_rx_frame(spi, buf); + if (buf[HI3110_FIFO_WOTIME_TAG_OFF] & HI3110_FIFO_WOTIME_TAG_IDE) { + /* IDE is recessive (1), indicating extended 29-bit frame */ + frame->can_id = CAN_EFF_FLAG; + frame->can_id |= + (buf[HI3110_FIFO_WOTIME_ID_OFF] << 21) | + (((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0xE0) >> 5) << 18) | + ((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0x07) << 15) | + (buf[HI3110_FIFO_WOTIME_ID_OFF + 2] << 7) | + (buf[HI3110_FIFO_WOTIME_ID_OFF + 3] >> 1); + } else { + /* IDE is dominant (0), frame indicating standard 11-bit */ + frame->can_id = + (buf[HI3110_FIFO_WOTIME_ID_OFF] << 3) | + ((buf[HI3110_FIFO_WOTIME_ID_OFF + 1] & 0xE0) >> 5); + } + + /* Data length */ + frame->len = can_cc_dlc2len(buf[HI3110_FIFO_WOTIME_DLC_OFF] & 0x0F); + + if (buf[HI3110_FIFO_WOTIME_ID_OFF + 3] & HI3110_FIFO_WOTIME_ID_RTR) { + frame->can_id |= CAN_RTR_FLAG; + } else { + memcpy(frame->data, buf + HI3110_FIFO_WOTIME_DAT_OFF, + frame->len); + + priv->net->stats.rx_bytes += frame->len; + } + priv->net->stats.rx_packets++; + + netif_rx(skb); +} + +static void hi3110_hw_sleep(struct spi_device *spi) +{ + hi3110_write(spi, HI3110_WRITE_CTRL0, HI3110_CTRL0_SLEEP_MODE); +} + +static netdev_tx_t hi3110_hard_start_xmit(struct sk_buff *skb, + struct net_device *net) +{ + struct hi3110_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + + if (priv->tx_skb || priv->tx_busy) { + dev_err(&spi->dev, "hard_xmit called while tx busy\n"); + return NETDEV_TX_BUSY; + } + + if (can_dev_dropped_skb(net, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(net); + priv->tx_skb = skb; + queue_work(priv->wq, &priv->tx_work); + + return NETDEV_TX_OK; +} + +static int hi3110_do_set_mode(struct net_device *net, enum can_mode mode) +{ + struct hi3110_priv *priv = netdev_priv(net); + + switch (mode) { + case CAN_MODE_START: + hi3110_clean(net); + /* We have to delay work since SPI I/O may sleep */ + priv->can.state = CAN_STATE_ERROR_ACTIVE; + priv->restart_tx = 1; + if (priv->can.restart_ms == 0) + priv->after_suspend = HI3110_AFTER_SUSPEND_RESTART; + queue_work(priv->wq, &priv->restart_work); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int hi3110_get_berr_counter(const struct net_device *net, + struct can_berr_counter *bec) +{ + struct hi3110_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + + mutex_lock(&priv->hi3110_lock); + bec->txerr = hi3110_read(spi, HI3110_READ_TEC); + bec->rxerr = hi3110_read(spi, HI3110_READ_REC); + mutex_unlock(&priv->hi3110_lock); + + return 0; +} + +static int hi3110_set_normal_mode(struct spi_device *spi) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + u8 reg = 0; + + hi3110_write(spi, HI3110_WRITE_INTE, HI3110_INT_BUSERR | + HI3110_INT_RXFIFO | HI3110_INT_TXCPLT); + + /* Enable TX */ + hi3110_write(spi, HI3110_WRITE_CTRL1, HI3110_CTRL1_TXEN); + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + reg = HI3110_CTRL0_LOOPBACK_MODE; + else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + reg = HI3110_CTRL0_MONITOR_MODE; + else + reg = HI3110_CTRL0_NORMAL_MODE; + + hi3110_write(spi, HI3110_WRITE_CTRL0, reg); + + /* Wait for the device to enter the mode */ + mdelay(HI3110_OST_DELAY_MS); + reg = hi3110_read(spi, HI3110_READ_CTRL0); + if ((reg & HI3110_CTRL0_MODE_MASK) != reg) + return -EBUSY; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + return 0; +} + +static int hi3110_do_set_bittiming(struct net_device *net) +{ + struct hi3110_priv *priv = netdev_priv(net); + struct can_bittiming *bt = &priv->can.bittiming; + struct spi_device *spi = priv->spi; + + hi3110_write(spi, HI3110_WRITE_BTR0, + ((bt->sjw - 1) << HI3110_BTR0_SJW_SHIFT) | + ((bt->brp - 1) << HI3110_BTR0_BRP_SHIFT)); + + hi3110_write(spi, HI3110_WRITE_BTR1, + (priv->can.ctrlmode & + CAN_CTRLMODE_3_SAMPLES ? + HI3110_BTR1_SAMP_3PERBIT : HI3110_BTR1_SAMP_1PERBIT) | + ((bt->phase_seg1 + bt->prop_seg - 1) + << HI3110_BTR1_TSEG1_SHIFT) | + ((bt->phase_seg2 - 1) << HI3110_BTR1_TSEG2_SHIFT)); + + dev_dbg(&spi->dev, "BT: 0x%02x 0x%02x\n", + hi3110_read(spi, HI3110_READ_BTR0), + hi3110_read(spi, HI3110_READ_BTR1)); + + return 0; +} + +static int hi3110_setup(struct net_device *net) +{ + hi3110_do_set_bittiming(net); + return 0; +} + +static int hi3110_hw_reset(struct spi_device *spi) +{ + u8 reg; + int ret; + + /* Wait for oscillator startup timer after power up */ + mdelay(HI3110_OST_DELAY_MS); + + ret = hi3110_cmd(spi, HI3110_MASTER_RESET); + if (ret) + return ret; + + /* Wait for oscillator startup timer after reset */ + mdelay(HI3110_OST_DELAY_MS); + + reg = hi3110_read(spi, HI3110_READ_CTRL0); + if ((reg & HI3110_CTRL0_MODE_MASK) != HI3110_CTRL0_INIT_MODE) + return -ENODEV; + + /* As per the datasheet it appears the error flags are + * not cleared on reset. Explicitly clear them by performing a read + */ + hi3110_read(spi, HI3110_READ_ERR); + + return 0; +} + +static int hi3110_hw_probe(struct spi_device *spi) +{ + u8 statf; + + hi3110_hw_reset(spi); + + /* Confirm correct operation by checking against reset values + * in datasheet + */ + statf = hi3110_read(spi, HI3110_READ_STATF); + + dev_dbg(&spi->dev, "statf: %02X\n", statf); + + if (statf != 0x82) + return -ENODEV; + + return 0; +} + +static int hi3110_power_enable(struct regulator *reg, int enable) +{ + if (IS_ERR_OR_NULL(reg)) + return 0; + + if (enable) + return regulator_enable(reg); + else + return regulator_disable(reg); +} + +static int hi3110_stop(struct net_device *net) +{ + struct hi3110_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + + close_candev(net); + + priv->force_quit = 1; + free_irq(spi->irq, priv); + destroy_workqueue(priv->wq); + priv->wq = NULL; + + mutex_lock(&priv->hi3110_lock); + + /* Disable transmit, interrupts and clear flags */ + hi3110_write(spi, HI3110_WRITE_CTRL1, 0x0); + hi3110_write(spi, HI3110_WRITE_INTE, 0x0); + hi3110_read(spi, HI3110_READ_INTF); + + hi3110_clean(net); + + hi3110_hw_sleep(spi); + + hi3110_power_enable(priv->transceiver, 0); + + priv->can.state = CAN_STATE_STOPPED; + + mutex_unlock(&priv->hi3110_lock); + + return 0; +} + +static void hi3110_tx_work_handler(struct work_struct *ws) +{ + struct hi3110_priv *priv = container_of(ws, struct hi3110_priv, + tx_work); + struct spi_device *spi = priv->spi; + struct net_device *net = priv->net; + struct can_frame *frame; + + mutex_lock(&priv->hi3110_lock); + if (priv->tx_skb) { + if (priv->can.state == CAN_STATE_BUS_OFF) { + hi3110_clean(net); + } else { + frame = (struct can_frame *)priv->tx_skb->data; + hi3110_hw_tx(spi, frame); + priv->tx_busy = true; + can_put_echo_skb(priv->tx_skb, net, 0, 0); + priv->tx_skb = NULL; + } + } + mutex_unlock(&priv->hi3110_lock); +} + +static void hi3110_restart_work_handler(struct work_struct *ws) +{ + struct hi3110_priv *priv = container_of(ws, struct hi3110_priv, + restart_work); + struct spi_device *spi = priv->spi; + struct net_device *net = priv->net; + + mutex_lock(&priv->hi3110_lock); + if (priv->after_suspend) { + hi3110_hw_reset(spi); + hi3110_setup(net); + if (priv->after_suspend & HI3110_AFTER_SUSPEND_RESTART) { + hi3110_set_normal_mode(spi); + } else if (priv->after_suspend & HI3110_AFTER_SUSPEND_UP) { + netif_device_attach(net); + hi3110_clean(net); + hi3110_set_normal_mode(spi); + netif_wake_queue(net); + } else { + hi3110_hw_sleep(spi); + } + priv->after_suspend = 0; + priv->force_quit = 0; + } + + if (priv->restart_tx) { + priv->restart_tx = 0; + hi3110_hw_reset(spi); + hi3110_setup(net); + hi3110_clean(net); + hi3110_set_normal_mode(spi); + netif_wake_queue(net); + } + mutex_unlock(&priv->hi3110_lock); +} + +static irqreturn_t hi3110_can_ist(int irq, void *dev_id) +{ + struct hi3110_priv *priv = dev_id; + struct spi_device *spi = priv->spi; + struct net_device *net = priv->net; + + mutex_lock(&priv->hi3110_lock); + + while (!priv->force_quit) { + enum can_state new_state; + u8 intf, eflag, statf; + + while (!(HI3110_STAT_RXFMTY & + (statf = hi3110_read(spi, HI3110_READ_STATF)))) { + hi3110_hw_rx(spi); + } + + intf = hi3110_read(spi, HI3110_READ_INTF); + eflag = hi3110_read(spi, HI3110_READ_ERR); + /* Update can state */ + if (eflag & HI3110_ERR_BUSOFF) + new_state = CAN_STATE_BUS_OFF; + else if (eflag & HI3110_ERR_PASSIVE_MASK) + new_state = CAN_STATE_ERROR_PASSIVE; + else if (statf & HI3110_STAT_ERRW) + new_state = CAN_STATE_ERROR_WARNING; + else + new_state = CAN_STATE_ERROR_ACTIVE; + + if (new_state != priv->can.state) { + struct can_frame *cf; + struct sk_buff *skb; + enum can_state rx_state, tx_state; + u8 rxerr, txerr; + + skb = alloc_can_err_skb(net, &cf); + if (!skb) + break; + + txerr = hi3110_read(spi, HI3110_READ_TEC); + rxerr = hi3110_read(spi, HI3110_READ_REC); + tx_state = txerr >= rxerr ? new_state : 0; + rx_state = txerr <= rxerr ? new_state : 0; + can_change_state(net, cf, tx_state, rx_state); + netif_rx(skb); + + if (new_state == CAN_STATE_BUS_OFF) { + can_bus_off(net); + if (priv->can.restart_ms == 0) { + priv->force_quit = 1; + hi3110_hw_sleep(spi); + break; + } + } else { + cf->can_id |= CAN_ERR_CNT; + cf->data[6] = txerr; + cf->data[7] = rxerr; + } + } + + /* Update bus errors */ + if ((intf & HI3110_INT_BUSERR) && + (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)) { + struct can_frame *cf; + struct sk_buff *skb; + + /* Check for protocol errors */ + if (eflag & HI3110_ERR_PROTOCOL_MASK) { + skb = alloc_can_err_skb(net, &cf); + if (!skb) + break; + + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + priv->can.can_stats.bus_error++; + priv->net->stats.rx_errors++; + if (eflag & HI3110_ERR_BITERR) + cf->data[2] |= CAN_ERR_PROT_BIT; + else if (eflag & HI3110_ERR_FRMERR) + cf->data[2] |= CAN_ERR_PROT_FORM; + else if (eflag & HI3110_ERR_STUFERR) + cf->data[2] |= CAN_ERR_PROT_STUFF; + else if (eflag & HI3110_ERR_CRCERR) + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; + else if (eflag & HI3110_ERR_ACKERR) + cf->data[3] |= CAN_ERR_PROT_LOC_ACK; + + cf->data[6] = hi3110_read(spi, HI3110_READ_TEC); + cf->data[7] = hi3110_read(spi, HI3110_READ_REC); + netdev_dbg(priv->net, "Bus Error\n"); + netif_rx(skb); + } + } + + if (priv->tx_busy && statf & HI3110_STAT_TXMTY) { + net->stats.tx_packets++; + net->stats.tx_bytes += can_get_echo_skb(net, 0, NULL); + priv->tx_busy = false; + netif_wake_queue(net); + } + + if (intf == 0) + break; + } + mutex_unlock(&priv->hi3110_lock); + return IRQ_HANDLED; +} + +static int hi3110_open(struct net_device *net) +{ + struct hi3110_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + unsigned long flags = IRQF_ONESHOT | IRQF_TRIGGER_HIGH; + int ret; + + ret = open_candev(net); + if (ret) + return ret; + + mutex_lock(&priv->hi3110_lock); + hi3110_power_enable(priv->transceiver, 1); + + priv->force_quit = 0; + priv->tx_skb = NULL; + priv->tx_busy = false; + + ret = request_threaded_irq(spi->irq, NULL, hi3110_can_ist, + flags, DEVICE_NAME, priv); + if (ret) { + dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq); + goto out_close; + } + + priv->wq = alloc_workqueue("hi3110_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM, + 0); + if (!priv->wq) { + ret = -ENOMEM; + goto out_free_irq; + } + INIT_WORK(&priv->tx_work, hi3110_tx_work_handler); + INIT_WORK(&priv->restart_work, hi3110_restart_work_handler); + + ret = hi3110_hw_reset(spi); + if (ret) + goto out_free_wq; + + ret = hi3110_setup(net); + if (ret) + goto out_free_wq; + + ret = hi3110_set_normal_mode(spi); + if (ret) + goto out_free_wq; + + netif_wake_queue(net); + mutex_unlock(&priv->hi3110_lock); + + return 0; + + out_free_wq: + destroy_workqueue(priv->wq); + out_free_irq: + free_irq(spi->irq, priv); + hi3110_hw_sleep(spi); + out_close: + hi3110_power_enable(priv->transceiver, 0); + close_candev(net); + mutex_unlock(&priv->hi3110_lock); + return ret; +} + +static const struct net_device_ops hi3110_netdev_ops = { + .ndo_open = hi3110_open, + .ndo_stop = hi3110_stop, + .ndo_start_xmit = hi3110_hard_start_xmit, +}; + +static const struct ethtool_ops hi3110_ethtool_ops = { + .get_ts_info = ethtool_op_get_ts_info, +}; + +static const struct of_device_id hi3110_of_match[] = { + { + .compatible = "holt,hi3110", + .data = (void *)CAN_HI3110_HI3110, + }, + { } +}; +MODULE_DEVICE_TABLE(of, hi3110_of_match); + +static const struct spi_device_id hi3110_id_table[] = { + { + .name = "hi3110", + .driver_data = (kernel_ulong_t)CAN_HI3110_HI3110, + }, + { } +}; +MODULE_DEVICE_TABLE(spi, hi3110_id_table); + +static int hi3110_can_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct net_device *net; + struct hi3110_priv *priv; + const void *match; + struct clk *clk; + u32 freq; + int ret; + + clk = devm_clk_get_optional(&spi->dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "no CAN clock source defined\n"); + + if (clk) { + freq = clk_get_rate(clk); + } else { + ret = device_property_read_u32(dev, "clock-frequency", &freq); + if (ret) + return dev_err_probe(dev, ret, "Failed to get clock-frequency!\n"); + } + + /* Sanity check */ + if (freq > 40000000) + return -ERANGE; + + /* Allocate can/net device */ + net = alloc_candev(sizeof(struct hi3110_priv), HI3110_TX_ECHO_SKB_MAX); + if (!net) + return -ENOMEM; + + ret = clk_prepare_enable(clk); + if (ret) + goto out_free; + + net->netdev_ops = &hi3110_netdev_ops; + net->ethtool_ops = &hi3110_ethtool_ops; + net->flags |= IFF_ECHO; + + priv = netdev_priv(net); + priv->can.bittiming_const = &hi3110_bittiming_const; + priv->can.do_set_mode = hi3110_do_set_mode; + priv->can.do_get_berr_counter = hi3110_get_berr_counter; + priv->can.clock.freq = freq / 2; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | + CAN_CTRLMODE_BERR_REPORTING; + + match = device_get_match_data(dev); + if (match) + priv->model = (enum hi3110_model)(uintptr_t)match; + else + priv->model = spi_get_device_id(spi)->driver_data; + priv->net = net; + priv->clk = clk; + + spi_set_drvdata(spi, priv); + + /* Configure the SPI bus */ + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret) + goto out_clk; + + priv->power = devm_regulator_get_optional(&spi->dev, "vdd"); + priv->transceiver = devm_regulator_get_optional(&spi->dev, "xceiver"); + if ((PTR_ERR(priv->power) == -EPROBE_DEFER) || + (PTR_ERR(priv->transceiver) == -EPROBE_DEFER)) { + ret = -EPROBE_DEFER; + goto out_clk; + } + + ret = hi3110_power_enable(priv->power, 1); + if (ret) + goto out_clk; + + priv->spi = spi; + mutex_init(&priv->hi3110_lock); + + priv->spi_tx_buf = devm_kzalloc(&spi->dev, HI3110_RX_BUF_LEN, + GFP_KERNEL); + if (!priv->spi_tx_buf) { + ret = -ENOMEM; + goto error_probe; + } + priv->spi_rx_buf = devm_kzalloc(&spi->dev, HI3110_RX_BUF_LEN, + GFP_KERNEL); + + if (!priv->spi_rx_buf) { + ret = -ENOMEM; + goto error_probe; + } + + SET_NETDEV_DEV(net, &spi->dev); + + ret = hi3110_hw_probe(spi); + if (ret) { + dev_err_probe(dev, ret, "Cannot initialize %x. Wrong wiring?\n", priv->model); + goto error_probe; + } + hi3110_hw_sleep(spi); + + ret = register_candev(net); + if (ret) + goto error_probe; + + netdev_info(net, "%x successfully initialized.\n", priv->model); + + return 0; + + error_probe: + hi3110_power_enable(priv->power, 0); + + out_clk: + clk_disable_unprepare(clk); + + out_free: + free_candev(net); + + return dev_err_probe(dev, ret, "Probe failed\n"); +} + +static void hi3110_can_remove(struct spi_device *spi) +{ + struct hi3110_priv *priv = spi_get_drvdata(spi); + struct net_device *net = priv->net; + + unregister_candev(net); + + hi3110_power_enable(priv->power, 0); + + clk_disable_unprepare(priv->clk); + + free_candev(net); +} + +static int __maybe_unused hi3110_can_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct hi3110_priv *priv = spi_get_drvdata(spi); + struct net_device *net = priv->net; + + priv->force_quit = 1; + disable_irq(spi->irq); + + /* Note: at this point neither IST nor workqueues are running. + * open/stop cannot be called anyway so locking is not needed + */ + if (netif_running(net)) { + netif_device_detach(net); + + hi3110_hw_sleep(spi); + hi3110_power_enable(priv->transceiver, 0); + priv->after_suspend = HI3110_AFTER_SUSPEND_UP; + } else { + priv->after_suspend = HI3110_AFTER_SUSPEND_DOWN; + } + + if (!IS_ERR_OR_NULL(priv->power)) { + regulator_disable(priv->power); + priv->after_suspend |= HI3110_AFTER_SUSPEND_POWER; + } + + return 0; +} + +static int __maybe_unused hi3110_can_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct hi3110_priv *priv = spi_get_drvdata(spi); + + if (priv->after_suspend & HI3110_AFTER_SUSPEND_POWER) + hi3110_power_enable(priv->power, 1); + + if (priv->after_suspend & HI3110_AFTER_SUSPEND_UP) { + hi3110_power_enable(priv->transceiver, 1); + queue_work(priv->wq, &priv->restart_work); + } else { + priv->after_suspend = 0; + } + + priv->force_quit = 0; + enable_irq(spi->irq); + return 0; +} + +static SIMPLE_DEV_PM_OPS(hi3110_can_pm_ops, hi3110_can_suspend, hi3110_can_resume); + +static struct spi_driver hi3110_can_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = hi3110_of_match, + .pm = &hi3110_can_pm_ops, + }, + .id_table = hi3110_id_table, + .probe = hi3110_can_probe, + .remove = hi3110_can_remove, +}; + +module_spi_driver(hi3110_can_driver); + +MODULE_AUTHOR("Akshay Bhat <akshay.bhat@timesys.com>"); +MODULE_AUTHOR("Casey Fitzpatrick <casey.fitzpatrick@timesys.com>"); +MODULE_DESCRIPTION("Holt HI-3110 CAN driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/spi/mcp251x.c b/drivers/net/can/spi/mcp251x.c new file mode 100644 index 0000000000..79c4bab5f7 --- /dev/null +++ b/drivers/net/can/spi/mcp251x.c @@ -0,0 +1,1523 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* CAN bus driver for Microchip 251x/25625 CAN Controller with SPI Interface + * + * MCP2510 support and bug fixes by Christian Pellegrin + * <chripell@evolware.org> + * + * Copyright 2009 Christian Pellegrin EVOL S.r.l. + * + * Copyright 2007 Raymarine UK, Ltd. All Rights Reserved. + * Written under contract by: + * Chris Elston, Katalix Systems, Ltd. + * + * Based on Microchip MCP251x CAN controller driver written by + * David Vrabel, Copyright 2006 Arcom Control Systems Ltd. + * + * Based on CAN bus driver for the CCAN controller written by + * - Sascha Hauer, Marc Kleine-Budde, Pengutronix + * - Simon Kallweit, intefo AG + * Copyright 2007 + */ + +#include <linux/bitfield.h> +#include <linux/can/core.h> +#include <linux/can/dev.h> +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/ethtool.h> +#include <linux/freezer.h> +#include <linux/gpio.h> +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/uaccess.h> + +/* SPI interface instruction set */ +#define INSTRUCTION_WRITE 0x02 +#define INSTRUCTION_READ 0x03 +#define INSTRUCTION_BIT_MODIFY 0x05 +#define INSTRUCTION_LOAD_TXB(n) (0x40 + 2 * (n)) +#define INSTRUCTION_READ_RXB(n) (((n) == 0) ? 0x90 : 0x94) +#define INSTRUCTION_RESET 0xC0 +#define RTS_TXB0 0x01 +#define RTS_TXB1 0x02 +#define RTS_TXB2 0x04 +#define INSTRUCTION_RTS(n) (0x80 | ((n) & 0x07)) + +/* MPC251x registers */ +#define BFPCTRL 0x0c +# define BFPCTRL_B0BFM BIT(0) +# define BFPCTRL_B1BFM BIT(1) +# define BFPCTRL_BFM(n) (BFPCTRL_B0BFM << (n)) +# define BFPCTRL_BFM_MASK GENMASK(1, 0) +# define BFPCTRL_B0BFE BIT(2) +# define BFPCTRL_B1BFE BIT(3) +# define BFPCTRL_BFE(n) (BFPCTRL_B0BFE << (n)) +# define BFPCTRL_BFE_MASK GENMASK(3, 2) +# define BFPCTRL_B0BFS BIT(4) +# define BFPCTRL_B1BFS BIT(5) +# define BFPCTRL_BFS(n) (BFPCTRL_B0BFS << (n)) +# define BFPCTRL_BFS_MASK GENMASK(5, 4) +#define TXRTSCTRL 0x0d +# define TXRTSCTRL_B0RTSM BIT(0) +# define TXRTSCTRL_B1RTSM BIT(1) +# define TXRTSCTRL_B2RTSM BIT(2) +# define TXRTSCTRL_RTSM(n) (TXRTSCTRL_B0RTSM << (n)) +# define TXRTSCTRL_RTSM_MASK GENMASK(2, 0) +# define TXRTSCTRL_B0RTS BIT(3) +# define TXRTSCTRL_B1RTS BIT(4) +# define TXRTSCTRL_B2RTS BIT(5) +# define TXRTSCTRL_RTS(n) (TXRTSCTRL_B0RTS << (n)) +# define TXRTSCTRL_RTS_MASK GENMASK(5, 3) +#define CANSTAT 0x0e +#define CANCTRL 0x0f +# define CANCTRL_REQOP_MASK 0xe0 +# define CANCTRL_REQOP_CONF 0x80 +# define CANCTRL_REQOP_LISTEN_ONLY 0x60 +# define CANCTRL_REQOP_LOOPBACK 0x40 +# define CANCTRL_REQOP_SLEEP 0x20 +# define CANCTRL_REQOP_NORMAL 0x00 +# define CANCTRL_OSM 0x08 +# define CANCTRL_ABAT 0x10 +#define TEC 0x1c +#define REC 0x1d +#define CNF1 0x2a +# define CNF1_SJW_SHIFT 6 +#define CNF2 0x29 +# define CNF2_BTLMODE 0x80 +# define CNF2_SAM 0x40 +# define CNF2_PS1_SHIFT 3 +#define CNF3 0x28 +# define CNF3_SOF 0x08 +# define CNF3_WAKFIL 0x04 +# define CNF3_PHSEG2_MASK 0x07 +#define CANINTE 0x2b +# define CANINTE_MERRE 0x80 +# define CANINTE_WAKIE 0x40 +# define CANINTE_ERRIE 0x20 +# define CANINTE_TX2IE 0x10 +# define CANINTE_TX1IE 0x08 +# define CANINTE_TX0IE 0x04 +# define CANINTE_RX1IE 0x02 +# define CANINTE_RX0IE 0x01 +#define CANINTF 0x2c +# define CANINTF_MERRF 0x80 +# define CANINTF_WAKIF 0x40 +# define CANINTF_ERRIF 0x20 +# define CANINTF_TX2IF 0x10 +# define CANINTF_TX1IF 0x08 +# define CANINTF_TX0IF 0x04 +# define CANINTF_RX1IF 0x02 +# define CANINTF_RX0IF 0x01 +# define CANINTF_RX (CANINTF_RX0IF | CANINTF_RX1IF) +# define CANINTF_TX (CANINTF_TX2IF | CANINTF_TX1IF | CANINTF_TX0IF) +# define CANINTF_ERR (CANINTF_ERRIF) +#define EFLG 0x2d +# define EFLG_EWARN 0x01 +# define EFLG_RXWAR 0x02 +# define EFLG_TXWAR 0x04 +# define EFLG_RXEP 0x08 +# define EFLG_TXEP 0x10 +# define EFLG_TXBO 0x20 +# define EFLG_RX0OVR 0x40 +# define EFLG_RX1OVR 0x80 +#define TXBCTRL(n) (((n) * 0x10) + 0x30 + TXBCTRL_OFF) +# define TXBCTRL_ABTF 0x40 +# define TXBCTRL_MLOA 0x20 +# define TXBCTRL_TXERR 0x10 +# define TXBCTRL_TXREQ 0x08 +#define TXBSIDH(n) (((n) * 0x10) + 0x30 + TXBSIDH_OFF) +# define SIDH_SHIFT 3 +#define TXBSIDL(n) (((n) * 0x10) + 0x30 + TXBSIDL_OFF) +# define SIDL_SID_MASK 7 +# define SIDL_SID_SHIFT 5 +# define SIDL_EXIDE_SHIFT 3 +# define SIDL_EID_SHIFT 16 +# define SIDL_EID_MASK 3 +#define TXBEID8(n) (((n) * 0x10) + 0x30 + TXBEID8_OFF) +#define TXBEID0(n) (((n) * 0x10) + 0x30 + TXBEID0_OFF) +#define TXBDLC(n) (((n) * 0x10) + 0x30 + TXBDLC_OFF) +# define DLC_RTR_SHIFT 6 +#define TXBCTRL_OFF 0 +#define TXBSIDH_OFF 1 +#define TXBSIDL_OFF 2 +#define TXBEID8_OFF 3 +#define TXBEID0_OFF 4 +#define TXBDLC_OFF 5 +#define TXBDAT_OFF 6 +#define RXBCTRL(n) (((n) * 0x10) + 0x60 + RXBCTRL_OFF) +# define RXBCTRL_BUKT 0x04 +# define RXBCTRL_RXM0 0x20 +# define RXBCTRL_RXM1 0x40 +#define RXBSIDH(n) (((n) * 0x10) + 0x60 + RXBSIDH_OFF) +# define RXBSIDH_SHIFT 3 +#define RXBSIDL(n) (((n) * 0x10) + 0x60 + RXBSIDL_OFF) +# define RXBSIDL_IDE 0x08 +# define RXBSIDL_SRR 0x10 +# define RXBSIDL_EID 3 +# define RXBSIDL_SHIFT 5 +#define RXBEID8(n) (((n) * 0x10) + 0x60 + RXBEID8_OFF) +#define RXBEID0(n) (((n) * 0x10) + 0x60 + RXBEID0_OFF) +#define RXBDLC(n) (((n) * 0x10) + 0x60 + RXBDLC_OFF) +# define RXBDLC_LEN_MASK 0x0f +# define RXBDLC_RTR 0x40 +#define RXBCTRL_OFF 0 +#define RXBSIDH_OFF 1 +#define RXBSIDL_OFF 2 +#define RXBEID8_OFF 3 +#define RXBEID0_OFF 4 +#define RXBDLC_OFF 5 +#define RXBDAT_OFF 6 +#define RXFSID(n) ((n < 3) ? 0 : 4) +#define RXFSIDH(n) ((n) * 4 + RXFSID(n)) +#define RXFSIDL(n) ((n) * 4 + 1 + RXFSID(n)) +#define RXFEID8(n) ((n) * 4 + 2 + RXFSID(n)) +#define RXFEID0(n) ((n) * 4 + 3 + RXFSID(n)) +#define RXMSIDH(n) ((n) * 4 + 0x20) +#define RXMSIDL(n) ((n) * 4 + 0x21) +#define RXMEID8(n) ((n) * 4 + 0x22) +#define RXMEID0(n) ((n) * 4 + 0x23) + +#define GET_BYTE(val, byte) \ + (((val) >> ((byte) * 8)) & 0xff) +#define SET_BYTE(val, byte) \ + (((val) & 0xff) << ((byte) * 8)) + +/* Buffer size required for the largest SPI transfer (i.e., reading a + * frame) + */ +#define CAN_FRAME_MAX_DATA_LEN 8 +#define SPI_TRANSFER_BUF_LEN (6 + CAN_FRAME_MAX_DATA_LEN) +#define CAN_FRAME_MAX_BITS 128 + +#define TX_ECHO_SKB_MAX 1 + +#define MCP251X_OST_DELAY_MS (5) + +#define DEVICE_NAME "mcp251x" + +static const struct can_bittiming_const mcp251x_bittiming_const = { + .name = DEVICE_NAME, + .tseg1_min = 3, + .tseg1_max = 16, + .tseg2_min = 2, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + +enum mcp251x_model { + CAN_MCP251X_MCP2510 = 0x2510, + CAN_MCP251X_MCP2515 = 0x2515, + CAN_MCP251X_MCP25625 = 0x25625, +}; + +struct mcp251x_priv { + struct can_priv can; + struct net_device *net; + struct spi_device *spi; + enum mcp251x_model model; + + struct mutex mcp_lock; /* SPI device lock */ + + u8 *spi_tx_buf; + u8 *spi_rx_buf; + + struct sk_buff *tx_skb; + + struct workqueue_struct *wq; + struct work_struct tx_work; + struct work_struct restart_work; + + 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; + bool tx_busy; + + struct regulator *power; + struct regulator *transceiver; + struct clk *clk; +#ifdef CONFIG_GPIOLIB + struct gpio_chip gpio; + u8 reg_bfpctrl; +#endif +}; + +#define MCP251X_IS(_model) \ +static inline int mcp251x_is_##_model(struct spi_device *spi) \ +{ \ + struct mcp251x_priv *priv = spi_get_drvdata(spi); \ + return priv->model == CAN_MCP251X_MCP##_model; \ +} + +MCP251X_IS(2510); + +static void mcp251x_clean(struct net_device *net) +{ + struct mcp251x_priv *priv = netdev_priv(net); + + if (priv->tx_skb || priv->tx_busy) + net->stats.tx_errors++; + dev_kfree_skb(priv->tx_skb); + if (priv->tx_busy) + can_free_echo_skb(priv->net, 0, NULL); + priv->tx_skb = NULL; + priv->tx_busy = false; +} + +/* Note about handling of error return of mcp251x_spi_trans: accessing + * registers via SPI is not really different conceptually than using + * normal I/O assembler instructions, although it's much more + * complicated from a practical POV. So it's not advisable to always + * check the return value of this function. Imagine that every + * read{b,l}, write{b,l} and friends would be bracketed in "if ( < 0) + * error();", it would be a great mess (well there are some situation + * when exception handling C++ like could be useful after all). So we + * just check that transfers are OK at the beginning of our + * conversation with the chip and to avoid doing really nasty things + * (like injecting bogus packets in the network stack). + */ +static int mcp251x_spi_trans(struct spi_device *spi, int len) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + struct spi_transfer t = { + .tx_buf = priv->spi_tx_buf, + .rx_buf = priv->spi_rx_buf, + .len = len, + .cs_change = 0, + }; + struct spi_message m; + int ret; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + ret = spi_sync(spi, &m); + if (ret) + dev_err(&spi->dev, "spi transfer failed: ret = %d\n", ret); + return ret; +} + +static int mcp251x_spi_write(struct spi_device *spi, int len) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + int ret; + + ret = spi_write(spi, priv->spi_tx_buf, len); + if (ret) + dev_err(&spi->dev, "spi write failed: ret = %d\n", ret); + + return ret; +} + +static u8 mcp251x_read_reg(struct spi_device *spi, u8 reg) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + u8 val = 0; + + priv->spi_tx_buf[0] = INSTRUCTION_READ; + priv->spi_tx_buf[1] = reg; + + if (spi->controller->flags & SPI_CONTROLLER_HALF_DUPLEX) { + spi_write_then_read(spi, priv->spi_tx_buf, 2, &val, 1); + } else { + mcp251x_spi_trans(spi, 3); + val = priv->spi_rx_buf[2]; + } + + return val; +} + +static void mcp251x_read_2regs(struct spi_device *spi, u8 reg, u8 *v1, u8 *v2) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + + priv->spi_tx_buf[0] = INSTRUCTION_READ; + priv->spi_tx_buf[1] = reg; + + if (spi->controller->flags & SPI_CONTROLLER_HALF_DUPLEX) { + u8 val[2] = { 0 }; + + spi_write_then_read(spi, priv->spi_tx_buf, 2, val, 2); + *v1 = val[0]; + *v2 = val[1]; + } else { + mcp251x_spi_trans(spi, 4); + + *v1 = priv->spi_rx_buf[2]; + *v2 = priv->spi_rx_buf[3]; + } +} + +static void mcp251x_write_reg(struct spi_device *spi, u8 reg, u8 val) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + + priv->spi_tx_buf[0] = INSTRUCTION_WRITE; + priv->spi_tx_buf[1] = reg; + priv->spi_tx_buf[2] = val; + + mcp251x_spi_write(spi, 3); +} + +static void mcp251x_write_2regs(struct spi_device *spi, u8 reg, u8 v1, u8 v2) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + + priv->spi_tx_buf[0] = INSTRUCTION_WRITE; + priv->spi_tx_buf[1] = reg; + priv->spi_tx_buf[2] = v1; + priv->spi_tx_buf[3] = v2; + + mcp251x_spi_write(spi, 4); +} + +static void mcp251x_write_bits(struct spi_device *spi, u8 reg, + u8 mask, u8 val) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + + priv->spi_tx_buf[0] = INSTRUCTION_BIT_MODIFY; + priv->spi_tx_buf[1] = reg; + priv->spi_tx_buf[2] = mask; + priv->spi_tx_buf[3] = val; + + mcp251x_spi_write(spi, 4); +} + +static u8 mcp251x_read_stat(struct spi_device *spi) +{ + return mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK; +} + +#define mcp251x_read_stat_poll_timeout(addr, val, cond, delay_us, timeout_us) \ + readx_poll_timeout(mcp251x_read_stat, addr, val, cond, \ + delay_us, timeout_us) + +#ifdef CONFIG_GPIOLIB +enum { + MCP251X_GPIO_TX0RTS = 0, /* inputs */ + MCP251X_GPIO_TX1RTS, + MCP251X_GPIO_TX2RTS, + MCP251X_GPIO_RX0BF, /* outputs */ + MCP251X_GPIO_RX1BF, +}; + +#define MCP251X_GPIO_INPUT_MASK \ + GENMASK(MCP251X_GPIO_TX2RTS, MCP251X_GPIO_TX0RTS) +#define MCP251X_GPIO_OUTPUT_MASK \ + GENMASK(MCP251X_GPIO_RX1BF, MCP251X_GPIO_RX0BF) + +static const char * const mcp251x_gpio_names[] = { + [MCP251X_GPIO_TX0RTS] = "TX0RTS", /* inputs */ + [MCP251X_GPIO_TX1RTS] = "TX1RTS", + [MCP251X_GPIO_TX2RTS] = "TX2RTS", + [MCP251X_GPIO_RX0BF] = "RX0BF", /* outputs */ + [MCP251X_GPIO_RX1BF] = "RX1BF", +}; + +static inline bool mcp251x_gpio_is_input(unsigned int offset) +{ + return offset <= MCP251X_GPIO_TX2RTS; +} + +static int mcp251x_gpio_request(struct gpio_chip *chip, + unsigned int offset) +{ + struct mcp251x_priv *priv = gpiochip_get_data(chip); + u8 val; + + /* nothing to be done for inputs */ + if (mcp251x_gpio_is_input(offset)) + return 0; + + val = BFPCTRL_BFE(offset - MCP251X_GPIO_RX0BF); + + mutex_lock(&priv->mcp_lock); + mcp251x_write_bits(priv->spi, BFPCTRL, val, val); + mutex_unlock(&priv->mcp_lock); + + priv->reg_bfpctrl |= val; + + return 0; +} + +static void mcp251x_gpio_free(struct gpio_chip *chip, + unsigned int offset) +{ + struct mcp251x_priv *priv = gpiochip_get_data(chip); + u8 val; + + /* nothing to be done for inputs */ + if (mcp251x_gpio_is_input(offset)) + return; + + val = BFPCTRL_BFE(offset - MCP251X_GPIO_RX0BF); + + mutex_lock(&priv->mcp_lock); + mcp251x_write_bits(priv->spi, BFPCTRL, val, 0); + mutex_unlock(&priv->mcp_lock); + + priv->reg_bfpctrl &= ~val; +} + +static int mcp251x_gpio_get_direction(struct gpio_chip *chip, + unsigned int offset) +{ + if (mcp251x_gpio_is_input(offset)) + return GPIOF_DIR_IN; + + return GPIOF_DIR_OUT; +} + +static int mcp251x_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct mcp251x_priv *priv = gpiochip_get_data(chip); + u8 reg, mask, val; + + if (mcp251x_gpio_is_input(offset)) { + reg = TXRTSCTRL; + mask = TXRTSCTRL_RTS(offset); + } else { + reg = BFPCTRL; + mask = BFPCTRL_BFS(offset - MCP251X_GPIO_RX0BF); + } + + mutex_lock(&priv->mcp_lock); + val = mcp251x_read_reg(priv->spi, reg); + mutex_unlock(&priv->mcp_lock); + + return !!(val & mask); +} + +static int mcp251x_gpio_get_multiple(struct gpio_chip *chip, + unsigned long *maskp, unsigned long *bitsp) +{ + struct mcp251x_priv *priv = gpiochip_get_data(chip); + unsigned long bits = 0; + u8 val; + + mutex_lock(&priv->mcp_lock); + if (maskp[0] & MCP251X_GPIO_INPUT_MASK) { + val = mcp251x_read_reg(priv->spi, TXRTSCTRL); + val = FIELD_GET(TXRTSCTRL_RTS_MASK, val); + bits |= FIELD_PREP(MCP251X_GPIO_INPUT_MASK, val); + } + if (maskp[0] & MCP251X_GPIO_OUTPUT_MASK) { + val = mcp251x_read_reg(priv->spi, BFPCTRL); + val = FIELD_GET(BFPCTRL_BFS_MASK, val); + bits |= FIELD_PREP(MCP251X_GPIO_OUTPUT_MASK, val); + } + mutex_unlock(&priv->mcp_lock); + + bitsp[0] = bits; + return 0; +} + +static void mcp251x_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) +{ + struct mcp251x_priv *priv = gpiochip_get_data(chip); + u8 mask, val; + + mask = BFPCTRL_BFS(offset - MCP251X_GPIO_RX0BF); + val = value ? mask : 0; + + mutex_lock(&priv->mcp_lock); + mcp251x_write_bits(priv->spi, BFPCTRL, mask, val); + mutex_unlock(&priv->mcp_lock); + + priv->reg_bfpctrl &= ~mask; + priv->reg_bfpctrl |= val; +} + +static void +mcp251x_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *maskp, unsigned long *bitsp) +{ + struct mcp251x_priv *priv = gpiochip_get_data(chip); + u8 mask, val; + + mask = FIELD_GET(MCP251X_GPIO_OUTPUT_MASK, maskp[0]); + mask = FIELD_PREP(BFPCTRL_BFS_MASK, mask); + + val = FIELD_GET(MCP251X_GPIO_OUTPUT_MASK, bitsp[0]); + val = FIELD_PREP(BFPCTRL_BFS_MASK, val); + + if (!mask) + return; + + mutex_lock(&priv->mcp_lock); + mcp251x_write_bits(priv->spi, BFPCTRL, mask, val); + mutex_unlock(&priv->mcp_lock); + + priv->reg_bfpctrl &= ~mask; + priv->reg_bfpctrl |= val; +} + +static void mcp251x_gpio_restore(struct spi_device *spi) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + + mcp251x_write_reg(spi, BFPCTRL, priv->reg_bfpctrl); +} + +static int mcp251x_gpio_setup(struct mcp251x_priv *priv) +{ + struct gpio_chip *gpio = &priv->gpio; + + if (!device_property_present(&priv->spi->dev, "gpio-controller")) + return 0; + + /* gpiochip handles TX[0..2]RTS and RX[0..1]BF */ + gpio->label = priv->spi->modalias; + gpio->parent = &priv->spi->dev; + gpio->owner = THIS_MODULE; + gpio->request = mcp251x_gpio_request; + gpio->free = mcp251x_gpio_free; + gpio->get_direction = mcp251x_gpio_get_direction; + gpio->get = mcp251x_gpio_get; + gpio->get_multiple = mcp251x_gpio_get_multiple; + gpio->set = mcp251x_gpio_set; + gpio->set_multiple = mcp251x_gpio_set_multiple; + gpio->base = -1; + gpio->ngpio = ARRAY_SIZE(mcp251x_gpio_names); + gpio->names = mcp251x_gpio_names; + gpio->can_sleep = true; + + return devm_gpiochip_add_data(&priv->spi->dev, gpio, priv); +} +#else +static inline void mcp251x_gpio_restore(struct spi_device *spi) +{ +} + +static inline int mcp251x_gpio_setup(struct mcp251x_priv *priv) +{ + return 0; +} +#endif + +static void mcp251x_hw_tx_frame(struct spi_device *spi, u8 *buf, + int len, int tx_buf_idx) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + + if (mcp251x_is_2510(spi)) { + int i; + + for (i = 1; i < TXBDAT_OFF + len; i++) + mcp251x_write_reg(spi, TXBCTRL(tx_buf_idx) + i, + buf[i]); + } else { + memcpy(priv->spi_tx_buf, buf, TXBDAT_OFF + len); + mcp251x_spi_write(spi, TXBDAT_OFF + len); + } +} + +static void mcp251x_hw_tx(struct spi_device *spi, struct can_frame *frame, + int tx_buf_idx) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + u32 sid, eid, exide, rtr; + u8 buf[SPI_TRANSFER_BUF_LEN]; + + exide = (frame->can_id & CAN_EFF_FLAG) ? 1 : 0; /* Extended ID Enable */ + if (exide) + sid = (frame->can_id & CAN_EFF_MASK) >> 18; + else + sid = frame->can_id & CAN_SFF_MASK; /* Standard ID */ + eid = frame->can_id & CAN_EFF_MASK; /* Extended ID */ + rtr = (frame->can_id & CAN_RTR_FLAG) ? 1 : 0; /* Remote transmission */ + + buf[TXBCTRL_OFF] = INSTRUCTION_LOAD_TXB(tx_buf_idx); + buf[TXBSIDH_OFF] = sid >> SIDH_SHIFT; + buf[TXBSIDL_OFF] = ((sid & SIDL_SID_MASK) << SIDL_SID_SHIFT) | + (exide << SIDL_EXIDE_SHIFT) | + ((eid >> SIDL_EID_SHIFT) & SIDL_EID_MASK); + buf[TXBEID8_OFF] = GET_BYTE(eid, 1); + buf[TXBEID0_OFF] = GET_BYTE(eid, 0); + buf[TXBDLC_OFF] = (rtr << DLC_RTR_SHIFT) | frame->len; + memcpy(buf + TXBDAT_OFF, frame->data, frame->len); + mcp251x_hw_tx_frame(spi, buf, frame->len, tx_buf_idx); + + /* use INSTRUCTION_RTS, to avoid "repeated frame problem" */ + priv->spi_tx_buf[0] = INSTRUCTION_RTS(1 << tx_buf_idx); + mcp251x_spi_write(priv->spi, 1); +} + +static void mcp251x_hw_rx_frame(struct spi_device *spi, u8 *buf, + int buf_idx) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + + if (mcp251x_is_2510(spi)) { + int i, len; + + for (i = 1; i < RXBDAT_OFF; i++) + buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i); + + len = can_cc_dlc2len(buf[RXBDLC_OFF] & RXBDLC_LEN_MASK); + for (; i < (RXBDAT_OFF + len); i++) + buf[i] = mcp251x_read_reg(spi, RXBCTRL(buf_idx) + i); + } else { + priv->spi_tx_buf[RXBCTRL_OFF] = INSTRUCTION_READ_RXB(buf_idx); + if (spi->controller->flags & SPI_CONTROLLER_HALF_DUPLEX) { + spi_write_then_read(spi, priv->spi_tx_buf, 1, + priv->spi_rx_buf, + SPI_TRANSFER_BUF_LEN); + memcpy(buf + 1, priv->spi_rx_buf, + SPI_TRANSFER_BUF_LEN - 1); + } else { + mcp251x_spi_trans(spi, SPI_TRANSFER_BUF_LEN); + memcpy(buf, priv->spi_rx_buf, SPI_TRANSFER_BUF_LEN); + } + } +} + +static void mcp251x_hw_rx(struct spi_device *spi, int buf_idx) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + struct sk_buff *skb; + struct can_frame *frame; + u8 buf[SPI_TRANSFER_BUF_LEN]; + + skb = alloc_can_skb(priv->net, &frame); + if (!skb) { + dev_err(&spi->dev, "cannot allocate RX skb\n"); + priv->net->stats.rx_dropped++; + return; + } + + mcp251x_hw_rx_frame(spi, buf, buf_idx); + if (buf[RXBSIDL_OFF] & RXBSIDL_IDE) { + /* Extended ID format */ + frame->can_id = CAN_EFF_FLAG; + frame->can_id |= + /* Extended ID part */ + SET_BYTE(buf[RXBSIDL_OFF] & RXBSIDL_EID, 2) | + SET_BYTE(buf[RXBEID8_OFF], 1) | + SET_BYTE(buf[RXBEID0_OFF], 0) | + /* Standard ID part */ + (((buf[RXBSIDH_OFF] << RXBSIDH_SHIFT) | + (buf[RXBSIDL_OFF] >> RXBSIDL_SHIFT)) << 18); + /* Remote transmission request */ + if (buf[RXBDLC_OFF] & RXBDLC_RTR) + frame->can_id |= CAN_RTR_FLAG; + } else { + /* Standard ID format */ + frame->can_id = + (buf[RXBSIDH_OFF] << RXBSIDH_SHIFT) | + (buf[RXBSIDL_OFF] >> RXBSIDL_SHIFT); + if (buf[RXBSIDL_OFF] & RXBSIDL_SRR) + frame->can_id |= CAN_RTR_FLAG; + } + /* Data length */ + frame->len = can_cc_dlc2len(buf[RXBDLC_OFF] & RXBDLC_LEN_MASK); + if (!(frame->can_id & CAN_RTR_FLAG)) { + memcpy(frame->data, buf + RXBDAT_OFF, frame->len); + + priv->net->stats.rx_bytes += frame->len; + } + priv->net->stats.rx_packets++; + + netif_rx(skb); +} + +static void mcp251x_hw_sleep(struct spi_device *spi) +{ + mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_SLEEP); +} + +/* May only be called when device is sleeping! */ +static int mcp251x_hw_wake(struct spi_device *spi) +{ + u8 value; + int ret; + + /* Force wakeup interrupt to wake device, but don't execute IST */ + disable_irq(spi->irq); + mcp251x_write_2regs(spi, CANINTE, CANINTE_WAKIE, CANINTF_WAKIF); + + /* Wait for oscillator startup timer after wake up */ + mdelay(MCP251X_OST_DELAY_MS); + + /* Put device into config mode */ + mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_CONF); + + /* Wait for the device to enter config mode */ + ret = mcp251x_read_stat_poll_timeout(spi, value, value == CANCTRL_REQOP_CONF, + MCP251X_OST_DELAY_MS * 1000, + USEC_PER_SEC); + if (ret) { + dev_err(&spi->dev, "MCP251x didn't enter in config mode\n"); + return ret; + } + + /* Disable and clear pending interrupts */ + mcp251x_write_2regs(spi, CANINTE, 0x00, 0x00); + enable_irq(spi->irq); + + return 0; +} + +static netdev_tx_t mcp251x_hard_start_xmit(struct sk_buff *skb, + struct net_device *net) +{ + struct mcp251x_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + + if (priv->tx_skb || priv->tx_busy) { + dev_warn(&spi->dev, "hard_xmit called while tx busy\n"); + return NETDEV_TX_BUSY; + } + + if (can_dev_dropped_skb(net, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(net); + priv->tx_skb = skb; + queue_work(priv->wq, &priv->tx_work); + + return NETDEV_TX_OK; +} + +static int mcp251x_do_set_mode(struct net_device *net, enum can_mode mode) +{ + struct mcp251x_priv *priv = netdev_priv(net); + + switch (mode) { + case CAN_MODE_START: + mcp251x_clean(net); + /* We have to delay work since SPI I/O may sleep */ + priv->can.state = CAN_STATE_ERROR_ACTIVE; + priv->restart_tx = 1; + if (priv->can.restart_ms == 0) + priv->after_suspend = AFTER_SUSPEND_RESTART; + queue_work(priv->wq, &priv->restart_work); + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int mcp251x_set_normal_mode(struct spi_device *spi) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + u8 value; + int ret; + + /* Enable interrupts */ + mcp251x_write_reg(spi, CANINTE, + CANINTE_ERRIE | CANINTE_TX2IE | CANINTE_TX1IE | + CANINTE_TX0IE | CANINTE_RX1IE | CANINTE_RX0IE); + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) { + /* Put device into loopback mode */ + mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_LOOPBACK); + } else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) { + /* Put device into listen-only mode */ + mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_LISTEN_ONLY); + } else { + /* Put device into normal mode */ + mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_NORMAL); + + /* Wait for the device to enter normal mode */ + ret = mcp251x_read_stat_poll_timeout(spi, value, value == 0, + MCP251X_OST_DELAY_MS * 1000, + USEC_PER_SEC); + if (ret) { + dev_err(&spi->dev, "MCP251x didn't enter in normal mode\n"); + return ret; + } + } + priv->can.state = CAN_STATE_ERROR_ACTIVE; + return 0; +} + +static int mcp251x_do_set_bittiming(struct net_device *net) +{ + struct mcp251x_priv *priv = netdev_priv(net); + struct can_bittiming *bt = &priv->can.bittiming; + struct spi_device *spi = priv->spi; + + mcp251x_write_reg(spi, CNF1, ((bt->sjw - 1) << CNF1_SJW_SHIFT) | + (bt->brp - 1)); + mcp251x_write_reg(spi, CNF2, CNF2_BTLMODE | + (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES ? + CNF2_SAM : 0) | + ((bt->phase_seg1 - 1) << CNF2_PS1_SHIFT) | + (bt->prop_seg - 1)); + mcp251x_write_bits(spi, CNF3, CNF3_PHSEG2_MASK, + (bt->phase_seg2 - 1)); + dev_dbg(&spi->dev, "CNF: 0x%02x 0x%02x 0x%02x\n", + mcp251x_read_reg(spi, CNF1), + mcp251x_read_reg(spi, CNF2), + mcp251x_read_reg(spi, CNF3)); + + return 0; +} + +static int mcp251x_setup(struct net_device *net, struct spi_device *spi) +{ + mcp251x_do_set_bittiming(net); + + mcp251x_write_reg(spi, RXBCTRL(0), + RXBCTRL_BUKT | RXBCTRL_RXM0 | RXBCTRL_RXM1); + mcp251x_write_reg(spi, RXBCTRL(1), + RXBCTRL_RXM0 | RXBCTRL_RXM1); + return 0; +} + +static int mcp251x_hw_reset(struct spi_device *spi) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + u8 value; + int ret; + + /* Wait for oscillator startup timer after power up */ + mdelay(MCP251X_OST_DELAY_MS); + + priv->spi_tx_buf[0] = INSTRUCTION_RESET; + ret = mcp251x_spi_write(spi, 1); + if (ret) + return ret; + + /* Wait for oscillator startup timer after reset */ + mdelay(MCP251X_OST_DELAY_MS); + + /* Wait for reset to finish */ + ret = mcp251x_read_stat_poll_timeout(spi, value, value == CANCTRL_REQOP_CONF, + MCP251X_OST_DELAY_MS * 1000, + USEC_PER_SEC); + if (ret) + dev_err(&spi->dev, "MCP251x didn't enter in conf mode after reset\n"); + return ret; +} + +static int mcp251x_hw_probe(struct spi_device *spi) +{ + u8 ctrl; + int ret; + + ret = mcp251x_hw_reset(spi); + if (ret) + return ret; + + ctrl = mcp251x_read_reg(spi, CANCTRL); + + dev_dbg(&spi->dev, "CANCTRL 0x%02x\n", ctrl); + + /* Check for power up default value */ + if ((ctrl & 0x17) != 0x07) + return -ENODEV; + + return 0; +} + +static int mcp251x_power_enable(struct regulator *reg, int enable) +{ + if (IS_ERR_OR_NULL(reg)) + return 0; + + if (enable) + return regulator_enable(reg); + else + return regulator_disable(reg); +} + +static int mcp251x_stop(struct net_device *net) +{ + struct mcp251x_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + + close_candev(net); + + priv->force_quit = 1; + free_irq(spi->irq, priv); + + mutex_lock(&priv->mcp_lock); + + /* Disable and clear pending interrupts */ + mcp251x_write_2regs(spi, CANINTE, 0x00, 0x00); + + mcp251x_write_reg(spi, TXBCTRL(0), 0); + mcp251x_clean(net); + + mcp251x_hw_sleep(spi); + + mcp251x_power_enable(priv->transceiver, 0); + + priv->can.state = CAN_STATE_STOPPED; + + mutex_unlock(&priv->mcp_lock); + + return 0; +} + +static void mcp251x_error_skb(struct net_device *net, int can_id, int data1) +{ + struct sk_buff *skb; + struct can_frame *frame; + + skb = alloc_can_err_skb(net, &frame); + if (skb) { + frame->can_id |= can_id; + frame->data[1] = data1; + netif_rx(skb); + } else { + netdev_err(net, "cannot allocate error skb\n"); + } +} + +static void mcp251x_tx_work_handler(struct work_struct *ws) +{ + struct mcp251x_priv *priv = container_of(ws, struct mcp251x_priv, + tx_work); + struct spi_device *spi = priv->spi; + struct net_device *net = priv->net; + struct can_frame *frame; + + mutex_lock(&priv->mcp_lock); + if (priv->tx_skb) { + if (priv->can.state == CAN_STATE_BUS_OFF) { + mcp251x_clean(net); + } else { + frame = (struct can_frame *)priv->tx_skb->data; + + if (frame->len > CAN_FRAME_MAX_DATA_LEN) + frame->len = CAN_FRAME_MAX_DATA_LEN; + mcp251x_hw_tx(spi, frame, 0); + priv->tx_busy = true; + can_put_echo_skb(priv->tx_skb, net, 0, 0); + priv->tx_skb = NULL; + } + } + mutex_unlock(&priv->mcp_lock); +} + +static void mcp251x_restart_work_handler(struct work_struct *ws) +{ + struct mcp251x_priv *priv = container_of(ws, struct mcp251x_priv, + restart_work); + struct spi_device *spi = priv->spi; + struct net_device *net = priv->net; + + mutex_lock(&priv->mcp_lock); + if (priv->after_suspend) { + if (priv->after_suspend & AFTER_SUSPEND_POWER) { + mcp251x_hw_reset(spi); + mcp251x_setup(net, spi); + mcp251x_gpio_restore(spi); + } else { + mcp251x_hw_wake(spi); + } + priv->force_quit = 0; + if (priv->after_suspend & AFTER_SUSPEND_RESTART) { + mcp251x_set_normal_mode(spi); + } else if (priv->after_suspend & AFTER_SUSPEND_UP) { + netif_device_attach(net); + mcp251x_clean(net); + mcp251x_set_normal_mode(spi); + netif_wake_queue(net); + } else { + mcp251x_hw_sleep(spi); + } + priv->after_suspend = 0; + } + + if (priv->restart_tx) { + priv->restart_tx = 0; + mcp251x_write_reg(spi, TXBCTRL(0), 0); + mcp251x_clean(net); + netif_wake_queue(net); + mcp251x_error_skb(net, CAN_ERR_RESTARTED, 0); + } + mutex_unlock(&priv->mcp_lock); +} + +static irqreturn_t mcp251x_can_ist(int irq, void *dev_id) +{ + struct mcp251x_priv *priv = dev_id; + struct spi_device *spi = priv->spi; + struct net_device *net = priv->net; + + mutex_lock(&priv->mcp_lock); + while (!priv->force_quit) { + enum can_state new_state; + u8 intf, eflag; + u8 clear_intf = 0; + int can_id = 0, data1 = 0; + + mcp251x_read_2regs(spi, CANINTF, &intf, &eflag); + + /* receive buffer 0 */ + if (intf & CANINTF_RX0IF) { + mcp251x_hw_rx(spi, 0); + /* Free one buffer ASAP + * (The MCP2515/25625 does this automatically.) + */ + if (mcp251x_is_2510(spi)) + mcp251x_write_bits(spi, CANINTF, + CANINTF_RX0IF, 0x00); + + /* check if buffer 1 is already known to be full, no need to re-read */ + if (!(intf & CANINTF_RX1IF)) { + u8 intf1, eflag1; + + /* intf needs to be read again to avoid a race condition */ + mcp251x_read_2regs(spi, CANINTF, &intf1, &eflag1); + + /* combine flags from both operations for error handling */ + intf |= intf1; + eflag |= eflag1; + } + } + + /* receive buffer 1 */ + if (intf & CANINTF_RX1IF) { + mcp251x_hw_rx(spi, 1); + /* The MCP2515/25625 does this automatically. */ + if (mcp251x_is_2510(spi)) + clear_intf |= CANINTF_RX1IF; + } + + /* mask out flags we don't care about */ + intf &= CANINTF_RX | CANINTF_TX | CANINTF_ERR; + + /* any error or tx interrupt we need to clear? */ + if (intf & (CANINTF_ERR | CANINTF_TX)) + clear_intf |= intf & (CANINTF_ERR | CANINTF_TX); + if (clear_intf) + mcp251x_write_bits(spi, CANINTF, clear_intf, 0x00); + + if (eflag & (EFLG_RX0OVR | EFLG_RX1OVR)) + mcp251x_write_bits(spi, EFLG, eflag, 0x00); + + /* Update can state */ + if (eflag & EFLG_TXBO) { + new_state = CAN_STATE_BUS_OFF; + can_id |= CAN_ERR_BUSOFF; + } else if (eflag & EFLG_TXEP) { + new_state = CAN_STATE_ERROR_PASSIVE; + can_id |= CAN_ERR_CRTL; + data1 |= CAN_ERR_CRTL_TX_PASSIVE; + } else if (eflag & EFLG_RXEP) { + new_state = CAN_STATE_ERROR_PASSIVE; + can_id |= CAN_ERR_CRTL; + data1 |= CAN_ERR_CRTL_RX_PASSIVE; + } else if (eflag & EFLG_TXWAR) { + new_state = CAN_STATE_ERROR_WARNING; + can_id |= CAN_ERR_CRTL; + data1 |= CAN_ERR_CRTL_TX_WARNING; + } else if (eflag & EFLG_RXWAR) { + new_state = CAN_STATE_ERROR_WARNING; + can_id |= CAN_ERR_CRTL; + data1 |= CAN_ERR_CRTL_RX_WARNING; + } else { + new_state = CAN_STATE_ERROR_ACTIVE; + } + + /* Update can state statistics */ + switch (priv->can.state) { + case CAN_STATE_ERROR_ACTIVE: + if (new_state >= CAN_STATE_ERROR_WARNING && + new_state <= CAN_STATE_BUS_OFF) + priv->can.can_stats.error_warning++; + fallthrough; + case CAN_STATE_ERROR_WARNING: + if (new_state >= CAN_STATE_ERROR_PASSIVE && + new_state <= CAN_STATE_BUS_OFF) + priv->can.can_stats.error_passive++; + break; + default: + break; + } + priv->can.state = new_state; + + if (intf & CANINTF_ERRIF) { + /* Handle overflow counters */ + if (eflag & (EFLG_RX0OVR | EFLG_RX1OVR)) { + if (eflag & EFLG_RX0OVR) { + net->stats.rx_over_errors++; + net->stats.rx_errors++; + } + if (eflag & EFLG_RX1OVR) { + net->stats.rx_over_errors++; + net->stats.rx_errors++; + } + can_id |= CAN_ERR_CRTL; + data1 |= CAN_ERR_CRTL_RX_OVERFLOW; + } + mcp251x_error_skb(net, can_id, data1); + } + + if (priv->can.state == CAN_STATE_BUS_OFF) { + if (priv->can.restart_ms == 0) { + priv->force_quit = 1; + priv->can.can_stats.bus_off++; + can_bus_off(net); + mcp251x_hw_sleep(spi); + break; + } + } + + if (intf == 0) + break; + + if (intf & CANINTF_TX) { + if (priv->tx_busy) { + net->stats.tx_packets++; + net->stats.tx_bytes += can_get_echo_skb(net, 0, + NULL); + priv->tx_busy = false; + } + netif_wake_queue(net); + } + } + mutex_unlock(&priv->mcp_lock); + return IRQ_HANDLED; +} + +static int mcp251x_open(struct net_device *net) +{ + struct mcp251x_priv *priv = netdev_priv(net); + struct spi_device *spi = priv->spi; + unsigned long flags = 0; + int ret; + + ret = open_candev(net); + if (ret) { + dev_err(&spi->dev, "unable to set initial baudrate!\n"); + return ret; + } + + mutex_lock(&priv->mcp_lock); + mcp251x_power_enable(priv->transceiver, 1); + + priv->force_quit = 0; + priv->tx_skb = NULL; + priv->tx_busy = false; + + if (!dev_fwnode(&spi->dev)) + flags = IRQF_TRIGGER_FALLING; + + ret = request_threaded_irq(spi->irq, NULL, mcp251x_can_ist, + flags | IRQF_ONESHOT, dev_name(&spi->dev), + priv); + if (ret) { + dev_err(&spi->dev, "failed to acquire irq %d\n", spi->irq); + goto out_close; + } + + ret = mcp251x_hw_wake(spi); + if (ret) + goto out_free_irq; + ret = mcp251x_setup(net, spi); + if (ret) + goto out_free_irq; + ret = mcp251x_set_normal_mode(spi); + if (ret) + goto out_free_irq; + + netif_wake_queue(net); + mutex_unlock(&priv->mcp_lock); + + return 0; + +out_free_irq: + free_irq(spi->irq, priv); + mcp251x_hw_sleep(spi); +out_close: + mcp251x_power_enable(priv->transceiver, 0); + close_candev(net); + mutex_unlock(&priv->mcp_lock); + return ret; +} + +static const struct net_device_ops mcp251x_netdev_ops = { + .ndo_open = mcp251x_open, + .ndo_stop = mcp251x_stop, + .ndo_start_xmit = mcp251x_hard_start_xmit, + .ndo_change_mtu = can_change_mtu, +}; + +static const struct ethtool_ops mcp251x_ethtool_ops = { + .get_ts_info = ethtool_op_get_ts_info, +}; + +static const struct of_device_id mcp251x_of_match[] = { + { + .compatible = "microchip,mcp2510", + .data = (void *)CAN_MCP251X_MCP2510, + }, + { + .compatible = "microchip,mcp2515", + .data = (void *)CAN_MCP251X_MCP2515, + }, + { + .compatible = "microchip,mcp25625", + .data = (void *)CAN_MCP251X_MCP25625, + }, + { } +}; +MODULE_DEVICE_TABLE(of, mcp251x_of_match); + +static const struct spi_device_id mcp251x_id_table[] = { + { + .name = "mcp2510", + .driver_data = (kernel_ulong_t)CAN_MCP251X_MCP2510, + }, + { + .name = "mcp2515", + .driver_data = (kernel_ulong_t)CAN_MCP251X_MCP2515, + }, + { + .name = "mcp25625", + .driver_data = (kernel_ulong_t)CAN_MCP251X_MCP25625, + }, + { } +}; +MODULE_DEVICE_TABLE(spi, mcp251x_id_table); + +static int mcp251x_can_probe(struct spi_device *spi) +{ + const void *match = device_get_match_data(&spi->dev); + struct net_device *net; + struct mcp251x_priv *priv; + struct clk *clk; + u32 freq; + int ret; + + clk = devm_clk_get_optional(&spi->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + freq = clk_get_rate(clk); + if (freq == 0) + device_property_read_u32(&spi->dev, "clock-frequency", &freq); + + /* Sanity check */ + if (freq < 1000000 || freq > 25000000) + return -ERANGE; + + /* Allocate can/net device */ + net = alloc_candev(sizeof(struct mcp251x_priv), TX_ECHO_SKB_MAX); + if (!net) + return -ENOMEM; + + ret = clk_prepare_enable(clk); + if (ret) + goto out_free; + + net->netdev_ops = &mcp251x_netdev_ops; + net->ethtool_ops = &mcp251x_ethtool_ops; + net->flags |= IFF_ECHO; + + priv = netdev_priv(net); + priv->can.bittiming_const = &mcp251x_bittiming_const; + priv->can.do_set_mode = mcp251x_do_set_mode; + priv->can.clock.freq = freq / 2; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES | + CAN_CTRLMODE_LOOPBACK | CAN_CTRLMODE_LISTENONLY; + if (match) + priv->model = (enum mcp251x_model)(uintptr_t)match; + else + priv->model = spi_get_device_id(spi)->driver_data; + priv->net = net; + priv->clk = clk; + + spi_set_drvdata(spi, priv); + + /* Configure the SPI bus */ + spi->bits_per_word = 8; + if (mcp251x_is_2510(spi)) + spi->max_speed_hz = spi->max_speed_hz ? : 5 * 1000 * 1000; + else + spi->max_speed_hz = spi->max_speed_hz ? : 10 * 1000 * 1000; + ret = spi_setup(spi); + if (ret) + goto out_clk; + + priv->power = devm_regulator_get_optional(&spi->dev, "vdd"); + priv->transceiver = devm_regulator_get_optional(&spi->dev, "xceiver"); + if ((PTR_ERR(priv->power) == -EPROBE_DEFER) || + (PTR_ERR(priv->transceiver) == -EPROBE_DEFER)) { + ret = -EPROBE_DEFER; + goto out_clk; + } + + ret = mcp251x_power_enable(priv->power, 1); + if (ret) + goto out_clk; + + priv->wq = alloc_workqueue("mcp251x_wq", WQ_FREEZABLE | WQ_MEM_RECLAIM, + 0); + if (!priv->wq) { + ret = -ENOMEM; + goto out_clk; + } + INIT_WORK(&priv->tx_work, mcp251x_tx_work_handler); + INIT_WORK(&priv->restart_work, mcp251x_restart_work_handler); + + priv->spi = spi; + mutex_init(&priv->mcp_lock); + + priv->spi_tx_buf = devm_kzalloc(&spi->dev, SPI_TRANSFER_BUF_LEN, + GFP_KERNEL); + if (!priv->spi_tx_buf) { + ret = -ENOMEM; + goto error_probe; + } + + priv->spi_rx_buf = devm_kzalloc(&spi->dev, SPI_TRANSFER_BUF_LEN, + GFP_KERNEL); + if (!priv->spi_rx_buf) { + ret = -ENOMEM; + goto error_probe; + } + + SET_NETDEV_DEV(net, &spi->dev); + + /* Here is OK to not lock the MCP, no one knows about it yet */ + ret = mcp251x_hw_probe(spi); + if (ret) { + if (ret == -ENODEV) + dev_err(&spi->dev, "Cannot initialize MCP%x. Wrong wiring?\n", + priv->model); + goto error_probe; + } + + mcp251x_hw_sleep(spi); + + ret = register_candev(net); + if (ret) + goto error_probe; + + ret = mcp251x_gpio_setup(priv); + if (ret) + goto out_unregister_candev; + + netdev_info(net, "MCP%x successfully initialized.\n", priv->model); + return 0; + +out_unregister_candev: + unregister_candev(net); + +error_probe: + destroy_workqueue(priv->wq); + priv->wq = NULL; + mcp251x_power_enable(priv->power, 0); + +out_clk: + clk_disable_unprepare(clk); + +out_free: + free_candev(net); + + dev_err(&spi->dev, "Probe failed, err=%d\n", -ret); + return ret; +} + +static void mcp251x_can_remove(struct spi_device *spi) +{ + struct mcp251x_priv *priv = spi_get_drvdata(spi); + struct net_device *net = priv->net; + + unregister_candev(net); + + mcp251x_power_enable(priv->power, 0); + + destroy_workqueue(priv->wq); + priv->wq = NULL; + + clk_disable_unprepare(priv->clk); + + free_candev(net); +} + +static int __maybe_unused mcp251x_can_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct mcp251x_priv *priv = spi_get_drvdata(spi); + struct net_device *net = priv->net; + + priv->force_quit = 1; + disable_irq(spi->irq); + /* Note: at this point neither IST nor workqueues are running. + * open/stop cannot be called anyway so locking is not needed + */ + if (netif_running(net)) { + netif_device_detach(net); + + mcp251x_hw_sleep(spi); + mcp251x_power_enable(priv->transceiver, 0); + priv->after_suspend = AFTER_SUSPEND_UP; + } else { + priv->after_suspend = AFTER_SUSPEND_DOWN; + } + + mcp251x_power_enable(priv->power, 0); + priv->after_suspend |= AFTER_SUSPEND_POWER; + + return 0; +} + +static int __maybe_unused mcp251x_can_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct mcp251x_priv *priv = spi_get_drvdata(spi); + + if (priv->after_suspend & AFTER_SUSPEND_POWER) + mcp251x_power_enable(priv->power, 1); + if (priv->after_suspend & AFTER_SUSPEND_UP) + mcp251x_power_enable(priv->transceiver, 1); + + if (priv->after_suspend & (AFTER_SUSPEND_POWER | AFTER_SUSPEND_UP)) + queue_work(priv->wq, &priv->restart_work); + else + priv->after_suspend = 0; + + priv->force_quit = 0; + enable_irq(spi->irq); + return 0; +} + +static SIMPLE_DEV_PM_OPS(mcp251x_can_pm_ops, mcp251x_can_suspend, + mcp251x_can_resume); + +static struct spi_driver mcp251x_can_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = mcp251x_of_match, + .pm = &mcp251x_can_pm_ops, + }, + .id_table = mcp251x_id_table, + .probe = mcp251x_can_probe, + .remove = mcp251x_can_remove, +}; +module_spi_driver(mcp251x_can_driver); + +MODULE_AUTHOR("Chris Elston <celston@katalix.com>, " + "Christian Pellegrin <chripell@evolware.org>"); +MODULE_DESCRIPTION("Microchip 251x/25625 CAN driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/spi/mcp251xfd/Kconfig b/drivers/net/can/spi/mcp251xfd/Kconfig new file mode 100644 index 0000000000..877e435601 --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config CAN_MCP251XFD + tristate "Microchip MCP251xFD SPI CAN controllers" + select CAN_RX_OFFLOAD + select REGMAP + select WANT_DEV_COREDUMP + help + Driver for the Microchip MCP251XFD SPI FD-CAN controller + family. + +config CAN_MCP251XFD_SANITY + depends on CAN_MCP251XFD + bool "Additional Sanity Checks" + help + This option enables additional sanity checks in the driver, + that compares various internal counters with the in chip + variants. This comes with a runtime overhead. + Disable if unsure. diff --git a/drivers/net/can/spi/mcp251xfd/Makefile b/drivers/net/can/spi/mcp251xfd/Makefile new file mode 100644 index 0000000000..94d7de9542 --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/Makefile @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_CAN_MCP251XFD) += mcp251xfd.o + +mcp251xfd-objs := +mcp251xfd-objs += mcp251xfd-chip-fifo.o +mcp251xfd-objs += mcp251xfd-core.o +mcp251xfd-objs += mcp251xfd-crc16.o +mcp251xfd-objs += mcp251xfd-ethtool.o +mcp251xfd-objs += mcp251xfd-ram.o +mcp251xfd-objs += mcp251xfd-regmap.o +mcp251xfd-objs += mcp251xfd-ring.o +mcp251xfd-objs += mcp251xfd-rx.o +mcp251xfd-objs += mcp251xfd-tef.o +mcp251xfd-objs += mcp251xfd-timestamp.o +mcp251xfd-objs += mcp251xfd-tx.o + +mcp251xfd-$(CONFIG_DEV_COREDUMP) += mcp251xfd-dump.o diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-chip-fifo.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-chip-fifo.c new file mode 100644 index 0000000000..0d96097a25 --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-chip-fifo.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mcp251xfd - Microchip MCP251xFD Family CAN controller driver +// +// Copyright (c) 2019, 2020, 2021 Pengutronix, +// Marc Kleine-Budde <kernel@pengutronix.de> +// +// Based on: +// +// CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface +// +// Copyright (c) 2019 Martin Sperl <kernel@martin.sperl.org> +// + +#include <linux/bitfield.h> + +#include "mcp251xfd.h" + +static int +mcp251xfd_chip_rx_fifo_init_one(const struct mcp251xfd_priv *priv, + const struct mcp251xfd_rx_ring *ring) +{ + u32 fifo_con; + + /* Enable RXOVIE on _all_ RX FIFOs, not just the last one. + * + * FIFOs hit by a RX MAB overflow and RXOVIE enabled will + * generate a RXOVIF, use this to properly detect RX MAB + * overflows. + */ + fifo_con = FIELD_PREP(MCP251XFD_REG_FIFOCON_FSIZE_MASK, + ring->obj_num - 1) | + MCP251XFD_REG_FIFOCON_RXTSEN | + MCP251XFD_REG_FIFOCON_RXOVIE | + MCP251XFD_REG_FIFOCON_TFNRFNIE; + + if (mcp251xfd_is_fd_mode(priv)) + fifo_con |= FIELD_PREP(MCP251XFD_REG_FIFOCON_PLSIZE_MASK, + MCP251XFD_REG_FIFOCON_PLSIZE_64); + else + fifo_con |= FIELD_PREP(MCP251XFD_REG_FIFOCON_PLSIZE_MASK, + MCP251XFD_REG_FIFOCON_PLSIZE_8); + + return regmap_write(priv->map_reg, + MCP251XFD_REG_FIFOCON(ring->fifo_nr), fifo_con); +} + +static int +mcp251xfd_chip_rx_filter_init_one(const struct mcp251xfd_priv *priv, + const struct mcp251xfd_rx_ring *ring) +{ + u32 fltcon; + + fltcon = MCP251XFD_REG_FLTCON_FLTEN(ring->nr) | + MCP251XFD_REG_FLTCON_FBP(ring->nr, ring->fifo_nr); + + return regmap_update_bits(priv->map_reg, + MCP251XFD_REG_FLTCON(ring->nr >> 2), + MCP251XFD_REG_FLTCON_FLT_MASK(ring->nr), + fltcon); +} + +int mcp251xfd_chip_fifo_init(const struct mcp251xfd_priv *priv) +{ + const struct mcp251xfd_tx_ring *tx_ring = priv->tx; + const struct mcp251xfd_rx_ring *rx_ring; + u32 val; + int err, n; + + /* TEF */ + val = FIELD_PREP(MCP251XFD_REG_TEFCON_FSIZE_MASK, + tx_ring->obj_num - 1) | + MCP251XFD_REG_TEFCON_TEFTSEN | + MCP251XFD_REG_TEFCON_TEFOVIE | + MCP251XFD_REG_TEFCON_TEFNEIE; + + err = regmap_write(priv->map_reg, MCP251XFD_REG_TEFCON, val); + if (err) + return err; + + /* TX FIFO */ + val = FIELD_PREP(MCP251XFD_REG_FIFOCON_FSIZE_MASK, + tx_ring->obj_num - 1) | + MCP251XFD_REG_FIFOCON_TXEN | + MCP251XFD_REG_FIFOCON_TXATIE; + + if (mcp251xfd_is_fd_mode(priv)) + val |= FIELD_PREP(MCP251XFD_REG_FIFOCON_PLSIZE_MASK, + MCP251XFD_REG_FIFOCON_PLSIZE_64); + else + val |= FIELD_PREP(MCP251XFD_REG_FIFOCON_PLSIZE_MASK, + MCP251XFD_REG_FIFOCON_PLSIZE_8); + + if (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) + val |= FIELD_PREP(MCP251XFD_REG_FIFOCON_TXAT_MASK, + MCP251XFD_REG_FIFOCON_TXAT_ONE_SHOT); + else + val |= FIELD_PREP(MCP251XFD_REG_FIFOCON_TXAT_MASK, + MCP251XFD_REG_FIFOCON_TXAT_UNLIMITED); + + err = regmap_write(priv->map_reg, + MCP251XFD_REG_FIFOCON(priv->tx->fifo_nr), + val); + if (err) + return err; + + /* RX FIFOs */ + mcp251xfd_for_each_rx_ring(priv, rx_ring, n) { + err = mcp251xfd_chip_rx_fifo_init_one(priv, rx_ring); + if (err) + return err; + + err = mcp251xfd_chip_rx_filter_init_one(priv, rx_ring); + if (err) + return err; + } + + return 0; +} diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c new file mode 100644 index 0000000000..eebf967f47 --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c @@ -0,0 +1,2207 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mcp251xfd - Microchip MCP251xFD Family CAN controller driver +// +// Copyright (c) 2019, 2020, 2021 Pengutronix, +// Marc Kleine-Budde <kernel@pengutronix.de> +// +// Based on: +// +// CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface +// +// Copyright (c) 2019 Martin Sperl <kernel@martin.sperl.org> +// + +#include <asm/unaligned.h> +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/property.h> + +#include "mcp251xfd.h" + +#define DEVICE_NAME "mcp251xfd" + +static const struct mcp251xfd_devtype_data mcp251xfd_devtype_data_mcp2517fd = { + .quirks = MCP251XFD_QUIRK_MAB_NO_WARN | MCP251XFD_QUIRK_CRC_REG | + MCP251XFD_QUIRK_CRC_RX | MCP251XFD_QUIRK_CRC_TX | + MCP251XFD_QUIRK_ECC, + .model = MCP251XFD_MODEL_MCP2517FD, +}; + +static const struct mcp251xfd_devtype_data mcp251xfd_devtype_data_mcp2518fd = { + .quirks = MCP251XFD_QUIRK_CRC_REG | MCP251XFD_QUIRK_CRC_RX | + MCP251XFD_QUIRK_CRC_TX | MCP251XFD_QUIRK_ECC, + .model = MCP251XFD_MODEL_MCP2518FD, +}; + +static const struct mcp251xfd_devtype_data mcp251xfd_devtype_data_mcp251863 = { + .quirks = MCP251XFD_QUIRK_CRC_REG | MCP251XFD_QUIRK_CRC_RX | + MCP251XFD_QUIRK_CRC_TX | MCP251XFD_QUIRK_ECC, + .model = MCP251XFD_MODEL_MCP251863, +}; + +/* Autodetect model, start with CRC enabled. */ +static const struct mcp251xfd_devtype_data mcp251xfd_devtype_data_mcp251xfd = { + .quirks = MCP251XFD_QUIRK_CRC_REG | MCP251XFD_QUIRK_CRC_RX | + MCP251XFD_QUIRK_CRC_TX | MCP251XFD_QUIRK_ECC, + .model = MCP251XFD_MODEL_MCP251XFD, +}; + +static const struct can_bittiming_const mcp251xfd_bittiming_const = { + .name = DEVICE_NAME, + .tseg1_min = 2, + .tseg1_max = 256, + .tseg2_min = 1, + .tseg2_max = 128, + .sjw_max = 128, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 1, +}; + +static const struct can_bittiming_const mcp251xfd_data_bittiming_const = { + .name = DEVICE_NAME, + .tseg1_min = 1, + .tseg1_max = 32, + .tseg2_min = 1, + .tseg2_max = 16, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 1, +}; + +static const char *__mcp251xfd_get_model_str(enum mcp251xfd_model model) +{ + switch (model) { + case MCP251XFD_MODEL_MCP2517FD: + return "MCP2517FD"; + case MCP251XFD_MODEL_MCP2518FD: + return "MCP2518FD"; + case MCP251XFD_MODEL_MCP251863: + return "MCP251863"; + case MCP251XFD_MODEL_MCP251XFD: + return "MCP251xFD"; + } + + return "<unknown>"; +} + +static inline const char * +mcp251xfd_get_model_str(const struct mcp251xfd_priv *priv) +{ + return __mcp251xfd_get_model_str(priv->devtype_data.model); +} + +static const char *mcp251xfd_get_mode_str(const u8 mode) +{ + switch (mode) { + case MCP251XFD_REG_CON_MODE_MIXED: + return "Mixed (CAN FD/CAN 2.0)"; + case MCP251XFD_REG_CON_MODE_SLEEP: + return "Sleep"; + case MCP251XFD_REG_CON_MODE_INT_LOOPBACK: + return "Internal Loopback"; + case MCP251XFD_REG_CON_MODE_LISTENONLY: + return "Listen Only"; + case MCP251XFD_REG_CON_MODE_CONFIG: + return "Configuration"; + case MCP251XFD_REG_CON_MODE_EXT_LOOPBACK: + return "External Loopback"; + case MCP251XFD_REG_CON_MODE_CAN2_0: + return "CAN 2.0"; + case MCP251XFD_REG_CON_MODE_RESTRICTED: + return "Restricted Operation"; + } + + return "<unknown>"; +} + +static const char * +mcp251xfd_get_osc_str(const u32 osc, const u32 osc_reference) +{ + switch (~osc & osc_reference & + (MCP251XFD_REG_OSC_OSCRDY | MCP251XFD_REG_OSC_PLLRDY)) { + case MCP251XFD_REG_OSC_PLLRDY: + return "PLL"; + case MCP251XFD_REG_OSC_OSCRDY: + return "Oscillator"; + case MCP251XFD_REG_OSC_PLLRDY | MCP251XFD_REG_OSC_OSCRDY: + return "Oscillator/PLL"; + } + + return "<unknown>"; +} + +static inline int mcp251xfd_vdd_enable(const struct mcp251xfd_priv *priv) +{ + if (!priv->reg_vdd) + return 0; + + return regulator_enable(priv->reg_vdd); +} + +static inline int mcp251xfd_vdd_disable(const struct mcp251xfd_priv *priv) +{ + if (!priv->reg_vdd) + return 0; + + return regulator_disable(priv->reg_vdd); +} + +static inline int +mcp251xfd_transceiver_enable(const struct mcp251xfd_priv *priv) +{ + if (!priv->reg_xceiver) + return 0; + + return regulator_enable(priv->reg_xceiver); +} + +static inline int +mcp251xfd_transceiver_disable(const struct mcp251xfd_priv *priv) +{ + if (!priv->reg_xceiver) + return 0; + + return regulator_disable(priv->reg_xceiver); +} + +static int mcp251xfd_clks_and_vdd_enable(const struct mcp251xfd_priv *priv) +{ + int err; + + err = clk_prepare_enable(priv->clk); + if (err) + return err; + + err = mcp251xfd_vdd_enable(priv); + if (err) + clk_disable_unprepare(priv->clk); + + /* Wait for oscillator stabilisation time after power up */ + usleep_range(MCP251XFD_OSC_STAB_SLEEP_US, + 2 * MCP251XFD_OSC_STAB_SLEEP_US); + + return err; +} + +static int mcp251xfd_clks_and_vdd_disable(const struct mcp251xfd_priv *priv) +{ + int err; + + err = mcp251xfd_vdd_disable(priv); + if (err) + return err; + + clk_disable_unprepare(priv->clk); + + return 0; +} + +static inline bool mcp251xfd_reg_invalid(u32 reg) +{ + return reg == 0x0 || reg == 0xffffffff; +} + +static inline int +mcp251xfd_chip_get_mode(const struct mcp251xfd_priv *priv, u8 *mode) +{ + u32 val; + int err; + + err = regmap_read(priv->map_reg, MCP251XFD_REG_CON, &val); + if (err) + return err; + + *mode = FIELD_GET(MCP251XFD_REG_CON_OPMOD_MASK, val); + + return 0; +} + +static int +__mcp251xfd_chip_set_mode(const struct mcp251xfd_priv *priv, + const u8 mode_req, bool nowait) +{ + const struct can_bittiming *bt = &priv->can.bittiming; + unsigned long timeout_us = MCP251XFD_POLL_TIMEOUT_US; + u32 con = 0, con_reqop, osc = 0; + u8 mode; + int err; + + con_reqop = FIELD_PREP(MCP251XFD_REG_CON_REQOP_MASK, mode_req); + err = regmap_update_bits(priv->map_reg, MCP251XFD_REG_CON, + MCP251XFD_REG_CON_REQOP_MASK, con_reqop); + if (err == -EBADMSG) { + netdev_err(priv->ndev, + "Failed to set Requested Operation Mode.\n"); + + return -ENODEV; + } else if (err) { + return err; + } + + if (mode_req == MCP251XFD_REG_CON_MODE_SLEEP || nowait) + return 0; + + if (bt->bitrate) + timeout_us = max_t(unsigned long, timeout_us, + MCP251XFD_FRAME_LEN_MAX_BITS * USEC_PER_SEC / + bt->bitrate); + + err = regmap_read_poll_timeout(priv->map_reg, MCP251XFD_REG_CON, con, + !mcp251xfd_reg_invalid(con) && + FIELD_GET(MCP251XFD_REG_CON_OPMOD_MASK, + con) == mode_req, + MCP251XFD_POLL_SLEEP_US, timeout_us); + if (err != -ETIMEDOUT && err != -EBADMSG) + return err; + + /* Ignore return value. + * Print below error messages, even if this fails. + */ + regmap_read(priv->map_reg, MCP251XFD_REG_OSC, &osc); + + if (mcp251xfd_reg_invalid(con)) { + netdev_err(priv->ndev, + "Failed to read CAN Control Register (con=0x%08x, osc=0x%08x).\n", + con, osc); + + return -ENODEV; + } + + mode = FIELD_GET(MCP251XFD_REG_CON_OPMOD_MASK, con); + netdev_err(priv->ndev, + "Controller failed to enter mode %s Mode (%u) and stays in %s Mode (%u) (con=0x%08x, osc=0x%08x).\n", + mcp251xfd_get_mode_str(mode_req), mode_req, + mcp251xfd_get_mode_str(mode), mode, + con, osc); + + return -ETIMEDOUT; +} + +static inline int +mcp251xfd_chip_set_mode(const struct mcp251xfd_priv *priv, + const u8 mode_req) +{ + return __mcp251xfd_chip_set_mode(priv, mode_req, false); +} + +static inline int __maybe_unused +mcp251xfd_chip_set_mode_nowait(const struct mcp251xfd_priv *priv, + const u8 mode_req) +{ + return __mcp251xfd_chip_set_mode(priv, mode_req, true); +} + +static int +mcp251xfd_chip_wait_for_osc_ready(const struct mcp251xfd_priv *priv, + u32 osc_reference, u32 osc_mask) +{ + u32 osc; + int err; + + err = regmap_read_poll_timeout(priv->map_reg, MCP251XFD_REG_OSC, osc, + !mcp251xfd_reg_invalid(osc) && + (osc & osc_mask) == osc_reference, + MCP251XFD_OSC_STAB_SLEEP_US, + MCP251XFD_OSC_STAB_TIMEOUT_US); + if (err != -ETIMEDOUT) + return err; + + if (mcp251xfd_reg_invalid(osc)) { + netdev_err(priv->ndev, + "Failed to read Oscillator Configuration Register (osc=0x%08x).\n", + osc); + return -ENODEV; + } + + netdev_err(priv->ndev, + "Timeout waiting for %s ready (osc=0x%08x, osc_reference=0x%08x, osc_mask=0x%08x).\n", + mcp251xfd_get_osc_str(osc, osc_reference), + osc, osc_reference, osc_mask); + + return -ETIMEDOUT; +} + +static int mcp251xfd_chip_wake(const struct mcp251xfd_priv *priv) +{ + u32 osc, osc_reference, osc_mask; + int err; + + /* For normal sleep on MCP2517FD and MCP2518FD, clearing + * "Oscillator Disable" will wake the chip. For low power mode + * on MCP2518FD, asserting the chip select will wake the + * chip. Writing to the Oscillator register will wake it in + * both cases. + */ + osc = FIELD_PREP(MCP251XFD_REG_OSC_CLKODIV_MASK, + MCP251XFD_REG_OSC_CLKODIV_10); + + /* We cannot check for the PLL ready bit (either set or + * unset), as the PLL might be enabled. This can happen if the + * system reboots, while the mcp251xfd stays powered. + */ + osc_reference = MCP251XFD_REG_OSC_OSCRDY; + osc_mask = MCP251XFD_REG_OSC_OSCRDY; + + /* If the controller is in Sleep Mode the following write only + * removes the "Oscillator Disable" bit and powers it up. All + * other bits are unaffected. + */ + err = regmap_write(priv->map_reg, MCP251XFD_REG_OSC, osc); + if (err) + return err; + + /* Sometimes the PLL is stuck enabled, the controller never + * sets the OSC Ready bit, and we get an -ETIMEDOUT. Our + * caller takes care of retry. + */ + return mcp251xfd_chip_wait_for_osc_ready(priv, osc_reference, osc_mask); +} + +static inline int mcp251xfd_chip_sleep(const struct mcp251xfd_priv *priv) +{ + if (priv->pll_enable) { + u32 osc; + int err; + + /* Turn off PLL */ + osc = FIELD_PREP(MCP251XFD_REG_OSC_CLKODIV_MASK, + MCP251XFD_REG_OSC_CLKODIV_10); + err = regmap_write(priv->map_reg, MCP251XFD_REG_OSC, osc); + if (err) + netdev_err(priv->ndev, + "Failed to disable PLL.\n"); + + priv->spi->max_speed_hz = priv->spi_max_speed_hz_slow; + } + + return mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_SLEEP); +} + +static int mcp251xfd_chip_softreset_do(const struct mcp251xfd_priv *priv) +{ + const __be16 cmd = mcp251xfd_cmd_reset(); + int err; + + /* The Set Mode and SPI Reset command only works if the + * controller is not in Sleep Mode. + */ + err = mcp251xfd_chip_wake(priv); + if (err) + return err; + + err = mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_CONFIG); + if (err) + return err; + + /* spi_write_then_read() works with non DMA-safe buffers */ + return spi_write_then_read(priv->spi, &cmd, sizeof(cmd), NULL, 0); +} + +static int mcp251xfd_chip_softreset_check(const struct mcp251xfd_priv *priv) +{ + u32 osc_reference, osc_mask; + u8 mode; + int err; + + /* Check for reset defaults of OSC reg. + * This will take care of stabilization period. + */ + osc_reference = MCP251XFD_REG_OSC_OSCRDY | + FIELD_PREP(MCP251XFD_REG_OSC_CLKODIV_MASK, + MCP251XFD_REG_OSC_CLKODIV_10); + osc_mask = osc_reference | MCP251XFD_REG_OSC_PLLRDY; + err = mcp251xfd_chip_wait_for_osc_ready(priv, osc_reference, osc_mask); + if (err) + return err; + + err = mcp251xfd_chip_get_mode(priv, &mode); + if (err) + return err; + + if (mode != MCP251XFD_REG_CON_MODE_CONFIG) { + netdev_info(priv->ndev, + "Controller not in Config Mode after reset, but in %s Mode (%u).\n", + mcp251xfd_get_mode_str(mode), mode); + return -ETIMEDOUT; + } + + return 0; +} + +static int mcp251xfd_chip_softreset(const struct mcp251xfd_priv *priv) +{ + int err, i; + + for (i = 0; i < MCP251XFD_SOFTRESET_RETRIES_MAX; i++) { + if (i) + netdev_info(priv->ndev, + "Retrying to reset controller.\n"); + + err = mcp251xfd_chip_softreset_do(priv); + if (err == -ETIMEDOUT) + continue; + if (err) + return err; + + err = mcp251xfd_chip_softreset_check(priv); + if (err == -ETIMEDOUT) + continue; + if (err) + return err; + + return 0; + } + + return err; +} + +static int mcp251xfd_chip_clock_init(const struct mcp251xfd_priv *priv) +{ + u32 osc, osc_reference, osc_mask; + int err; + + /* Activate Low Power Mode on Oscillator Disable. This only + * works on the MCP2518FD. The MCP2517FD will go into normal + * Sleep Mode instead. + */ + osc = MCP251XFD_REG_OSC_LPMEN | + FIELD_PREP(MCP251XFD_REG_OSC_CLKODIV_MASK, + MCP251XFD_REG_OSC_CLKODIV_10); + osc_reference = MCP251XFD_REG_OSC_OSCRDY; + osc_mask = MCP251XFD_REG_OSC_OSCRDY | MCP251XFD_REG_OSC_PLLRDY; + + if (priv->pll_enable) { + osc |= MCP251XFD_REG_OSC_PLLEN; + osc_reference |= MCP251XFD_REG_OSC_PLLRDY; + } + + err = regmap_write(priv->map_reg, MCP251XFD_REG_OSC, osc); + if (err) + return err; + + err = mcp251xfd_chip_wait_for_osc_ready(priv, osc_reference, osc_mask); + if (err) + return err; + + priv->spi->max_speed_hz = priv->spi_max_speed_hz_fast; + + return 0; +} + +static int mcp251xfd_chip_timestamp_init(const struct mcp251xfd_priv *priv) +{ + /* Set Time Base Counter Prescaler to 1. + * + * This means an overflow of the 32 bit Time Base Counter + * register at 40 MHz every 107 seconds. + */ + return regmap_write(priv->map_reg, MCP251XFD_REG_TSCON, + MCP251XFD_REG_TSCON_TBCEN); +} + +static int mcp251xfd_set_bittiming(const struct mcp251xfd_priv *priv) +{ + const struct can_bittiming *bt = &priv->can.bittiming; + const struct can_bittiming *dbt = &priv->can.data_bittiming; + u32 val = 0; + s8 tdco; + int err; + + /* CAN Control Register + * + * - no transmit bandwidth sharing + * - config mode + * - disable transmit queue + * - store in transmit FIFO event + * - transition to restricted operation mode on system error + * - ESI is transmitted recessive when ESI of message is high or + * CAN controller error passive + * - restricted retransmission attempts, + * use TQXCON_TXAT and FIFOCON_TXAT + * - wake-up filter bits T11FILTER + * - use CAN bus line filter for wakeup + * - protocol exception is treated as a form error + * - Do not compare data bytes + */ + val = FIELD_PREP(MCP251XFD_REG_CON_REQOP_MASK, + MCP251XFD_REG_CON_MODE_CONFIG) | + MCP251XFD_REG_CON_STEF | + MCP251XFD_REG_CON_ESIGM | + MCP251XFD_REG_CON_RTXAT | + FIELD_PREP(MCP251XFD_REG_CON_WFT_MASK, + MCP251XFD_REG_CON_WFT_T11FILTER) | + MCP251XFD_REG_CON_WAKFIL | + MCP251XFD_REG_CON_PXEDIS; + + if (!(priv->can.ctrlmode & CAN_CTRLMODE_FD_NON_ISO)) + val |= MCP251XFD_REG_CON_ISOCRCEN; + + err = regmap_write(priv->map_reg, MCP251XFD_REG_CON, val); + if (err) + return err; + + /* Nominal Bit Time */ + val = FIELD_PREP(MCP251XFD_REG_NBTCFG_BRP_MASK, bt->brp - 1) | + FIELD_PREP(MCP251XFD_REG_NBTCFG_TSEG1_MASK, + bt->prop_seg + bt->phase_seg1 - 1) | + FIELD_PREP(MCP251XFD_REG_NBTCFG_TSEG2_MASK, + bt->phase_seg2 - 1) | + FIELD_PREP(MCP251XFD_REG_NBTCFG_SJW_MASK, bt->sjw - 1); + + err = regmap_write(priv->map_reg, MCP251XFD_REG_NBTCFG, val); + if (err) + return err; + + if (!(priv->can.ctrlmode & CAN_CTRLMODE_FD)) + return 0; + + /* Data Bit Time */ + val = FIELD_PREP(MCP251XFD_REG_DBTCFG_BRP_MASK, dbt->brp - 1) | + FIELD_PREP(MCP251XFD_REG_DBTCFG_TSEG1_MASK, + dbt->prop_seg + dbt->phase_seg1 - 1) | + FIELD_PREP(MCP251XFD_REG_DBTCFG_TSEG2_MASK, + dbt->phase_seg2 - 1) | + FIELD_PREP(MCP251XFD_REG_DBTCFG_SJW_MASK, dbt->sjw - 1); + + err = regmap_write(priv->map_reg, MCP251XFD_REG_DBTCFG, val); + if (err) + return err; + + /* Transmitter Delay Compensation */ + tdco = clamp_t(int, dbt->brp * (dbt->prop_seg + dbt->phase_seg1), + -64, 63); + val = FIELD_PREP(MCP251XFD_REG_TDC_TDCMOD_MASK, + MCP251XFD_REG_TDC_TDCMOD_AUTO) | + FIELD_PREP(MCP251XFD_REG_TDC_TDCO_MASK, tdco); + + return regmap_write(priv->map_reg, MCP251XFD_REG_TDC, val); +} + +static int mcp251xfd_chip_rx_int_enable(const struct mcp251xfd_priv *priv) +{ + u32 val; + + if (!priv->rx_int) + return 0; + + /* Configure GPIOs: + * - PIN0: GPIO Input + * - PIN1: GPIO Input/RX Interrupt + * + * PIN1 must be Input, otherwise there is a glitch on the + * rx-INT line. It happens between setting the PIN as output + * (in the first byte of the SPI transfer) and configuring the + * PIN as interrupt (in the last byte of the SPI transfer). + */ + val = MCP251XFD_REG_IOCON_PM0 | MCP251XFD_REG_IOCON_TRIS1 | + MCP251XFD_REG_IOCON_TRIS0; + return regmap_write(priv->map_reg, MCP251XFD_REG_IOCON, val); +} + +static int mcp251xfd_chip_rx_int_disable(const struct mcp251xfd_priv *priv) +{ + u32 val; + + if (!priv->rx_int) + return 0; + + /* Configure GPIOs: + * - PIN0: GPIO Input + * - PIN1: GPIO Input + */ + val = MCP251XFD_REG_IOCON_PM1 | MCP251XFD_REG_IOCON_PM0 | + MCP251XFD_REG_IOCON_TRIS1 | MCP251XFD_REG_IOCON_TRIS0; + return regmap_write(priv->map_reg, MCP251XFD_REG_IOCON, val); +} + +static int mcp251xfd_chip_ecc_init(struct mcp251xfd_priv *priv) +{ + struct mcp251xfd_ecc *ecc = &priv->ecc; + void *ram; + u32 val = 0; + int err; + + ecc->ecc_stat = 0; + + if (priv->devtype_data.quirks & MCP251XFD_QUIRK_ECC) + val = MCP251XFD_REG_ECCCON_ECCEN; + + err = regmap_update_bits(priv->map_reg, MCP251XFD_REG_ECCCON, + MCP251XFD_REG_ECCCON_ECCEN, val); + if (err) + return err; + + ram = kzalloc(MCP251XFD_RAM_SIZE, GFP_KERNEL); + if (!ram) + return -ENOMEM; + + err = regmap_raw_write(priv->map_reg, MCP251XFD_RAM_START, ram, + MCP251XFD_RAM_SIZE); + kfree(ram); + + return err; +} + +static u8 mcp251xfd_get_normal_mode(const struct mcp251xfd_priv *priv) +{ + u8 mode; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LOOPBACK) + mode = MCP251XFD_REG_CON_MODE_INT_LOOPBACK; + else if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + mode = MCP251XFD_REG_CON_MODE_LISTENONLY; + else if (priv->can.ctrlmode & CAN_CTRLMODE_FD) + mode = MCP251XFD_REG_CON_MODE_MIXED; + else + mode = MCP251XFD_REG_CON_MODE_CAN2_0; + + return mode; +} + +static int +__mcp251xfd_chip_set_normal_mode(const struct mcp251xfd_priv *priv, + bool nowait) +{ + u8 mode; + + mode = mcp251xfd_get_normal_mode(priv); + + return __mcp251xfd_chip_set_mode(priv, mode, nowait); +} + +static inline int +mcp251xfd_chip_set_normal_mode(const struct mcp251xfd_priv *priv) +{ + return __mcp251xfd_chip_set_normal_mode(priv, false); +} + +static inline int +mcp251xfd_chip_set_normal_mode_nowait(const struct mcp251xfd_priv *priv) +{ + return __mcp251xfd_chip_set_normal_mode(priv, true); +} + +static int mcp251xfd_chip_interrupts_enable(const struct mcp251xfd_priv *priv) +{ + u32 val; + int err; + + val = MCP251XFD_REG_CRC_FERRIE | MCP251XFD_REG_CRC_CRCERRIE; + err = regmap_write(priv->map_reg, MCP251XFD_REG_CRC, val); + if (err) + return err; + + val = MCP251XFD_REG_ECCCON_DEDIE | MCP251XFD_REG_ECCCON_SECIE; + err = regmap_update_bits(priv->map_reg, MCP251XFD_REG_ECCCON, val, val); + if (err) + return err; + + val = MCP251XFD_REG_INT_CERRIE | + MCP251XFD_REG_INT_SERRIE | + MCP251XFD_REG_INT_RXOVIE | + MCP251XFD_REG_INT_TXATIE | + MCP251XFD_REG_INT_SPICRCIE | + MCP251XFD_REG_INT_ECCIE | + MCP251XFD_REG_INT_TEFIE | + MCP251XFD_REG_INT_MODIE | + MCP251XFD_REG_INT_RXIE; + + if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) + val |= MCP251XFD_REG_INT_IVMIE; + + return regmap_write(priv->map_reg, MCP251XFD_REG_INT, val); +} + +static int mcp251xfd_chip_interrupts_disable(const struct mcp251xfd_priv *priv) +{ + int err; + u32 mask; + + err = regmap_write(priv->map_reg, MCP251XFD_REG_INT, 0); + if (err) + return err; + + mask = MCP251XFD_REG_ECCCON_DEDIE | MCP251XFD_REG_ECCCON_SECIE; + err = regmap_update_bits(priv->map_reg, MCP251XFD_REG_ECCCON, + mask, 0x0); + if (err) + return err; + + return regmap_write(priv->map_reg, MCP251XFD_REG_CRC, 0); +} + +static void mcp251xfd_chip_stop(struct mcp251xfd_priv *priv, + const enum can_state state) +{ + priv->can.state = state; + + mcp251xfd_chip_interrupts_disable(priv); + mcp251xfd_chip_rx_int_disable(priv); + mcp251xfd_chip_sleep(priv); +} + +static int mcp251xfd_chip_start(struct mcp251xfd_priv *priv) +{ + int err; + + err = mcp251xfd_chip_softreset(priv); + if (err) + goto out_chip_stop; + + err = mcp251xfd_chip_clock_init(priv); + if (err) + goto out_chip_stop; + + err = mcp251xfd_chip_timestamp_init(priv); + if (err) + goto out_chip_stop; + + err = mcp251xfd_set_bittiming(priv); + if (err) + goto out_chip_stop; + + err = mcp251xfd_chip_rx_int_enable(priv); + if (err) + goto out_chip_stop; + + err = mcp251xfd_chip_ecc_init(priv); + if (err) + goto out_chip_stop; + + err = mcp251xfd_ring_init(priv); + if (err) + goto out_chip_stop; + + err = mcp251xfd_chip_fifo_init(priv); + if (err) + goto out_chip_stop; + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + err = mcp251xfd_chip_set_normal_mode(priv); + if (err) + goto out_chip_stop; + + return 0; + + out_chip_stop: + mcp251xfd_dump(priv); + mcp251xfd_chip_stop(priv, CAN_STATE_STOPPED); + + return err; +} + +static int mcp251xfd_set_mode(struct net_device *ndev, enum can_mode mode) +{ + struct mcp251xfd_priv *priv = netdev_priv(ndev); + int err; + + switch (mode) { + case CAN_MODE_START: + err = mcp251xfd_chip_start(priv); + if (err) + return err; + + err = mcp251xfd_chip_interrupts_enable(priv); + if (err) { + mcp251xfd_chip_stop(priv, CAN_STATE_STOPPED); + return err; + } + + netif_wake_queue(ndev); + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int __mcp251xfd_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + const struct mcp251xfd_priv *priv = netdev_priv(ndev); + u32 trec; + int err; + + err = regmap_read(priv->map_reg, MCP251XFD_REG_TREC, &trec); + if (err) + return err; + + if (trec & MCP251XFD_REG_TREC_TXBO) + bec->txerr = 256; + else + bec->txerr = FIELD_GET(MCP251XFD_REG_TREC_TEC_MASK, trec); + bec->rxerr = FIELD_GET(MCP251XFD_REG_TREC_REC_MASK, trec); + + return 0; +} + +static int mcp251xfd_get_berr_counter(const struct net_device *ndev, + struct can_berr_counter *bec) +{ + const struct mcp251xfd_priv *priv = netdev_priv(ndev); + + /* Avoid waking up the controller if the interface is down */ + if (!(ndev->flags & IFF_UP)) + return 0; + + /* The controller is powered down during Bus Off, use saved + * bec values. + */ + if (priv->can.state == CAN_STATE_BUS_OFF) { + *bec = priv->bec; + return 0; + } + + return __mcp251xfd_get_berr_counter(ndev, bec); +} + +static struct sk_buff * +mcp251xfd_alloc_can_err_skb(struct mcp251xfd_priv *priv, + struct can_frame **cf, u32 *timestamp) +{ + struct sk_buff *skb; + int err; + + err = mcp251xfd_get_timestamp(priv, timestamp); + if (err) + return NULL; + + skb = alloc_can_err_skb(priv->ndev, cf); + if (skb) + mcp251xfd_skb_set_timestamp(priv, skb, *timestamp); + + return skb; +} + +static int mcp251xfd_handle_rxovif(struct mcp251xfd_priv *priv) +{ + struct net_device_stats *stats = &priv->ndev->stats; + struct mcp251xfd_rx_ring *ring; + struct sk_buff *skb; + struct can_frame *cf; + u32 timestamp, rxovif; + int err, i; + + stats->rx_over_errors++; + stats->rx_errors++; + + err = regmap_read(priv->map_reg, MCP251XFD_REG_RXOVIF, &rxovif); + if (err) + return err; + + mcp251xfd_for_each_rx_ring(priv, ring, i) { + if (!(rxovif & BIT(ring->fifo_nr))) + continue; + + /* If SERRIF is active, there was a RX MAB overflow. */ + if (priv->regs_status.intf & MCP251XFD_REG_INT_SERRIF) { + if (net_ratelimit()) + netdev_dbg(priv->ndev, + "RX-%d: MAB overflow detected.\n", + ring->nr); + } else { + if (net_ratelimit()) + netdev_dbg(priv->ndev, + "RX-%d: FIFO overflow.\n", + ring->nr); + } + + err = regmap_update_bits(priv->map_reg, + MCP251XFD_REG_FIFOSTA(ring->fifo_nr), + MCP251XFD_REG_FIFOSTA_RXOVIF, + 0x0); + if (err) + return err; + } + + skb = mcp251xfd_alloc_can_err_skb(priv, &cf, ×tamp); + if (!skb) + return 0; + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + err = can_rx_offload_queue_timestamp(&priv->offload, skb, timestamp); + if (err) + stats->rx_fifo_errors++; + + return 0; +} + +static int mcp251xfd_handle_txatif(struct mcp251xfd_priv *priv) +{ + netdev_info(priv->ndev, "%s\n", __func__); + + return 0; +} + +static int mcp251xfd_handle_ivmif(struct mcp251xfd_priv *priv) +{ + struct net_device_stats *stats = &priv->ndev->stats; + u32 bdiag1, timestamp; + struct sk_buff *skb; + struct can_frame *cf = NULL; + int err; + + err = mcp251xfd_get_timestamp(priv, ×tamp); + if (err) + return err; + + err = regmap_read(priv->map_reg, MCP251XFD_REG_BDIAG1, &bdiag1); + if (err) + return err; + + /* Write 0s to clear error bits, don't write 1s to non active + * bits, as they will be set. + */ + err = regmap_write(priv->map_reg, MCP251XFD_REG_BDIAG1, 0x0); + if (err) + return err; + + priv->can.can_stats.bus_error++; + + skb = alloc_can_err_skb(priv->ndev, &cf); + if (cf) + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR; + + /* Controller misconfiguration */ + if (WARN_ON(bdiag1 & MCP251XFD_REG_BDIAG1_DLCMM)) + netdev_err(priv->ndev, + "recv'd DLC is larger than PLSIZE of FIFO element."); + + /* RX errors */ + if (bdiag1 & (MCP251XFD_REG_BDIAG1_DCRCERR | + MCP251XFD_REG_BDIAG1_NCRCERR)) { + netdev_dbg(priv->ndev, "CRC error\n"); + + stats->rx_errors++; + if (cf) + cf->data[3] |= CAN_ERR_PROT_LOC_CRC_SEQ; + } + if (bdiag1 & (MCP251XFD_REG_BDIAG1_DSTUFERR | + MCP251XFD_REG_BDIAG1_NSTUFERR)) { + netdev_dbg(priv->ndev, "Stuff error\n"); + + stats->rx_errors++; + if (cf) + cf->data[2] |= CAN_ERR_PROT_STUFF; + } + if (bdiag1 & (MCP251XFD_REG_BDIAG1_DFORMERR | + MCP251XFD_REG_BDIAG1_NFORMERR)) { + netdev_dbg(priv->ndev, "Format error\n"); + + stats->rx_errors++; + if (cf) + cf->data[2] |= CAN_ERR_PROT_FORM; + } + + /* TX errors */ + if (bdiag1 & MCP251XFD_REG_BDIAG1_NACKERR) { + netdev_dbg(priv->ndev, "NACK error\n"); + + stats->tx_errors++; + if (cf) { + cf->can_id |= CAN_ERR_ACK; + cf->data[2] |= CAN_ERR_PROT_TX; + } + } + if (bdiag1 & (MCP251XFD_REG_BDIAG1_DBIT1ERR | + MCP251XFD_REG_BDIAG1_NBIT1ERR)) { + netdev_dbg(priv->ndev, "Bit1 error\n"); + + stats->tx_errors++; + if (cf) + cf->data[2] |= CAN_ERR_PROT_TX | CAN_ERR_PROT_BIT1; + } + if (bdiag1 & (MCP251XFD_REG_BDIAG1_DBIT0ERR | + MCP251XFD_REG_BDIAG1_NBIT0ERR)) { + netdev_dbg(priv->ndev, "Bit0 error\n"); + + stats->tx_errors++; + if (cf) + cf->data[2] |= CAN_ERR_PROT_TX | CAN_ERR_PROT_BIT0; + } + + if (!cf) + return 0; + + mcp251xfd_skb_set_timestamp(priv, skb, timestamp); + err = can_rx_offload_queue_timestamp(&priv->offload, skb, timestamp); + if (err) + stats->rx_fifo_errors++; + + return 0; +} + +static int mcp251xfd_handle_cerrif(struct mcp251xfd_priv *priv) +{ + struct net_device_stats *stats = &priv->ndev->stats; + struct sk_buff *skb; + struct can_frame *cf = NULL; + enum can_state new_state, rx_state, tx_state; + u32 trec, timestamp; + int err; + + err = regmap_read(priv->map_reg, MCP251XFD_REG_TREC, &trec); + if (err) + return err; + + if (trec & MCP251XFD_REG_TREC_TXBO) + tx_state = CAN_STATE_BUS_OFF; + else if (trec & MCP251XFD_REG_TREC_TXBP) + tx_state = CAN_STATE_ERROR_PASSIVE; + else if (trec & MCP251XFD_REG_TREC_TXWARN) + tx_state = CAN_STATE_ERROR_WARNING; + else + tx_state = CAN_STATE_ERROR_ACTIVE; + + if (trec & MCP251XFD_REG_TREC_RXBP) + rx_state = CAN_STATE_ERROR_PASSIVE; + else if (trec & MCP251XFD_REG_TREC_RXWARN) + rx_state = CAN_STATE_ERROR_WARNING; + else + rx_state = CAN_STATE_ERROR_ACTIVE; + + new_state = max(tx_state, rx_state); + if (new_state == priv->can.state) + return 0; + + /* The skb allocation might fail, but can_change_state() + * handles cf == NULL. + */ + skb = mcp251xfd_alloc_can_err_skb(priv, &cf, ×tamp); + can_change_state(priv->ndev, cf, tx_state, rx_state); + + if (new_state == CAN_STATE_BUS_OFF) { + /* As we're going to switch off the chip now, let's + * save the error counters and return them to + * userspace, if do_get_berr_counter() is called while + * the chip is in Bus Off. + */ + err = __mcp251xfd_get_berr_counter(priv->ndev, &priv->bec); + if (err) + return err; + + mcp251xfd_chip_stop(priv, CAN_STATE_BUS_OFF); + can_bus_off(priv->ndev); + } + + if (!skb) + return 0; + + if (new_state != CAN_STATE_BUS_OFF) { + struct can_berr_counter bec; + + err = mcp251xfd_get_berr_counter(priv->ndev, &bec); + if (err) + return err; + cf->can_id |= CAN_ERR_CNT; + cf->data[6] = bec.txerr; + cf->data[7] = bec.rxerr; + } + + err = can_rx_offload_queue_timestamp(&priv->offload, skb, timestamp); + if (err) + stats->rx_fifo_errors++; + + return 0; +} + +static int +mcp251xfd_handle_modif(const struct mcp251xfd_priv *priv, bool *set_normal_mode) +{ + const u8 mode_reference = mcp251xfd_get_normal_mode(priv); + u8 mode; + int err; + + err = mcp251xfd_chip_get_mode(priv, &mode); + if (err) + return err; + + if (mode == mode_reference) { + netdev_dbg(priv->ndev, + "Controller changed into %s Mode (%u).\n", + mcp251xfd_get_mode_str(mode), mode); + return 0; + } + + /* According to MCP2517FD errata DS80000792B 1., during a TX + * MAB underflow, the controller will transition to Restricted + * Operation Mode or Listen Only Mode (depending on SERR2LOM). + * + * However this is not always the case. If SERR2LOM is + * configured for Restricted Operation Mode (SERR2LOM not set) + * the MCP2517FD will sometimes transition to Listen Only Mode + * first. When polling this bit we see that it will transition + * to Restricted Operation Mode shortly after. + */ + if ((priv->devtype_data.quirks & MCP251XFD_QUIRK_MAB_NO_WARN) && + (mode == MCP251XFD_REG_CON_MODE_RESTRICTED || + mode == MCP251XFD_REG_CON_MODE_LISTENONLY)) + netdev_dbg(priv->ndev, + "Controller changed into %s Mode (%u).\n", + mcp251xfd_get_mode_str(mode), mode); + else + netdev_err(priv->ndev, + "Controller changed into %s Mode (%u).\n", + mcp251xfd_get_mode_str(mode), mode); + + /* After the application requests Normal mode, the controller + * will automatically attempt to retransmit the message that + * caused the TX MAB underflow. + * + * However, if there is an ECC error in the TX-RAM, we first + * have to reload the tx-object before requesting Normal + * mode. This is done later in mcp251xfd_handle_eccif(). + */ + if (priv->regs_status.intf & MCP251XFD_REG_INT_ECCIF) { + *set_normal_mode = true; + return 0; + } + + return mcp251xfd_chip_set_normal_mode_nowait(priv); +} + +static int mcp251xfd_handle_serrif(struct mcp251xfd_priv *priv) +{ + struct mcp251xfd_ecc *ecc = &priv->ecc; + struct net_device_stats *stats = &priv->ndev->stats; + bool handled = false; + + /* TX MAB underflow + * + * According to MCP2517FD Errata DS80000792B 1. a TX MAB + * underflow is indicated by SERRIF and MODIF. + * + * In addition to the effects mentioned in the Errata, there + * are Bus Errors due to the aborted CAN frame, so a IVMIF + * will be seen as well. + * + * Sometimes there is an ECC error in the TX-RAM, which leads + * to a TX MAB underflow. + * + * However, probably due to a race condition, there is no + * associated MODIF pending. + * + * Further, there are situations, where the SERRIF is caused + * by an ECC error in the TX-RAM, but not even the ECCIF is + * set. This only seems to happen _after_ the first occurrence + * of a ECCIF (which is tracked in ecc->cnt). + * + * Treat all as a known system errors.. + */ + if ((priv->regs_status.intf & MCP251XFD_REG_INT_MODIF && + priv->regs_status.intf & MCP251XFD_REG_INT_IVMIF) || + priv->regs_status.intf & MCP251XFD_REG_INT_ECCIF || + ecc->cnt) { + const char *msg; + + if (priv->regs_status.intf & MCP251XFD_REG_INT_ECCIF || + ecc->cnt) + msg = "TX MAB underflow due to ECC error detected."; + else + msg = "TX MAB underflow detected."; + + if (priv->devtype_data.quirks & MCP251XFD_QUIRK_MAB_NO_WARN) + netdev_dbg(priv->ndev, "%s\n", msg); + else + netdev_info(priv->ndev, "%s\n", msg); + + stats->tx_aborted_errors++; + stats->tx_errors++; + handled = true; + } + + /* RX MAB overflow + * + * According to MCP2517FD Errata DS80000792B 1. a RX MAB + * overflow is indicated by SERRIF. + * + * In addition to the effects mentioned in the Errata, (most + * of the times) a RXOVIF is raised, if the FIFO that is being + * received into has the RXOVIE activated (and we have enabled + * RXOVIE on all FIFOs). + * + * Sometimes there is no RXOVIF just a RXIF is pending. + * + * Treat all as a known system errors.. + */ + if (priv->regs_status.intf & MCP251XFD_REG_INT_RXOVIF || + priv->regs_status.intf & MCP251XFD_REG_INT_RXIF) { + stats->rx_dropped++; + handled = true; + } + + if (!handled) + netdev_err(priv->ndev, + "Unhandled System Error Interrupt (intf=0x%08x)!\n", + priv->regs_status.intf); + + return 0; +} + +static int +mcp251xfd_handle_eccif_recover(struct mcp251xfd_priv *priv, u8 nr) +{ + struct mcp251xfd_tx_ring *tx_ring = priv->tx; + struct mcp251xfd_ecc *ecc = &priv->ecc; + struct mcp251xfd_tx_obj *tx_obj; + u8 chip_tx_tail, tx_tail, offset; + u16 addr; + int err; + + addr = FIELD_GET(MCP251XFD_REG_ECCSTAT_ERRADDR_MASK, ecc->ecc_stat); + + err = mcp251xfd_tx_tail_get_from_chip(priv, &chip_tx_tail); + if (err) + return err; + + tx_tail = mcp251xfd_get_tx_tail(tx_ring); + offset = (nr - chip_tx_tail) & (tx_ring->obj_num - 1); + + /* Bail out if one of the following is met: + * - tx_tail information is inconsistent + * - for mcp2517fd: offset not 0 + * - for mcp2518fd: offset not 0 or 1 + */ + if (chip_tx_tail != tx_tail || + !(offset == 0 || (offset == 1 && (mcp251xfd_is_2518FD(priv) || + mcp251xfd_is_251863(priv))))) { + netdev_err(priv->ndev, + "ECC Error information inconsistent (addr=0x%04x, nr=%d, tx_tail=0x%08x(%d), chip_tx_tail=%d, offset=%d).\n", + addr, nr, tx_ring->tail, tx_tail, chip_tx_tail, + offset); + return -EINVAL; + } + + netdev_info(priv->ndev, + "Recovering %s ECC Error at address 0x%04x (in TX-RAM, tx_obj=%d, tx_tail=0x%08x(%d), offset=%d).\n", + ecc->ecc_stat & MCP251XFD_REG_ECCSTAT_SECIF ? + "Single" : "Double", + addr, nr, tx_ring->tail, tx_tail, offset); + + /* reload tx_obj into controller RAM ... */ + tx_obj = &tx_ring->obj[nr]; + err = spi_sync_transfer(priv->spi, tx_obj->xfer, 1); + if (err) + return err; + + /* ... and trigger retransmit */ + return mcp251xfd_chip_set_normal_mode(priv); +} + +static int +mcp251xfd_handle_eccif(struct mcp251xfd_priv *priv, bool set_normal_mode) +{ + struct mcp251xfd_ecc *ecc = &priv->ecc; + const char *msg; + bool in_tx_ram; + u32 ecc_stat; + u16 addr; + u8 nr; + int err; + + err = regmap_read(priv->map_reg, MCP251XFD_REG_ECCSTAT, &ecc_stat); + if (err) + return err; + + err = regmap_update_bits(priv->map_reg, MCP251XFD_REG_ECCSTAT, + MCP251XFD_REG_ECCSTAT_IF_MASK, ~ecc_stat); + if (err) + return err; + + /* Check if ECC error occurred in TX-RAM */ + addr = FIELD_GET(MCP251XFD_REG_ECCSTAT_ERRADDR_MASK, ecc_stat); + err = mcp251xfd_get_tx_nr_by_addr(priv->tx, &nr, addr); + if (!err) + in_tx_ram = true; + else if (err == -ENOENT) + in_tx_ram = false; + else + return err; + + /* Errata Reference: + * mcp2517fd: DS80000789B, mcp2518fd: DS80000792C 2. + * + * ECC single error correction does not work in all cases: + * + * Fix/Work Around: + * Enable single error correction and double error detection + * interrupts by setting SECIE and DEDIE. Handle SECIF as a + * detection interrupt and do not rely on the error + * correction. Instead, handle both interrupts as a + * notification that the RAM word at ERRADDR was corrupted. + */ + if (ecc_stat & MCP251XFD_REG_ECCSTAT_SECIF) + msg = "Single ECC Error detected at address"; + else if (ecc_stat & MCP251XFD_REG_ECCSTAT_DEDIF) + msg = "Double ECC Error detected at address"; + else + return -EINVAL; + + if (!in_tx_ram) { + ecc->ecc_stat = 0; + + netdev_notice(priv->ndev, "%s 0x%04x.\n", msg, addr); + } else { + /* Re-occurring error? */ + if (ecc->ecc_stat == ecc_stat) { + ecc->cnt++; + } else { + ecc->ecc_stat = ecc_stat; + ecc->cnt = 1; + } + + netdev_info(priv->ndev, + "%s 0x%04x (in TX-RAM, tx_obj=%d), occurred %d time%s.\n", + msg, addr, nr, ecc->cnt, ecc->cnt > 1 ? "s" : ""); + + if (ecc->cnt >= MCP251XFD_ECC_CNT_MAX) + return mcp251xfd_handle_eccif_recover(priv, nr); + } + + if (set_normal_mode) + return mcp251xfd_chip_set_normal_mode_nowait(priv); + + return 0; +} + +static int mcp251xfd_handle_spicrcif(struct mcp251xfd_priv *priv) +{ + int err; + u32 crc; + + err = regmap_read(priv->map_reg, MCP251XFD_REG_CRC, &crc); + if (err) + return err; + + err = regmap_update_bits(priv->map_reg, MCP251XFD_REG_CRC, + MCP251XFD_REG_CRC_IF_MASK, + ~crc); + if (err) + return err; + + if (crc & MCP251XFD_REG_CRC_FERRIF) + netdev_notice(priv->ndev, "CRC write command format error.\n"); + else if (crc & MCP251XFD_REG_CRC_CRCERRIF) + netdev_notice(priv->ndev, + "CRC write error detected. CRC=0x%04lx.\n", + FIELD_GET(MCP251XFD_REG_CRC_MASK, crc)); + + return 0; +} + +static int mcp251xfd_read_regs_status(struct mcp251xfd_priv *priv) +{ + const int val_bytes = regmap_get_val_bytes(priv->map_reg); + size_t len; + + if (priv->rx_ring_num == 1) + len = sizeof(priv->regs_status.intf); + else + len = sizeof(priv->regs_status); + + return regmap_bulk_read(priv->map_reg, MCP251XFD_REG_INT, + &priv->regs_status, len / val_bytes); +} + +#define mcp251xfd_handle(priv, irq, ...) \ +({ \ + struct mcp251xfd_priv *_priv = (priv); \ + int err; \ +\ + err = mcp251xfd_handle_##irq(_priv, ## __VA_ARGS__); \ + if (err) \ + netdev_err(_priv->ndev, \ + "IRQ handler mcp251xfd_handle_%s() returned %d.\n", \ + __stringify(irq), err); \ + err; \ +}) + +static irqreturn_t mcp251xfd_irq(int irq, void *dev_id) +{ + struct mcp251xfd_priv *priv = dev_id; + irqreturn_t handled = IRQ_NONE; + int err; + + if (priv->rx_int) + do { + int rx_pending; + + rx_pending = gpiod_get_value_cansleep(priv->rx_int); + if (!rx_pending) + break; + + /* Assume 1st RX-FIFO pending, if other FIFOs + * are pending the main IRQ handler will take + * care. + */ + priv->regs_status.rxif = BIT(priv->rx[0]->fifo_nr); + err = mcp251xfd_handle(priv, rxif); + if (err) + goto out_fail; + + handled = IRQ_HANDLED; + + /* We don't know which RX-FIFO is pending, but only + * handle the 1st RX-FIFO. Leave loop here if we have + * more than 1 RX-FIFO to avoid starvation. + */ + } while (priv->rx_ring_num == 1); + + do { + u32 intf_pending, intf_pending_clearable; + bool set_normal_mode = false; + + err = mcp251xfd_read_regs_status(priv); + if (err) + goto out_fail; + + intf_pending = FIELD_GET(MCP251XFD_REG_INT_IF_MASK, + priv->regs_status.intf) & + FIELD_GET(MCP251XFD_REG_INT_IE_MASK, + priv->regs_status.intf); + + if (!(intf_pending)) { + can_rx_offload_threaded_irq_finish(&priv->offload); + return handled; + } + + /* Some interrupts must be ACKed in the + * MCP251XFD_REG_INT register. + * - First ACK then handle, to avoid lost-IRQ race + * condition on fast re-occurring interrupts. + * - Write "0" to clear active IRQs, "1" to all other, + * to avoid r/m/w race condition on the + * MCP251XFD_REG_INT register. + */ + intf_pending_clearable = intf_pending & + MCP251XFD_REG_INT_IF_CLEARABLE_MASK; + if (intf_pending_clearable) { + err = regmap_update_bits(priv->map_reg, + MCP251XFD_REG_INT, + MCP251XFD_REG_INT_IF_MASK, + ~intf_pending_clearable); + if (err) + goto out_fail; + } + + if (intf_pending & MCP251XFD_REG_INT_MODIF) { + err = mcp251xfd_handle(priv, modif, &set_normal_mode); + if (err) + goto out_fail; + } + + if (intf_pending & MCP251XFD_REG_INT_RXIF) { + err = mcp251xfd_handle(priv, rxif); + if (err) + goto out_fail; + } + + if (intf_pending & MCP251XFD_REG_INT_TEFIF) { + err = mcp251xfd_handle(priv, tefif); + if (err) + goto out_fail; + } + + if (intf_pending & MCP251XFD_REG_INT_RXOVIF) { + err = mcp251xfd_handle(priv, rxovif); + if (err) + goto out_fail; + } + + if (intf_pending & MCP251XFD_REG_INT_TXATIF) { + err = mcp251xfd_handle(priv, txatif); + if (err) + goto out_fail; + } + + if (intf_pending & MCP251XFD_REG_INT_IVMIF) { + err = mcp251xfd_handle(priv, ivmif); + if (err) + goto out_fail; + } + + if (intf_pending & MCP251XFD_REG_INT_SERRIF) { + err = mcp251xfd_handle(priv, serrif); + if (err) + goto out_fail; + } + + if (intf_pending & MCP251XFD_REG_INT_ECCIF) { + err = mcp251xfd_handle(priv, eccif, set_normal_mode); + if (err) + goto out_fail; + } + + if (intf_pending & MCP251XFD_REG_INT_SPICRCIF) { + err = mcp251xfd_handle(priv, spicrcif); + if (err) + goto out_fail; + } + + /* On the MCP2527FD and MCP2518FD, we don't get a + * CERRIF IRQ on the transition TX ERROR_WARNING -> TX + * ERROR_ACTIVE. + */ + if (intf_pending & MCP251XFD_REG_INT_CERRIF || + priv->can.state > CAN_STATE_ERROR_ACTIVE) { + err = mcp251xfd_handle(priv, cerrif); + if (err) + goto out_fail; + + /* In Bus Off we completely shut down the + * controller. Every subsequent register read + * will read bogus data, and if + * MCP251XFD_QUIRK_CRC_REG is enabled the CRC + * check will fail, too. So leave IRQ handler + * directly. + */ + if (priv->can.state == CAN_STATE_BUS_OFF) { + can_rx_offload_threaded_irq_finish(&priv->offload); + return IRQ_HANDLED; + } + } + + handled = IRQ_HANDLED; + } while (1); + + out_fail: + can_rx_offload_threaded_irq_finish(&priv->offload); + + netdev_err(priv->ndev, "IRQ handler returned %d (intf=0x%08x).\n", + err, priv->regs_status.intf); + mcp251xfd_dump(priv); + mcp251xfd_chip_interrupts_disable(priv); + mcp251xfd_timestamp_stop(priv); + + return handled; +} + +static int mcp251xfd_open(struct net_device *ndev) +{ + struct mcp251xfd_priv *priv = netdev_priv(ndev); + const struct spi_device *spi = priv->spi; + int err; + + err = open_candev(ndev); + if (err) + return err; + + err = pm_runtime_resume_and_get(ndev->dev.parent); + if (err) + goto out_close_candev; + + err = mcp251xfd_ring_alloc(priv); + if (err) + goto out_pm_runtime_put; + + err = mcp251xfd_transceiver_enable(priv); + if (err) + goto out_mcp251xfd_ring_free; + + err = mcp251xfd_chip_start(priv); + if (err) + goto out_transceiver_disable; + + mcp251xfd_timestamp_init(priv); + clear_bit(MCP251XFD_FLAGS_DOWN, priv->flags); + can_rx_offload_enable(&priv->offload); + + err = request_threaded_irq(spi->irq, NULL, mcp251xfd_irq, + IRQF_SHARED | IRQF_ONESHOT, + dev_name(&spi->dev), priv); + if (err) + goto out_can_rx_offload_disable; + + err = mcp251xfd_chip_interrupts_enable(priv); + if (err) + goto out_free_irq; + + netif_start_queue(ndev); + + return 0; + + out_free_irq: + free_irq(spi->irq, priv); + out_can_rx_offload_disable: + can_rx_offload_disable(&priv->offload); + set_bit(MCP251XFD_FLAGS_DOWN, priv->flags); + mcp251xfd_timestamp_stop(priv); + out_transceiver_disable: + mcp251xfd_transceiver_disable(priv); + out_mcp251xfd_ring_free: + mcp251xfd_ring_free(priv); + out_pm_runtime_put: + mcp251xfd_chip_stop(priv, CAN_STATE_STOPPED); + pm_runtime_put(ndev->dev.parent); + out_close_candev: + close_candev(ndev); + + return err; +} + +static int mcp251xfd_stop(struct net_device *ndev) +{ + struct mcp251xfd_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + set_bit(MCP251XFD_FLAGS_DOWN, priv->flags); + hrtimer_cancel(&priv->rx_irq_timer); + hrtimer_cancel(&priv->tx_irq_timer); + mcp251xfd_chip_interrupts_disable(priv); + free_irq(ndev->irq, priv); + can_rx_offload_disable(&priv->offload); + mcp251xfd_timestamp_stop(priv); + mcp251xfd_chip_stop(priv, CAN_STATE_STOPPED); + mcp251xfd_transceiver_disable(priv); + mcp251xfd_ring_free(priv); + close_candev(ndev); + + pm_runtime_put(ndev->dev.parent); + + return 0; +} + +static const struct net_device_ops mcp251xfd_netdev_ops = { + .ndo_open = mcp251xfd_open, + .ndo_stop = mcp251xfd_stop, + .ndo_start_xmit = mcp251xfd_start_xmit, + .ndo_eth_ioctl = can_eth_ioctl_hwts, + .ndo_change_mtu = can_change_mtu, +}; + +static void +mcp251xfd_register_quirks(struct mcp251xfd_priv *priv) +{ + const struct spi_device *spi = priv->spi; + const struct spi_controller *ctlr = spi->controller; + + if (ctlr->flags & SPI_CONTROLLER_HALF_DUPLEX) + priv->devtype_data.quirks |= MCP251XFD_QUIRK_HALF_DUPLEX; +} + +static int mcp251xfd_register_chip_detect(struct mcp251xfd_priv *priv) +{ + const struct net_device *ndev = priv->ndev; + const struct mcp251xfd_devtype_data *devtype_data; + u32 osc; + int err; + + /* The OSC_LPMEN is only supported on MCP2518FD and MCP251863, + * so use it to autodetect the model. + */ + err = regmap_update_bits(priv->map_reg, MCP251XFD_REG_OSC, + MCP251XFD_REG_OSC_LPMEN, + MCP251XFD_REG_OSC_LPMEN); + if (err) + return err; + + err = regmap_read(priv->map_reg, MCP251XFD_REG_OSC, &osc); + if (err) + return err; + + if (osc & MCP251XFD_REG_OSC_LPMEN) { + /* We cannot distinguish between MCP2518FD and + * MCP251863. If firmware specifies MCP251863, keep + * it, otherwise set to MCP2518FD. + */ + if (mcp251xfd_is_251863(priv)) + devtype_data = &mcp251xfd_devtype_data_mcp251863; + else + devtype_data = &mcp251xfd_devtype_data_mcp2518fd; + } else { + devtype_data = &mcp251xfd_devtype_data_mcp2517fd; + } + + if (!mcp251xfd_is_251XFD(priv) && + priv->devtype_data.model != devtype_data->model) { + netdev_info(ndev, + "Detected %s, but firmware specifies a %s. Fixing up.\n", + __mcp251xfd_get_model_str(devtype_data->model), + mcp251xfd_get_model_str(priv)); + } + priv->devtype_data = *devtype_data; + + /* We need to preserve the Half Duplex Quirk. */ + mcp251xfd_register_quirks(priv); + + /* Re-init regmap with quirks of detected model. */ + return mcp251xfd_regmap_init(priv); +} + +static int mcp251xfd_register_check_rx_int(struct mcp251xfd_priv *priv) +{ + int err, rx_pending; + + if (!priv->rx_int) + return 0; + + err = mcp251xfd_chip_rx_int_enable(priv); + if (err) + return err; + + /* Check if RX_INT is properly working. The RX_INT should not + * be active after a softreset. + */ + rx_pending = gpiod_get_value_cansleep(priv->rx_int); + + err = mcp251xfd_chip_rx_int_disable(priv); + if (err) + return err; + + if (!rx_pending) + return 0; + + netdev_info(priv->ndev, + "RX_INT active after softreset, disabling RX_INT support.\n"); + devm_gpiod_put(&priv->spi->dev, priv->rx_int); + priv->rx_int = NULL; + + return 0; +} + +static int +mcp251xfd_register_get_dev_id(const struct mcp251xfd_priv *priv, u32 *dev_id, + u32 *effective_speed_hz_slow, + u32 *effective_speed_hz_fast) +{ + struct mcp251xfd_map_buf_nocrc *buf_rx; + struct mcp251xfd_map_buf_nocrc *buf_tx; + struct spi_transfer xfer[2] = { }; + int err; + + buf_rx = kzalloc(sizeof(*buf_rx), GFP_KERNEL); + if (!buf_rx) + return -ENOMEM; + + buf_tx = kzalloc(sizeof(*buf_tx), GFP_KERNEL); + if (!buf_tx) { + err = -ENOMEM; + goto out_kfree_buf_rx; + } + + xfer[0].tx_buf = buf_tx; + xfer[0].len = sizeof(buf_tx->cmd); + xfer[0].speed_hz = priv->spi_max_speed_hz_slow; + xfer[1].rx_buf = buf_rx->data; + xfer[1].len = sizeof(*dev_id); + xfer[1].speed_hz = priv->spi_max_speed_hz_fast; + + mcp251xfd_spi_cmd_read_nocrc(&buf_tx->cmd, MCP251XFD_REG_DEVID); + + err = spi_sync_transfer(priv->spi, xfer, ARRAY_SIZE(xfer)); + if (err) + goto out_kfree_buf_tx; + + *dev_id = get_unaligned_le32(buf_rx->data); + *effective_speed_hz_slow = xfer[0].effective_speed_hz; + *effective_speed_hz_fast = xfer[1].effective_speed_hz; + + out_kfree_buf_tx: + kfree(buf_tx); + out_kfree_buf_rx: + kfree(buf_rx); + + return err; +} + +#define MCP251XFD_QUIRK_ACTIVE(quirk) \ + (priv->devtype_data.quirks & MCP251XFD_QUIRK_##quirk ? '+' : '-') + +static int +mcp251xfd_register_done(const struct mcp251xfd_priv *priv) +{ + u32 dev_id, effective_speed_hz_slow, effective_speed_hz_fast; + unsigned long clk_rate; + int err; + + err = mcp251xfd_register_get_dev_id(priv, &dev_id, + &effective_speed_hz_slow, + &effective_speed_hz_fast); + if (err) + return err; + + clk_rate = clk_get_rate(priv->clk); + + netdev_info(priv->ndev, + "%s rev%lu.%lu (%cRX_INT %cPLL %cMAB_NO_WARN %cCRC_REG %cCRC_RX %cCRC_TX %cECC %cHD o:%lu.%02luMHz c:%u.%02uMHz m:%u.%02uMHz rs:%u.%02uMHz es:%u.%02uMHz rf:%u.%02uMHz ef:%u.%02uMHz) successfully initialized.\n", + mcp251xfd_get_model_str(priv), + FIELD_GET(MCP251XFD_REG_DEVID_ID_MASK, dev_id), + FIELD_GET(MCP251XFD_REG_DEVID_REV_MASK, dev_id), + priv->rx_int ? '+' : '-', + priv->pll_enable ? '+' : '-', + MCP251XFD_QUIRK_ACTIVE(MAB_NO_WARN), + MCP251XFD_QUIRK_ACTIVE(CRC_REG), + MCP251XFD_QUIRK_ACTIVE(CRC_RX), + MCP251XFD_QUIRK_ACTIVE(CRC_TX), + MCP251XFD_QUIRK_ACTIVE(ECC), + MCP251XFD_QUIRK_ACTIVE(HALF_DUPLEX), + clk_rate / 1000000, + clk_rate % 1000000 / 1000 / 10, + priv->can.clock.freq / 1000000, + priv->can.clock.freq % 1000000 / 1000 / 10, + priv->spi_max_speed_hz_orig / 1000000, + priv->spi_max_speed_hz_orig % 1000000 / 1000 / 10, + priv->spi_max_speed_hz_slow / 1000000, + priv->spi_max_speed_hz_slow % 1000000 / 1000 / 10, + effective_speed_hz_slow / 1000000, + effective_speed_hz_slow % 1000000 / 1000 / 10, + priv->spi_max_speed_hz_fast / 1000000, + priv->spi_max_speed_hz_fast % 1000000 / 1000 / 10, + effective_speed_hz_fast / 1000000, + effective_speed_hz_fast % 1000000 / 1000 / 10); + + return 0; +} + +static int mcp251xfd_register(struct mcp251xfd_priv *priv) +{ + struct net_device *ndev = priv->ndev; + int err; + + err = mcp251xfd_clks_and_vdd_enable(priv); + if (err) + return err; + + pm_runtime_get_noresume(ndev->dev.parent); + err = pm_runtime_set_active(ndev->dev.parent); + if (err) + goto out_runtime_put_noidle; + pm_runtime_enable(ndev->dev.parent); + + mcp251xfd_register_quirks(priv); + + err = mcp251xfd_chip_softreset(priv); + if (err == -ENODEV) + goto out_runtime_disable; + if (err) + goto out_chip_sleep; + + err = mcp251xfd_chip_clock_init(priv); + if (err == -ENODEV) + goto out_runtime_disable; + if (err) + goto out_chip_sleep; + + err = mcp251xfd_register_chip_detect(priv); + if (err) + goto out_chip_sleep; + + err = mcp251xfd_register_check_rx_int(priv); + if (err) + goto out_chip_sleep; + + mcp251xfd_ethtool_init(priv); + + err = register_candev(ndev); + if (err) + goto out_chip_sleep; + + err = mcp251xfd_register_done(priv); + if (err) + goto out_unregister_candev; + + /* Put controller into sleep mode and let pm_runtime_put() + * disable the clocks and vdd. If CONFIG_PM is not enabled, + * the clocks and vdd will stay powered. + */ + err = mcp251xfd_chip_sleep(priv); + if (err) + goto out_unregister_candev; + + pm_runtime_put(ndev->dev.parent); + + return 0; + + out_unregister_candev: + unregister_candev(ndev); + out_chip_sleep: + mcp251xfd_chip_sleep(priv); + out_runtime_disable: + pm_runtime_disable(ndev->dev.parent); + out_runtime_put_noidle: + pm_runtime_put_noidle(ndev->dev.parent); + mcp251xfd_clks_and_vdd_disable(priv); + + return err; +} + +static inline void mcp251xfd_unregister(struct mcp251xfd_priv *priv) +{ + struct net_device *ndev = priv->ndev; + + unregister_candev(ndev); + + if (pm_runtime_enabled(ndev->dev.parent)) + pm_runtime_disable(ndev->dev.parent); + else + mcp251xfd_clks_and_vdd_disable(priv); +} + +static const struct of_device_id mcp251xfd_of_match[] = { + { + .compatible = "microchip,mcp2517fd", + .data = &mcp251xfd_devtype_data_mcp2517fd, + }, { + .compatible = "microchip,mcp2518fd", + .data = &mcp251xfd_devtype_data_mcp2518fd, + }, { + .compatible = "microchip,mcp251863", + .data = &mcp251xfd_devtype_data_mcp251863, + }, { + .compatible = "microchip,mcp251xfd", + .data = &mcp251xfd_devtype_data_mcp251xfd, + }, { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(of, mcp251xfd_of_match); + +static const struct spi_device_id mcp251xfd_id_table[] = { + { + .name = "mcp2517fd", + .driver_data = (kernel_ulong_t)&mcp251xfd_devtype_data_mcp2517fd, + }, { + .name = "mcp2518fd", + .driver_data = (kernel_ulong_t)&mcp251xfd_devtype_data_mcp2518fd, + }, { + .name = "mcp251863", + .driver_data = (kernel_ulong_t)&mcp251xfd_devtype_data_mcp251863, + }, { + .name = "mcp251xfd", + .driver_data = (kernel_ulong_t)&mcp251xfd_devtype_data_mcp251xfd, + }, { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(spi, mcp251xfd_id_table); + +static int mcp251xfd_probe(struct spi_device *spi) +{ + const void *match; + struct net_device *ndev; + struct mcp251xfd_priv *priv; + struct gpio_desc *rx_int; + struct regulator *reg_vdd, *reg_xceiver; + struct clk *clk; + bool pll_enable = false; + u32 freq = 0; + int err; + + if (!spi->irq) + return dev_err_probe(&spi->dev, -ENXIO, + "No IRQ specified (maybe node \"interrupts-extended\" in DT missing)!\n"); + + rx_int = devm_gpiod_get_optional(&spi->dev, "microchip,rx-int", + GPIOD_IN); + if (IS_ERR(rx_int)) + return dev_err_probe(&spi->dev, PTR_ERR(rx_int), + "Failed to get RX-INT!\n"); + + reg_vdd = devm_regulator_get_optional(&spi->dev, "vdd"); + if (PTR_ERR(reg_vdd) == -ENODEV) + reg_vdd = NULL; + else if (IS_ERR(reg_vdd)) + return dev_err_probe(&spi->dev, PTR_ERR(reg_vdd), + "Failed to get VDD regulator!\n"); + + reg_xceiver = devm_regulator_get_optional(&spi->dev, "xceiver"); + if (PTR_ERR(reg_xceiver) == -ENODEV) + reg_xceiver = NULL; + else if (IS_ERR(reg_xceiver)) + return dev_err_probe(&spi->dev, PTR_ERR(reg_xceiver), + "Failed to get Transceiver regulator!\n"); + + clk = devm_clk_get_optional(&spi->dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(&spi->dev, PTR_ERR(clk), + "Failed to get Oscillator (clock)!\n"); + if (clk) { + freq = clk_get_rate(clk); + } else { + err = device_property_read_u32(&spi->dev, "clock-frequency", + &freq); + if (err) + return dev_err_probe(&spi->dev, err, + "Failed to get clock-frequency!\n"); + } + + /* Sanity check */ + if (freq < MCP251XFD_SYSCLOCK_HZ_MIN || + freq > MCP251XFD_SYSCLOCK_HZ_MAX) { + dev_err(&spi->dev, + "Oscillator frequency (%u Hz) is too low or high.\n", + freq); + return -ERANGE; + } + + if (freq <= MCP251XFD_SYSCLOCK_HZ_MAX / MCP251XFD_OSC_PLL_MULTIPLIER) + pll_enable = true; + + ndev = alloc_candev(sizeof(struct mcp251xfd_priv), + MCP251XFD_TX_OBJ_NUM_MAX); + if (!ndev) + return -ENOMEM; + + SET_NETDEV_DEV(ndev, &spi->dev); + + ndev->netdev_ops = &mcp251xfd_netdev_ops; + ndev->irq = spi->irq; + ndev->flags |= IFF_ECHO; + + priv = netdev_priv(ndev); + spi_set_drvdata(spi, priv); + priv->can.clock.freq = freq; + if (pll_enable) + priv->can.clock.freq *= MCP251XFD_OSC_PLL_MULTIPLIER; + priv->can.do_set_mode = mcp251xfd_set_mode; + priv->can.do_get_berr_counter = mcp251xfd_get_berr_counter; + priv->can.bittiming_const = &mcp251xfd_bittiming_const; + priv->can.data_bittiming_const = &mcp251xfd_data_bittiming_const; + priv->can.ctrlmode_supported = CAN_CTRLMODE_LOOPBACK | + CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_BERR_REPORTING | + CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO | + CAN_CTRLMODE_CC_LEN8_DLC; + set_bit(MCP251XFD_FLAGS_DOWN, priv->flags); + priv->ndev = ndev; + priv->spi = spi; + priv->rx_int = rx_int; + priv->clk = clk; + priv->pll_enable = pll_enable; + priv->reg_vdd = reg_vdd; + priv->reg_xceiver = reg_xceiver; + + match = device_get_match_data(&spi->dev); + if (match) + priv->devtype_data = *(struct mcp251xfd_devtype_data *)match; + else + priv->devtype_data = *(struct mcp251xfd_devtype_data *) + spi_get_device_id(spi)->driver_data; + + /* Errata Reference: + * mcp2517fd: DS80000792C 5., mcp2518fd: DS80000789C 4. + * + * The SPI can write corrupted data to the RAM at fast SPI + * speeds: + * + * Simultaneous activity on the CAN bus while writing data to + * RAM via the SPI interface, with high SCK frequency, can + * lead to corrupted data being written to RAM. + * + * Fix/Work Around: + * Ensure that FSCK is less than or equal to 0.85 * + * (FSYSCLK/2). + * + * Known good combinations are: + * + * MCP ext-clk SoC SPI SPI-clk max-clk parent-clk config + * + * 2518 20 MHz allwinner,sun8i-h3 allwinner,sun8i-h3-spi 8333333 Hz 83.33% 600000000 Hz assigned-clocks = <&ccu CLK_SPIx> + * 2518 40 MHz allwinner,sun8i-h3 allwinner,sun8i-h3-spi 16666667 Hz 83.33% 600000000 Hz assigned-clocks = <&ccu CLK_SPIx> + * 2517 40 MHz atmel,sama5d27 atmel,at91rm9200-spi 16400000 Hz 82.00% 82000000 Hz default + * 2518 40 MHz atmel,sama5d27 atmel,at91rm9200-spi 16400000 Hz 82.00% 82000000 Hz default + * 2518 40 MHz fsl,imx6dl fsl,imx51-ecspi 15000000 Hz 75.00% 30000000 Hz default + * 2517 20 MHz fsl,imx8mm fsl,imx51-ecspi 8333333 Hz 83.33% 16666667 Hz assigned-clocks = <&clk IMX8MM_CLK_ECSPIx_ROOT> + * + */ + priv->spi_max_speed_hz_orig = spi->max_speed_hz; + priv->spi_max_speed_hz_slow = min(spi->max_speed_hz, + freq / 2 / 1000 * 850); + if (priv->pll_enable) + priv->spi_max_speed_hz_fast = min(spi->max_speed_hz, + freq * + MCP251XFD_OSC_PLL_MULTIPLIER / + 2 / 1000 * 850); + else + priv->spi_max_speed_hz_fast = priv->spi_max_speed_hz_slow; + spi->max_speed_hz = priv->spi_max_speed_hz_slow; + spi->bits_per_word = 8; + spi->rt = true; + err = spi_setup(spi); + if (err) + goto out_free_candev; + + err = mcp251xfd_regmap_init(priv); + if (err) + goto out_free_candev; + + err = can_rx_offload_add_manual(ndev, &priv->offload, + MCP251XFD_NAPI_WEIGHT); + if (err) + goto out_free_candev; + + err = mcp251xfd_register(priv); + if (err) { + dev_err_probe(&spi->dev, err, "Failed to detect %s.\n", + mcp251xfd_get_model_str(priv)); + goto out_can_rx_offload_del; + } + + return 0; + + out_can_rx_offload_del: + can_rx_offload_del(&priv->offload); + out_free_candev: + spi->max_speed_hz = priv->spi_max_speed_hz_orig; + + free_candev(ndev); + + return err; +} + +static void mcp251xfd_remove(struct spi_device *spi) +{ + struct mcp251xfd_priv *priv = spi_get_drvdata(spi); + struct net_device *ndev = priv->ndev; + + can_rx_offload_del(&priv->offload); + mcp251xfd_unregister(priv); + spi->max_speed_hz = priv->spi_max_speed_hz_orig; + free_candev(ndev); +} + +static int __maybe_unused mcp251xfd_runtime_suspend(struct device *device) +{ + const struct mcp251xfd_priv *priv = dev_get_drvdata(device); + + return mcp251xfd_clks_and_vdd_disable(priv); +} + +static int __maybe_unused mcp251xfd_runtime_resume(struct device *device) +{ + const struct mcp251xfd_priv *priv = dev_get_drvdata(device); + + return mcp251xfd_clks_and_vdd_enable(priv); +} + +static const struct dev_pm_ops mcp251xfd_pm_ops = { + SET_RUNTIME_PM_OPS(mcp251xfd_runtime_suspend, + mcp251xfd_runtime_resume, NULL) +}; + +static struct spi_driver mcp251xfd_driver = { + .driver = { + .name = DEVICE_NAME, + .pm = &mcp251xfd_pm_ops, + .of_match_table = mcp251xfd_of_match, + }, + .probe = mcp251xfd_probe, + .remove = mcp251xfd_remove, + .id_table = mcp251xfd_id_table, +}; +module_spi_driver(mcp251xfd_driver); + +MODULE_AUTHOR("Marc Kleine-Budde <mkl@pengutronix.de>"); +MODULE_DESCRIPTION("Microchip MCP251xFD Family CAN controller driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-crc16.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-crc16.c new file mode 100644 index 0000000000..a02ca76ac2 --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-crc16.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mcp251xfd - Microchip MCP251xFD Family CAN controller driver +// +// Copyright (c) 2020 Pengutronix, +// Marc Kleine-Budde <kernel@pengutronix.de> +// +// Based on: +// +// CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface +// +// Copyright (c) 2019 Martin Sperl <kernel@martin.sperl.org> +// + +#include "mcp251xfd.h" + +/* The standard crc16 in linux/crc16.h is unfortunately not computing + * the correct results (left shift vs. right shift). So here an + * implementation with a table generated with the help of: + * + * http://lkml.iu.edu/hypermail/linux/kernel/0508.1/1085.html + */ +static const u16 mcp251xfd_crc16_table[] = { + 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041, + 0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2, + 0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1, + 0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1, + 0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192, + 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1, + 0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1, + 0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2, + 0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342, + 0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1, + 0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2, + 0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2, + 0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291, + 0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2, + 0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2, + 0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1, + 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202 +}; + +static inline u16 mcp251xfd_crc16_byte(u16 crc, const u8 data) +{ + u8 index = (crc >> 8) ^ data; + + return (crc << 8) ^ mcp251xfd_crc16_table[index]; +} + +static u16 mcp251xfd_crc16(u16 crc, u8 const *buffer, size_t len) +{ + while (len--) + crc = mcp251xfd_crc16_byte(crc, *buffer++); + + return crc; +} + +u16 mcp251xfd_crc16_compute(const void *data, size_t data_size) +{ + u16 crc = 0xffff; + + return mcp251xfd_crc16(crc, data, data_size); +} + +u16 mcp251xfd_crc16_compute2(const void *cmd, size_t cmd_size, + const void *data, size_t data_size) +{ + u16 crc; + + crc = mcp251xfd_crc16_compute(cmd, cmd_size); + crc = mcp251xfd_crc16(crc, data, data_size); + + return crc; +} diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-dump.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-dump.c new file mode 100644 index 0000000000..004eaf9626 --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-dump.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mcp251xfd - Microchip MCP251xFD Family CAN controller driver +// +// Copyright (c) 2020, 2021 Pengutronix, +// Marc Kleine-Budde <kernel@pengutronix.de> +// Copyright (C) 2015-2018 Etnaviv Project +// + +#include <linux/devcoredump.h> + +#include "mcp251xfd.h" +#include "mcp251xfd-dump.h" + +struct mcp251xfd_dump_iter { + void *start; + struct mcp251xfd_dump_object_header *hdr; + void *data; +}; + +struct mcp251xfd_dump_reg_space { + u16 base; + u16 size; +}; + +struct mcp251xfd_dump_ring { + enum mcp251xfd_dump_object_ring_key key; + u32 val; +}; + +static const struct mcp251xfd_dump_reg_space mcp251xfd_dump_reg_space[] = { + { + .base = MCP251XFD_REG_CON, + .size = MCP251XFD_REG_FLTOBJ(32) - MCP251XFD_REG_CON, + }, { + .base = MCP251XFD_RAM_START, + .size = MCP251XFD_RAM_SIZE, + }, { + .base = MCP251XFD_REG_OSC, + .size = MCP251XFD_REG_DEVID - MCP251XFD_REG_OSC, + }, +}; + +static void mcp251xfd_dump_header(struct mcp251xfd_dump_iter *iter, + enum mcp251xfd_dump_object_type object_type, + const void *data_end) +{ + struct mcp251xfd_dump_object_header *hdr = iter->hdr; + unsigned int len; + + len = data_end - iter->data; + if (!len) + return; + + hdr->magic = cpu_to_le32(MCP251XFD_DUMP_MAGIC); + hdr->type = cpu_to_le32(object_type); + hdr->offset = cpu_to_le32(iter->data - iter->start); + hdr->len = cpu_to_le32(len); + + iter->hdr++; + iter->data += len; +} + +static void mcp251xfd_dump_registers(const struct mcp251xfd_priv *priv, + struct mcp251xfd_dump_iter *iter) +{ + const int val_bytes = regmap_get_val_bytes(priv->map_rx); + struct mcp251xfd_dump_object_reg *reg = iter->data; + unsigned int i, j; + int err; + + for (i = 0; i < ARRAY_SIZE(mcp251xfd_dump_reg_space); i++) { + const struct mcp251xfd_dump_reg_space *reg_space; + void *buf; + + reg_space = &mcp251xfd_dump_reg_space[i]; + + buf = kmalloc(reg_space->size, GFP_KERNEL); + if (!buf) + goto out; + + err = regmap_bulk_read(priv->map_reg, reg_space->base, + buf, reg_space->size / val_bytes); + if (err) { + kfree(buf); + continue; + } + + for (j = 0; j < reg_space->size; j += sizeof(u32), reg++) { + reg->reg = cpu_to_le32(reg_space->base + j); + reg->val = cpu_to_le32p(buf + j); + } + + kfree(buf); + } + + out: + mcp251xfd_dump_header(iter, MCP251XFD_DUMP_OBJECT_TYPE_REG, reg); +} + +static void mcp251xfd_dump_ring(struct mcp251xfd_dump_iter *iter, + enum mcp251xfd_dump_object_type object_type, + const struct mcp251xfd_dump_ring *dump_ring, + unsigned int len) +{ + struct mcp251xfd_dump_object_reg *reg = iter->data; + unsigned int i; + + for (i = 0; i < len; i++, reg++) { + reg->reg = cpu_to_le32(dump_ring[i].key); + reg->val = cpu_to_le32(dump_ring[i].val); + } + + mcp251xfd_dump_header(iter, object_type, reg); +} + +static void mcp251xfd_dump_tef_ring(const struct mcp251xfd_priv *priv, + struct mcp251xfd_dump_iter *iter) +{ + const struct mcp251xfd_tef_ring *tef = priv->tef; + const struct mcp251xfd_tx_ring *tx = priv->tx; + const struct mcp251xfd_dump_ring dump_ring[] = { + { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD, + .val = tef->head, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL, + .val = tef->tail, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_BASE, + .val = 0, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_NR, + .val = 0, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR, + .val = 0, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM, + .val = tx->obj_num, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE, + .val = sizeof(struct mcp251xfd_hw_tef_obj), + }, + }; + + mcp251xfd_dump_ring(iter, MCP251XFD_DUMP_OBJECT_TYPE_TEF, + dump_ring, ARRAY_SIZE(dump_ring)); +} + +static void mcp251xfd_dump_rx_ring_one(const struct mcp251xfd_priv *priv, + struct mcp251xfd_dump_iter *iter, + const struct mcp251xfd_rx_ring *rx) +{ + const struct mcp251xfd_dump_ring dump_ring[] = { + { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD, + .val = rx->head, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL, + .val = rx->tail, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_BASE, + .val = rx->base, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_NR, + .val = rx->nr, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR, + .val = rx->fifo_nr, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM, + .val = rx->obj_num, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE, + .val = rx->obj_size, + }, + }; + + mcp251xfd_dump_ring(iter, MCP251XFD_DUMP_OBJECT_TYPE_RX, + dump_ring, ARRAY_SIZE(dump_ring)); +} + +static void mcp251xfd_dump_rx_ring(const struct mcp251xfd_priv *priv, + struct mcp251xfd_dump_iter *iter) +{ + struct mcp251xfd_rx_ring *rx_ring; + unsigned int i; + + mcp251xfd_for_each_rx_ring(priv, rx_ring, i) + mcp251xfd_dump_rx_ring_one(priv, iter, rx_ring); +} + +static void mcp251xfd_dump_tx_ring(const struct mcp251xfd_priv *priv, + struct mcp251xfd_dump_iter *iter) +{ + const struct mcp251xfd_tx_ring *tx = priv->tx; + const struct mcp251xfd_dump_ring dump_ring[] = { + { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD, + .val = tx->head, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL, + .val = tx->tail, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_BASE, + .val = tx->base, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_NR, + .val = tx->nr, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR, + .val = tx->fifo_nr, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM, + .val = tx->obj_num, + }, { + .key = MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE, + .val = tx->obj_size, + }, + }; + + mcp251xfd_dump_ring(iter, MCP251XFD_DUMP_OBJECT_TYPE_TX, + dump_ring, ARRAY_SIZE(dump_ring)); +} + +static void mcp251xfd_dump_end(const struct mcp251xfd_priv *priv, + struct mcp251xfd_dump_iter *iter) +{ + struct mcp251xfd_dump_object_header *hdr = iter->hdr; + + hdr->magic = cpu_to_le32(MCP251XFD_DUMP_MAGIC); + hdr->type = cpu_to_le32(MCP251XFD_DUMP_OBJECT_TYPE_END); + hdr->offset = cpu_to_le32(0); + hdr->len = cpu_to_le32(0); + + /* provoke NULL pointer access, if used after END object */ + iter->hdr = NULL; +} + +void mcp251xfd_dump(const struct mcp251xfd_priv *priv) +{ + struct mcp251xfd_dump_iter iter; + unsigned int rings_num, obj_num; + unsigned int file_size = 0; + unsigned int i; + + /* register space + end marker */ + obj_num = 2; + + /* register space */ + for (i = 0; i < ARRAY_SIZE(mcp251xfd_dump_reg_space); i++) + file_size += mcp251xfd_dump_reg_space[i].size / sizeof(u32) * + sizeof(struct mcp251xfd_dump_object_reg); + + /* TEF ring, RX rings, TX ring */ + rings_num = 1 + priv->rx_ring_num + 1; + obj_num += rings_num; + file_size += rings_num * __MCP251XFD_DUMP_OBJECT_RING_KEY_MAX * + sizeof(struct mcp251xfd_dump_object_reg); + + /* size of the headers */ + file_size += sizeof(*iter.hdr) * obj_num; + + /* allocate the file in vmalloc memory, it's likely to be big */ + iter.start = __vmalloc(file_size, GFP_KERNEL | __GFP_NOWARN | + __GFP_ZERO | __GFP_NORETRY); + if (!iter.start) { + netdev_warn(priv->ndev, "Failed to allocate devcoredump file.\n"); + return; + } + + /* point the data member after the headers */ + iter.hdr = iter.start; + iter.data = &iter.hdr[obj_num]; + + mcp251xfd_dump_registers(priv, &iter); + mcp251xfd_dump_tef_ring(priv, &iter); + mcp251xfd_dump_rx_ring(priv, &iter); + mcp251xfd_dump_tx_ring(priv, &iter); + mcp251xfd_dump_end(priv, &iter); + + dev_coredumpv(&priv->spi->dev, iter.start, + iter.data - iter.start, GFP_KERNEL); +} diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-dump.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd-dump.h new file mode 100644 index 0000000000..e7560b0712 --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-dump.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * mcp251xfd - Microchip MCP251xFD Family CAN controller driver + * + * Copyright (c) 2019, 2020, 2021 Pengutronix, + * Marc Kleine-Budde <kernel@pengutronix.de> + */ + +#ifndef _MCP251XFD_DUMP_H +#define _MCP251XFD_DUMP_H + +#define MCP251XFD_DUMP_MAGIC 0x1825434d + +enum mcp251xfd_dump_object_type { + MCP251XFD_DUMP_OBJECT_TYPE_REG, + MCP251XFD_DUMP_OBJECT_TYPE_TEF, + MCP251XFD_DUMP_OBJECT_TYPE_RX, + MCP251XFD_DUMP_OBJECT_TYPE_TX, + MCP251XFD_DUMP_OBJECT_TYPE_END = -1, +}; + +enum mcp251xfd_dump_object_ring_key { + MCP251XFD_DUMP_OBJECT_RING_KEY_HEAD, + MCP251XFD_DUMP_OBJECT_RING_KEY_TAIL, + MCP251XFD_DUMP_OBJECT_RING_KEY_BASE, + MCP251XFD_DUMP_OBJECT_RING_KEY_NR, + MCP251XFD_DUMP_OBJECT_RING_KEY_FIFO_NR, + MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_NUM, + MCP251XFD_DUMP_OBJECT_RING_KEY_OBJ_SIZE, + __MCP251XFD_DUMP_OBJECT_RING_KEY_MAX, +}; + +struct mcp251xfd_dump_object_header { + __le32 magic; + __le32 type; + __le32 offset; + __le32 len; +}; + +struct mcp251xfd_dump_object_reg { + __le32 reg; + __le32 val; +}; + +#endif diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c new file mode 100644 index 0000000000..57eeb066a9 --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ethtool.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mcp251xfd - Microchip MCP251xFD Family CAN controller driver +// +// Copyright (c) 2021, 2022 Pengutronix, +// Marc Kleine-Budde <kernel@pengutronix.de> +// + +#include <linux/ethtool.h> + +#include "mcp251xfd.h" +#include "mcp251xfd-ram.h" + +static void +mcp251xfd_ring_get_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *extack) +{ + const struct mcp251xfd_priv *priv = netdev_priv(ndev); + const bool fd_mode = mcp251xfd_is_fd_mode(priv); + struct can_ram_layout layout; + + can_ram_get_layout(&layout, &mcp251xfd_ram_config, NULL, NULL, fd_mode); + ring->rx_max_pending = layout.max_rx; + ring->tx_max_pending = layout.max_tx; + + ring->rx_pending = priv->rx_obj_num; + ring->tx_pending = priv->tx->obj_num; +} + +static int +mcp251xfd_ring_set_ringparam(struct net_device *ndev, + struct ethtool_ringparam *ring, + struct kernel_ethtool_ringparam *kernel_ring, + struct netlink_ext_ack *extack) +{ + struct mcp251xfd_priv *priv = netdev_priv(ndev); + const bool fd_mode = mcp251xfd_is_fd_mode(priv); + struct can_ram_layout layout; + + can_ram_get_layout(&layout, &mcp251xfd_ram_config, ring, NULL, fd_mode); + if ((layout.cur_rx != priv->rx_obj_num || + layout.cur_tx != priv->tx->obj_num) && + netif_running(ndev)) + return -EBUSY; + + priv->rx_obj_num = layout.cur_rx; + priv->rx_obj_num_coalesce_irq = layout.rx_coalesce; + priv->tx->obj_num = layout.cur_tx; + priv->tx_obj_num_coalesce_irq = layout.tx_coalesce; + + return 0; +} + +static int mcp251xfd_ring_get_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kec, + struct netlink_ext_ack *ext_ack) +{ + struct mcp251xfd_priv *priv = netdev_priv(ndev); + u32 rx_max_frames, tx_max_frames; + + /* The ethtool doc says: + * To disable coalescing, set usecs = 0 and max_frames = 1. + */ + if (priv->rx_obj_num_coalesce_irq == 0) + rx_max_frames = 1; + else + rx_max_frames = priv->rx_obj_num_coalesce_irq; + + ec->rx_max_coalesced_frames_irq = rx_max_frames; + ec->rx_coalesce_usecs_irq = priv->rx_coalesce_usecs_irq; + + if (priv->tx_obj_num_coalesce_irq == 0) + tx_max_frames = 1; + else + tx_max_frames = priv->tx_obj_num_coalesce_irq; + + ec->tx_max_coalesced_frames_irq = tx_max_frames; + ec->tx_coalesce_usecs_irq = priv->tx_coalesce_usecs_irq; + + return 0; +} + +static int mcp251xfd_ring_set_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kec, + struct netlink_ext_ack *ext_ack) +{ + struct mcp251xfd_priv *priv = netdev_priv(ndev); + const bool fd_mode = mcp251xfd_is_fd_mode(priv); + const struct ethtool_ringparam ring = { + .rx_pending = priv->rx_obj_num, + .tx_pending = priv->tx->obj_num, + }; + struct can_ram_layout layout; + + can_ram_get_layout(&layout, &mcp251xfd_ram_config, &ring, ec, fd_mode); + + if ((layout.rx_coalesce != priv->rx_obj_num_coalesce_irq || + ec->rx_coalesce_usecs_irq != priv->rx_coalesce_usecs_irq || + layout.tx_coalesce != priv->tx_obj_num_coalesce_irq || + ec->tx_coalesce_usecs_irq != priv->tx_coalesce_usecs_irq) && + netif_running(ndev)) + return -EBUSY; + + priv->rx_obj_num = layout.cur_rx; + priv->rx_obj_num_coalesce_irq = layout.rx_coalesce; + priv->rx_coalesce_usecs_irq = ec->rx_coalesce_usecs_irq; + + priv->tx->obj_num = layout.cur_tx; + priv->tx_obj_num_coalesce_irq = layout.tx_coalesce; + priv->tx_coalesce_usecs_irq = ec->tx_coalesce_usecs_irq; + + return 0; +} + +static const struct ethtool_ops mcp251xfd_ethtool_ops = { + .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS_IRQ | + ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ | + ETHTOOL_COALESCE_TX_USECS_IRQ | + ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ, + .get_ringparam = mcp251xfd_ring_get_ringparam, + .set_ringparam = mcp251xfd_ring_set_ringparam, + .get_coalesce = mcp251xfd_ring_get_coalesce, + .set_coalesce = mcp251xfd_ring_set_coalesce, + .get_ts_info = can_ethtool_op_get_ts_info_hwts, +}; + +void mcp251xfd_ethtool_init(struct mcp251xfd_priv *priv) +{ + struct can_ram_layout layout; + + priv->ndev->ethtool_ops = &mcp251xfd_ethtool_ops; + + can_ram_get_layout(&layout, &mcp251xfd_ram_config, NULL, NULL, false); + priv->rx_obj_num = layout.default_rx; + priv->tx->obj_num = layout.default_tx; + + priv->rx_obj_num_coalesce_irq = 0; + priv->tx_obj_num_coalesce_irq = 0; + priv->rx_coalesce_usecs_irq = 0; + priv->tx_coalesce_usecs_irq = 0; +} diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.c new file mode 100644 index 0000000000..9e8e82cdba --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mcp251xfd - Microchip MCP251xFD Family CAN controller driver +// +// Copyright (c) 2021, 2022 Pengutronix, +// Marc Kleine-Budde <kernel@pengutronix.de> +// + +#include "mcp251xfd-ram.h" + +static inline u8 can_ram_clamp(const struct can_ram_config *config, + const struct can_ram_obj_config *obj, + u8 val) +{ + u8 max; + + max = min_t(u8, obj->max, obj->fifo_num * config->fifo_depth); + return clamp(val, obj->min, max); +} + +static u8 +can_ram_rounddown_pow_of_two(const struct can_ram_config *config, + const struct can_ram_obj_config *obj, + const u8 coalesce, u8 val) +{ + u8 fifo_num = obj->fifo_num; + u8 ret = 0, i; + + val = can_ram_clamp(config, obj, val); + + if (coalesce) { + /* Use 1st FIFO for coalescing, if requested. + * + * Either use complete FIFO (and FIFO Full IRQ) for + * coalescing or only half of FIFO (FIFO Half Full + * IRQ) and use remaining half for normal objects. + */ + ret = min_t(u8, coalesce * 2, config->fifo_depth); + val -= ret; + fifo_num--; + } + + for (i = 0; i < fifo_num && val; i++) { + u8 n; + + n = min_t(u8, rounddown_pow_of_two(val), + config->fifo_depth); + + /* skip small FIFOs */ + if (n < obj->fifo_depth_min) + return ret; + + ret += n; + val -= n; + } + + return ret; +} + +void can_ram_get_layout(struct can_ram_layout *layout, + const struct can_ram_config *config, + const struct ethtool_ringparam *ring, + const struct ethtool_coalesce *ec, + const bool fd_mode) +{ + u8 num_rx, num_tx; + u16 ram_free; + + /* default CAN */ + + num_tx = config->tx.def[fd_mode]; + num_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx); + + ram_free = config->size; + ram_free -= config->tx.size[fd_mode] * num_tx; + + num_rx = ram_free / config->rx.size[fd_mode]; + + layout->default_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, num_rx); + layout->default_tx = num_tx; + + /* MAX CAN */ + + ram_free = config->size; + ram_free -= config->tx.size[fd_mode] * config->tx.min; + num_rx = ram_free / config->rx.size[fd_mode]; + + ram_free = config->size; + ram_free -= config->rx.size[fd_mode] * config->rx.min; + num_tx = ram_free / config->tx.size[fd_mode]; + + layout->max_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, num_rx); + layout->max_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx); + + /* cur CAN */ + + if (ring) { + u8 num_rx_coalesce = 0, num_tx_coalesce = 0; + + num_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, ring->rx_pending); + + /* The ethtool doc says: + * To disable coalescing, set usecs = 0 and max_frames = 1. + */ + if (ec && !(ec->rx_coalesce_usecs_irq == 0 && + ec->rx_max_coalesced_frames_irq == 1)) { + u8 max; + + /* use only max half of available objects for coalescing */ + max = min_t(u8, num_rx / 2, config->fifo_depth); + num_rx_coalesce = clamp(ec->rx_max_coalesced_frames_irq, + (u32)config->rx.fifo_depth_coalesce_min, + (u32)max); + num_rx_coalesce = rounddown_pow_of_two(num_rx_coalesce); + + num_rx = can_ram_rounddown_pow_of_two(config, &config->rx, + num_rx_coalesce, num_rx); + } + + ram_free = config->size - config->rx.size[fd_mode] * num_rx; + num_tx = ram_free / config->tx.size[fd_mode]; + num_tx = min_t(u8, ring->tx_pending, num_tx); + num_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx); + + /* The ethtool doc says: + * To disable coalescing, set usecs = 0 and max_frames = 1. + */ + if (ec && !(ec->tx_coalesce_usecs_irq == 0 && + ec->tx_max_coalesced_frames_irq == 1)) { + u8 max; + + /* use only max half of available objects for coalescing */ + max = min_t(u8, num_tx / 2, config->fifo_depth); + num_tx_coalesce = clamp(ec->tx_max_coalesced_frames_irq, + (u32)config->tx.fifo_depth_coalesce_min, + (u32)max); + num_tx_coalesce = rounddown_pow_of_two(num_tx_coalesce); + + num_tx = can_ram_rounddown_pow_of_two(config, &config->tx, + num_tx_coalesce, num_tx); + } + + layout->cur_rx = num_rx; + layout->cur_tx = num_tx; + layout->rx_coalesce = num_rx_coalesce; + layout->tx_coalesce = num_tx_coalesce; + } else { + layout->cur_rx = layout->default_rx; + layout->cur_tx = layout->default_tx; + layout->rx_coalesce = 0; + layout->tx_coalesce = 0; + } +} diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.h new file mode 100644 index 0000000000..7558c1510c --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ram.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * mcp251xfd - Microchip MCP251xFD Family CAN controller driver + * + * Copyright (c) 2021, 2022 Pengutronix, + * Marc Kleine-Budde <kernel@pengutronix.de> + */ + +#ifndef _MCP251XFD_RAM_H +#define _MCP251XFD_RAM_H + +#include <linux/ethtool.h> + +#define CAN_RAM_NUM_MAX (-1) + +enum can_ram_mode { + CAN_RAM_MODE_CAN, + CAN_RAM_MODE_CANFD, + __CAN_RAM_MODE_MAX +}; + +struct can_ram_obj_config { + u8 size[__CAN_RAM_MODE_MAX]; + + u8 def[__CAN_RAM_MODE_MAX]; + u8 min; + u8 max; + + u8 fifo_num; + u8 fifo_depth_min; + u8 fifo_depth_coalesce_min; +}; + +struct can_ram_config { + const struct can_ram_obj_config rx; + const struct can_ram_obj_config tx; + + u16 size; + u8 fifo_depth; +}; + +struct can_ram_layout { + u8 default_rx; + u8 default_tx; + + u8 max_rx; + u8 max_tx; + + u8 cur_rx; + u8 cur_tx; + + u8 rx_coalesce; + u8 tx_coalesce; +}; + +void can_ram_get_layout(struct can_ram_layout *layout, + const struct can_ram_config *config, + const struct ethtool_ringparam *ring, + const struct ethtool_coalesce *ec, + const bool fd_mode); + +#endif diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c new file mode 100644 index 0000000000..92b7bc7f14 --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-regmap.c @@ -0,0 +1,609 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mcp251xfd - Microchip MCP251xFD Family CAN controller driver +// +// Copyright (c) 2019, 2020, 2021 Pengutronix, +// Marc Kleine-Budde <kernel@pengutronix.de> +// + +#include "mcp251xfd.h" + +#include <asm/unaligned.h> + +static const struct regmap_config mcp251xfd_regmap_crc; + +static int +mcp251xfd_regmap_nocrc_write(void *context, const void *data, size_t count) +{ + struct spi_device *spi = context; + + return spi_write(spi, data, count); +} + +static int +mcp251xfd_regmap_nocrc_gather_write(void *context, + const void *reg, size_t reg_len, + const void *val, size_t val_len) +{ + struct spi_device *spi = context; + struct mcp251xfd_priv *priv = spi_get_drvdata(spi); + struct mcp251xfd_map_buf_nocrc *buf_tx = priv->map_buf_nocrc_tx; + struct spi_transfer xfer[] = { + { + .tx_buf = buf_tx, + .len = sizeof(buf_tx->cmd) + val_len, + }, + }; + + BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16)); + + if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && + reg_len != sizeof(buf_tx->cmd.cmd)) + return -EINVAL; + + memcpy(&buf_tx->cmd, reg, sizeof(buf_tx->cmd)); + memcpy(buf_tx->data, val, val_len); + + return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); +} + +static inline bool +mcp251xfd_update_bits_read_reg(const struct mcp251xfd_priv *priv, + unsigned int reg) +{ + struct mcp251xfd_rx_ring *ring; + int n; + + switch (reg) { + case MCP251XFD_REG_INT: + case MCP251XFD_REG_TEFCON: + case MCP251XFD_REG_FLTCON(0): + case MCP251XFD_REG_ECCSTAT: + case MCP251XFD_REG_CRC: + return false; + case MCP251XFD_REG_CON: + case MCP251XFD_REG_OSC: + case MCP251XFD_REG_ECCCON: + return true; + default: + mcp251xfd_for_each_rx_ring(priv, ring, n) { + if (reg == MCP251XFD_REG_FIFOCON(ring->fifo_nr)) + return false; + if (reg == MCP251XFD_REG_FIFOSTA(ring->fifo_nr)) + return true; + } + + WARN(1, "Status of reg 0x%04x unknown.\n", reg); + } + + return true; +} + +static int +mcp251xfd_regmap_nocrc_update_bits(void *context, unsigned int reg, + unsigned int mask, unsigned int val) +{ + struct spi_device *spi = context; + struct mcp251xfd_priv *priv = spi_get_drvdata(spi); + struct mcp251xfd_map_buf_nocrc *buf_rx = priv->map_buf_nocrc_rx; + struct mcp251xfd_map_buf_nocrc *buf_tx = priv->map_buf_nocrc_tx; + __le32 orig_le32 = 0, mask_le32, val_le32, tmp_le32; + u8 first_byte, last_byte, len; + int err; + + BUILD_BUG_ON(sizeof(buf_rx->cmd) != sizeof(__be16)); + BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16)); + + if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && + mask == 0) + return -EINVAL; + + first_byte = mcp251xfd_first_byte_set(mask); + last_byte = mcp251xfd_last_byte_set(mask); + len = last_byte - first_byte + 1; + + if (mcp251xfd_update_bits_read_reg(priv, reg)) { + struct spi_transfer xfer[2] = { }; + struct spi_message msg; + + spi_message_init(&msg); + spi_message_add_tail(&xfer[0], &msg); + + if (priv->devtype_data.quirks & MCP251XFD_QUIRK_HALF_DUPLEX) { + xfer[0].tx_buf = buf_tx; + xfer[0].len = sizeof(buf_tx->cmd); + + xfer[1].rx_buf = buf_rx->data; + xfer[1].len = len; + spi_message_add_tail(&xfer[1], &msg); + } else { + xfer[0].tx_buf = buf_tx; + xfer[0].rx_buf = buf_rx; + xfer[0].len = sizeof(buf_tx->cmd) + len; + + if (MCP251XFD_SANITIZE_SPI) + memset(buf_tx->data, 0x0, len); + } + + mcp251xfd_spi_cmd_read_nocrc(&buf_tx->cmd, reg + first_byte); + err = spi_sync(spi, &msg); + if (err) + return err; + + memcpy(&orig_le32, buf_rx->data, len); + } + + mask_le32 = cpu_to_le32(mask >> BITS_PER_BYTE * first_byte); + val_le32 = cpu_to_le32(val >> BITS_PER_BYTE * first_byte); + + tmp_le32 = orig_le32 & ~mask_le32; + tmp_le32 |= val_le32 & mask_le32; + + mcp251xfd_spi_cmd_write_nocrc(&buf_tx->cmd, reg + first_byte); + memcpy(buf_tx->data, &tmp_le32, len); + + return spi_write(spi, buf_tx, sizeof(buf_tx->cmd) + len); +} + +static int +mcp251xfd_regmap_nocrc_read(void *context, + const void *reg, size_t reg_len, + void *val_buf, size_t val_len) +{ + struct spi_device *spi = context; + struct mcp251xfd_priv *priv = spi_get_drvdata(spi); + struct mcp251xfd_map_buf_nocrc *buf_rx = priv->map_buf_nocrc_rx; + struct mcp251xfd_map_buf_nocrc *buf_tx = priv->map_buf_nocrc_tx; + struct spi_transfer xfer[2] = { }; + struct spi_message msg; + int err; + + BUILD_BUG_ON(sizeof(buf_rx->cmd) != sizeof(__be16)); + BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16)); + + if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && + reg_len != sizeof(buf_tx->cmd.cmd)) + return -EINVAL; + + spi_message_init(&msg); + spi_message_add_tail(&xfer[0], &msg); + + if (priv->devtype_data.quirks & MCP251XFD_QUIRK_HALF_DUPLEX) { + xfer[0].tx_buf = reg; + xfer[0].len = sizeof(buf_tx->cmd); + + xfer[1].rx_buf = val_buf; + xfer[1].len = val_len; + spi_message_add_tail(&xfer[1], &msg); + } else { + xfer[0].tx_buf = buf_tx; + xfer[0].rx_buf = buf_rx; + xfer[0].len = sizeof(buf_tx->cmd) + val_len; + + memcpy(&buf_tx->cmd, reg, sizeof(buf_tx->cmd)); + if (MCP251XFD_SANITIZE_SPI) + memset(buf_tx->data, 0x0, val_len); + } + + err = spi_sync(spi, &msg); + if (err) + return err; + + if (!(priv->devtype_data.quirks & MCP251XFD_QUIRK_HALF_DUPLEX)) + memcpy(val_buf, buf_rx->data, val_len); + + return 0; +} + +static int +mcp251xfd_regmap_crc_gather_write(void *context, + const void *reg_p, size_t reg_len, + const void *val, size_t val_len) +{ + struct spi_device *spi = context; + struct mcp251xfd_priv *priv = spi_get_drvdata(spi); + struct mcp251xfd_map_buf_crc *buf_tx = priv->map_buf_crc_tx; + struct spi_transfer xfer[] = { + { + .tx_buf = buf_tx, + .len = sizeof(buf_tx->cmd) + val_len + + sizeof(buf_tx->crc), + }, + }; + u16 reg = *(u16 *)reg_p; + u16 crc; + + BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16) + sizeof(u8)); + + if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && + reg_len != sizeof(buf_tx->cmd.cmd) + + mcp251xfd_regmap_crc.pad_bits / BITS_PER_BYTE) + return -EINVAL; + + mcp251xfd_spi_cmd_write_crc(&buf_tx->cmd, reg, val_len); + memcpy(buf_tx->data, val, val_len); + + crc = mcp251xfd_crc16_compute(buf_tx, sizeof(buf_tx->cmd) + val_len); + put_unaligned_be16(crc, buf_tx->data + val_len); + + return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); +} + +static int +mcp251xfd_regmap_crc_write(void *context, + const void *data, size_t count) +{ + const size_t data_offset = sizeof(__be16) + + mcp251xfd_regmap_crc.pad_bits / BITS_PER_BYTE; + + return mcp251xfd_regmap_crc_gather_write(context, + data, data_offset, + data + data_offset, + count - data_offset); +} + +static int +mcp251xfd_regmap_crc_read_check_crc(const struct mcp251xfd_map_buf_crc * const buf_rx, + const struct mcp251xfd_map_buf_crc * const buf_tx, + unsigned int data_len) +{ + u16 crc_received, crc_calculated; + + crc_received = get_unaligned_be16(buf_rx->data + data_len); + crc_calculated = mcp251xfd_crc16_compute2(&buf_tx->cmd, + sizeof(buf_tx->cmd), + buf_rx->data, + data_len); + if (crc_received != crc_calculated) + return -EBADMSG; + + return 0; +} + +static int +mcp251xfd_regmap_crc_read_one(struct mcp251xfd_priv *priv, + struct spi_message *msg, unsigned int data_len) +{ + const struct mcp251xfd_map_buf_crc *buf_rx = priv->map_buf_crc_rx; + const struct mcp251xfd_map_buf_crc *buf_tx = priv->map_buf_crc_tx; + int err; + + BUILD_BUG_ON(sizeof(buf_rx->cmd) != sizeof(__be16) + sizeof(u8)); + BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16) + sizeof(u8)); + + err = spi_sync(priv->spi, msg); + if (err) + return err; + + return mcp251xfd_regmap_crc_read_check_crc(buf_rx, buf_tx, data_len); +} + +static int +mcp251xfd_regmap_crc_read(void *context, + const void *reg_p, size_t reg_len, + void *val_buf, size_t val_len) +{ + struct spi_device *spi = context; + struct mcp251xfd_priv *priv = spi_get_drvdata(spi); + struct mcp251xfd_map_buf_crc *buf_rx = priv->map_buf_crc_rx; + struct mcp251xfd_map_buf_crc *buf_tx = priv->map_buf_crc_tx; + struct spi_transfer xfer[2] = { }; + struct spi_message msg; + u16 reg = *(u16 *)reg_p; + int i, err; + + BUILD_BUG_ON(sizeof(buf_rx->cmd) != sizeof(__be16) + sizeof(u8)); + BUILD_BUG_ON(sizeof(buf_tx->cmd) != sizeof(__be16) + sizeof(u8)); + + if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && + reg_len != sizeof(buf_tx->cmd.cmd) + + mcp251xfd_regmap_crc.pad_bits / BITS_PER_BYTE) + return -EINVAL; + + spi_message_init(&msg); + spi_message_add_tail(&xfer[0], &msg); + + if (priv->devtype_data.quirks & MCP251XFD_QUIRK_HALF_DUPLEX) { + xfer[0].tx_buf = buf_tx; + xfer[0].len = sizeof(buf_tx->cmd); + + xfer[1].rx_buf = buf_rx->data; + xfer[1].len = val_len + sizeof(buf_tx->crc); + spi_message_add_tail(&xfer[1], &msg); + } else { + xfer[0].tx_buf = buf_tx; + xfer[0].rx_buf = buf_rx; + xfer[0].len = sizeof(buf_tx->cmd) + val_len + + sizeof(buf_tx->crc); + + if (MCP251XFD_SANITIZE_SPI) + memset(buf_tx->data, 0x0, val_len + + sizeof(buf_tx->crc)); + } + + mcp251xfd_spi_cmd_read_crc(&buf_tx->cmd, reg, val_len); + + for (i = 0; i < MCP251XFD_READ_CRC_RETRIES_MAX; i++) { + err = mcp251xfd_regmap_crc_read_one(priv, &msg, val_len); + if (!err) + goto out; + if (err != -EBADMSG) + return err; + + /* MCP251XFD_REG_TBC is the time base counter + * register. It increments once per SYS clock tick, + * which is 20 or 40 MHz. + * + * Observation on the mcp2518fd shows that if the + * lowest byte (which is transferred first on the SPI + * bus) of that register is 0x00 or 0x80 the + * calculated CRC doesn't always match the transferred + * one. On the mcp2517fd this problem is not limited + * to the first byte being 0x00 or 0x80. + * + * If the highest bit in the lowest byte is flipped + * the transferred CRC matches the calculated one. We + * assume for now the CRC operates on the correct + * data. + */ + if (reg == MCP251XFD_REG_TBC && + ((buf_rx->data[0] & 0xf8) == 0x0 || + (buf_rx->data[0] & 0xf8) == 0x80)) { + /* Flip highest bit in lowest byte of le32 */ + buf_rx->data[0] ^= 0x80; + + /* re-check CRC */ + err = mcp251xfd_regmap_crc_read_check_crc(buf_rx, + buf_tx, + val_len); + if (!err) { + /* If CRC is now correct, assume + * flipped data is OK. + */ + goto out; + } + } + + /* MCP251XFD_REG_OSC is the first ever reg we read from. + * + * The chip may be in deep sleep and this SPI transfer + * (i.e. the assertion of the CS) will wake the chip + * up. This takes about 3ms. The CRC of this transfer + * is wrong. + * + * Or there isn't a chip at all, in this case the CRC + * will be wrong, too. + * + * In both cases ignore the CRC and copy the read data + * to the caller. It will take care of both cases. + * + */ + if (reg == MCP251XFD_REG_OSC && val_len == sizeof(__le32)) { + err = 0; + goto out; + } + + netdev_info(priv->ndev, + "CRC read error at address 0x%04x (length=%zd, data=%*ph, CRC=0x%04x) retrying.\n", + reg, val_len, (int)val_len, buf_rx->data, + get_unaligned_be16(buf_rx->data + val_len)); + } + + if (err) { + netdev_err(priv->ndev, + "CRC read error at address 0x%04x (length=%zd, data=%*ph, CRC=0x%04x).\n", + reg, val_len, (int)val_len, buf_rx->data, + get_unaligned_be16(buf_rx->data + val_len)); + + return err; + } + out: + memcpy(val_buf, buf_rx->data, val_len); + + return 0; +} + +static const struct regmap_range mcp251xfd_reg_table_yes_range[] = { + regmap_reg_range(0x000, 0x2ec), /* CAN FD Controller Module SFR */ + regmap_reg_range(0x400, 0xbfc), /* RAM */ + regmap_reg_range(0xe00, 0xe14), /* MCP2517/18FD SFR */ +}; + +static const struct regmap_access_table mcp251xfd_reg_table = { + .yes_ranges = mcp251xfd_reg_table_yes_range, + .n_yes_ranges = ARRAY_SIZE(mcp251xfd_reg_table_yes_range), +}; + +static const struct regmap_config mcp251xfd_regmap_nocrc = { + .name = "nocrc", + .reg_bits = 16, + .reg_stride = 4, + .pad_bits = 0, + .val_bits = 32, + .max_register = 0xffc, + .wr_table = &mcp251xfd_reg_table, + .rd_table = &mcp251xfd_reg_table, + .cache_type = REGCACHE_NONE, + .read_flag_mask = (__force unsigned long) + cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_READ), + .write_flag_mask = (__force unsigned long) + cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_WRITE), +}; + +static const struct regmap_bus mcp251xfd_bus_nocrc = { + .write = mcp251xfd_regmap_nocrc_write, + .gather_write = mcp251xfd_regmap_nocrc_gather_write, + .reg_update_bits = mcp251xfd_regmap_nocrc_update_bits, + .read = mcp251xfd_regmap_nocrc_read, + .reg_format_endian_default = REGMAP_ENDIAN_BIG, + .val_format_endian_default = REGMAP_ENDIAN_LITTLE, + .max_raw_read = sizeof_field(struct mcp251xfd_map_buf_nocrc, data), + .max_raw_write = sizeof_field(struct mcp251xfd_map_buf_nocrc, data), +}; + +static const struct regmap_config mcp251xfd_regmap_crc = { + .name = "crc", + .reg_bits = 16, + .reg_stride = 4, + .pad_bits = 16, /* keep data bits aligned */ + .val_bits = 32, + .max_register = 0xffc, + .wr_table = &mcp251xfd_reg_table, + .rd_table = &mcp251xfd_reg_table, + .cache_type = REGCACHE_NONE, +}; + +static const struct regmap_bus mcp251xfd_bus_crc = { + .write = mcp251xfd_regmap_crc_write, + .gather_write = mcp251xfd_regmap_crc_gather_write, + .read = mcp251xfd_regmap_crc_read, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_LITTLE, + .max_raw_read = sizeof_field(struct mcp251xfd_map_buf_crc, data), + .max_raw_write = sizeof_field(struct mcp251xfd_map_buf_crc, data), +}; + +static inline bool +mcp251xfd_regmap_use_nocrc(struct mcp251xfd_priv *priv) +{ + return (!(priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_REG)) || + (!(priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_RX)); +} + +static inline bool +mcp251xfd_regmap_use_crc(struct mcp251xfd_priv *priv) +{ + return (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_REG) || + (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_RX); +} + +static int +mcp251xfd_regmap_init_nocrc(struct mcp251xfd_priv *priv) +{ + if (!priv->map_nocrc) { + struct regmap *map; + + map = devm_regmap_init(&priv->spi->dev, &mcp251xfd_bus_nocrc, + priv->spi, &mcp251xfd_regmap_nocrc); + if (IS_ERR(map)) + return PTR_ERR(map); + + priv->map_nocrc = map; + } + + if (!priv->map_buf_nocrc_rx) { + priv->map_buf_nocrc_rx = + devm_kzalloc(&priv->spi->dev, + sizeof(*priv->map_buf_nocrc_rx), + GFP_KERNEL); + if (!priv->map_buf_nocrc_rx) + return -ENOMEM; + } + + if (!priv->map_buf_nocrc_tx) { + priv->map_buf_nocrc_tx = + devm_kzalloc(&priv->spi->dev, + sizeof(*priv->map_buf_nocrc_tx), + GFP_KERNEL); + if (!priv->map_buf_nocrc_tx) + return -ENOMEM; + } + + if (!(priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_REG)) + priv->map_reg = priv->map_nocrc; + + if (!(priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_RX)) + priv->map_rx = priv->map_nocrc; + + return 0; +} + +static void mcp251xfd_regmap_destroy_nocrc(struct mcp251xfd_priv *priv) +{ + if (priv->map_buf_nocrc_rx) { + devm_kfree(&priv->spi->dev, priv->map_buf_nocrc_rx); + priv->map_buf_nocrc_rx = NULL; + } + if (priv->map_buf_nocrc_tx) { + devm_kfree(&priv->spi->dev, priv->map_buf_nocrc_tx); + priv->map_buf_nocrc_tx = NULL; + } +} + +static int +mcp251xfd_regmap_init_crc(struct mcp251xfd_priv *priv) +{ + if (!priv->map_crc) { + struct regmap *map; + + map = devm_regmap_init(&priv->spi->dev, &mcp251xfd_bus_crc, + priv->spi, &mcp251xfd_regmap_crc); + if (IS_ERR(map)) + return PTR_ERR(map); + + priv->map_crc = map; + } + + if (!priv->map_buf_crc_rx) { + priv->map_buf_crc_rx = + devm_kzalloc(&priv->spi->dev, + sizeof(*priv->map_buf_crc_rx), + GFP_KERNEL); + if (!priv->map_buf_crc_rx) + return -ENOMEM; + } + + if (!priv->map_buf_crc_tx) { + priv->map_buf_crc_tx = + devm_kzalloc(&priv->spi->dev, + sizeof(*priv->map_buf_crc_tx), + GFP_KERNEL); + if (!priv->map_buf_crc_tx) + return -ENOMEM; + } + + if (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_REG) + priv->map_reg = priv->map_crc; + + if (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_RX) + priv->map_rx = priv->map_crc; + + return 0; +} + +static void mcp251xfd_regmap_destroy_crc(struct mcp251xfd_priv *priv) +{ + if (priv->map_buf_crc_rx) { + devm_kfree(&priv->spi->dev, priv->map_buf_crc_rx); + priv->map_buf_crc_rx = NULL; + } + if (priv->map_buf_crc_tx) { + devm_kfree(&priv->spi->dev, priv->map_buf_crc_tx); + priv->map_buf_crc_tx = NULL; + } +} + +int mcp251xfd_regmap_init(struct mcp251xfd_priv *priv) +{ + int err; + + if (mcp251xfd_regmap_use_nocrc(priv)) { + err = mcp251xfd_regmap_init_nocrc(priv); + + if (err) + return err; + } else { + mcp251xfd_regmap_destroy_nocrc(priv); + } + + if (mcp251xfd_regmap_use_crc(priv)) { + err = mcp251xfd_regmap_init_crc(priv); + + if (err) + return err; + } else { + mcp251xfd_regmap_destroy_crc(priv); + } + + return 0; +} diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-ring.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ring.c new file mode 100644 index 0000000000..bfe4caa0c9 --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-ring.c @@ -0,0 +1,522 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mcp251xfd - Microchip MCP251xFD Family CAN controller driver +// +// Copyright (c) 2019, 2020, 2021 Pengutronix, +// Marc Kleine-Budde <kernel@pengutronix.de> +// +// Based on: +// +// CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface +// +// Copyright (c) 2019 Martin Sperl <kernel@martin.sperl.org> +// + +#include <asm/unaligned.h> + +#include "mcp251xfd.h" +#include "mcp251xfd-ram.h" + +static inline u8 +mcp251xfd_cmd_prepare_write_reg(const struct mcp251xfd_priv *priv, + union mcp251xfd_write_reg_buf *write_reg_buf, + const u16 reg, const u32 mask, const u32 val) +{ + u8 first_byte, last_byte, len; + u8 *data; + __le32 val_le32; + + first_byte = mcp251xfd_first_byte_set(mask); + last_byte = mcp251xfd_last_byte_set(mask); + len = last_byte - first_byte + 1; + + data = mcp251xfd_spi_cmd_write(priv, write_reg_buf, reg + first_byte, len); + val_le32 = cpu_to_le32(val >> BITS_PER_BYTE * first_byte); + memcpy(data, &val_le32, len); + + if (!(priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_REG)) { + len += sizeof(write_reg_buf->nocrc.cmd); + } else if (len == 1) { + u16 crc; + + /* CRC */ + len += sizeof(write_reg_buf->safe.cmd); + crc = mcp251xfd_crc16_compute(&write_reg_buf->safe, len); + put_unaligned_be16(crc, (void *)write_reg_buf + len); + + /* Total length */ + len += sizeof(write_reg_buf->safe.crc); + } else { + u16 crc; + + mcp251xfd_spi_cmd_crc_set_len_in_reg(&write_reg_buf->crc.cmd, + len); + /* CRC */ + len += sizeof(write_reg_buf->crc.cmd); + crc = mcp251xfd_crc16_compute(&write_reg_buf->crc, len); + put_unaligned_be16(crc, (void *)write_reg_buf + len); + + /* Total length */ + len += sizeof(write_reg_buf->crc.crc); + } + + return len; +} + +static void +mcp251xfd_ring_init_tef(struct mcp251xfd_priv *priv, u16 *base) +{ + struct mcp251xfd_tef_ring *tef_ring; + struct spi_transfer *xfer; + u32 val; + u16 addr; + u8 len; + int i; + + /* TEF */ + tef_ring = priv->tef; + tef_ring->head = 0; + tef_ring->tail = 0; + + /* TEF- and TX-FIFO have same number of objects */ + *base = mcp251xfd_get_tef_obj_addr(priv->tx->obj_num); + + /* FIFO IRQ enable */ + addr = MCP251XFD_REG_TEFCON; + val = MCP251XFD_REG_TEFCON_TEFOVIE | MCP251XFD_REG_TEFCON_TEFNEIE; + + len = mcp251xfd_cmd_prepare_write_reg(priv, &tef_ring->irq_enable_buf, + addr, val, val); + tef_ring->irq_enable_xfer.tx_buf = &tef_ring->irq_enable_buf; + tef_ring->irq_enable_xfer.len = len; + spi_message_init_with_transfers(&tef_ring->irq_enable_msg, + &tef_ring->irq_enable_xfer, 1); + + /* FIFO increment TEF tail pointer */ + addr = MCP251XFD_REG_TEFCON; + val = MCP251XFD_REG_TEFCON_UINC; + len = mcp251xfd_cmd_prepare_write_reg(priv, &tef_ring->uinc_buf, + addr, val, val); + + for (i = 0; i < ARRAY_SIZE(tef_ring->uinc_xfer); i++) { + xfer = &tef_ring->uinc_xfer[i]; + xfer->tx_buf = &tef_ring->uinc_buf; + xfer->len = len; + xfer->cs_change = 1; + xfer->cs_change_delay.value = 0; + xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + } + + /* "cs_change == 1" on the last transfer results in an active + * chip select after the complete SPI message. This causes the + * controller to interpret the next register access as + * data. Set "cs_change" of the last transfer to "0" to + * properly deactivate the chip select at the end of the + * message. + */ + xfer->cs_change = 0; + + if (priv->tx_coalesce_usecs_irq || priv->tx_obj_num_coalesce_irq) { + val = MCP251XFD_REG_TEFCON_UINC | + MCP251XFD_REG_TEFCON_TEFOVIE | + MCP251XFD_REG_TEFCON_TEFHIE; + + len = mcp251xfd_cmd_prepare_write_reg(priv, + &tef_ring->uinc_irq_disable_buf, + addr, val, val); + xfer->tx_buf = &tef_ring->uinc_irq_disable_buf; + xfer->len = len; + } +} + +static void +mcp251xfd_tx_ring_init_tx_obj(const struct mcp251xfd_priv *priv, + const struct mcp251xfd_tx_ring *ring, + struct mcp251xfd_tx_obj *tx_obj, + const u8 rts_buf_len, + const u8 n) +{ + struct spi_transfer *xfer; + u16 addr; + + /* FIFO load */ + addr = mcp251xfd_get_tx_obj_addr(ring, n); + if (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_TX) + mcp251xfd_spi_cmd_write_crc_set_addr(&tx_obj->buf.crc.cmd, + addr); + else + mcp251xfd_spi_cmd_write_nocrc(&tx_obj->buf.nocrc.cmd, + addr); + + xfer = &tx_obj->xfer[0]; + xfer->tx_buf = &tx_obj->buf; + xfer->len = 0; /* actual len is assigned on the fly */ + xfer->cs_change = 1; + xfer->cs_change_delay.value = 0; + xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + + /* FIFO request to send */ + xfer = &tx_obj->xfer[1]; + xfer->tx_buf = &ring->rts_buf; + xfer->len = rts_buf_len; + + /* SPI message */ + spi_message_init_with_transfers(&tx_obj->msg, tx_obj->xfer, + ARRAY_SIZE(tx_obj->xfer)); +} + +static void +mcp251xfd_ring_init_tx(struct mcp251xfd_priv *priv, u16 *base, u8 *fifo_nr) +{ + struct mcp251xfd_tx_ring *tx_ring; + struct mcp251xfd_tx_obj *tx_obj; + u32 val; + u16 addr; + u8 len; + int i; + + tx_ring = priv->tx; + tx_ring->head = 0; + tx_ring->tail = 0; + tx_ring->base = *base; + tx_ring->nr = 0; + tx_ring->fifo_nr = *fifo_nr; + + *base = mcp251xfd_get_tx_obj_addr(tx_ring, tx_ring->obj_num); + *fifo_nr += 1; + + /* FIFO request to send */ + addr = MCP251XFD_REG_FIFOCON(tx_ring->fifo_nr); + val = MCP251XFD_REG_FIFOCON_TXREQ | MCP251XFD_REG_FIFOCON_UINC; + len = mcp251xfd_cmd_prepare_write_reg(priv, &tx_ring->rts_buf, + addr, val, val); + + mcp251xfd_for_each_tx_obj(tx_ring, tx_obj, i) + mcp251xfd_tx_ring_init_tx_obj(priv, tx_ring, tx_obj, len, i); +} + +static void +mcp251xfd_ring_init_rx(struct mcp251xfd_priv *priv, u16 *base, u8 *fifo_nr) +{ + struct mcp251xfd_rx_ring *rx_ring; + struct spi_transfer *xfer; + u32 val; + u16 addr; + u8 len; + int i, j; + + mcp251xfd_for_each_rx_ring(priv, rx_ring, i) { + rx_ring->head = 0; + rx_ring->tail = 0; + rx_ring->base = *base; + rx_ring->nr = i; + rx_ring->fifo_nr = *fifo_nr; + + *base = mcp251xfd_get_rx_obj_addr(rx_ring, rx_ring->obj_num); + *fifo_nr += 1; + + /* FIFO IRQ enable */ + addr = MCP251XFD_REG_FIFOCON(rx_ring->fifo_nr); + val = MCP251XFD_REG_FIFOCON_RXOVIE | + MCP251XFD_REG_FIFOCON_TFNRFNIE; + len = mcp251xfd_cmd_prepare_write_reg(priv, &rx_ring->irq_enable_buf, + addr, val, val); + rx_ring->irq_enable_xfer.tx_buf = &rx_ring->irq_enable_buf; + rx_ring->irq_enable_xfer.len = len; + spi_message_init_with_transfers(&rx_ring->irq_enable_msg, + &rx_ring->irq_enable_xfer, 1); + + /* FIFO increment RX tail pointer */ + val = MCP251XFD_REG_FIFOCON_UINC; + len = mcp251xfd_cmd_prepare_write_reg(priv, &rx_ring->uinc_buf, + addr, val, val); + + for (j = 0; j < ARRAY_SIZE(rx_ring->uinc_xfer); j++) { + xfer = &rx_ring->uinc_xfer[j]; + xfer->tx_buf = &rx_ring->uinc_buf; + xfer->len = len; + xfer->cs_change = 1; + xfer->cs_change_delay.value = 0; + xfer->cs_change_delay.unit = SPI_DELAY_UNIT_NSECS; + } + + /* "cs_change == 1" on the last transfer results in an + * active chip select after the complete SPI + * message. This causes the controller to interpret + * the next register access as data. Set "cs_change" + * of the last transfer to "0" to properly deactivate + * the chip select at the end of the message. + */ + xfer->cs_change = 0; + + /* Use 1st RX-FIFO for IRQ coalescing. If enabled + * (rx_coalesce_usecs_irq or rx_max_coalesce_frames_irq + * is activated), use the last transfer to disable: + * + * - TFNRFNIE (Receive FIFO Not Empty Interrupt) + * + * and enable: + * + * - TFHRFHIE (Receive FIFO Half Full Interrupt) + * - or - + * - TFERFFIE (Receive FIFO Full Interrupt) + * + * depending on rx_max_coalesce_frames_irq. + * + * The RXOVIE (Overflow Interrupt) is always enabled. + */ + if (rx_ring->nr == 0 && (priv->rx_coalesce_usecs_irq || + priv->rx_obj_num_coalesce_irq)) { + val = MCP251XFD_REG_FIFOCON_UINC | + MCP251XFD_REG_FIFOCON_RXOVIE; + + if (priv->rx_obj_num_coalesce_irq == rx_ring->obj_num) + val |= MCP251XFD_REG_FIFOCON_TFERFFIE; + else if (priv->rx_obj_num_coalesce_irq) + val |= MCP251XFD_REG_FIFOCON_TFHRFHIE; + + len = mcp251xfd_cmd_prepare_write_reg(priv, + &rx_ring->uinc_irq_disable_buf, + addr, val, val); + xfer->tx_buf = &rx_ring->uinc_irq_disable_buf; + xfer->len = len; + } + } +} + +int mcp251xfd_ring_init(struct mcp251xfd_priv *priv) +{ + const struct mcp251xfd_rx_ring *rx_ring; + u16 base = 0, ram_used; + u8 fifo_nr = 1; + int i; + + netdev_reset_queue(priv->ndev); + + mcp251xfd_ring_init_tef(priv, &base); + mcp251xfd_ring_init_rx(priv, &base, &fifo_nr); + mcp251xfd_ring_init_tx(priv, &base, &fifo_nr); + + /* mcp251xfd_handle_rxif() will iterate over all RX rings. + * Rings with their corresponding bit set in + * priv->regs_status.rxif are read out. + * + * If the chip is configured for only 1 RX-FIFO, and if there + * is an RX interrupt pending (RXIF in INT register is set), + * it must be the 1st RX-FIFO. + * + * We mark the RXIF of the 1st FIFO as pending here, so that + * we can skip the read of the RXIF register in + * mcp251xfd_read_regs_status() for the 1 RX-FIFO only case. + * + * If we use more than 1 RX-FIFO, this value gets overwritten + * in mcp251xfd_read_regs_status(), so set it unconditionally + * here. + */ + priv->regs_status.rxif = BIT(priv->rx[0]->fifo_nr); + + if (priv->tx_obj_num_coalesce_irq) { + netdev_dbg(priv->ndev, + "FIFO setup: TEF: 0x%03x: %2d*%zu bytes = %4zu bytes (coalesce)\n", + mcp251xfd_get_tef_obj_addr(0), + priv->tx_obj_num_coalesce_irq, + sizeof(struct mcp251xfd_hw_tef_obj), + priv->tx_obj_num_coalesce_irq * + sizeof(struct mcp251xfd_hw_tef_obj)); + + netdev_dbg(priv->ndev, + " 0x%03x: %2d*%zu bytes = %4zu bytes\n", + mcp251xfd_get_tef_obj_addr(priv->tx_obj_num_coalesce_irq), + priv->tx->obj_num - priv->tx_obj_num_coalesce_irq, + sizeof(struct mcp251xfd_hw_tef_obj), + (priv->tx->obj_num - priv->tx_obj_num_coalesce_irq) * + sizeof(struct mcp251xfd_hw_tef_obj)); + } else { + netdev_dbg(priv->ndev, + "FIFO setup: TEF: 0x%03x: %2d*%zu bytes = %4zu bytes\n", + mcp251xfd_get_tef_obj_addr(0), + priv->tx->obj_num, sizeof(struct mcp251xfd_hw_tef_obj), + priv->tx->obj_num * sizeof(struct mcp251xfd_hw_tef_obj)); + } + + mcp251xfd_for_each_rx_ring(priv, rx_ring, i) { + if (rx_ring->nr == 0 && priv->rx_obj_num_coalesce_irq) { + netdev_dbg(priv->ndev, + "FIFO setup: RX-%u: FIFO %u/0x%03x: %2u*%u bytes = %4u bytes (coalesce)\n", + rx_ring->nr, rx_ring->fifo_nr, + mcp251xfd_get_rx_obj_addr(rx_ring, 0), + priv->rx_obj_num_coalesce_irq, rx_ring->obj_size, + priv->rx_obj_num_coalesce_irq * rx_ring->obj_size); + + if (priv->rx_obj_num_coalesce_irq == MCP251XFD_FIFO_DEPTH) + continue; + + netdev_dbg(priv->ndev, + " 0x%03x: %2u*%u bytes = %4u bytes\n", + mcp251xfd_get_rx_obj_addr(rx_ring, + priv->rx_obj_num_coalesce_irq), + rx_ring->obj_num - priv->rx_obj_num_coalesce_irq, + rx_ring->obj_size, + (rx_ring->obj_num - priv->rx_obj_num_coalesce_irq) * + rx_ring->obj_size); + } else { + netdev_dbg(priv->ndev, + "FIFO setup: RX-%u: FIFO %u/0x%03x: %2u*%u bytes = %4u bytes\n", + rx_ring->nr, rx_ring->fifo_nr, + mcp251xfd_get_rx_obj_addr(rx_ring, 0), + rx_ring->obj_num, rx_ring->obj_size, + rx_ring->obj_num * rx_ring->obj_size); + } + } + + netdev_dbg(priv->ndev, + "FIFO setup: TX: FIFO %u/0x%03x: %2u*%u bytes = %4u bytes\n", + priv->tx->fifo_nr, + mcp251xfd_get_tx_obj_addr(priv->tx, 0), + priv->tx->obj_num, priv->tx->obj_size, + priv->tx->obj_num * priv->tx->obj_size); + + netdev_dbg(priv->ndev, + "FIFO setup: free: %4d bytes\n", + MCP251XFD_RAM_SIZE - (base - MCP251XFD_RAM_START)); + + ram_used = base - MCP251XFD_RAM_START; + if (ram_used > MCP251XFD_RAM_SIZE) { + netdev_err(priv->ndev, + "Error during ring configuration, using more RAM (%u bytes) than available (%u bytes).\n", + ram_used, MCP251XFD_RAM_SIZE); + return -ENOMEM; + } + + return 0; +} + +void mcp251xfd_ring_free(struct mcp251xfd_priv *priv) +{ + int i; + + for (i = ARRAY_SIZE(priv->rx) - 1; i >= 0; i--) { + kfree(priv->rx[i]); + priv->rx[i] = NULL; + } +} + +static enum hrtimer_restart mcp251xfd_rx_irq_timer(struct hrtimer *t) +{ + struct mcp251xfd_priv *priv = container_of(t, struct mcp251xfd_priv, + rx_irq_timer); + struct mcp251xfd_rx_ring *ring = priv->rx[0]; + + if (test_bit(MCP251XFD_FLAGS_DOWN, priv->flags)) + return HRTIMER_NORESTART; + + spi_async(priv->spi, &ring->irq_enable_msg); + + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart mcp251xfd_tx_irq_timer(struct hrtimer *t) +{ + struct mcp251xfd_priv *priv = container_of(t, struct mcp251xfd_priv, + tx_irq_timer); + struct mcp251xfd_tef_ring *ring = priv->tef; + + if (test_bit(MCP251XFD_FLAGS_DOWN, priv->flags)) + return HRTIMER_NORESTART; + + spi_async(priv->spi, &ring->irq_enable_msg); + + return HRTIMER_NORESTART; +} + +const struct can_ram_config mcp251xfd_ram_config = { + .rx = { + .size[CAN_RAM_MODE_CAN] = sizeof(struct mcp251xfd_hw_rx_obj_can), + .size[CAN_RAM_MODE_CANFD] = sizeof(struct mcp251xfd_hw_rx_obj_canfd), + .min = MCP251XFD_RX_OBJ_NUM_MIN, + .max = MCP251XFD_RX_OBJ_NUM_MAX, + .def[CAN_RAM_MODE_CAN] = CAN_RAM_NUM_MAX, + .def[CAN_RAM_MODE_CANFD] = CAN_RAM_NUM_MAX, + .fifo_num = MCP251XFD_FIFO_RX_NUM, + .fifo_depth_min = MCP251XFD_RX_FIFO_DEPTH_MIN, + .fifo_depth_coalesce_min = MCP251XFD_RX_FIFO_DEPTH_COALESCE_MIN, + }, + .tx = { + .size[CAN_RAM_MODE_CAN] = sizeof(struct mcp251xfd_hw_tef_obj) + + sizeof(struct mcp251xfd_hw_tx_obj_can), + .size[CAN_RAM_MODE_CANFD] = sizeof(struct mcp251xfd_hw_tef_obj) + + sizeof(struct mcp251xfd_hw_tx_obj_canfd), + .min = MCP251XFD_TX_OBJ_NUM_MIN, + .max = MCP251XFD_TX_OBJ_NUM_MAX, + .def[CAN_RAM_MODE_CAN] = MCP251XFD_TX_OBJ_NUM_CAN_DEFAULT, + .def[CAN_RAM_MODE_CANFD] = MCP251XFD_TX_OBJ_NUM_CANFD_DEFAULT, + .fifo_num = MCP251XFD_FIFO_TX_NUM, + .fifo_depth_min = MCP251XFD_TX_FIFO_DEPTH_MIN, + .fifo_depth_coalesce_min = MCP251XFD_TX_FIFO_DEPTH_COALESCE_MIN, + }, + .size = MCP251XFD_RAM_SIZE, + .fifo_depth = MCP251XFD_FIFO_DEPTH, +}; + +int mcp251xfd_ring_alloc(struct mcp251xfd_priv *priv) +{ + const bool fd_mode = mcp251xfd_is_fd_mode(priv); + struct mcp251xfd_tx_ring *tx_ring = priv->tx; + struct mcp251xfd_rx_ring *rx_ring; + u8 tx_obj_size, rx_obj_size; + u8 rem, i; + + /* switching from CAN-2.0 to CAN-FD mode or vice versa */ + if (fd_mode != test_bit(MCP251XFD_FLAGS_FD_MODE, priv->flags)) { + struct can_ram_layout layout; + + can_ram_get_layout(&layout, &mcp251xfd_ram_config, NULL, NULL, fd_mode); + priv->rx_obj_num = layout.default_rx; + tx_ring->obj_num = layout.default_tx; + } + + if (fd_mode) { + tx_obj_size = sizeof(struct mcp251xfd_hw_tx_obj_canfd); + rx_obj_size = sizeof(struct mcp251xfd_hw_rx_obj_canfd); + set_bit(MCP251XFD_FLAGS_FD_MODE, priv->flags); + } else { + tx_obj_size = sizeof(struct mcp251xfd_hw_tx_obj_can); + rx_obj_size = sizeof(struct mcp251xfd_hw_rx_obj_can); + clear_bit(MCP251XFD_FLAGS_FD_MODE, priv->flags); + } + + tx_ring->obj_size = tx_obj_size; + + rem = priv->rx_obj_num; + for (i = 0; i < ARRAY_SIZE(priv->rx) && rem; i++) { + u8 rx_obj_num; + + if (i == 0 && priv->rx_obj_num_coalesce_irq) + rx_obj_num = min_t(u8, priv->rx_obj_num_coalesce_irq * 2, + MCP251XFD_FIFO_DEPTH); + else + rx_obj_num = min_t(u8, rounddown_pow_of_two(rem), + MCP251XFD_FIFO_DEPTH); + rem -= rx_obj_num; + + rx_ring = kzalloc(sizeof(*rx_ring) + rx_obj_size * rx_obj_num, + GFP_KERNEL); + if (!rx_ring) { + mcp251xfd_ring_free(priv); + return -ENOMEM; + } + + rx_ring->obj_num = rx_obj_num; + rx_ring->obj_size = rx_obj_size; + priv->rx[i] = rx_ring; + } + priv->rx_ring_num = i; + + hrtimer_init(&priv->rx_irq_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + priv->rx_irq_timer.function = mcp251xfd_rx_irq_timer; + + hrtimer_init(&priv->tx_irq_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + priv->tx_irq_timer.function = mcp251xfd_tx_irq_timer; + + return 0; +} diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c new file mode 100644 index 0000000000..ced8d9c81f --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-rx.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mcp251xfd - Microchip MCP251xFD Family CAN controller driver +// +// Copyright (c) 2019, 2020, 2021 Pengutronix, +// Marc Kleine-Budde <kernel@pengutronix.de> +// +// Based on: +// +// CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface +// +// Copyright (c) 2019 Martin Sperl <kernel@martin.sperl.org> +// + +#include <linux/bitfield.h> + +#include "mcp251xfd.h" + +static inline int +mcp251xfd_rx_head_get_from_chip(const struct mcp251xfd_priv *priv, + const struct mcp251xfd_rx_ring *ring, + u8 *rx_head, bool *fifo_empty) +{ + u32 fifo_sta; + int err; + + err = regmap_read(priv->map_reg, MCP251XFD_REG_FIFOSTA(ring->fifo_nr), + &fifo_sta); + if (err) + return err; + + *rx_head = FIELD_GET(MCP251XFD_REG_FIFOSTA_FIFOCI_MASK, fifo_sta); + *fifo_empty = !(fifo_sta & MCP251XFD_REG_FIFOSTA_TFNRFNIF); + + return 0; +} + +static inline int +mcp251xfd_rx_tail_get_from_chip(const struct mcp251xfd_priv *priv, + const struct mcp251xfd_rx_ring *ring, + u8 *rx_tail) +{ + u32 fifo_ua; + int err; + + err = regmap_read(priv->map_reg, MCP251XFD_REG_FIFOUA(ring->fifo_nr), + &fifo_ua); + if (err) + return err; + + fifo_ua -= ring->base - MCP251XFD_RAM_START; + *rx_tail = fifo_ua / ring->obj_size; + + return 0; +} + +static int +mcp251xfd_check_rx_tail(const struct mcp251xfd_priv *priv, + const struct mcp251xfd_rx_ring *ring) +{ + u8 rx_tail_chip, rx_tail; + int err; + + if (!IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY)) + return 0; + + err = mcp251xfd_rx_tail_get_from_chip(priv, ring, &rx_tail_chip); + if (err) + return err; + + rx_tail = mcp251xfd_get_rx_tail(ring); + if (rx_tail_chip != rx_tail) { + netdev_err(priv->ndev, + "RX tail of chip (%d) and ours (%d) inconsistent.\n", + rx_tail_chip, rx_tail); + return -EILSEQ; + } + + return 0; +} + +static int +mcp251xfd_rx_ring_update(const struct mcp251xfd_priv *priv, + struct mcp251xfd_rx_ring *ring) +{ + u32 new_head; + u8 chip_rx_head; + bool fifo_empty; + int err; + + err = mcp251xfd_rx_head_get_from_chip(priv, ring, &chip_rx_head, + &fifo_empty); + if (err || fifo_empty) + return err; + + /* chip_rx_head, is the next RX-Object filled by the HW. + * The new RX head must be >= the old head. + */ + new_head = round_down(ring->head, ring->obj_num) + chip_rx_head; + if (new_head <= ring->head) + new_head += ring->obj_num; + + ring->head = new_head; + + return mcp251xfd_check_rx_tail(priv, ring); +} + +static void +mcp251xfd_hw_rx_obj_to_skb(const struct mcp251xfd_priv *priv, + const struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj, + struct sk_buff *skb) +{ + struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + u8 dlc; + + if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_IDE) { + u32 sid, eid; + + eid = FIELD_GET(MCP251XFD_OBJ_ID_EID_MASK, hw_rx_obj->id); + sid = FIELD_GET(MCP251XFD_OBJ_ID_SID_MASK, hw_rx_obj->id); + + cfd->can_id = CAN_EFF_FLAG | + FIELD_PREP(MCP251XFD_REG_FRAME_EFF_EID_MASK, eid) | + FIELD_PREP(MCP251XFD_REG_FRAME_EFF_SID_MASK, sid); + } else { + cfd->can_id = FIELD_GET(MCP251XFD_OBJ_ID_SID_MASK, + hw_rx_obj->id); + } + + dlc = FIELD_GET(MCP251XFD_OBJ_FLAGS_DLC_MASK, hw_rx_obj->flags); + + /* CANFD */ + if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_FDF) { + if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_ESI) + cfd->flags |= CANFD_ESI; + + if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_BRS) + cfd->flags |= CANFD_BRS; + + cfd->len = can_fd_dlc2len(dlc); + } else { + if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_RTR) + cfd->can_id |= CAN_RTR_FLAG; + + can_frame_set_cc_len((struct can_frame *)cfd, dlc, + priv->can.ctrlmode); + } + + if (!(hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_RTR)) + memcpy(cfd->data, hw_rx_obj->data, cfd->len); + + mcp251xfd_skb_set_timestamp(priv, skb, hw_rx_obj->ts); +} + +static int +mcp251xfd_handle_rxif_one(struct mcp251xfd_priv *priv, + struct mcp251xfd_rx_ring *ring, + const struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj) +{ + struct net_device_stats *stats = &priv->ndev->stats; + struct sk_buff *skb; + struct canfd_frame *cfd; + int err; + + if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_FDF) + skb = alloc_canfd_skb(priv->ndev, &cfd); + else + skb = alloc_can_skb(priv->ndev, (struct can_frame **)&cfd); + + if (!skb) { + stats->rx_dropped++; + return 0; + } + + mcp251xfd_hw_rx_obj_to_skb(priv, hw_rx_obj, skb); + err = can_rx_offload_queue_timestamp(&priv->offload, skb, hw_rx_obj->ts); + if (err) + stats->rx_fifo_errors++; + + return 0; +} + +static inline int +mcp251xfd_rx_obj_read(const struct mcp251xfd_priv *priv, + const struct mcp251xfd_rx_ring *ring, + struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj, + const u8 offset, const u8 len) +{ + const int val_bytes = regmap_get_val_bytes(priv->map_rx); + int err; + + err = regmap_bulk_read(priv->map_rx, + mcp251xfd_get_rx_obj_addr(ring, offset), + hw_rx_obj, + len * ring->obj_size / val_bytes); + + return err; +} + +static int +mcp251xfd_handle_rxif_ring(struct mcp251xfd_priv *priv, + struct mcp251xfd_rx_ring *ring) +{ + struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj = ring->obj; + u8 rx_tail, len; + int err, i; + + err = mcp251xfd_rx_ring_update(priv, ring); + if (err) + return err; + + while ((len = mcp251xfd_get_rx_linear_len(ring))) { + int offset; + + rx_tail = mcp251xfd_get_rx_tail(ring); + + err = mcp251xfd_rx_obj_read(priv, ring, hw_rx_obj, + rx_tail, len); + if (err) + return err; + + for (i = 0; i < len; i++) { + err = mcp251xfd_handle_rxif_one(priv, ring, + (void *)hw_rx_obj + + i * ring->obj_size); + if (err) + return err; + } + + /* Increment the RX FIFO tail pointer 'len' times in a + * single SPI message. + * + * Note: + * Calculate offset, so that the SPI transfer ends on + * the last message of the uinc_xfer array, which has + * "cs_change == 0", to properly deactivate the chip + * select. + */ + offset = ARRAY_SIZE(ring->uinc_xfer) - len; + err = spi_sync_transfer(priv->spi, + ring->uinc_xfer + offset, len); + if (err) + return err; + + ring->tail += len; + } + + return 0; +} + +int mcp251xfd_handle_rxif(struct mcp251xfd_priv *priv) +{ + struct mcp251xfd_rx_ring *ring; + int err, n; + + mcp251xfd_for_each_rx_ring(priv, ring, n) { + /* - if RX IRQ coalescing is active always handle ring 0 + * - only handle rings if RX IRQ is active + */ + if ((ring->nr > 0 || !priv->rx_obj_num_coalesce_irq) && + !(priv->regs_status.rxif & BIT(ring->fifo_nr))) + continue; + + err = mcp251xfd_handle_rxif_ring(priv, ring); + if (err) + return err; + } + + if (priv->rx_coalesce_usecs_irq) + hrtimer_start(&priv->rx_irq_timer, + ns_to_ktime(priv->rx_coalesce_usecs_irq * + NSEC_PER_USEC), + HRTIMER_MODE_REL); + + return 0; +} diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c new file mode 100644 index 0000000000..e5bd57b65a --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mcp251xfd - Microchip MCP251xFD Family CAN controller driver +// +// Copyright (c) 2019, 2020, 2021 Pengutronix, +// Marc Kleine-Budde <kernel@pengutronix.de> +// +// Based on: +// +// CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface +// +// Copyright (c) 2019 Martin Sperl <kernel@martin.sperl.org> +// + +#include <linux/bitfield.h> + +#include "mcp251xfd.h" + +static inline int +mcp251xfd_tef_tail_get_from_chip(const struct mcp251xfd_priv *priv, + u8 *tef_tail) +{ + u32 tef_ua; + int err; + + err = regmap_read(priv->map_reg, MCP251XFD_REG_TEFUA, &tef_ua); + if (err) + return err; + + *tef_tail = tef_ua / sizeof(struct mcp251xfd_hw_tef_obj); + + return 0; +} + +static int mcp251xfd_check_tef_tail(const struct mcp251xfd_priv *priv) +{ + u8 tef_tail_chip, tef_tail; + int err; + + if (!IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY)) + return 0; + + err = mcp251xfd_tef_tail_get_from_chip(priv, &tef_tail_chip); + if (err) + return err; + + tef_tail = mcp251xfd_get_tef_tail(priv); + if (tef_tail_chip != tef_tail) { + netdev_err(priv->ndev, + "TEF tail of chip (0x%02x) and ours (0x%08x) inconsistent.\n", + tef_tail_chip, tef_tail); + return -EILSEQ; + } + + return 0; +} + +static int +mcp251xfd_handle_tefif_recover(const struct mcp251xfd_priv *priv, const u32 seq) +{ + const struct mcp251xfd_tx_ring *tx_ring = priv->tx; + u32 tef_sta; + int err; + + err = regmap_read(priv->map_reg, MCP251XFD_REG_TEFSTA, &tef_sta); + if (err) + return err; + + if (tef_sta & MCP251XFD_REG_TEFSTA_TEFOVIF) { + netdev_err(priv->ndev, + "Transmit Event FIFO buffer overflow.\n"); + return -ENOBUFS; + } + + netdev_info(priv->ndev, + "Transmit Event FIFO buffer %s. (seq=0x%08x, tef_tail=0x%08x, tef_head=0x%08x, tx_head=0x%08x).\n", + tef_sta & MCP251XFD_REG_TEFSTA_TEFFIF ? + "full" : tef_sta & MCP251XFD_REG_TEFSTA_TEFNEIF ? + "not empty" : "empty", + seq, priv->tef->tail, priv->tef->head, tx_ring->head); + + /* The Sequence Number in the TEF doesn't match our tef_tail. */ + return -EAGAIN; +} + +static int +mcp251xfd_handle_tefif_one(struct mcp251xfd_priv *priv, + const struct mcp251xfd_hw_tef_obj *hw_tef_obj, + unsigned int *frame_len_ptr) +{ + struct net_device_stats *stats = &priv->ndev->stats; + struct sk_buff *skb; + u32 seq, seq_masked, tef_tail_masked, tef_tail; + + seq = FIELD_GET(MCP251XFD_OBJ_FLAGS_SEQ_MCP2518FD_MASK, + hw_tef_obj->flags); + + /* Use the MCP2517FD mask on the MCP2518FD, too. We only + * compare 7 bits, this should be enough to detect + * net-yet-completed, i.e. old TEF objects. + */ + seq_masked = seq & + field_mask(MCP251XFD_OBJ_FLAGS_SEQ_MCP2517FD_MASK); + tef_tail_masked = priv->tef->tail & + field_mask(MCP251XFD_OBJ_FLAGS_SEQ_MCP2517FD_MASK); + if (seq_masked != tef_tail_masked) + return mcp251xfd_handle_tefif_recover(priv, seq); + + tef_tail = mcp251xfd_get_tef_tail(priv); + skb = priv->can.echo_skb[tef_tail]; + if (skb) + mcp251xfd_skb_set_timestamp(priv, skb, hw_tef_obj->ts); + stats->tx_bytes += + can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload, + tef_tail, hw_tef_obj->ts, + frame_len_ptr); + stats->tx_packets++; + priv->tef->tail++; + + return 0; +} + +static int mcp251xfd_tef_ring_update(struct mcp251xfd_priv *priv) +{ + const struct mcp251xfd_tx_ring *tx_ring = priv->tx; + unsigned int new_head; + u8 chip_tx_tail; + int err; + + err = mcp251xfd_tx_tail_get_from_chip(priv, &chip_tx_tail); + if (err) + return err; + + /* chip_tx_tail, is the next TX-Object send by the HW. + * The new TEF head must be >= the old head, ... + */ + new_head = round_down(priv->tef->head, tx_ring->obj_num) + chip_tx_tail; + if (new_head <= priv->tef->head) + new_head += tx_ring->obj_num; + + /* ... but it cannot exceed the TX head. */ + priv->tef->head = min(new_head, tx_ring->head); + + return mcp251xfd_check_tef_tail(priv); +} + +static inline int +mcp251xfd_tef_obj_read(const struct mcp251xfd_priv *priv, + struct mcp251xfd_hw_tef_obj *hw_tef_obj, + const u8 offset, const u8 len) +{ + const struct mcp251xfd_tx_ring *tx_ring = priv->tx; + const int val_bytes = regmap_get_val_bytes(priv->map_rx); + + if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && + (offset > tx_ring->obj_num || + len > tx_ring->obj_num || + offset + len > tx_ring->obj_num)) { + netdev_err(priv->ndev, + "Trying to read too many TEF objects (max=%d, offset=%d, len=%d).\n", + tx_ring->obj_num, offset, len); + return -ERANGE; + } + + return regmap_bulk_read(priv->map_rx, + mcp251xfd_get_tef_obj_addr(offset), + hw_tef_obj, + sizeof(*hw_tef_obj) / val_bytes * len); +} + +static inline void mcp251xfd_ecc_tefif_successful(struct mcp251xfd_priv *priv) +{ + struct mcp251xfd_ecc *ecc = &priv->ecc; + + ecc->ecc_stat = 0; +} + +int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv) +{ + struct mcp251xfd_hw_tef_obj hw_tef_obj[MCP251XFD_TX_OBJ_NUM_MAX]; + unsigned int total_frame_len = 0; + u8 tef_tail, len, l; + int err, i; + + err = mcp251xfd_tef_ring_update(priv); + if (err) + return err; + + tef_tail = mcp251xfd_get_tef_tail(priv); + len = mcp251xfd_get_tef_len(priv); + l = mcp251xfd_get_tef_linear_len(priv); + err = mcp251xfd_tef_obj_read(priv, hw_tef_obj, tef_tail, l); + if (err) + return err; + + if (l < len) { + err = mcp251xfd_tef_obj_read(priv, &hw_tef_obj[l], 0, len - l); + if (err) + return err; + } + + for (i = 0; i < len; i++) { + unsigned int frame_len = 0; + + err = mcp251xfd_handle_tefif_one(priv, &hw_tef_obj[i], &frame_len); + /* -EAGAIN means the Sequence Number in the TEF + * doesn't match our tef_tail. This can happen if we + * read the TEF objects too early. Leave loop let the + * interrupt handler call us again. + */ + if (err == -EAGAIN) + goto out_netif_wake_queue; + if (err) + return err; + + total_frame_len += frame_len; + } + + out_netif_wake_queue: + len = i; /* number of handled goods TEFs */ + if (len) { + struct mcp251xfd_tef_ring *ring = priv->tef; + struct mcp251xfd_tx_ring *tx_ring = priv->tx; + int offset; + + /* Increment the TEF FIFO tail pointer 'len' times in + * a single SPI message. + * + * Note: + * Calculate offset, so that the SPI transfer ends on + * the last message of the uinc_xfer array, which has + * "cs_change == 0", to properly deactivate the chip + * select. + */ + offset = ARRAY_SIZE(ring->uinc_xfer) - len; + err = spi_sync_transfer(priv->spi, + ring->uinc_xfer + offset, len); + if (err) + return err; + + tx_ring->tail += len; + netdev_completed_queue(priv->ndev, len, total_frame_len); + + err = mcp251xfd_check_tef_tail(priv); + if (err) + return err; + } + + mcp251xfd_ecc_tefif_successful(priv); + + if (mcp251xfd_get_tx_free(priv->tx)) { + /* Make sure that anybody stopping the queue after + * this sees the new tx_ring->tail. + */ + smp_mb(); + netif_wake_queue(priv->ndev); + } + + if (priv->tx_coalesce_usecs_irq) + hrtimer_start(&priv->tx_irq_timer, + ns_to_ktime(priv->tx_coalesce_usecs_irq * + NSEC_PER_USEC), + HRTIMER_MODE_REL); + + return 0; +} diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-timestamp.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-timestamp.c new file mode 100644 index 0000000000..712e091869 --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-timestamp.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mcp251xfd - Microchip MCP251xFD Family CAN controller driver +// +// Copyright (c) 2021 Pengutronix, +// Marc Kleine-Budde <kernel@pengutronix.de> +// + +#include <linux/clocksource.h> +#include <linux/workqueue.h> + +#include "mcp251xfd.h" + +static u64 mcp251xfd_timestamp_read(const struct cyclecounter *cc) +{ + const struct mcp251xfd_priv *priv; + u32 timestamp = 0; + int err; + + priv = container_of(cc, struct mcp251xfd_priv, cc); + err = mcp251xfd_get_timestamp(priv, ×tamp); + if (err) + netdev_err(priv->ndev, + "Error %d while reading timestamp. HW timestamps may be inaccurate.", + err); + + return timestamp; +} + +static void mcp251xfd_timestamp_work(struct work_struct *work) +{ + struct delayed_work *delayed_work = to_delayed_work(work); + struct mcp251xfd_priv *priv; + + priv = container_of(delayed_work, struct mcp251xfd_priv, timestamp); + timecounter_read(&priv->tc); + + schedule_delayed_work(&priv->timestamp, + MCP251XFD_TIMESTAMP_WORK_DELAY_SEC * HZ); +} + +void mcp251xfd_skb_set_timestamp(const struct mcp251xfd_priv *priv, + struct sk_buff *skb, u32 timestamp) +{ + struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb); + u64 ns; + + ns = timecounter_cyc2time(&priv->tc, timestamp); + hwtstamps->hwtstamp = ns_to_ktime(ns); +} + +void mcp251xfd_timestamp_init(struct mcp251xfd_priv *priv) +{ + struct cyclecounter *cc = &priv->cc; + + cc->read = mcp251xfd_timestamp_read; + cc->mask = CYCLECOUNTER_MASK(32); + cc->shift = 1; + cc->mult = clocksource_hz2mult(priv->can.clock.freq, cc->shift); + + timecounter_init(&priv->tc, &priv->cc, ktime_get_real_ns()); + + INIT_DELAYED_WORK(&priv->timestamp, mcp251xfd_timestamp_work); + schedule_delayed_work(&priv->timestamp, + MCP251XFD_TIMESTAMP_WORK_DELAY_SEC * HZ); +} + +void mcp251xfd_timestamp_stop(struct mcp251xfd_priv *priv) +{ + cancel_delayed_work_sync(&priv->timestamp); +} diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-tx.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-tx.c new file mode 100644 index 0000000000..160528d3cc --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-tx.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mcp251xfd - Microchip MCP251xFD Family CAN controller driver +// +// Copyright (c) 2019, 2020, 2021 Pengutronix, +// Marc Kleine-Budde <kernel@pengutronix.de> +// +// Based on: +// +// CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface +// +// Copyright (c) 2019 Martin Sperl <kernel@martin.sperl.org> +// + +#include <asm/unaligned.h> +#include <linux/bitfield.h> + +#include "mcp251xfd.h" + +static inline struct +mcp251xfd_tx_obj *mcp251xfd_get_tx_obj_next(struct mcp251xfd_tx_ring *tx_ring) +{ + u8 tx_head; + + tx_head = mcp251xfd_get_tx_head(tx_ring); + + return &tx_ring->obj[tx_head]; +} + +static void +mcp251xfd_tx_obj_from_skb(const struct mcp251xfd_priv *priv, + struct mcp251xfd_tx_obj *tx_obj, + const struct sk_buff *skb, + unsigned int seq) +{ + const struct canfd_frame *cfd = (struct canfd_frame *)skb->data; + struct mcp251xfd_hw_tx_obj_raw *hw_tx_obj; + union mcp251xfd_tx_obj_load_buf *load_buf; + u8 dlc; + u32 id, flags; + int len_sanitized = 0, len; + + if (cfd->can_id & CAN_EFF_FLAG) { + u32 sid, eid; + + sid = FIELD_GET(MCP251XFD_REG_FRAME_EFF_SID_MASK, cfd->can_id); + eid = FIELD_GET(MCP251XFD_REG_FRAME_EFF_EID_MASK, cfd->can_id); + + id = FIELD_PREP(MCP251XFD_OBJ_ID_EID_MASK, eid) | + FIELD_PREP(MCP251XFD_OBJ_ID_SID_MASK, sid); + + flags = MCP251XFD_OBJ_FLAGS_IDE; + } else { + id = FIELD_PREP(MCP251XFD_OBJ_ID_SID_MASK, cfd->can_id); + flags = 0; + } + + /* Use the MCP2518FD mask even on the MCP2517FD. It doesn't + * harm, only the lower 7 bits will be transferred into the + * TEF object. + */ + flags |= FIELD_PREP(MCP251XFD_OBJ_FLAGS_SEQ_MCP2518FD_MASK, seq); + + if (cfd->can_id & CAN_RTR_FLAG) + flags |= MCP251XFD_OBJ_FLAGS_RTR; + else + len_sanitized = canfd_sanitize_len(cfd->len); + + /* CANFD */ + if (can_is_canfd_skb(skb)) { + if (cfd->flags & CANFD_ESI) + flags |= MCP251XFD_OBJ_FLAGS_ESI; + + flags |= MCP251XFD_OBJ_FLAGS_FDF; + + if (cfd->flags & CANFD_BRS) + flags |= MCP251XFD_OBJ_FLAGS_BRS; + + dlc = can_fd_len2dlc(cfd->len); + } else { + dlc = can_get_cc_dlc((struct can_frame *)cfd, + priv->can.ctrlmode); + } + + flags |= FIELD_PREP(MCP251XFD_OBJ_FLAGS_DLC_MASK, dlc); + + load_buf = &tx_obj->buf; + if (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_TX) + hw_tx_obj = &load_buf->crc.hw_tx_obj; + else + hw_tx_obj = &load_buf->nocrc.hw_tx_obj; + + put_unaligned_le32(id, &hw_tx_obj->id); + put_unaligned_le32(flags, &hw_tx_obj->flags); + + /* Copy data */ + memcpy(hw_tx_obj->data, cfd->data, cfd->len); + + /* Clear unused data at end of CAN frame */ + if (MCP251XFD_SANITIZE_CAN && len_sanitized) { + int pad_len; + + pad_len = len_sanitized - cfd->len; + if (pad_len) + memset(hw_tx_obj->data + cfd->len, 0x0, pad_len); + } + + /* Number of bytes to be written into the RAM of the controller */ + len = sizeof(hw_tx_obj->id) + sizeof(hw_tx_obj->flags); + if (MCP251XFD_SANITIZE_CAN) + len += round_up(len_sanitized, sizeof(u32)); + else + len += round_up(cfd->len, sizeof(u32)); + + if (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_TX) { + u16 crc; + + mcp251xfd_spi_cmd_crc_set_len_in_ram(&load_buf->crc.cmd, + len); + /* CRC */ + len += sizeof(load_buf->crc.cmd); + crc = mcp251xfd_crc16_compute(&load_buf->crc, len); + put_unaligned_be16(crc, (void *)load_buf + len); + + /* Total length */ + len += sizeof(load_buf->crc.crc); + } else { + len += sizeof(load_buf->nocrc.cmd); + } + + tx_obj->xfer[0].len = len; +} + +static int mcp251xfd_tx_obj_write(const struct mcp251xfd_priv *priv, + struct mcp251xfd_tx_obj *tx_obj) +{ + return spi_async(priv->spi, &tx_obj->msg); +} + +static bool mcp251xfd_tx_busy(const struct mcp251xfd_priv *priv, + struct mcp251xfd_tx_ring *tx_ring) +{ + if (mcp251xfd_get_tx_free(tx_ring) > 0) + return false; + + netif_stop_queue(priv->ndev); + + /* Memory barrier before checking tx_free (head and tail) */ + smp_mb(); + + if (mcp251xfd_get_tx_free(tx_ring) == 0) { + netdev_dbg(priv->ndev, + "Stopping tx-queue (tx_head=0x%08x, tx_tail=0x%08x, len=%d).\n", + tx_ring->head, tx_ring->tail, + tx_ring->head - tx_ring->tail); + + return true; + } + + netif_start_queue(priv->ndev); + + return false; +} + +netdev_tx_t mcp251xfd_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct mcp251xfd_priv *priv = netdev_priv(ndev); + struct mcp251xfd_tx_ring *tx_ring = priv->tx; + struct mcp251xfd_tx_obj *tx_obj; + unsigned int frame_len; + u8 tx_head; + int err; + + if (can_dev_dropped_skb(ndev, skb)) + return NETDEV_TX_OK; + + if (mcp251xfd_tx_busy(priv, tx_ring)) + return NETDEV_TX_BUSY; + + tx_obj = mcp251xfd_get_tx_obj_next(tx_ring); + mcp251xfd_tx_obj_from_skb(priv, tx_obj, skb, tx_ring->head); + + /* Stop queue if we occupy the complete TX FIFO */ + tx_head = mcp251xfd_get_tx_head(tx_ring); + tx_ring->head++; + if (mcp251xfd_get_tx_free(tx_ring) == 0) + netif_stop_queue(ndev); + + frame_len = can_skb_get_frame_len(skb); + err = can_put_echo_skb(skb, ndev, tx_head, frame_len); + if (!err) + netdev_sent_queue(priv->ndev, frame_len); + + err = mcp251xfd_tx_obj_write(priv, tx_obj); + if (err) + goto out_err; + + return NETDEV_TX_OK; + + out_err: + netdev_err(priv->ndev, "ERROR in %s: %d\n", __func__, err); + + return NETDEV_TX_OK; +} diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd.h b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h new file mode 100644 index 0000000000..24510b3b80 --- /dev/null +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd.h @@ -0,0 +1,966 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * mcp251xfd - Microchip MCP251xFD Family CAN controller driver + * + * Copyright (c) 2019, 2020, 2021 Pengutronix, + * Marc Kleine-Budde <kernel@pengutronix.de> + * Copyright (c) 2019 Martin Sperl <kernel@martin.sperl.org> + */ + +#ifndef _MCP251XFD_H +#define _MCP251XFD_H + +#include <linux/bitfield.h> +#include <linux/can/core.h> +#include <linux/can/dev.h> +#include <linux/can/rx-offload.h> +#include <linux/gpio/consumer.h> +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/timecounter.h> +#include <linux/workqueue.h> + +/* MPC251x registers */ + +/* CAN FD Controller Module SFR */ +#define MCP251XFD_REG_CON 0x00 +#define MCP251XFD_REG_CON_TXBWS_MASK GENMASK(31, 28) +#define MCP251XFD_REG_CON_ABAT BIT(27) +#define MCP251XFD_REG_CON_REQOP_MASK GENMASK(26, 24) +#define MCP251XFD_REG_CON_MODE_MIXED 0 +#define MCP251XFD_REG_CON_MODE_SLEEP 1 +#define MCP251XFD_REG_CON_MODE_INT_LOOPBACK 2 +#define MCP251XFD_REG_CON_MODE_LISTENONLY 3 +#define MCP251XFD_REG_CON_MODE_CONFIG 4 +#define MCP251XFD_REG_CON_MODE_EXT_LOOPBACK 5 +#define MCP251XFD_REG_CON_MODE_CAN2_0 6 +#define MCP251XFD_REG_CON_MODE_RESTRICTED 7 +#define MCP251XFD_REG_CON_OPMOD_MASK GENMASK(23, 21) +#define MCP251XFD_REG_CON_TXQEN BIT(20) +#define MCP251XFD_REG_CON_STEF BIT(19) +#define MCP251XFD_REG_CON_SERR2LOM BIT(18) +#define MCP251XFD_REG_CON_ESIGM BIT(17) +#define MCP251XFD_REG_CON_RTXAT BIT(16) +#define MCP251XFD_REG_CON_BRSDIS BIT(12) +#define MCP251XFD_REG_CON_BUSY BIT(11) +#define MCP251XFD_REG_CON_WFT_MASK GENMASK(10, 9) +#define MCP251XFD_REG_CON_WFT_T00FILTER 0x0 +#define MCP251XFD_REG_CON_WFT_T01FILTER 0x1 +#define MCP251XFD_REG_CON_WFT_T10FILTER 0x2 +#define MCP251XFD_REG_CON_WFT_T11FILTER 0x3 +#define MCP251XFD_REG_CON_WAKFIL BIT(8) +#define MCP251XFD_REG_CON_PXEDIS BIT(6) +#define MCP251XFD_REG_CON_ISOCRCEN BIT(5) +#define MCP251XFD_REG_CON_DNCNT_MASK GENMASK(4, 0) + +#define MCP251XFD_REG_NBTCFG 0x04 +#define MCP251XFD_REG_NBTCFG_BRP_MASK GENMASK(31, 24) +#define MCP251XFD_REG_NBTCFG_TSEG1_MASK GENMASK(23, 16) +#define MCP251XFD_REG_NBTCFG_TSEG2_MASK GENMASK(14, 8) +#define MCP251XFD_REG_NBTCFG_SJW_MASK GENMASK(6, 0) + +#define MCP251XFD_REG_DBTCFG 0x08 +#define MCP251XFD_REG_DBTCFG_BRP_MASK GENMASK(31, 24) +#define MCP251XFD_REG_DBTCFG_TSEG1_MASK GENMASK(20, 16) +#define MCP251XFD_REG_DBTCFG_TSEG2_MASK GENMASK(11, 8) +#define MCP251XFD_REG_DBTCFG_SJW_MASK GENMASK(3, 0) + +#define MCP251XFD_REG_TDC 0x0c +#define MCP251XFD_REG_TDC_EDGFLTEN BIT(25) +#define MCP251XFD_REG_TDC_SID11EN BIT(24) +#define MCP251XFD_REG_TDC_TDCMOD_MASK GENMASK(17, 16) +#define MCP251XFD_REG_TDC_TDCMOD_AUTO 2 +#define MCP251XFD_REG_TDC_TDCMOD_MANUAL 1 +#define MCP251XFD_REG_TDC_TDCMOD_DISABLED 0 +#define MCP251XFD_REG_TDC_TDCO_MASK GENMASK(14, 8) +#define MCP251XFD_REG_TDC_TDCV_MASK GENMASK(5, 0) + +#define MCP251XFD_REG_TBC 0x10 + +#define MCP251XFD_REG_TSCON 0x14 +#define MCP251XFD_REG_TSCON_TSRES BIT(18) +#define MCP251XFD_REG_TSCON_TSEOF BIT(17) +#define MCP251XFD_REG_TSCON_TBCEN BIT(16) +#define MCP251XFD_REG_TSCON_TBCPRE_MASK GENMASK(9, 0) + +#define MCP251XFD_REG_VEC 0x18 +#define MCP251XFD_REG_VEC_RXCODE_MASK GENMASK(30, 24) +#define MCP251XFD_REG_VEC_TXCODE_MASK GENMASK(22, 16) +#define MCP251XFD_REG_VEC_FILHIT_MASK GENMASK(12, 8) +#define MCP251XFD_REG_VEC_ICODE_MASK GENMASK(6, 0) + +#define MCP251XFD_REG_INT 0x1c +#define MCP251XFD_REG_INT_IF_MASK GENMASK(15, 0) +#define MCP251XFD_REG_INT_IE_MASK GENMASK(31, 16) +#define MCP251XFD_REG_INT_IVMIE BIT(31) +#define MCP251XFD_REG_INT_WAKIE BIT(30) +#define MCP251XFD_REG_INT_CERRIE BIT(29) +#define MCP251XFD_REG_INT_SERRIE BIT(28) +#define MCP251XFD_REG_INT_RXOVIE BIT(27) +#define MCP251XFD_REG_INT_TXATIE BIT(26) +#define MCP251XFD_REG_INT_SPICRCIE BIT(25) +#define MCP251XFD_REG_INT_ECCIE BIT(24) +#define MCP251XFD_REG_INT_TEFIE BIT(20) +#define MCP251XFD_REG_INT_MODIE BIT(19) +#define MCP251XFD_REG_INT_TBCIE BIT(18) +#define MCP251XFD_REG_INT_RXIE BIT(17) +#define MCP251XFD_REG_INT_TXIE BIT(16) +#define MCP251XFD_REG_INT_IVMIF BIT(15) +#define MCP251XFD_REG_INT_WAKIF BIT(14) +#define MCP251XFD_REG_INT_CERRIF BIT(13) +#define MCP251XFD_REG_INT_SERRIF BIT(12) +#define MCP251XFD_REG_INT_RXOVIF BIT(11) +#define MCP251XFD_REG_INT_TXATIF BIT(10) +#define MCP251XFD_REG_INT_SPICRCIF BIT(9) +#define MCP251XFD_REG_INT_ECCIF BIT(8) +#define MCP251XFD_REG_INT_TEFIF BIT(4) +#define MCP251XFD_REG_INT_MODIF BIT(3) +#define MCP251XFD_REG_INT_TBCIF BIT(2) +#define MCP251XFD_REG_INT_RXIF BIT(1) +#define MCP251XFD_REG_INT_TXIF BIT(0) +/* These IRQ flags must be cleared by SW in the CAN_INT register */ +#define MCP251XFD_REG_INT_IF_CLEARABLE_MASK \ + (MCP251XFD_REG_INT_IVMIF | MCP251XFD_REG_INT_WAKIF | \ + MCP251XFD_REG_INT_CERRIF | MCP251XFD_REG_INT_SERRIF | \ + MCP251XFD_REG_INT_MODIF) + +#define MCP251XFD_REG_RXIF 0x20 +#define MCP251XFD_REG_TXIF 0x24 +#define MCP251XFD_REG_RXOVIF 0x28 +#define MCP251XFD_REG_TXATIF 0x2c +#define MCP251XFD_REG_TXREQ 0x30 + +#define MCP251XFD_REG_TREC 0x34 +#define MCP251XFD_REG_TREC_TXBO BIT(21) +#define MCP251XFD_REG_TREC_TXBP BIT(20) +#define MCP251XFD_REG_TREC_RXBP BIT(19) +#define MCP251XFD_REG_TREC_TXWARN BIT(18) +#define MCP251XFD_REG_TREC_RXWARN BIT(17) +#define MCP251XFD_REG_TREC_EWARN BIT(16) +#define MCP251XFD_REG_TREC_TEC_MASK GENMASK(15, 8) +#define MCP251XFD_REG_TREC_REC_MASK GENMASK(7, 0) + +#define MCP251XFD_REG_BDIAG0 0x38 +#define MCP251XFD_REG_BDIAG0_DTERRCNT_MASK GENMASK(31, 24) +#define MCP251XFD_REG_BDIAG0_DRERRCNT_MASK GENMASK(23, 16) +#define MCP251XFD_REG_BDIAG0_NTERRCNT_MASK GENMASK(15, 8) +#define MCP251XFD_REG_BDIAG0_NRERRCNT_MASK GENMASK(7, 0) + +#define MCP251XFD_REG_BDIAG1 0x3c +#define MCP251XFD_REG_BDIAG1_DLCMM BIT(31) +#define MCP251XFD_REG_BDIAG1_ESI BIT(30) +#define MCP251XFD_REG_BDIAG1_DCRCERR BIT(29) +#define MCP251XFD_REG_BDIAG1_DSTUFERR BIT(28) +#define MCP251XFD_REG_BDIAG1_DFORMERR BIT(27) +#define MCP251XFD_REG_BDIAG1_DBIT1ERR BIT(25) +#define MCP251XFD_REG_BDIAG1_DBIT0ERR BIT(24) +#define MCP251XFD_REG_BDIAG1_TXBOERR BIT(23) +#define MCP251XFD_REG_BDIAG1_NCRCERR BIT(21) +#define MCP251XFD_REG_BDIAG1_NSTUFERR BIT(20) +#define MCP251XFD_REG_BDIAG1_NFORMERR BIT(19) +#define MCP251XFD_REG_BDIAG1_NACKERR BIT(18) +#define MCP251XFD_REG_BDIAG1_NBIT1ERR BIT(17) +#define MCP251XFD_REG_BDIAG1_NBIT0ERR BIT(16) +#define MCP251XFD_REG_BDIAG1_BERR_MASK \ + (MCP251XFD_REG_BDIAG1_DLCMM | MCP251XFD_REG_BDIAG1_ESI | \ + MCP251XFD_REG_BDIAG1_DCRCERR | MCP251XFD_REG_BDIAG1_DSTUFERR | \ + MCP251XFD_REG_BDIAG1_DFORMERR | MCP251XFD_REG_BDIAG1_DBIT1ERR | \ + MCP251XFD_REG_BDIAG1_DBIT0ERR | MCP251XFD_REG_BDIAG1_TXBOERR | \ + MCP251XFD_REG_BDIAG1_NCRCERR | MCP251XFD_REG_BDIAG1_NSTUFERR | \ + MCP251XFD_REG_BDIAG1_NFORMERR | MCP251XFD_REG_BDIAG1_NACKERR | \ + MCP251XFD_REG_BDIAG1_NBIT1ERR | MCP251XFD_REG_BDIAG1_NBIT0ERR) +#define MCP251XFD_REG_BDIAG1_EFMSGCNT_MASK GENMASK(15, 0) + +#define MCP251XFD_REG_TEFCON 0x40 +#define MCP251XFD_REG_TEFCON_FSIZE_MASK GENMASK(28, 24) +#define MCP251XFD_REG_TEFCON_FRESET BIT(10) +#define MCP251XFD_REG_TEFCON_UINC BIT(8) +#define MCP251XFD_REG_TEFCON_TEFTSEN BIT(5) +#define MCP251XFD_REG_TEFCON_TEFOVIE BIT(3) +#define MCP251XFD_REG_TEFCON_TEFFIE BIT(2) +#define MCP251XFD_REG_TEFCON_TEFHIE BIT(1) +#define MCP251XFD_REG_TEFCON_TEFNEIE BIT(0) + +#define MCP251XFD_REG_TEFSTA 0x44 +#define MCP251XFD_REG_TEFSTA_TEFOVIF BIT(3) +#define MCP251XFD_REG_TEFSTA_TEFFIF BIT(2) +#define MCP251XFD_REG_TEFSTA_TEFHIF BIT(1) +#define MCP251XFD_REG_TEFSTA_TEFNEIF BIT(0) + +#define MCP251XFD_REG_TEFUA 0x48 + +#define MCP251XFD_REG_TXQCON 0x50 +#define MCP251XFD_REG_TXQCON_PLSIZE_MASK GENMASK(31, 29) +#define MCP251XFD_REG_TXQCON_PLSIZE_8 0 +#define MCP251XFD_REG_TXQCON_PLSIZE_12 1 +#define MCP251XFD_REG_TXQCON_PLSIZE_16 2 +#define MCP251XFD_REG_TXQCON_PLSIZE_20 3 +#define MCP251XFD_REG_TXQCON_PLSIZE_24 4 +#define MCP251XFD_REG_TXQCON_PLSIZE_32 5 +#define MCP251XFD_REG_TXQCON_PLSIZE_48 6 +#define MCP251XFD_REG_TXQCON_PLSIZE_64 7 +#define MCP251XFD_REG_TXQCON_FSIZE_MASK GENMASK(28, 24) +#define MCP251XFD_REG_TXQCON_TXAT_UNLIMITED 3 +#define MCP251XFD_REG_TXQCON_TXAT_THREE_SHOT 1 +#define MCP251XFD_REG_TXQCON_TXAT_ONE_SHOT 0 +#define MCP251XFD_REG_TXQCON_TXAT_MASK GENMASK(22, 21) +#define MCP251XFD_REG_TXQCON_TXPRI_MASK GENMASK(20, 16) +#define MCP251XFD_REG_TXQCON_FRESET BIT(10) +#define MCP251XFD_REG_TXQCON_TXREQ BIT(9) +#define MCP251XFD_REG_TXQCON_UINC BIT(8) +#define MCP251XFD_REG_TXQCON_TXEN BIT(7) +#define MCP251XFD_REG_TXQCON_TXATIE BIT(4) +#define MCP251XFD_REG_TXQCON_TXQEIE BIT(2) +#define MCP251XFD_REG_TXQCON_TXQNIE BIT(0) + +#define MCP251XFD_REG_TXQSTA 0x54 +#define MCP251XFD_REG_TXQSTA_TXQCI_MASK GENMASK(12, 8) +#define MCP251XFD_REG_TXQSTA_TXABT BIT(7) +#define MCP251XFD_REG_TXQSTA_TXLARB BIT(6) +#define MCP251XFD_REG_TXQSTA_TXERR BIT(5) +#define MCP251XFD_REG_TXQSTA_TXATIF BIT(4) +#define MCP251XFD_REG_TXQSTA_TXQEIF BIT(2) +#define MCP251XFD_REG_TXQSTA_TXQNIF BIT(0) + +#define MCP251XFD_REG_TXQUA 0x58 + +#define MCP251XFD_REG_FIFOCON(x) (0x50 + 0xc * (x)) +#define MCP251XFD_REG_FIFOCON_PLSIZE_MASK GENMASK(31, 29) +#define MCP251XFD_REG_FIFOCON_PLSIZE_8 0 +#define MCP251XFD_REG_FIFOCON_PLSIZE_12 1 +#define MCP251XFD_REG_FIFOCON_PLSIZE_16 2 +#define MCP251XFD_REG_FIFOCON_PLSIZE_20 3 +#define MCP251XFD_REG_FIFOCON_PLSIZE_24 4 +#define MCP251XFD_REG_FIFOCON_PLSIZE_32 5 +#define MCP251XFD_REG_FIFOCON_PLSIZE_48 6 +#define MCP251XFD_REG_FIFOCON_PLSIZE_64 7 +#define MCP251XFD_REG_FIFOCON_FSIZE_MASK GENMASK(28, 24) +#define MCP251XFD_REG_FIFOCON_TXAT_MASK GENMASK(22, 21) +#define MCP251XFD_REG_FIFOCON_TXAT_ONE_SHOT 0 +#define MCP251XFD_REG_FIFOCON_TXAT_THREE_SHOT 1 +#define MCP251XFD_REG_FIFOCON_TXAT_UNLIMITED 3 +#define MCP251XFD_REG_FIFOCON_TXPRI_MASK GENMASK(20, 16) +#define MCP251XFD_REG_FIFOCON_FRESET BIT(10) +#define MCP251XFD_REG_FIFOCON_TXREQ BIT(9) +#define MCP251XFD_REG_FIFOCON_UINC BIT(8) +#define MCP251XFD_REG_FIFOCON_TXEN BIT(7) +#define MCP251XFD_REG_FIFOCON_RTREN BIT(6) +#define MCP251XFD_REG_FIFOCON_RXTSEN BIT(5) +#define MCP251XFD_REG_FIFOCON_TXATIE BIT(4) +#define MCP251XFD_REG_FIFOCON_RXOVIE BIT(3) +#define MCP251XFD_REG_FIFOCON_TFERFFIE BIT(2) +#define MCP251XFD_REG_FIFOCON_TFHRFHIE BIT(1) +#define MCP251XFD_REG_FIFOCON_TFNRFNIE BIT(0) + +#define MCP251XFD_REG_FIFOSTA(x) (0x54 + 0xc * (x)) +#define MCP251XFD_REG_FIFOSTA_FIFOCI_MASK GENMASK(12, 8) +#define MCP251XFD_REG_FIFOSTA_TXABT BIT(7) +#define MCP251XFD_REG_FIFOSTA_TXLARB BIT(6) +#define MCP251XFD_REG_FIFOSTA_TXERR BIT(5) +#define MCP251XFD_REG_FIFOSTA_TXATIF BIT(4) +#define MCP251XFD_REG_FIFOSTA_RXOVIF BIT(3) +#define MCP251XFD_REG_FIFOSTA_TFERFFIF BIT(2) +#define MCP251XFD_REG_FIFOSTA_TFHRFHIF BIT(1) +#define MCP251XFD_REG_FIFOSTA_TFNRFNIF BIT(0) + +#define MCP251XFD_REG_FIFOUA(x) (0x58 + 0xc * (x)) + +#define MCP251XFD_REG_FLTCON(x) (0x1d0 + 0x4 * (x)) +#define MCP251XFD_REG_FLTCON_FLTEN3 BIT(31) +#define MCP251XFD_REG_FLTCON_F3BP_MASK GENMASK(28, 24) +#define MCP251XFD_REG_FLTCON_FLTEN2 BIT(23) +#define MCP251XFD_REG_FLTCON_F2BP_MASK GENMASK(20, 16) +#define MCP251XFD_REG_FLTCON_FLTEN1 BIT(15) +#define MCP251XFD_REG_FLTCON_F1BP_MASK GENMASK(12, 8) +#define MCP251XFD_REG_FLTCON_FLTEN0 BIT(7) +#define MCP251XFD_REG_FLTCON_F0BP_MASK GENMASK(4, 0) +#define MCP251XFD_REG_FLTCON_FLTEN(x) (BIT(7) << 8 * ((x) & 0x3)) +#define MCP251XFD_REG_FLTCON_FLT_MASK(x) (GENMASK(7, 0) << (8 * ((x) & 0x3))) +#define MCP251XFD_REG_FLTCON_FBP(x, fifo) ((fifo) << 8 * ((x) & 0x3)) + +#define MCP251XFD_REG_FLTOBJ(x) (0x1f0 + 0x8 * (x)) +#define MCP251XFD_REG_FLTOBJ_EXIDE BIT(30) +#define MCP251XFD_REG_FLTOBJ_SID11 BIT(29) +#define MCP251XFD_REG_FLTOBJ_EID_MASK GENMASK(28, 11) +#define MCP251XFD_REG_FLTOBJ_SID_MASK GENMASK(10, 0) + +#define MCP251XFD_REG_FLTMASK(x) (0x1f4 + 0x8 * (x)) +#define MCP251XFD_REG_MASK_MIDE BIT(30) +#define MCP251XFD_REG_MASK_MSID11 BIT(29) +#define MCP251XFD_REG_MASK_MEID_MASK GENMASK(28, 11) +#define MCP251XFD_REG_MASK_MSID_MASK GENMASK(10, 0) + +/* RAM */ +#define MCP251XFD_RAM_START 0x400 +#define MCP251XFD_RAM_SIZE SZ_2K + +/* Message Object */ +#define MCP251XFD_OBJ_ID_SID11 BIT(29) +#define MCP251XFD_OBJ_ID_EID_MASK GENMASK(28, 11) +#define MCP251XFD_OBJ_ID_SID_MASK GENMASK(10, 0) +#define MCP251XFD_OBJ_FLAGS_SEQ_MCP2518FD_MASK GENMASK(31, 9) +#define MCP251XFD_OBJ_FLAGS_SEQ_MCP2517FD_MASK GENMASK(15, 9) +#define MCP251XFD_OBJ_FLAGS_SEQ_MASK MCP251XFD_OBJ_FLAGS_SEQ_MCP2518FD_MASK +#define MCP251XFD_OBJ_FLAGS_ESI BIT(8) +#define MCP251XFD_OBJ_FLAGS_FDF BIT(7) +#define MCP251XFD_OBJ_FLAGS_BRS BIT(6) +#define MCP251XFD_OBJ_FLAGS_RTR BIT(5) +#define MCP251XFD_OBJ_FLAGS_IDE BIT(4) +#define MCP251XFD_OBJ_FLAGS_DLC_MASK GENMASK(3, 0) + +#define MCP251XFD_REG_FRAME_EFF_SID_MASK GENMASK(28, 18) +#define MCP251XFD_REG_FRAME_EFF_EID_MASK GENMASK(17, 0) + +/* MCP2517/18FD SFR */ +#define MCP251XFD_REG_OSC 0xe00 +#define MCP251XFD_REG_OSC_SCLKRDY BIT(12) +#define MCP251XFD_REG_OSC_OSCRDY BIT(10) +#define MCP251XFD_REG_OSC_PLLRDY BIT(8) +#define MCP251XFD_REG_OSC_CLKODIV_10 3 +#define MCP251XFD_REG_OSC_CLKODIV_4 2 +#define MCP251XFD_REG_OSC_CLKODIV_2 1 +#define MCP251XFD_REG_OSC_CLKODIV_1 0 +#define MCP251XFD_REG_OSC_CLKODIV_MASK GENMASK(6, 5) +#define MCP251XFD_REG_OSC_SCLKDIV BIT(4) +#define MCP251XFD_REG_OSC_LPMEN BIT(3) /* MCP2518FD only */ +#define MCP251XFD_REG_OSC_OSCDIS BIT(2) +#define MCP251XFD_REG_OSC_PLLEN BIT(0) + +#define MCP251XFD_REG_IOCON 0xe04 +#define MCP251XFD_REG_IOCON_INTOD BIT(30) +#define MCP251XFD_REG_IOCON_SOF BIT(29) +#define MCP251XFD_REG_IOCON_TXCANOD BIT(28) +#define MCP251XFD_REG_IOCON_PM1 BIT(25) +#define MCP251XFD_REG_IOCON_PM0 BIT(24) +#define MCP251XFD_REG_IOCON_GPIO1 BIT(17) +#define MCP251XFD_REG_IOCON_GPIO0 BIT(16) +#define MCP251XFD_REG_IOCON_LAT1 BIT(9) +#define MCP251XFD_REG_IOCON_LAT0 BIT(8) +#define MCP251XFD_REG_IOCON_XSTBYEN BIT(6) +#define MCP251XFD_REG_IOCON_TRIS1 BIT(1) +#define MCP251XFD_REG_IOCON_TRIS0 BIT(0) + +#define MCP251XFD_REG_CRC 0xe08 +#define MCP251XFD_REG_CRC_FERRIE BIT(25) +#define MCP251XFD_REG_CRC_CRCERRIE BIT(24) +#define MCP251XFD_REG_CRC_FERRIF BIT(17) +#define MCP251XFD_REG_CRC_CRCERRIF BIT(16) +#define MCP251XFD_REG_CRC_IF_MASK GENMASK(17, 16) +#define MCP251XFD_REG_CRC_MASK GENMASK(15, 0) + +#define MCP251XFD_REG_ECCCON 0xe0c +#define MCP251XFD_REG_ECCCON_PARITY_MASK GENMASK(14, 8) +#define MCP251XFD_REG_ECCCON_DEDIE BIT(2) +#define MCP251XFD_REG_ECCCON_SECIE BIT(1) +#define MCP251XFD_REG_ECCCON_ECCEN BIT(0) + +#define MCP251XFD_REG_ECCSTAT 0xe10 +#define MCP251XFD_REG_ECCSTAT_ERRADDR_MASK GENMASK(27, 16) +#define MCP251XFD_REG_ECCSTAT_IF_MASK GENMASK(2, 1) +#define MCP251XFD_REG_ECCSTAT_DEDIF BIT(2) +#define MCP251XFD_REG_ECCSTAT_SECIF BIT(1) + +#define MCP251XFD_REG_DEVID 0xe14 /* MCP2518FD only */ +#define MCP251XFD_REG_DEVID_ID_MASK GENMASK(7, 4) +#define MCP251XFD_REG_DEVID_REV_MASK GENMASK(3, 0) + +/* SPI commands */ +#define MCP251XFD_SPI_INSTRUCTION_RESET 0x0000 +#define MCP251XFD_SPI_INSTRUCTION_WRITE 0x2000 +#define MCP251XFD_SPI_INSTRUCTION_READ 0x3000 +#define MCP251XFD_SPI_INSTRUCTION_WRITE_CRC 0xa000 +#define MCP251XFD_SPI_INSTRUCTION_READ_CRC 0xb000 +#define MCP251XFD_SPI_INSTRUCTION_WRITE_CRC_SAFE 0xc000 +#define MCP251XFD_SPI_ADDRESS_MASK GENMASK(11, 0) + +#define MCP251XFD_SYSCLOCK_HZ_MAX 40000000 +#define MCP251XFD_SYSCLOCK_HZ_MIN 1000000 +#define MCP251XFD_SPICLOCK_HZ_MAX 20000000 +#define MCP251XFD_TIMESTAMP_WORK_DELAY_SEC 45 +static_assert(MCP251XFD_TIMESTAMP_WORK_DELAY_SEC < + CYCLECOUNTER_MASK(32) / MCP251XFD_SYSCLOCK_HZ_MAX / 2); +#define MCP251XFD_OSC_PLL_MULTIPLIER 10 +#define MCP251XFD_OSC_STAB_SLEEP_US (3 * USEC_PER_MSEC) +#define MCP251XFD_OSC_STAB_TIMEOUT_US (10 * MCP251XFD_OSC_STAB_SLEEP_US) +#define MCP251XFD_POLL_SLEEP_US (10) +#define MCP251XFD_POLL_TIMEOUT_US (USEC_PER_MSEC) +#define MCP251XFD_FRAME_LEN_MAX_BITS (736) + +/* Misc */ +#define MCP251XFD_NAPI_WEIGHT 32 +#define MCP251XFD_SOFTRESET_RETRIES_MAX 3 +#define MCP251XFD_READ_CRC_RETRIES_MAX 3 +#define MCP251XFD_ECC_CNT_MAX 2 +#define MCP251XFD_SANITIZE_SPI 1 +#define MCP251XFD_SANITIZE_CAN 1 + +/* FIFO and Ring */ +#define MCP251XFD_FIFO_TEF_NUM 1U +#define MCP251XFD_FIFO_RX_NUM 3U +#define MCP251XFD_FIFO_TX_NUM 1U + +#define MCP251XFD_FIFO_DEPTH 32U + +#define MCP251XFD_RX_OBJ_NUM_MIN 16U +#define MCP251XFD_RX_OBJ_NUM_MAX (MCP251XFD_FIFO_RX_NUM * MCP251XFD_FIFO_DEPTH) +#define MCP251XFD_RX_FIFO_DEPTH_MIN 4U +#define MCP251XFD_RX_FIFO_DEPTH_COALESCE_MIN 8U + +#define MCP251XFD_TX_OBJ_NUM_MIN 2U +#define MCP251XFD_TX_OBJ_NUM_MAX 16U +#define MCP251XFD_TX_OBJ_NUM_CAN_DEFAULT 8U +#define MCP251XFD_TX_OBJ_NUM_CANFD_DEFAULT 4U +#define MCP251XFD_TX_FIFO_DEPTH_MIN 2U +#define MCP251XFD_TX_FIFO_DEPTH_COALESCE_MIN 2U + +static_assert(MCP251XFD_FIFO_TEF_NUM == 1U); +static_assert(MCP251XFD_FIFO_TEF_NUM == MCP251XFD_FIFO_TX_NUM); +static_assert(MCP251XFD_FIFO_RX_NUM <= 4U); + +/* Silence TX MAB overflow warnings */ +#define MCP251XFD_QUIRK_MAB_NO_WARN BIT(0) +/* Use CRC to access registers */ +#define MCP251XFD_QUIRK_CRC_REG BIT(1) +/* Use CRC to access RX/TEF-RAM */ +#define MCP251XFD_QUIRK_CRC_RX BIT(2) +/* Use CRC to access TX-RAM */ +#define MCP251XFD_QUIRK_CRC_TX BIT(3) +/* Enable ECC for RAM */ +#define MCP251XFD_QUIRK_ECC BIT(4) +/* Use Half Duplex SPI transfers */ +#define MCP251XFD_QUIRK_HALF_DUPLEX BIT(5) + +struct mcp251xfd_hw_tef_obj { + u32 id; + u32 flags; + u32 ts; +}; + +/* The tx_obj_raw version is used in spi async, i.e. without + * regmap. We have to take care of endianness ourselves. + */ +struct __packed mcp251xfd_hw_tx_obj_raw { + __le32 id; + __le32 flags; + u8 data[sizeof_field(struct canfd_frame, data)]; +}; + +struct mcp251xfd_hw_tx_obj_can { + u32 id; + u32 flags; + u8 data[sizeof_field(struct can_frame, data)]; +}; + +struct mcp251xfd_hw_tx_obj_canfd { + u32 id; + u32 flags; + u8 data[sizeof_field(struct canfd_frame, data)]; +}; + +struct mcp251xfd_hw_rx_obj_can { + u32 id; + u32 flags; + u32 ts; + u8 data[sizeof_field(struct can_frame, data)]; +}; + +struct mcp251xfd_hw_rx_obj_canfd { + u32 id; + u32 flags; + u32 ts; + u8 data[sizeof_field(struct canfd_frame, data)]; +}; + +struct __packed mcp251xfd_buf_cmd { + __be16 cmd; +}; + +struct __packed mcp251xfd_buf_cmd_crc { + __be16 cmd; + u8 len; +}; + +union mcp251xfd_tx_obj_load_buf { + struct __packed { + struct mcp251xfd_buf_cmd cmd; + struct mcp251xfd_hw_tx_obj_raw hw_tx_obj; + } nocrc; + struct __packed { + struct mcp251xfd_buf_cmd_crc cmd; + struct mcp251xfd_hw_tx_obj_raw hw_tx_obj; + __be16 crc; + } crc; +} ____cacheline_aligned; + +union mcp251xfd_write_reg_buf { + struct __packed { + struct mcp251xfd_buf_cmd cmd; + u8 data[4]; + } nocrc; + struct __packed { + struct mcp251xfd_buf_cmd_crc cmd; + u8 data[4]; + __be16 crc; + } crc; + struct __packed { + struct mcp251xfd_buf_cmd cmd; + u8 data[1]; + __be16 crc; + } safe; +} ____cacheline_aligned; + +struct mcp251xfd_tx_obj { + struct spi_message msg; + struct spi_transfer xfer[2]; + union mcp251xfd_tx_obj_load_buf buf; +}; + +struct mcp251xfd_tef_ring { + unsigned int head; + unsigned int tail; + + /* u8 obj_num equals tx_ring->obj_num */ + /* u8 obj_size equals sizeof(struct mcp251xfd_hw_tef_obj) */ + + union mcp251xfd_write_reg_buf irq_enable_buf; + struct spi_transfer irq_enable_xfer; + struct spi_message irq_enable_msg; + + union mcp251xfd_write_reg_buf uinc_buf; + union mcp251xfd_write_reg_buf uinc_irq_disable_buf; + struct spi_transfer uinc_xfer[MCP251XFD_TX_OBJ_NUM_MAX]; +}; + +struct mcp251xfd_tx_ring { + unsigned int head; + unsigned int tail; + + u16 base; + u8 nr; + u8 fifo_nr; + u8 obj_num; + u8 obj_size; + + struct mcp251xfd_tx_obj obj[MCP251XFD_TX_OBJ_NUM_MAX]; + union mcp251xfd_write_reg_buf rts_buf; +}; + +struct mcp251xfd_rx_ring { + unsigned int head; + unsigned int tail; + + u16 base; + u8 nr; + u8 fifo_nr; + u8 obj_num; + u8 obj_size; + + union mcp251xfd_write_reg_buf irq_enable_buf; + struct spi_transfer irq_enable_xfer; + struct spi_message irq_enable_msg; + + union mcp251xfd_write_reg_buf uinc_buf; + union mcp251xfd_write_reg_buf uinc_irq_disable_buf; + struct spi_transfer uinc_xfer[MCP251XFD_FIFO_DEPTH]; + struct mcp251xfd_hw_rx_obj_canfd obj[]; +}; + +struct __packed mcp251xfd_map_buf_nocrc { + struct mcp251xfd_buf_cmd cmd; + u8 data[256]; +} ____cacheline_aligned; + +struct __packed mcp251xfd_map_buf_crc { + struct mcp251xfd_buf_cmd_crc cmd; + u8 data[256 - 4]; + __be16 crc; +} ____cacheline_aligned; + +struct mcp251xfd_ecc { + u32 ecc_stat; + int cnt; +}; + +struct mcp251xfd_regs_status { + u32 intf; + u32 rxif; +}; + +enum mcp251xfd_model { + MCP251XFD_MODEL_MCP2517FD = 0x2517, + MCP251XFD_MODEL_MCP2518FD = 0x2518, + MCP251XFD_MODEL_MCP251863 = 0x251863, + MCP251XFD_MODEL_MCP251XFD = 0xffffffff, /* autodetect model */ +}; + +struct mcp251xfd_devtype_data { + enum mcp251xfd_model model; + u32 quirks; +}; + +enum mcp251xfd_flags { + MCP251XFD_FLAGS_DOWN, + MCP251XFD_FLAGS_FD_MODE, + + __MCP251XFD_FLAGS_SIZE__ +}; + +struct mcp251xfd_priv { + struct can_priv can; + struct can_rx_offload offload; + struct net_device *ndev; + + struct regmap *map_reg; /* register access */ + struct regmap *map_rx; /* RX/TEF RAM access */ + + struct regmap *map_nocrc; + struct mcp251xfd_map_buf_nocrc *map_buf_nocrc_rx; + struct mcp251xfd_map_buf_nocrc *map_buf_nocrc_tx; + + struct regmap *map_crc; + struct mcp251xfd_map_buf_crc *map_buf_crc_rx; + struct mcp251xfd_map_buf_crc *map_buf_crc_tx; + + struct spi_device *spi; + u32 spi_max_speed_hz_orig; + u32 spi_max_speed_hz_fast; + u32 spi_max_speed_hz_slow; + + struct mcp251xfd_tef_ring tef[MCP251XFD_FIFO_TEF_NUM]; + struct mcp251xfd_rx_ring *rx[MCP251XFD_FIFO_RX_NUM]; + struct mcp251xfd_tx_ring tx[MCP251XFD_FIFO_TX_NUM]; + + DECLARE_BITMAP(flags, __MCP251XFD_FLAGS_SIZE__); + + u8 rx_ring_num; + u8 rx_obj_num; + u8 rx_obj_num_coalesce_irq; + u8 tx_obj_num_coalesce_irq; + + u32 rx_coalesce_usecs_irq; + u32 tx_coalesce_usecs_irq; + struct hrtimer rx_irq_timer; + struct hrtimer tx_irq_timer; + + struct mcp251xfd_ecc ecc; + struct mcp251xfd_regs_status regs_status; + + struct cyclecounter cc; + struct timecounter tc; + struct delayed_work timestamp; + + struct gpio_desc *rx_int; + struct clk *clk; + bool pll_enable; + struct regulator *reg_vdd; + struct regulator *reg_xceiver; + + struct mcp251xfd_devtype_data devtype_data; + struct can_berr_counter bec; +}; + +#define MCP251XFD_IS(_model) \ +static inline bool \ +mcp251xfd_is_##_model(const struct mcp251xfd_priv *priv) \ +{ \ + return priv->devtype_data.model == MCP251XFD_MODEL_MCP##_model; \ +} + +MCP251XFD_IS(2517FD); +MCP251XFD_IS(2518FD); +MCP251XFD_IS(251863); +MCP251XFD_IS(251XFD); + +static inline bool mcp251xfd_is_fd_mode(const struct mcp251xfd_priv *priv) +{ + /* listen-only mode works like FD mode */ + return priv->can.ctrlmode & (CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_FD); +} + +static inline u8 mcp251xfd_first_byte_set(u32 mask) +{ + return (mask & 0x0000ffff) ? + ((mask & 0x000000ff) ? 0 : 1) : + ((mask & 0x00ff0000) ? 2 : 3); +} + +static inline u8 mcp251xfd_last_byte_set(u32 mask) +{ + return (mask & 0xffff0000) ? + ((mask & 0xff000000) ? 3 : 2) : + ((mask & 0x0000ff00) ? 1 : 0); +} + +static inline __be16 mcp251xfd_cmd_reset(void) +{ + return cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_RESET); +} + +static inline void +mcp251xfd_spi_cmd_read_nocrc(struct mcp251xfd_buf_cmd *cmd, u16 addr) +{ + cmd->cmd = cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_READ | addr); +} + +static inline void +mcp251xfd_spi_cmd_write_nocrc(struct mcp251xfd_buf_cmd *cmd, u16 addr) +{ + cmd->cmd = cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_WRITE | addr); +} + +static inline bool mcp251xfd_reg_in_ram(unsigned int reg) +{ + static const struct regmap_range range = + regmap_reg_range(MCP251XFD_RAM_START, + MCP251XFD_RAM_START + MCP251XFD_RAM_SIZE - 4); + + return regmap_reg_in_range(reg, &range); +} + +static inline void +__mcp251xfd_spi_cmd_crc_set_len(struct mcp251xfd_buf_cmd_crc *cmd, + u16 len, bool in_ram) +{ + /* Number of u32 for RAM access, number of u8 otherwise. */ + if (in_ram) + cmd->len = len >> 2; + else + cmd->len = len; +} + +static inline void +mcp251xfd_spi_cmd_crc_set_len_in_ram(struct mcp251xfd_buf_cmd_crc *cmd, u16 len) +{ + __mcp251xfd_spi_cmd_crc_set_len(cmd, len, true); +} + +static inline void +mcp251xfd_spi_cmd_crc_set_len_in_reg(struct mcp251xfd_buf_cmd_crc *cmd, u16 len) +{ + __mcp251xfd_spi_cmd_crc_set_len(cmd, len, false); +} + +static inline void +mcp251xfd_spi_cmd_read_crc_set_addr(struct mcp251xfd_buf_cmd_crc *cmd, u16 addr) +{ + cmd->cmd = cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_READ_CRC | addr); +} + +static inline void +mcp251xfd_spi_cmd_read_crc(struct mcp251xfd_buf_cmd_crc *cmd, + u16 addr, u16 len) +{ + mcp251xfd_spi_cmd_read_crc_set_addr(cmd, addr); + __mcp251xfd_spi_cmd_crc_set_len(cmd, len, mcp251xfd_reg_in_ram(addr)); +} + +static inline void +mcp251xfd_spi_cmd_write_crc_set_addr(struct mcp251xfd_buf_cmd_crc *cmd, + u16 addr) +{ + cmd->cmd = cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_WRITE_CRC | addr); +} + +static inline void +mcp251xfd_spi_cmd_write_safe_set_addr(struct mcp251xfd_buf_cmd *cmd, + u16 addr) +{ + cmd->cmd = cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_WRITE_CRC_SAFE | addr); +} + +static inline void +mcp251xfd_spi_cmd_write_crc(struct mcp251xfd_buf_cmd_crc *cmd, + u16 addr, u16 len) +{ + mcp251xfd_spi_cmd_write_crc_set_addr(cmd, addr); + __mcp251xfd_spi_cmd_crc_set_len(cmd, len, mcp251xfd_reg_in_ram(addr)); +} + +static inline u8 * +mcp251xfd_spi_cmd_write(const struct mcp251xfd_priv *priv, + union mcp251xfd_write_reg_buf *write_reg_buf, + u16 addr, u8 len) +{ + u8 *data; + + if (priv->devtype_data.quirks & MCP251XFD_QUIRK_CRC_REG) { + if (len == 1) { + mcp251xfd_spi_cmd_write_safe_set_addr(&write_reg_buf->safe.cmd, + addr); + data = write_reg_buf->safe.data; + } else { + mcp251xfd_spi_cmd_write_crc_set_addr(&write_reg_buf->crc.cmd, + addr); + data = write_reg_buf->crc.data; + } + } else { + mcp251xfd_spi_cmd_write_nocrc(&write_reg_buf->nocrc.cmd, + addr); + data = write_reg_buf->nocrc.data; + } + + return data; +} + +static inline int mcp251xfd_get_timestamp(const struct mcp251xfd_priv *priv, + u32 *timestamp) +{ + return regmap_read(priv->map_reg, MCP251XFD_REG_TBC, timestamp); +} + +static inline u16 mcp251xfd_get_tef_obj_addr(u8 n) +{ + return MCP251XFD_RAM_START + + sizeof(struct mcp251xfd_hw_tef_obj) * n; +} + +static inline u16 +mcp251xfd_get_tx_obj_addr(const struct mcp251xfd_tx_ring *ring, u8 n) +{ + return ring->base + ring->obj_size * n; +} + +static inline u16 +mcp251xfd_get_rx_obj_addr(const struct mcp251xfd_rx_ring *ring, u8 n) +{ + return ring->base + ring->obj_size * n; +} + +static inline int +mcp251xfd_tx_tail_get_from_chip(const struct mcp251xfd_priv *priv, + u8 *tx_tail) +{ + u32 fifo_sta; + int err; + + err = regmap_read(priv->map_reg, + MCP251XFD_REG_FIFOSTA(priv->tx->fifo_nr), + &fifo_sta); + if (err) + return err; + + *tx_tail = FIELD_GET(MCP251XFD_REG_FIFOSTA_FIFOCI_MASK, fifo_sta); + + return 0; +} + +static inline u8 mcp251xfd_get_tef_head(const struct mcp251xfd_priv *priv) +{ + return priv->tef->head & (priv->tx->obj_num - 1); +} + +static inline u8 mcp251xfd_get_tef_tail(const struct mcp251xfd_priv *priv) +{ + return priv->tef->tail & (priv->tx->obj_num - 1); +} + +static inline u8 mcp251xfd_get_tef_len(const struct mcp251xfd_priv *priv) +{ + return priv->tef->head - priv->tef->tail; +} + +static inline u8 mcp251xfd_get_tef_linear_len(const struct mcp251xfd_priv *priv) +{ + u8 len; + + len = mcp251xfd_get_tef_len(priv); + + return min_t(u8, len, priv->tx->obj_num - mcp251xfd_get_tef_tail(priv)); +} + +static inline u8 mcp251xfd_get_tx_head(const struct mcp251xfd_tx_ring *ring) +{ + return ring->head & (ring->obj_num - 1); +} + +static inline u8 mcp251xfd_get_tx_tail(const struct mcp251xfd_tx_ring *ring) +{ + return ring->tail & (ring->obj_num - 1); +} + +static inline u8 mcp251xfd_get_tx_free(const struct mcp251xfd_tx_ring *ring) +{ + return ring->obj_num - (ring->head - ring->tail); +} + +static inline int +mcp251xfd_get_tx_nr_by_addr(const struct mcp251xfd_tx_ring *tx_ring, u8 *nr, + u16 addr) +{ + if (addr < mcp251xfd_get_tx_obj_addr(tx_ring, 0) || + addr >= mcp251xfd_get_tx_obj_addr(tx_ring, tx_ring->obj_num)) + return -ENOENT; + + *nr = (addr - mcp251xfd_get_tx_obj_addr(tx_ring, 0)) / + tx_ring->obj_size; + + return 0; +} + +static inline u8 mcp251xfd_get_rx_head(const struct mcp251xfd_rx_ring *ring) +{ + return ring->head & (ring->obj_num - 1); +} + +static inline u8 mcp251xfd_get_rx_tail(const struct mcp251xfd_rx_ring *ring) +{ + return ring->tail & (ring->obj_num - 1); +} + +static inline u8 mcp251xfd_get_rx_len(const struct mcp251xfd_rx_ring *ring) +{ + return ring->head - ring->tail; +} + +static inline u8 +mcp251xfd_get_rx_linear_len(const struct mcp251xfd_rx_ring *ring) +{ + u8 len; + + len = mcp251xfd_get_rx_len(ring); + + return min_t(u8, len, ring->obj_num - mcp251xfd_get_rx_tail(ring)); +} + +#define mcp251xfd_for_each_tx_obj(ring, _obj, n) \ + for ((n) = 0, (_obj) = &(ring)->obj[(n)]; \ + (n) < (ring)->obj_num; \ + (n)++, (_obj) = &(ring)->obj[(n)]) + +#define mcp251xfd_for_each_rx_ring(priv, ring, n) \ + for ((n) = 0, (ring) = *((priv)->rx + (n)); \ + (n) < (priv)->rx_ring_num; \ + (n)++, (ring) = *((priv)->rx + (n))) + +int mcp251xfd_chip_fifo_init(const struct mcp251xfd_priv *priv); +u16 mcp251xfd_crc16_compute2(const void *cmd, size_t cmd_size, + const void *data, size_t data_size); +u16 mcp251xfd_crc16_compute(const void *data, size_t data_size); +void mcp251xfd_ethtool_init(struct mcp251xfd_priv *priv); +int mcp251xfd_regmap_init(struct mcp251xfd_priv *priv); +extern const struct can_ram_config mcp251xfd_ram_config; +int mcp251xfd_ring_init(struct mcp251xfd_priv *priv); +void mcp251xfd_ring_free(struct mcp251xfd_priv *priv); +int mcp251xfd_ring_alloc(struct mcp251xfd_priv *priv); +int mcp251xfd_handle_rxif(struct mcp251xfd_priv *priv); +int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv); +void mcp251xfd_skb_set_timestamp(const struct mcp251xfd_priv *priv, + struct sk_buff *skb, u32 timestamp); +void mcp251xfd_timestamp_init(struct mcp251xfd_priv *priv); +void mcp251xfd_timestamp_stop(struct mcp251xfd_priv *priv); + +netdev_tx_t mcp251xfd_start_xmit(struct sk_buff *skb, + struct net_device *ndev); + +#if IS_ENABLED(CONFIG_DEV_COREDUMP) +void mcp251xfd_dump(const struct mcp251xfd_priv *priv); +#else +static inline void mcp251xfd_dump(const struct mcp251xfd_priv *priv) +{ +} +#endif + +#endif |