1a1439a5aSMarc Kleine-Budde // SPDX-License-Identifier: GPL-2.0
2a1439a5aSMarc Kleine-Budde //
3a1439a5aSMarc Kleine-Budde // mcp251xfd - Microchip MCP251xFD Family CAN controller driver
4a1439a5aSMarc Kleine-Budde //
5a1439a5aSMarc Kleine-Budde // Copyright (c) 2021, 2022 Pengutronix,
6a1439a5aSMarc Kleine-Budde //               Marc Kleine-Budde <kernel@pengutronix.de>
7a1439a5aSMarc Kleine-Budde //
8a1439a5aSMarc Kleine-Budde 
9a1439a5aSMarc Kleine-Budde #include "mcp251xfd-ram.h"
10a1439a5aSMarc Kleine-Budde 
can_ram_clamp(const struct can_ram_config * config,const struct can_ram_obj_config * obj,u8 val)11a1439a5aSMarc Kleine-Budde static inline u8 can_ram_clamp(const struct can_ram_config *config,
12a1439a5aSMarc Kleine-Budde 			       const struct can_ram_obj_config *obj,
13a1439a5aSMarc Kleine-Budde 			       u8 val)
14a1439a5aSMarc Kleine-Budde {
15a1439a5aSMarc Kleine-Budde 	u8 max;
16a1439a5aSMarc Kleine-Budde 
17a1439a5aSMarc Kleine-Budde 	max = min_t(u8, obj->max, obj->fifo_num * config->fifo_depth);
18a1439a5aSMarc Kleine-Budde 	return clamp(val, obj->min, max);
19a1439a5aSMarc Kleine-Budde }
20a1439a5aSMarc Kleine-Budde 
21a1439a5aSMarc Kleine-Budde 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)22a1439a5aSMarc Kleine-Budde can_ram_rounddown_pow_of_two(const struct can_ram_config *config,
23*b8123d94SMarc Kleine-Budde 			     const struct can_ram_obj_config *obj,
24*b8123d94SMarc Kleine-Budde 			     const u8 coalesce, u8 val)
25a1439a5aSMarc Kleine-Budde {
26a1439a5aSMarc Kleine-Budde 	u8 fifo_num = obj->fifo_num;
27a1439a5aSMarc Kleine-Budde 	u8 ret = 0, i;
28a1439a5aSMarc Kleine-Budde 
29a1439a5aSMarc Kleine-Budde 	val = can_ram_clamp(config, obj, val);
30a1439a5aSMarc Kleine-Budde 
31*b8123d94SMarc Kleine-Budde 	if (coalesce) {
32*b8123d94SMarc Kleine-Budde 		/* Use 1st FIFO for coalescing, if requested.
33*b8123d94SMarc Kleine-Budde 		 *
34*b8123d94SMarc Kleine-Budde 		 * Either use complete FIFO (and FIFO Full IRQ) for
35*b8123d94SMarc Kleine-Budde 		 * coalescing or only half of FIFO (FIFO Half Full
36*b8123d94SMarc Kleine-Budde 		 * IRQ) and use remaining half for normal objects.
37*b8123d94SMarc Kleine-Budde 		 */
38*b8123d94SMarc Kleine-Budde 		ret = min_t(u8, coalesce * 2, config->fifo_depth);
39*b8123d94SMarc Kleine-Budde 		val -= ret;
40*b8123d94SMarc Kleine-Budde 		fifo_num--;
41*b8123d94SMarc Kleine-Budde 	}
42*b8123d94SMarc Kleine-Budde 
43a1439a5aSMarc Kleine-Budde 	for (i = 0; i < fifo_num && val; i++) {
44a1439a5aSMarc Kleine-Budde 		u8 n;
45a1439a5aSMarc Kleine-Budde 
46a1439a5aSMarc Kleine-Budde 		n = min_t(u8, rounddown_pow_of_two(val),
47a1439a5aSMarc Kleine-Budde 			  config->fifo_depth);
48a1439a5aSMarc Kleine-Budde 
49a1439a5aSMarc Kleine-Budde 		/* skip small FIFOs */
50a1439a5aSMarc Kleine-Budde 		if (n < obj->fifo_depth_min)
51a1439a5aSMarc Kleine-Budde 			return ret;
52a1439a5aSMarc Kleine-Budde 
53a1439a5aSMarc Kleine-Budde 		ret += n;
54a1439a5aSMarc Kleine-Budde 		val -= n;
55a1439a5aSMarc Kleine-Budde 	}
56a1439a5aSMarc Kleine-Budde 
57a1439a5aSMarc Kleine-Budde 	return ret;
58a1439a5aSMarc Kleine-Budde }
59a1439a5aSMarc Kleine-Budde 
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)60a1439a5aSMarc Kleine-Budde void can_ram_get_layout(struct can_ram_layout *layout,
61a1439a5aSMarc Kleine-Budde 			const struct can_ram_config *config,
62a1439a5aSMarc Kleine-Budde 			const struct ethtool_ringparam *ring,
63*b8123d94SMarc Kleine-Budde 			const struct ethtool_coalesce *ec,
64a1439a5aSMarc Kleine-Budde 			const bool fd_mode)
65a1439a5aSMarc Kleine-Budde {
66a1439a5aSMarc Kleine-Budde 	u8 num_rx, num_tx;
67a1439a5aSMarc Kleine-Budde 	u16 ram_free;
68a1439a5aSMarc Kleine-Budde 
69a1439a5aSMarc Kleine-Budde 	/* default CAN */
70a1439a5aSMarc Kleine-Budde 
71a1439a5aSMarc Kleine-Budde 	num_tx = config->tx.def[fd_mode];
72*b8123d94SMarc Kleine-Budde 	num_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx);
73a1439a5aSMarc Kleine-Budde 
74a1439a5aSMarc Kleine-Budde 	ram_free = config->size;
75a1439a5aSMarc Kleine-Budde 	ram_free -= config->tx.size[fd_mode] * num_tx;
76a1439a5aSMarc Kleine-Budde 
77a1439a5aSMarc Kleine-Budde 	num_rx = ram_free / config->rx.size[fd_mode];
78a1439a5aSMarc Kleine-Budde 
79*b8123d94SMarc Kleine-Budde 	layout->default_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, num_rx);
80a1439a5aSMarc Kleine-Budde 	layout->default_tx = num_tx;
81a1439a5aSMarc Kleine-Budde 
82a1439a5aSMarc Kleine-Budde 	/* MAX CAN */
83a1439a5aSMarc Kleine-Budde 
84a1439a5aSMarc Kleine-Budde 	ram_free = config->size;
85a1439a5aSMarc Kleine-Budde 	ram_free -= config->tx.size[fd_mode] * config->tx.min;
86a1439a5aSMarc Kleine-Budde 	num_rx = ram_free / config->rx.size[fd_mode];
87a1439a5aSMarc Kleine-Budde 
88a1439a5aSMarc Kleine-Budde 	ram_free = config->size;
89a1439a5aSMarc Kleine-Budde 	ram_free -= config->rx.size[fd_mode] * config->rx.min;
90a1439a5aSMarc Kleine-Budde 	num_tx = ram_free / config->tx.size[fd_mode];
91a1439a5aSMarc Kleine-Budde 
92*b8123d94SMarc Kleine-Budde 	layout->max_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, num_rx);
93*b8123d94SMarc Kleine-Budde 	layout->max_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx);
94a1439a5aSMarc Kleine-Budde 
95a1439a5aSMarc Kleine-Budde 	/* cur CAN */
96a1439a5aSMarc Kleine-Budde 
97a1439a5aSMarc Kleine-Budde 	if (ring) {
98*b8123d94SMarc Kleine-Budde 		u8 num_rx_coalesce = 0, num_tx_coalesce = 0;
99*b8123d94SMarc Kleine-Budde 
100*b8123d94SMarc Kleine-Budde 		num_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, ring->rx_pending);
101*b8123d94SMarc Kleine-Budde 
102*b8123d94SMarc Kleine-Budde 		/* The ethtool doc says:
103*b8123d94SMarc Kleine-Budde 		 * To disable coalescing, set usecs = 0 and max_frames = 1.
104*b8123d94SMarc Kleine-Budde 		 */
105*b8123d94SMarc Kleine-Budde 		if (ec && !(ec->rx_coalesce_usecs_irq == 0 &&
106*b8123d94SMarc Kleine-Budde 			    ec->rx_max_coalesced_frames_irq == 1)) {
107*b8123d94SMarc Kleine-Budde 			u8 max;
108*b8123d94SMarc Kleine-Budde 
109*b8123d94SMarc Kleine-Budde 			/* use only max half of available objects for coalescing */
110*b8123d94SMarc Kleine-Budde 			max = min_t(u8, num_rx / 2, config->fifo_depth);
111*b8123d94SMarc Kleine-Budde 			num_rx_coalesce = clamp(ec->rx_max_coalesced_frames_irq,
112*b8123d94SMarc Kleine-Budde 						(u32)config->rx.fifo_depth_coalesce_min,
113*b8123d94SMarc Kleine-Budde 						(u32)max);
114*b8123d94SMarc Kleine-Budde 			num_rx_coalesce = rounddown_pow_of_two(num_rx_coalesce);
115*b8123d94SMarc Kleine-Budde 
116*b8123d94SMarc Kleine-Budde 			num_rx = can_ram_rounddown_pow_of_two(config, &config->rx,
117*b8123d94SMarc Kleine-Budde 							      num_rx_coalesce, num_rx);
118*b8123d94SMarc Kleine-Budde 		}
119a1439a5aSMarc Kleine-Budde 
120a1439a5aSMarc Kleine-Budde 		ram_free = config->size - config->rx.size[fd_mode] * num_rx;
121a1439a5aSMarc Kleine-Budde 		num_tx = ram_free / config->tx.size[fd_mode];
122a1439a5aSMarc Kleine-Budde 		num_tx = min_t(u8, ring->tx_pending, num_tx);
123*b8123d94SMarc Kleine-Budde 		num_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx);
124*b8123d94SMarc Kleine-Budde 
125*b8123d94SMarc Kleine-Budde 		/* The ethtool doc says:
126*b8123d94SMarc Kleine-Budde 		 * To disable coalescing, set usecs = 0 and max_frames = 1.
127*b8123d94SMarc Kleine-Budde 		 */
128*b8123d94SMarc Kleine-Budde 		if (ec && !(ec->tx_coalesce_usecs_irq == 0 &&
129*b8123d94SMarc Kleine-Budde 			    ec->tx_max_coalesced_frames_irq == 1)) {
130*b8123d94SMarc Kleine-Budde 			u8 max;
131*b8123d94SMarc Kleine-Budde 
132*b8123d94SMarc Kleine-Budde 			/* use only max half of available objects for coalescing */
133*b8123d94SMarc Kleine-Budde 			max = min_t(u8, num_tx / 2, config->fifo_depth);
134*b8123d94SMarc Kleine-Budde 			num_tx_coalesce = clamp(ec->tx_max_coalesced_frames_irq,
135*b8123d94SMarc Kleine-Budde 						(u32)config->tx.fifo_depth_coalesce_min,
136*b8123d94SMarc Kleine-Budde 						(u32)max);
137*b8123d94SMarc Kleine-Budde 			num_tx_coalesce = rounddown_pow_of_two(num_tx_coalesce);
138*b8123d94SMarc Kleine-Budde 
139*b8123d94SMarc Kleine-Budde 			num_tx = can_ram_rounddown_pow_of_two(config, &config->tx,
140*b8123d94SMarc Kleine-Budde 							      num_tx_coalesce, num_tx);
141*b8123d94SMarc Kleine-Budde 		}
142a1439a5aSMarc Kleine-Budde 
143a1439a5aSMarc Kleine-Budde 		layout->cur_rx = num_rx;
144a1439a5aSMarc Kleine-Budde 		layout->cur_tx = num_tx;
145*b8123d94SMarc Kleine-Budde 		layout->rx_coalesce = num_rx_coalesce;
146*b8123d94SMarc Kleine-Budde 		layout->tx_coalesce = num_tx_coalesce;
147a1439a5aSMarc Kleine-Budde 	} else {
148a1439a5aSMarc Kleine-Budde 		layout->cur_rx = layout->default_rx;
149a1439a5aSMarc Kleine-Budde 		layout->cur_tx = layout->default_tx;
150*b8123d94SMarc Kleine-Budde 		layout->rx_coalesce = 0;
151*b8123d94SMarc Kleine-Budde 		layout->tx_coalesce = 0;
152a1439a5aSMarc Kleine-Budde 	}
153a1439a5aSMarc Kleine-Budde }
154