// SPDX-License-Identifier: GPL-2.0 // // mcp251xfd - Microchip MCP251xFD Family CAN controller driver // // Copyright (c) 2021, 2022 Pengutronix, // Marc Kleine-Budde // #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; } }