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, |
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 | |
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, obj: &config->tx, coalesce: 0, val: 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, obj: &config->rx, coalesce: 0, val: 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, obj: &config->rx, coalesce: 0, val: num_rx); |
93 | layout->max_tx = can_ram_rounddown_pow_of_two(config, obj: &config->tx, coalesce: 0, val: num_tx); |
94 | |
95 | /* cur CAN */ |
96 | |
97 | if (ring) { |
98 | u8 num_rx_coalesce = 0, num_tx_coalesce = 0; |
99 | |
100 | num_rx = can_ram_rounddown_pow_of_two(config, obj: &config->rx, coalesce: 0, val: ring->rx_pending); |
101 | |
102 | /* The ethtool doc says: |
103 | * To disable coalescing, set usecs = 0 and max_frames = 1. |
104 | */ |
105 | if (ec && !(ec->rx_coalesce_usecs_irq == 0 && |
106 | ec->rx_max_coalesced_frames_irq == 1)) { |
107 | u8 max; |
108 | |
109 | /* use only max half of available objects for coalescing */ |
110 | max = min_t(u8, num_rx / 2, config->fifo_depth); |
111 | num_rx_coalesce = clamp(ec->rx_max_coalesced_frames_irq, |
112 | (u32)config->rx.fifo_depth_coalesce_min, |
113 | (u32)max); |
114 | num_rx_coalesce = rounddown_pow_of_two(num_rx_coalesce); |
115 | |
116 | num_rx = can_ram_rounddown_pow_of_two(config, obj: &config->rx, |
117 | coalesce: num_rx_coalesce, val: num_rx); |
118 | } |
119 | |
120 | ram_free = config->size - config->rx.size[fd_mode] * num_rx; |
121 | num_tx = ram_free / config->tx.size[fd_mode]; |
122 | num_tx = min_t(u8, ring->tx_pending, num_tx); |
123 | num_tx = can_ram_rounddown_pow_of_two(config, obj: &config->tx, coalesce: 0, val: num_tx); |
124 | |
125 | /* The ethtool doc says: |
126 | * To disable coalescing, set usecs = 0 and max_frames = 1. |
127 | */ |
128 | if (ec && !(ec->tx_coalesce_usecs_irq == 0 && |
129 | ec->tx_max_coalesced_frames_irq == 1)) { |
130 | u8 max; |
131 | |
132 | /* use only max half of available objects for coalescing */ |
133 | max = min_t(u8, num_tx / 2, config->fifo_depth); |
134 | num_tx_coalesce = clamp(ec->tx_max_coalesced_frames_irq, |
135 | (u32)config->tx.fifo_depth_coalesce_min, |
136 | (u32)max); |
137 | num_tx_coalesce = rounddown_pow_of_two(num_tx_coalesce); |
138 | |
139 | num_tx = can_ram_rounddown_pow_of_two(config, obj: &config->tx, |
140 | coalesce: num_tx_coalesce, val: num_tx); |
141 | } |
142 | |
143 | layout->cur_rx = num_rx; |
144 | layout->cur_tx = num_tx; |
145 | layout->rx_coalesce = num_rx_coalesce; |
146 | layout->tx_coalesce = num_tx_coalesce; |
147 | } else { |
148 | layout->cur_rx = layout->default_rx; |
149 | layout->cur_tx = layout->default_tx; |
150 | layout->rx_coalesce = 0; |
151 | layout->tx_coalesce = 0; |
152 | } |
153 | } |
154 | |