1 // SPDX-License-Identifier: GPL-2.0
2 //
3 // mcp251xfd - Microchip MCP251xFD Family CAN controller driver
4 //
5 // Copyright (c) 2021, 2022 Pengutronix,
6 //               Marc Kleine-Budde <kernel@pengutronix.de>
7 //
8 
9 #include "mcp251xfd-ram.h"
10 
11 static inline u8 can_ram_clamp(const struct can_ram_config *config,
12 			       const struct can_ram_obj_config *obj,
13 			       u8 val)
14 {
15 	u8 max;
16 
17 	max = min_t(u8, obj->max, obj->fifo_num * config->fifo_depth);
18 	return clamp(val, obj->min, max);
19 }
20 
21 static u8
22 can_ram_rounddown_pow_of_two(const struct can_ram_config *config,
23 			     const struct can_ram_obj_config *obj, u8 val)
24 {
25 	u8 fifo_num = obj->fifo_num;
26 	u8 ret = 0, i;
27 
28 	val = can_ram_clamp(config, obj, val);
29 
30 	for (i = 0; i < fifo_num && val; i++) {
31 		u8 n;
32 
33 		n = min_t(u8, rounddown_pow_of_two(val),
34 			  config->fifo_depth);
35 
36 		/* skip small FIFOs */
37 		if (n < obj->fifo_depth_min)
38 			return ret;
39 
40 		ret += n;
41 		val -= n;
42 	}
43 
44 	return ret;
45 }
46 
47 void can_ram_get_layout(struct can_ram_layout *layout,
48 			const struct can_ram_config *config,
49 			const struct ethtool_ringparam *ring,
50 			const bool fd_mode)
51 {
52 	u8 num_rx, num_tx;
53 	u16 ram_free;
54 
55 	/* default CAN */
56 
57 	num_tx = config->tx.def[fd_mode];
58 	num_tx = can_ram_rounddown_pow_of_two(config, &config->tx, num_tx);
59 
60 	ram_free = config->size;
61 	ram_free -= config->tx.size[fd_mode] * num_tx;
62 
63 	num_rx = ram_free / config->rx.size[fd_mode];
64 
65 	layout->default_rx = can_ram_rounddown_pow_of_two(config, &config->rx, num_rx);
66 	layout->default_tx = num_tx;
67 
68 	/* MAX CAN */
69 
70 	ram_free = config->size;
71 	ram_free -= config->tx.size[fd_mode] * config->tx.min;
72 	num_rx = ram_free / config->rx.size[fd_mode];
73 
74 	ram_free = config->size;
75 	ram_free -= config->rx.size[fd_mode] * config->rx.min;
76 	num_tx = ram_free / config->tx.size[fd_mode];
77 
78 	layout->max_rx = can_ram_rounddown_pow_of_two(config, &config->rx, num_rx);
79 	layout->max_tx = can_ram_rounddown_pow_of_two(config, &config->tx, num_tx);
80 
81 	/* cur CAN */
82 
83 	if (ring) {
84 		num_rx = can_ram_rounddown_pow_of_two(config, &config->rx, ring->rx_pending);
85 
86 		ram_free = config->size - config->rx.size[fd_mode] * num_rx;
87 		num_tx = ram_free / config->tx.size[fd_mode];
88 		num_tx = min_t(u8, ring->tx_pending, num_tx);
89 		num_tx = can_ram_rounddown_pow_of_two(config, &config->tx, num_tx);
90 
91 		layout->cur_rx = num_rx;
92 		layout->cur_tx = num_tx;
93 	} else {
94 		layout->cur_rx = layout->default_rx;
95 		layout->cur_tx = layout->default_tx;
96 	}
97 }
98