root/drivers/net/can/spi/mcp251xfd/mcp251xfd-chip-fifo.c
// 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;
}