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

                /* If the ring parameters have been configured in
                 * CAN-CC mode, but and we are in CAN-FD mode now,
                 * they might be to big. Use the default CAN-FD values
                 * in this case.
                 */
                num_rx = ring->rx_pending;
                if (num_rx > layout->max_rx)
                        num_rx = layout->default_rx;

                num_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, num_rx);

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