blob: 848b8b2ecb5f1e1bb6e195907cfceb703e89e8c2 [file] [log] [blame]
// 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"
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);
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) {
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);
} else {
len += sizeof(write_reg_buf->nocrc.cmd);
}
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 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;
}
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 increment RX tail pointer */
addr = MCP251XFD_REG_FIFOCON(rx_ring->fifo_nr);
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;
}
}
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);
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) {
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: %4u 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;
}
}
int mcp251xfd_ring_alloc(struct mcp251xfd_priv *priv)
{
struct mcp251xfd_tx_ring *tx_ring;
struct mcp251xfd_rx_ring *rx_ring;
int tef_obj_size, tx_obj_size, rx_obj_size;
int tx_obj_num;
int ram_free, i;
tef_obj_size = sizeof(struct mcp251xfd_hw_tef_obj);
if (mcp251xfd_is_fd_mode(priv)) {
tx_obj_num = MCP251XFD_TX_OBJ_NUM_CANFD;
tx_obj_size = sizeof(struct mcp251xfd_hw_tx_obj_canfd);
rx_obj_size = sizeof(struct mcp251xfd_hw_rx_obj_canfd);
} else {
tx_obj_num = MCP251XFD_TX_OBJ_NUM_CAN;
tx_obj_size = sizeof(struct mcp251xfd_hw_tx_obj_can);
rx_obj_size = sizeof(struct mcp251xfd_hw_rx_obj_can);
}
tx_ring = priv->tx;
tx_ring->obj_num = tx_obj_num;
tx_ring->obj_size = tx_obj_size;
ram_free = MCP251XFD_RAM_SIZE - tx_obj_num *
(tef_obj_size + tx_obj_size);
for (i = 0;
i < ARRAY_SIZE(priv->rx) && ram_free >= rx_obj_size;
i++) {
int rx_obj_num;
rx_obj_num = ram_free / rx_obj_size;
rx_obj_num = min(1 << (fls(rx_obj_num) - 1),
MCP251XFD_RX_OBJ_NUM_MAX);
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;
ram_free -= rx_ring->obj_num * rx_ring->obj_size;
}
priv->rx_ring_num = i;
return 0;
}