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 
can_ram_clamp(const struct can_ram_config * config,const struct can_ram_obj_config * obj,u8 val)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
can_ram_rounddown_pow_of_two(const struct can_ram_config * config,const struct can_ram_obj_config * obj,const u8 coalesce,u8 val)22 can_ram_rounddown_pow_of_two(const struct can_ram_config *config,
23 			     const struct can_ram_obj_config *obj,
24 			     const u8 coalesce, u8 val)
25 {
26 	u8 fifo_num = obj->fifo_num;
27 	u8 ret = 0, i;
28 
29 	val = can_ram_clamp(config, obj, val);
30 
31 	if (coalesce) {
32 		/* Use 1st FIFO for coalescing, if requested.
33 		 *
34 		 * Either use complete FIFO (and FIFO Full IRQ) for
35 		 * coalescing or only half of FIFO (FIFO Half Full
36 		 * IRQ) and use remaining half for normal objects.
37 		 */
38 		ret = min_t(u8, coalesce * 2, config->fifo_depth);
39 		val -= ret;
40 		fifo_num--;
41 	}
42 
43 	for (i = 0; i < fifo_num && val; i++) {
44 		u8 n;
45 
46 		n = min_t(u8, rounddown_pow_of_two(val),
47 			  config->fifo_depth);
48 
49 		/* skip small FIFOs */
50 		if (n < obj->fifo_depth_min)
51 			return ret;
52 
53 		ret += n;
54 		val -= n;
55 	}
56 
57 	return ret;
58 }
59 
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)60 void can_ram_get_layout(struct can_ram_layout *layout,
61 			const struct can_ram_config *config,
62 			const struct ethtool_ringparam *ring,
63 			const struct ethtool_coalesce *ec,
64 			const bool fd_mode)
65 {
66 	u8 num_rx, num_tx;
67 	u16 ram_free;
68 
69 	/* default CAN */
70 
71 	num_tx = config->tx.def[fd_mode];
72 	num_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx);
73 
74 	ram_free = config->size;
75 	ram_free -= config->tx.size[fd_mode] * num_tx;
76 
77 	num_rx = ram_free / config->rx.size[fd_mode];
78 
79 	layout->default_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, num_rx);
80 	layout->default_tx = num_tx;
81 
82 	/* MAX CAN */
83 
84 	ram_free = config->size;
85 	ram_free -= config->tx.size[fd_mode] * config->tx.min;
86 	num_rx = ram_free / config->rx.size[fd_mode];
87 
88 	ram_free = config->size;
89 	ram_free -= config->rx.size[fd_mode] * config->rx.min;
90 	num_tx = ram_free / config->tx.size[fd_mode];
91 
92 	layout->max_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, num_rx);
93 	layout->max_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx);
94 
95 	/* cur CAN */
96 
97 	if (ring) {
98 		u8 num_rx_coalesce = 0, num_tx_coalesce = 0;
99 
100 		/* If the ring parameters have been configured in
101 		 * CAN-CC mode, but and we are in CAN-FD mode now,
102 		 * they might be to big. Use the default CAN-FD values
103 		 * in this case.
104 		 */
105 		num_rx = ring->rx_pending;
106 		if (num_rx > layout->max_rx)
107 			num_rx = layout->default_rx;
108 
109 		num_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, num_rx);
110 
111 		/* The ethtool doc says:
112 		 * To disable coalescing, set usecs = 0 and max_frames = 1.
113 		 */
114 		if (ec && !(ec->rx_coalesce_usecs_irq == 0 &&
115 			    ec->rx_max_coalesced_frames_irq == 1)) {
116 			u8 max;
117 
118 			/* use only max half of available objects for coalescing */
119 			max = min_t(u8, num_rx / 2, config->fifo_depth);
120 			num_rx_coalesce = clamp(ec->rx_max_coalesced_frames_irq,
121 						(u32)config->rx.fifo_depth_coalesce_min,
122 						(u32)max);
123 			num_rx_coalesce = rounddown_pow_of_two(num_rx_coalesce);
124 
125 			num_rx = can_ram_rounddown_pow_of_two(config, &config->rx,
126 							      num_rx_coalesce, num_rx);
127 		}
128 
129 		ram_free = config->size - config->rx.size[fd_mode] * num_rx;
130 		num_tx = ram_free / config->tx.size[fd_mode];
131 		num_tx = min_t(u8, ring->tx_pending, num_tx);
132 		num_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx);
133 
134 		/* The ethtool doc says:
135 		 * To disable coalescing, set usecs = 0 and max_frames = 1.
136 		 */
137 		if (ec && !(ec->tx_coalesce_usecs_irq == 0 &&
138 			    ec->tx_max_coalesced_frames_irq == 1)) {
139 			u8 max;
140 
141 			/* use only max half of available objects for coalescing */
142 			max = min_t(u8, num_tx / 2, config->fifo_depth);
143 			num_tx_coalesce = clamp(ec->tx_max_coalesced_frames_irq,
144 						(u32)config->tx.fifo_depth_coalesce_min,
145 						(u32)max);
146 			num_tx_coalesce = rounddown_pow_of_two(num_tx_coalesce);
147 
148 			num_tx = can_ram_rounddown_pow_of_two(config, &config->tx,
149 							      num_tx_coalesce, num_tx);
150 		}
151 
152 		layout->cur_rx = num_rx;
153 		layout->cur_tx = num_tx;
154 		layout->rx_coalesce = num_rx_coalesce;
155 		layout->tx_coalesce = num_tx_coalesce;
156 	} else {
157 		layout->cur_rx = layout->default_rx;
158 		layout->cur_tx = layout->default_tx;
159 		layout->rx_coalesce = 0;
160 		layout->tx_coalesce = 0;
161 	}
162 }
163