]>
Commit | Line | Data |
---|---|---|
5cfd54d7 DB |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Atlantic Network Driver | |
3 | * Copyright (C) 2020 Marvell International Ltd. | |
4 | */ | |
5 | ||
6 | #include <linux/iopoll.h> | |
7 | ||
8 | #include "aq_hw.h" | |
b4de6c49 | 9 | #include "aq_hw_utils.h" |
ce6a690c | 10 | #include "aq_nic.h" |
5cfd54d7 DB |
11 | #include "hw_atl/hw_atl_llh.h" |
12 | #include "hw_atl2_utils.h" | |
13 | #include "hw_atl2_llh.h" | |
14 | #include "hw_atl2_internal.h" | |
15 | ||
16 | #define AQ_A2_FW_READ_TRY_MAX 1000 | |
17 | ||
18 | #define hw_atl2_shared_buffer_write(HW, ITEM, VARIABLE) \ | |
8664240e MS |
19 | {\ |
20 | BUILD_BUG_ON_MSG((offsetof(struct fw_interface_in, ITEM) % \ | |
21 | sizeof(u32)) != 0,\ | |
22 | "Unaligned write " # ITEM);\ | |
23 | BUILD_BUG_ON_MSG((sizeof(VARIABLE) % sizeof(u32)) != 0,\ | |
24 | "Unaligned write length " # ITEM);\ | |
5cfd54d7 DB |
25 | hw_atl2_mif_shared_buf_write(HW,\ |
26 | (offsetof(struct fw_interface_in, ITEM) / sizeof(u32)),\ | |
8664240e MS |
27 | (u32 *)&(VARIABLE), sizeof(VARIABLE) / sizeof(u32));\ |
28 | } | |
5cfd54d7 DB |
29 | |
30 | #define hw_atl2_shared_buffer_get(HW, ITEM, VARIABLE) \ | |
8664240e MS |
31 | {\ |
32 | BUILD_BUG_ON_MSG((offsetof(struct fw_interface_in, ITEM) % \ | |
33 | sizeof(u32)) != 0,\ | |
34 | "Unaligned get " # ITEM);\ | |
35 | BUILD_BUG_ON_MSG((sizeof(VARIABLE) % sizeof(u32)) != 0,\ | |
36 | "Unaligned get length " # ITEM);\ | |
5cfd54d7 DB |
37 | hw_atl2_mif_shared_buf_get(HW, \ |
38 | (offsetof(struct fw_interface_in, ITEM) / sizeof(u32)),\ | |
39 | (u32 *)&(VARIABLE), \ | |
8664240e MS |
40 | sizeof(VARIABLE) / sizeof(u32));\ |
41 | } | |
5cfd54d7 DB |
42 | |
43 | /* This should never be used on non atomic fields, | |
44 | * treat any > u32 read as non atomic. | |
45 | */ | |
46 | #define hw_atl2_shared_buffer_read(HW, ITEM, VARIABLE) \ | |
47 | {\ | |
48 | BUILD_BUG_ON_MSG((offsetof(struct fw_interface_out, ITEM) % \ | |
49 | sizeof(u32)) != 0,\ | |
8664240e MS |
50 | "Unaligned read " # ITEM);\ |
51 | BUILD_BUG_ON_MSG((sizeof(VARIABLE) % sizeof(u32)) != 0,\ | |
52 | "Unaligned read length " # ITEM);\ | |
5cfd54d7 DB |
53 | BUILD_BUG_ON_MSG(sizeof(VARIABLE) > sizeof(u32),\ |
54 | "Non atomic read " # ITEM);\ | |
55 | hw_atl2_mif_shared_buf_read(HW, \ | |
56 | (offsetof(struct fw_interface_out, ITEM) / sizeof(u32)),\ | |
57 | (u32 *)&(VARIABLE), sizeof(VARIABLE) / sizeof(u32));\ | |
58 | } | |
59 | ||
60 | #define hw_atl2_shared_buffer_read_safe(HW, ITEM, DATA) \ | |
8664240e MS |
61 | ({\ |
62 | BUILD_BUG_ON_MSG((offsetof(struct fw_interface_out, ITEM) % \ | |
63 | sizeof(u32)) != 0,\ | |
64 | "Unaligned read_safe " # ITEM);\ | |
65 | BUILD_BUG_ON_MSG((sizeof(((struct fw_interface_out *)0)->ITEM) % \ | |
66 | sizeof(u32)) != 0,\ | |
67 | "Unaligned read_safe length " # ITEM);\ | |
5cfd54d7 DB |
68 | hw_atl2_shared_buffer_read_block((HW), \ |
69 | (offsetof(struct fw_interface_out, ITEM) / sizeof(u32)),\ | |
70 | sizeof(((struct fw_interface_out *)0)->ITEM) / sizeof(u32),\ | |
8664240e MS |
71 | (DATA));\ |
72 | }) | |
5cfd54d7 DB |
73 | |
74 | static int hw_atl2_shared_buffer_read_block(struct aq_hw_s *self, | |
75 | u32 offset, u32 dwords, void *data) | |
76 | { | |
77 | struct transaction_counter_s tid1, tid2; | |
78 | int cnt = 0; | |
79 | ||
80 | do { | |
81 | do { | |
82 | hw_atl2_shared_buffer_read(self, transaction_id, tid1); | |
83 | cnt++; | |
84 | if (cnt > AQ_A2_FW_READ_TRY_MAX) | |
85 | return -ETIME; | |
86 | if (tid1.transaction_cnt_a != tid1.transaction_cnt_b) | |
a609a075 | 87 | mdelay(1); |
5cfd54d7 DB |
88 | } while (tid1.transaction_cnt_a != tid1.transaction_cnt_b); |
89 | ||
90 | hw_atl2_mif_shared_buf_read(self, offset, (u32 *)data, dwords); | |
91 | ||
92 | hw_atl2_shared_buffer_read(self, transaction_id, tid2); | |
93 | ||
94 | cnt++; | |
95 | if (cnt > AQ_A2_FW_READ_TRY_MAX) | |
96 | return -ETIME; | |
97 | } while (tid2.transaction_cnt_a != tid2.transaction_cnt_b || | |
98 | tid1.transaction_cnt_a != tid2.transaction_cnt_a); | |
99 | ||
100 | return 0; | |
101 | } | |
102 | ||
103 | static inline int hw_atl2_shared_buffer_finish_ack(struct aq_hw_s *self) | |
104 | { | |
105 | u32 val; | |
106 | int err; | |
107 | ||
108 | hw_atl2_mif_host_finished_write_set(self, 1U); | |
109 | err = readx_poll_timeout_atomic(hw_atl2_mif_mcp_finished_read_get, | |
110 | self, val, val == 0U, | |
111 | 100, 100000U); | |
112 | WARN(err, "hw_atl2_shared_buffer_finish_ack"); | |
113 | ||
114 | return err; | |
115 | } | |
116 | ||
117 | static int aq_a2_fw_init(struct aq_hw_s *self) | |
118 | { | |
119 | struct link_control_s link_control; | |
120 | u32 mtu; | |
121 | u32 val; | |
122 | int err; | |
123 | ||
124 | hw_atl2_shared_buffer_get(self, link_control, link_control); | |
125 | link_control.mode = AQ_HOST_MODE_ACTIVE; | |
126 | hw_atl2_shared_buffer_write(self, link_control, link_control); | |
127 | ||
128 | hw_atl2_shared_buffer_get(self, mtu, mtu); | |
129 | mtu = HW_ATL2_MTU_JUMBO; | |
130 | hw_atl2_shared_buffer_write(self, mtu, mtu); | |
131 | ||
132 | hw_atl2_mif_host_finished_write_set(self, 1U); | |
133 | err = readx_poll_timeout_atomic(hw_atl2_mif_mcp_finished_read_get, | |
134 | self, val, val == 0U, | |
135 | 100, 5000000U); | |
136 | WARN(err, "hw_atl2_shared_buffer_finish_ack"); | |
137 | ||
138 | return err; | |
139 | } | |
140 | ||
141 | static int aq_a2_fw_deinit(struct aq_hw_s *self) | |
142 | { | |
143 | struct link_control_s link_control; | |
144 | ||
145 | hw_atl2_shared_buffer_get(self, link_control, link_control); | |
146 | link_control.mode = AQ_HOST_MODE_SHUTDOWN; | |
147 | hw_atl2_shared_buffer_write(self, link_control, link_control); | |
148 | ||
149 | return hw_atl2_shared_buffer_finish_ack(self); | |
150 | } | |
151 | ||
152 | static void a2_link_speed_mask2fw(u32 speed, | |
153 | struct link_options_s *link_options) | |
154 | { | |
155 | link_options->rate_10G = !!(speed & AQ_NIC_RATE_10G); | |
156 | link_options->rate_5G = !!(speed & AQ_NIC_RATE_5G); | |
4ca2a4b0 | 157 | link_options->rate_N5G = link_options->rate_5G; |
843e1396 | 158 | link_options->rate_2P5G = !!(speed & AQ_NIC_RATE_2G5); |
5cfd54d7 DB |
159 | link_options->rate_N2P5G = link_options->rate_2P5G; |
160 | link_options->rate_1G = !!(speed & AQ_NIC_RATE_1G); | |
161 | link_options->rate_100M = !!(speed & AQ_NIC_RATE_100M); | |
162 | link_options->rate_10M = !!(speed & AQ_NIC_RATE_10M); | |
071a0204 IR |
163 | |
164 | link_options->rate_1G_hd = !!(speed & AQ_NIC_RATE_1G_HALF); | |
165 | link_options->rate_100M_hd = !!(speed & AQ_NIC_RATE_100M_HALF); | |
166 | link_options->rate_10M_hd = !!(speed & AQ_NIC_RATE_10M_HALF); | |
5cfd54d7 DB |
167 | } |
168 | ||
ce6a690c ND |
169 | static u32 a2_fw_dev_to_eee_mask(struct device_link_caps_s *device_link_caps) |
170 | { | |
171 | u32 rate = 0; | |
172 | ||
173 | if (device_link_caps->eee_10G) | |
174 | rate |= AQ_NIC_RATE_EEE_10G; | |
175 | if (device_link_caps->eee_5G) | |
176 | rate |= AQ_NIC_RATE_EEE_5G; | |
177 | if (device_link_caps->eee_2P5G) | |
178 | rate |= AQ_NIC_RATE_EEE_2G5; | |
179 | if (device_link_caps->eee_1G) | |
180 | rate |= AQ_NIC_RATE_EEE_1G; | |
181 | if (device_link_caps->eee_100M) | |
182 | rate |= AQ_NIC_RATE_EEE_100M; | |
183 | ||
184 | return rate; | |
185 | } | |
186 | ||
187 | static u32 a2_fw_lkp_to_mask(struct lkp_link_caps_s *lkp_link_caps) | |
188 | { | |
189 | u32 rate = 0; | |
190 | ||
2b53b04d DB |
191 | if (lkp_link_caps->rate_10G) |
192 | rate |= AQ_NIC_RATE_10G; | |
193 | if (lkp_link_caps->rate_5G) | |
194 | rate |= AQ_NIC_RATE_5G; | |
2b53b04d DB |
195 | if (lkp_link_caps->rate_2P5G) |
196 | rate |= AQ_NIC_RATE_2G5; | |
197 | if (lkp_link_caps->rate_1G) | |
198 | rate |= AQ_NIC_RATE_1G; | |
199 | if (lkp_link_caps->rate_1G_hd) | |
200 | rate |= AQ_NIC_RATE_1G_HALF; | |
201 | if (lkp_link_caps->rate_100M) | |
202 | rate |= AQ_NIC_RATE_100M; | |
203 | if (lkp_link_caps->rate_100M_hd) | |
204 | rate |= AQ_NIC_RATE_100M_HALF; | |
205 | if (lkp_link_caps->rate_10M) | |
206 | rate |= AQ_NIC_RATE_10M; | |
207 | if (lkp_link_caps->rate_10M_hd) | |
208 | rate |= AQ_NIC_RATE_10M_HALF; | |
209 | ||
ce6a690c ND |
210 | if (lkp_link_caps->eee_10G) |
211 | rate |= AQ_NIC_RATE_EEE_10G; | |
212 | if (lkp_link_caps->eee_5G) | |
213 | rate |= AQ_NIC_RATE_EEE_5G; | |
214 | if (lkp_link_caps->eee_2P5G) | |
215 | rate |= AQ_NIC_RATE_EEE_2G5; | |
216 | if (lkp_link_caps->eee_1G) | |
217 | rate |= AQ_NIC_RATE_EEE_1G; | |
218 | if (lkp_link_caps->eee_100M) | |
219 | rate |= AQ_NIC_RATE_EEE_100M; | |
220 | ||
221 | return rate; | |
222 | } | |
223 | ||
5cfd54d7 DB |
224 | static int aq_a2_fw_set_link_speed(struct aq_hw_s *self, u32 speed) |
225 | { | |
226 | struct link_options_s link_options; | |
227 | ||
228 | hw_atl2_shared_buffer_get(self, link_options, link_options); | |
229 | link_options.link_up = 1U; | |
230 | a2_link_speed_mask2fw(speed, &link_options); | |
231 | hw_atl2_shared_buffer_write(self, link_options, link_options); | |
232 | ||
233 | return hw_atl2_shared_buffer_finish_ack(self); | |
234 | } | |
235 | ||
3e168de5 IR |
236 | static void aq_a2_fw_set_mpi_flow_control(struct aq_hw_s *self, |
237 | struct link_options_s *link_options) | |
238 | { | |
239 | u32 flow_control = self->aq_nic_cfg->fc.req; | |
240 | ||
241 | link_options->pause_rx = !!(flow_control & AQ_NIC_FC_RX); | |
242 | link_options->pause_tx = !!(flow_control & AQ_NIC_FC_TX); | |
243 | } | |
244 | ||
ce6a690c ND |
245 | static void aq_a2_fw_upd_eee_rate_bits(struct aq_hw_s *self, |
246 | struct link_options_s *link_options, | |
247 | u32 eee_speeds) | |
248 | { | |
249 | link_options->eee_10G = !!(eee_speeds & AQ_NIC_RATE_EEE_10G); | |
250 | link_options->eee_5G = !!(eee_speeds & AQ_NIC_RATE_EEE_5G); | |
251 | link_options->eee_2P5G = !!(eee_speeds & AQ_NIC_RATE_EEE_2G5); | |
252 | link_options->eee_1G = !!(eee_speeds & AQ_NIC_RATE_EEE_1G); | |
253 | link_options->eee_100M = !!(eee_speeds & AQ_NIC_RATE_EEE_100M); | |
254 | } | |
255 | ||
5cfd54d7 DB |
256 | static int aq_a2_fw_set_state(struct aq_hw_s *self, |
257 | enum hal_atl_utils_fw_state_e state) | |
258 | { | |
259 | struct link_options_s link_options; | |
260 | ||
261 | hw_atl2_shared_buffer_get(self, link_options, link_options); | |
262 | ||
263 | switch (state) { | |
264 | case MPI_INIT: | |
265 | link_options.link_up = 1U; | |
ce6a690c ND |
266 | aq_a2_fw_upd_eee_rate_bits(self, &link_options, |
267 | self->aq_nic_cfg->eee_speeds); | |
3e168de5 | 268 | aq_a2_fw_set_mpi_flow_control(self, &link_options); |
5cfd54d7 DB |
269 | break; |
270 | case MPI_DEINIT: | |
271 | link_options.link_up = 0U; | |
272 | break; | |
273 | case MPI_RESET: | |
274 | case MPI_POWER: | |
275 | /* No actions */ | |
276 | break; | |
277 | } | |
278 | ||
279 | hw_atl2_shared_buffer_write(self, link_options, link_options); | |
280 | ||
281 | return hw_atl2_shared_buffer_finish_ack(self); | |
282 | } | |
283 | ||
284 | static int aq_a2_fw_update_link_status(struct aq_hw_s *self) | |
285 | { | |
2b53b04d | 286 | struct lkp_link_caps_s lkp_link_caps; |
5cfd54d7 DB |
287 | struct link_status_s link_status; |
288 | ||
289 | hw_atl2_shared_buffer_read(self, link_status, link_status); | |
290 | ||
291 | switch (link_status.link_rate) { | |
292 | case AQ_A2_FW_LINK_RATE_10G: | |
293 | self->aq_link_status.mbps = 10000; | |
294 | break; | |
295 | case AQ_A2_FW_LINK_RATE_5G: | |
296 | self->aq_link_status.mbps = 5000; | |
297 | break; | |
298 | case AQ_A2_FW_LINK_RATE_2G5: | |
299 | self->aq_link_status.mbps = 2500; | |
300 | break; | |
301 | case AQ_A2_FW_LINK_RATE_1G: | |
302 | self->aq_link_status.mbps = 1000; | |
303 | break; | |
304 | case AQ_A2_FW_LINK_RATE_100M: | |
305 | self->aq_link_status.mbps = 100; | |
306 | break; | |
307 | case AQ_A2_FW_LINK_RATE_10M: | |
308 | self->aq_link_status.mbps = 10; | |
309 | break; | |
310 | default: | |
311 | self->aq_link_status.mbps = 0; | |
312 | } | |
071a0204 | 313 | self->aq_link_status.full_duplex = link_status.duplex; |
5cfd54d7 | 314 | |
2b53b04d DB |
315 | hw_atl2_shared_buffer_read(self, lkp_link_caps, lkp_link_caps); |
316 | ||
317 | self->aq_link_status.lp_link_speed_msk = | |
318 | a2_fw_lkp_to_mask(&lkp_link_caps); | |
319 | self->aq_link_status.lp_flow_control = | |
320 | ((lkp_link_caps.pause_rx) ? AQ_NIC_FC_RX : 0) | | |
321 | ((lkp_link_caps.pause_tx) ? AQ_NIC_FC_TX : 0); | |
322 | ||
5cfd54d7 DB |
323 | return 0; |
324 | } | |
325 | ||
326 | static int aq_a2_fw_get_mac_permanent(struct aq_hw_s *self, u8 *mac) | |
327 | { | |
328 | struct mac_address_aligned_s mac_address; | |
329 | ||
330 | hw_atl2_shared_buffer_get(self, mac_address, mac_address); | |
331 | ether_addr_copy(mac, (u8 *)mac_address.aligned.mac_address); | |
332 | ||
5cfd54d7 DB |
333 | return 0; |
334 | } | |
335 | ||
0c7bf6d4 DB |
336 | static void aq_a2_fill_a0_stats(struct aq_hw_s *self, |
337 | struct statistics_s *stats) | |
5cfd54d7 DB |
338 | { |
339 | struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv; | |
0c7bf6d4 DB |
340 | struct aq_stats_s *cs = &self->curr_stats; |
341 | struct aq_stats_s curr_stats = *cs; | |
342 | bool corrupted_stats = false; | |
343 | ||
344 | #define AQ_SDELTA(_N, _F) \ | |
345 | do { \ | |
346 | if (!corrupted_stats && \ | |
347 | ((s64)(stats->a0.msm._F - priv->last_stats.a0.msm._F)) >= 0) \ | |
348 | curr_stats._N += stats->a0.msm._F - priv->last_stats.a0.msm._F;\ | |
349 | else \ | |
350 | corrupted_stats = true; \ | |
351 | } while (0) | |
5cfd54d7 DB |
352 | |
353 | if (self->aq_link_status.mbps) { | |
354 | AQ_SDELTA(uprc, rx_unicast_frames); | |
355 | AQ_SDELTA(mprc, rx_multicast_frames); | |
356 | AQ_SDELTA(bprc, rx_broadcast_frames); | |
357 | AQ_SDELTA(erpr, rx_error_frames); | |
358 | ||
359 | AQ_SDELTA(uptc, tx_unicast_frames); | |
360 | AQ_SDELTA(mptc, tx_multicast_frames); | |
361 | AQ_SDELTA(bptc, tx_broadcast_frames); | |
362 | AQ_SDELTA(erpt, tx_errors); | |
363 | ||
364 | AQ_SDELTA(ubrc, rx_unicast_octets); | |
365 | AQ_SDELTA(ubtc, tx_unicast_octets); | |
366 | AQ_SDELTA(mbrc, rx_multicast_octets); | |
367 | AQ_SDELTA(mbtc, tx_multicast_octets); | |
368 | AQ_SDELTA(bbrc, rx_broadcast_octets); | |
369 | AQ_SDELTA(bbtc, tx_broadcast_octets); | |
0c7bf6d4 DB |
370 | |
371 | if (!corrupted_stats) | |
372 | *cs = curr_stats; | |
5cfd54d7 DB |
373 | } |
374 | #undef AQ_SDELTA | |
0c7bf6d4 DB |
375 | |
376 | } | |
377 | ||
378 | static void aq_a2_fill_b0_stats(struct aq_hw_s *self, | |
379 | struct statistics_s *stats) | |
380 | { | |
381 | struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv; | |
382 | struct aq_stats_s *cs = &self->curr_stats; | |
383 | struct aq_stats_s curr_stats = *cs; | |
384 | bool corrupted_stats = false; | |
385 | ||
386 | #define AQ_SDELTA(_N, _F) \ | |
387 | do { \ | |
388 | if (!corrupted_stats && \ | |
389 | ((s64)(stats->b0._F - priv->last_stats.b0._F)) >= 0) \ | |
390 | curr_stats._N += stats->b0._F - priv->last_stats.b0._F; \ | |
391 | else \ | |
392 | corrupted_stats = true; \ | |
393 | } while (0) | |
394 | ||
395 | if (self->aq_link_status.mbps) { | |
396 | AQ_SDELTA(uprc, rx_unicast_frames); | |
397 | AQ_SDELTA(mprc, rx_multicast_frames); | |
398 | AQ_SDELTA(bprc, rx_broadcast_frames); | |
399 | AQ_SDELTA(erpr, rx_errors); | |
400 | AQ_SDELTA(brc, rx_good_octets); | |
401 | ||
402 | AQ_SDELTA(uptc, tx_unicast_frames); | |
403 | AQ_SDELTA(mptc, tx_multicast_frames); | |
404 | AQ_SDELTA(bptc, tx_broadcast_frames); | |
405 | AQ_SDELTA(erpt, tx_errors); | |
406 | AQ_SDELTA(btc, tx_good_octets); | |
407 | ||
408 | if (!corrupted_stats) | |
409 | *cs = curr_stats; | |
410 | } | |
411 | #undef AQ_SDELTA | |
412 | } | |
413 | ||
414 | static int aq_a2_fw_update_stats(struct aq_hw_s *self) | |
415 | { | |
416 | struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv; | |
417 | struct aq_stats_s *cs = &self->curr_stats; | |
418 | struct statistics_s stats; | |
419 | struct version_s version; | |
420 | int err; | |
421 | ||
422 | err = hw_atl2_shared_buffer_read_safe(self, version, &version); | |
423 | if (err) | |
424 | return err; | |
425 | ||
426 | err = hw_atl2_shared_buffer_read_safe(self, stats, &stats); | |
427 | if (err) | |
428 | return err; | |
429 | ||
430 | if (version.drv_iface_ver == AQ_A2_FW_INTERFACE_A0) | |
431 | aq_a2_fill_a0_stats(self, &stats); | |
432 | else | |
433 | aq_a2_fill_b0_stats(self, &stats); | |
434 | ||
435 | cs->dma_pkt_rc = hw_atl_stats_rx_dma_good_pkt_counter_get(self); | |
436 | cs->dma_pkt_tc = hw_atl_stats_tx_dma_good_pkt_counter_get(self); | |
437 | cs->dma_oct_rc = hw_atl_stats_rx_dma_good_octet_counter_get(self); | |
438 | cs->dma_oct_tc = hw_atl_stats_tx_dma_good_octet_counter_get(self); | |
439 | cs->dpc = hw_atl_rpb_rx_dma_drop_pkt_cnt_get(self); | |
5cfd54d7 DB |
440 | |
441 | memcpy(&priv->last_stats, &stats, sizeof(stats)); | |
442 | ||
443 | return 0; | |
444 | } | |
445 | ||
8dcf2ad3 MS |
446 | static int aq_a2_fw_get_phy_temp(struct aq_hw_s *self, int *temp) |
447 | { | |
448 | struct phy_health_monitor_s phy_health_monitor; | |
449 | ||
450 | hw_atl2_shared_buffer_read_safe(self, phy_health_monitor, | |
451 | &phy_health_monitor); | |
452 | ||
453 | *temp = (int8_t)phy_health_monitor.phy_temperature * 1000; | |
454 | return 0; | |
455 | } | |
456 | ||
457 | static int aq_a2_fw_get_mac_temp(struct aq_hw_s *self, int *temp) | |
458 | { | |
459 | /* There's only one temperature sensor on A2, use it for | |
460 | * both MAC and PHY. | |
461 | */ | |
462 | return aq_a2_fw_get_phy_temp(self, temp); | |
463 | } | |
464 | ||
ce6a690c ND |
465 | static int aq_a2_fw_set_eee_rate(struct aq_hw_s *self, u32 speed) |
466 | { | |
467 | struct link_options_s link_options; | |
468 | ||
469 | hw_atl2_shared_buffer_get(self, link_options, link_options); | |
470 | ||
471 | aq_a2_fw_upd_eee_rate_bits(self, &link_options, speed); | |
472 | ||
473 | hw_atl2_shared_buffer_write(self, link_options, link_options); | |
474 | ||
475 | return hw_atl2_shared_buffer_finish_ack(self); | |
476 | } | |
477 | ||
478 | static int aq_a2_fw_get_eee_rate(struct aq_hw_s *self, u32 *rate, | |
479 | u32 *supported_rates) | |
480 | { | |
481 | struct device_link_caps_s device_link_caps; | |
482 | struct lkp_link_caps_s lkp_link_caps; | |
483 | ||
484 | hw_atl2_shared_buffer_read(self, device_link_caps, device_link_caps); | |
485 | hw_atl2_shared_buffer_read(self, lkp_link_caps, lkp_link_caps); | |
486 | ||
487 | *supported_rates = a2_fw_dev_to_eee_mask(&device_link_caps); | |
488 | *rate = a2_fw_lkp_to_mask(&lkp_link_caps); | |
489 | ||
490 | return 0; | |
491 | } | |
492 | ||
5cfd54d7 DB |
493 | static int aq_a2_fw_renegotiate(struct aq_hw_s *self) |
494 | { | |
495 | struct link_options_s link_options; | |
496 | int err; | |
497 | ||
498 | hw_atl2_shared_buffer_get(self, link_options, link_options); | |
499 | link_options.link_renegotiate = 1U; | |
500 | hw_atl2_shared_buffer_write(self, link_options, link_options); | |
501 | ||
502 | err = hw_atl2_shared_buffer_finish_ack(self); | |
503 | ||
504 | /* We should put renegotiate status back to zero | |
505 | * after command completes | |
506 | */ | |
507 | link_options.link_renegotiate = 0U; | |
508 | hw_atl2_shared_buffer_write(self, link_options, link_options); | |
509 | ||
510 | return err; | |
511 | } | |
512 | ||
3e168de5 IR |
513 | static int aq_a2_fw_set_flow_control(struct aq_hw_s *self) |
514 | { | |
515 | struct link_options_s link_options; | |
516 | ||
517 | hw_atl2_shared_buffer_get(self, link_options, link_options); | |
518 | ||
519 | aq_a2_fw_set_mpi_flow_control(self, &link_options); | |
520 | ||
521 | hw_atl2_shared_buffer_write(self, link_options, link_options); | |
522 | ||
523 | return hw_atl2_shared_buffer_finish_ack(self); | |
524 | } | |
525 | ||
526 | static u32 aq_a2_fw_get_flow_control(struct aq_hw_s *self, u32 *fcmode) | |
527 | { | |
528 | struct link_status_s link_status; | |
529 | ||
530 | hw_atl2_shared_buffer_read(self, link_status, link_status); | |
531 | ||
532 | *fcmode = ((link_status.pause_rx) ? AQ_NIC_FC_RX : 0) | | |
533 | ((link_status.pause_tx) ? AQ_NIC_FC_TX : 0); | |
534 | return 0; | |
535 | } | |
536 | ||
ecab7870 DB |
537 | static int aq_a2_fw_set_phyloopback(struct aq_hw_s *self, u32 mode, bool enable) |
538 | { | |
539 | struct link_options_s link_options; | |
540 | ||
541 | hw_atl2_shared_buffer_get(self, link_options, link_options); | |
542 | ||
543 | switch (mode) { | |
544 | case AQ_HW_LOOPBACK_PHYINT_SYS: | |
545 | link_options.internal_loopback = enable; | |
546 | break; | |
547 | case AQ_HW_LOOPBACK_PHYEXT_SYS: | |
548 | link_options.external_loopback = enable; | |
549 | break; | |
550 | default: | |
551 | return -EINVAL; | |
552 | } | |
553 | ||
554 | hw_atl2_shared_buffer_write(self, link_options, link_options); | |
555 | ||
556 | return hw_atl2_shared_buffer_finish_ack(self); | |
557 | } | |
558 | ||
c1be0bf0 DB |
559 | u32 hw_atl2_utils_get_fw_version(struct aq_hw_s *self) |
560 | { | |
561 | struct version_s version; | |
562 | ||
563 | hw_atl2_shared_buffer_read_safe(self, version, &version); | |
564 | ||
565 | /* A2 FW version is stored in reverse order */ | |
0d3d34c0 SS |
566 | return version.bundle.major << 24 | |
567 | version.bundle.minor << 16 | | |
568 | version.bundle.build; | |
c1be0bf0 DB |
569 | } |
570 | ||
5cfd54d7 DB |
571 | int hw_atl2_utils_get_action_resolve_table_caps(struct aq_hw_s *self, |
572 | u8 *base_index, u8 *count) | |
573 | { | |
574 | struct filter_caps_s filter_caps; | |
575 | int err; | |
576 | ||
577 | err = hw_atl2_shared_buffer_read_safe(self, filter_caps, &filter_caps); | |
578 | if (err) | |
579 | return err; | |
580 | ||
581 | *base_index = filter_caps.rslv_tbl_base_index; | |
582 | *count = filter_caps.rslv_tbl_count; | |
583 | return 0; | |
584 | } | |
585 | ||
e193c3ab IR |
586 | static int aq_a2_fw_set_downshift(struct aq_hw_s *self, u32 counter) |
587 | { | |
588 | struct link_options_s link_options; | |
589 | ||
590 | hw_atl2_shared_buffer_get(self, link_options, link_options); | |
591 | link_options.downshift = !!counter; | |
592 | link_options.downshift_retry = counter; | |
593 | hw_atl2_shared_buffer_write(self, link_options, link_options); | |
594 | ||
595 | return hw_atl2_shared_buffer_finish_ack(self); | |
596 | } | |
597 | ||
5cfd54d7 DB |
598 | const struct aq_fw_ops aq_a2_fw_ops = { |
599 | .init = aq_a2_fw_init, | |
600 | .deinit = aq_a2_fw_deinit, | |
601 | .reset = NULL, | |
602 | .renegotiate = aq_a2_fw_renegotiate, | |
603 | .get_mac_permanent = aq_a2_fw_get_mac_permanent, | |
604 | .set_link_speed = aq_a2_fw_set_link_speed, | |
605 | .set_state = aq_a2_fw_set_state, | |
606 | .update_link_status = aq_a2_fw_update_link_status, | |
607 | .update_stats = aq_a2_fw_update_stats, | |
8dcf2ad3 MS |
608 | .get_mac_temp = aq_a2_fw_get_mac_temp, |
609 | .get_phy_temp = aq_a2_fw_get_phy_temp, | |
ce6a690c ND |
610 | .set_eee_rate = aq_a2_fw_set_eee_rate, |
611 | .get_eee_rate = aq_a2_fw_get_eee_rate, | |
3e168de5 IR |
612 | .set_flow_control = aq_a2_fw_set_flow_control, |
613 | .get_flow_control = aq_a2_fw_get_flow_control, | |
ecab7870 | 614 | .set_phyloopback = aq_a2_fw_set_phyloopback, |
e193c3ab | 615 | .set_downshift = aq_a2_fw_set_downshift, |
5cfd54d7 | 616 | }; |