1 /*-
2 * Atlantic 2 (AQC113/114/115/116) firmware operations for aq(4).
3 *
4 * Adapted from the OpenBSD/NetBSD if_aq driver:
5 *
6 * Copyright (c) 2021 Jonathan Matthew <jonathan@d14n.org>
7 * Copyright (c) 2021 Mike Larkin <mlarkin@openbsd.org>
8 *
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 */
21
22 #include <sys/cdefs.h>
23
24 #include <sys/param.h>
25 #include <sys/systm.h>
26 #include <sys/errno.h>
27 #include <sys/endian.h>
28 #include <net/ethernet.h>
29
30 #include "aq_common.h"
31 #include "aq_hw.h"
32 #include "aq_hw_llh.h"
33 #include "aq2_hw.h"
34 #include "aq_fw.h"
35 #include "aq_dbg.h"
36
37 static int aq2_fw_reset(struct aq_hw *hw);
38 static int aq2_fw_set_mode(struct aq_hw *hw, enum aq_hw_fw_mpi_state mode,
39 enum aq_fw_link_speed speed);
40 static int aq2_fw_get_mode(struct aq_hw *hw, enum aq_hw_fw_mpi_state *mode,
41 enum aq_fw_link_speed *speed, enum aq_fw_link_fc *fc);
42 static int aq2_fw_get_link_info(struct aq_hw *hw, struct aq_hw_link_info *info);
43 static int aq2_fw_get_mac_addr(struct aq_hw *hw, uint8_t *mac);
44 static int aq2_fw_get_stats(struct aq_hw *hw, struct aq_hw_stats *stats);
45 static int aq2_fw_get_link_counters(struct aq_hw *hw, uint32_t *up,
46 uint32_t *down);
47 static int aq2_fw_get_temp(struct aq_hw *hw, int *temp_mc);
48 static int aq2_fw_get_thermal_limit(struct aq_hw *hw, int *limit_mc);
49 static int aq2_fw_get_phy_hot_warning(struct aq_hw *hw, bool *hot);
50
51 /* Coherent OUT-window read, bracketed by the transaction id. */
52 static int
aq2_fw_interface_buffer_read(struct aq_hw * hw,uint32_t reg0,uint32_t * data0,uint32_t size0)53 aq2_fw_interface_buffer_read(struct aq_hw *hw, uint32_t reg0, uint32_t *data0,
54 uint32_t size0)
55 {
56 uint32_t tid0, tid1, reg, *data, size;
57 int timo;
58
59 for (timo = 10000; timo > 0; timo--) {
60 tid0 = AQ_READ_REG(hw, AQ2_FW_INTERFACE_OUT_TRANSACTION_ID_REG);
61 if (((tid0 & AQ2_FW_INTERFACE_OUT_TRANSACTION_ID_A) >>
62 AQ2_FW_INTERFACE_OUT_TRANSACTION_ID_A_S) !=
63 ((tid0 & AQ2_FW_INTERFACE_OUT_TRANSACTION_ID_B) >>
64 AQ2_FW_INTERFACE_OUT_TRANSACTION_ID_B_S)) {
65 DELAY(10);
66 continue;
67 }
68
69 /* size0 is a 4-byte multiple: full register-width reads. */
70 for (reg = reg0, data = data0, size = size0;
71 size >= 4; reg += 4, data++, size -= 4)
72 *data = AQ_READ_REG(hw, reg);
73
74 tid1 = AQ_READ_REG(hw, AQ2_FW_INTERFACE_OUT_TRANSACTION_ID_REG);
75 if (tid0 == tid1)
76 break;
77 }
78 if (timo == 0) {
79 device_printf(hw->dev, "A2 interface buffer read timeout\n");
80 return (ETIMEDOUT);
81 }
82 return (0);
83 }
84
85 /* Boot the A2 firmware and select the A2 firmware ops. */
86 int
aq2_fw_reboot(struct aq_hw * hw)87 aq2_fw_reboot(struct aq_hw *hw)
88 {
89 uint32_t v, filter_caps[3];
90 int timo, err;
91 const char *iface;
92
93 hw->fw_ops = &aq2_fw_ops;
94
95 AQ_WRITE_REG(hw, AQ2_MCP_HOST_REQ_INT_CLR_REG, 1);
96 AQ_WRITE_REG(hw, AQ2_MIF_BOOT_REG, 1); /* reboot request */
97 for (timo = 20000; timo > 0; timo--) {
98 v = AQ_READ_REG(hw, AQ2_MIF_BOOT_REG);
99 if ((v & AQ2_MIF_BOOT_BOOT_STARTED) && v != 0xffffffff)
100 break;
101 DELAY(10);
102 }
103 if (timo <= 0) {
104 device_printf(hw->dev, "A2 firmware reboot timeout\n");
105 return (ETIMEDOUT);
106 }
107 trace(hw, dbg_init, "aq2> F/W boot started, %d ms", (20000 - timo) / 100);
108
109 for (timo = 200000; timo > 0; timo--) {
110 v = AQ_READ_REG(hw, AQ2_MIF_BOOT_REG);
111 if (v & AQ2_MIF_BOOT_COMPLETE)
112 break;
113 v = AQ_READ_REG(hw, AQ2_MCP_HOST_REQ_INT_REG);
114 if (v & AQ2_MCP_HOST_REQ_INT_READY)
115 break;
116 DELAY(10);
117 }
118 if (timo <= 0) {
119 device_printf(hw->dev, "A2 firmware restart timeout\n");
120 return (ETIMEDOUT);
121 }
122 trace(hw, dbg_init, "aq2> F/W boot complete, %d ms",
123 (200000 - timo) / 100);
124
125 v = AQ_READ_REG(hw, AQ2_MIF_BOOT_REG);
126 if (v & AQ2_MIF_BOOT_FAILED) {
127 device_printf(hw->dev, "A2 firmware restart failed\n");
128 return (EIO);
129 }
130 v = AQ_READ_REG(hw, AQ2_MCP_HOST_REQ_INT_REG);
131 if (v & AQ2_MCP_HOST_REQ_INT_READY) {
132 device_printf(hw->dev, "A2 firmware required but not present\n");
133 return (ENXIO);
134 }
135
136 /* Repack into fw_version's major.minor.build layout. */
137 err = aq2_fw_interface_buffer_read(hw,
138 AQ2_FW_INTERFACE_OUT_VERSION_BUNDLE_REG, &v, sizeof(v));
139 if (err != 0)
140 return (err);
141 hw->fw_version.raw =
142 (((v & AQ2_FW_INTERFACE_OUT_VERSION_MAJOR) >>
143 AQ2_FW_INTERFACE_OUT_VERSION_MAJOR_S) << 24) |
144 (((v & AQ2_FW_INTERFACE_OUT_VERSION_MINOR) >>
145 AQ2_FW_INTERFACE_OUT_VERSION_MINOR_S) << 16) |
146 ((v & AQ2_FW_INTERFACE_OUT_VERSION_BUILD) >>
147 AQ2_FW_INTERFACE_OUT_VERSION_BUILD_S);
148
149 err = aq2_fw_interface_buffer_read(hw,
150 AQ2_FW_INTERFACE_OUT_VERSION_IFACE_REG, &v, sizeof(v));
151 if (err != 0)
152 return (err);
153 hw->aq2_iface = v & AQ2_FW_INTERFACE_OUT_VERSION_IFACE_VER;
154 switch (hw->aq2_iface) {
155 case AQ2_FW_INTERFACE_OUT_VERSION_IFACE_VER_A0:
156 iface = "A0";
157 break;
158 case AQ2_FW_INTERFACE_OUT_VERSION_IFACE_VER_B0:
159 iface = "B0";
160 break;
161 default:
162 iface = "unknown";
163 break;
164 }
165 if (!hw->fw_announced || bootverbose) {
166 device_printf(hw->dev, "Atlantic 2 %s, firmware %u.%u.%u\n",
167 iface, hw->fw_version.major_version,
168 hw->fw_version.minor_version, hw->fw_version.build_number);
169 hw->fw_announced = true;
170 }
171
172 /* Base row added to every action-resolver-table index. */
173 err = aq2_fw_interface_buffer_read(hw,
174 AQ2_FW_INTERFACE_OUT_FILTER_CAPS_REG, filter_caps,
175 sizeof(filter_caps));
176 if (err != 0)
177 return (err);
178 hw->art_filter_base_index = ((filter_caps[2] &
179 AQ2_FW_INTERFACE_OUT_FILTER_CAPS3_RESOLVER_BASE_INDEX) >>
180 AQ2_FW_INTERFACE_OUT_FILTER_CAPS3_RESOLVER_BASE_INDEX_SHIFT) * 8;
181 trace(hw, dbg_init, "aq2> ART filter base index = %u",
182 hw->art_filter_base_index);
183
184 return (0);
185 }
186
187 /* Commit an IN-window write; wait for the MCP ack. */
188 static int
aq2_fw_wait_shared_ack(struct aq_hw * hw)189 aq2_fw_wait_shared_ack(struct aq_hw *hw)
190 {
191 int err;
192
193 AQ_WRITE_REG(hw, AQ2_MIF_HOST_FINISHED_STATUS_WRITE_REG,
194 AQ2_MIF_HOST_FINISHED_STATUS_ACK);
195 err = AQ_HW_WAIT_FOR((AQ_READ_REG(hw,
196 AQ2_MIF_HOST_FINISHED_STATUS_READ_REG) &
197 AQ2_MIF_HOST_FINISHED_STATUS_ACK) == 0, 100, 1000);
198 if (err != 0)
199 trace_error(hw, dbg_fw, "aq2> shared buffer ack timed out");
200
201 return (err);
202 }
203
204 static int
aq2_fw_reset(struct aq_hw * hw)205 aq2_fw_reset(struct aq_hw *hw)
206 {
207 uint32_t v;
208 int err;
209
210 AQ_WRITE_REG_BIT(hw, AQ2_FW_INTERFACE_IN_LINK_CONTROL_REG,
211 AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE, 0,
212 AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE_ACTIVE);
213
214 AQ_WRITE_REG(hw, AQ2_FW_INTERFACE_IN_MTU_REG, HW_ATL_B0_MTU_JUMBO);
215
216 v = AQ_READ_REG(hw, AQ2_FW_INTERFACE_IN_REQUEST_POLICY_REG);
217 v |= AQ2_FW_INTERFACE_IN_REQUEST_POLICY_MCAST_QUEUE_OR_TC;
218 v &= ~AQ2_FW_INTERFACE_IN_REQUEST_POLICY_MCAST_RX_QUEUE_TC_INDEX;
219 v |= AQ2_FW_INTERFACE_IN_REQUEST_POLICY_MCAST_ACCEPT;
220 v |= AQ2_FW_INTERFACE_IN_REQUEST_POLICY_BCAST_QUEUE_OR_TC;
221 v &= ~AQ2_FW_INTERFACE_IN_REQUEST_POLICY_BCAST_RX_QUEUE_TC_INDEX;
222 v |= AQ2_FW_INTERFACE_IN_REQUEST_POLICY_BCAST_ACCEPT;
223 v |= AQ2_FW_INTERFACE_IN_REQUEST_POLICY_PROMISC_QUEUE_OR_TC;
224 v &= ~AQ2_FW_INTERFACE_IN_REQUEST_POLICY_PROMISC_RX_QUEUE_TX_INDEX;
225 AQ_WRITE_REG(hw, AQ2_FW_INTERFACE_IN_REQUEST_POLICY_REG, v);
226
227 trace(hw, dbg_init, "aq2> reset: mtu %u, request policy %#x",
228 HW_ATL_B0_MTU_JUMBO, v);
229
230 err = aq2_fw_wait_shared_ack(hw);
231 if (err != 0)
232 device_printf(hw->dev, "A2 firmware reset timed out\n");
233 return (err);
234 }
235
236 static int
aq2_fw_get_mac_addr(struct aq_hw * hw,uint8_t * mac)237 aq2_fw_get_mac_addr(struct aq_hw *hw, uint8_t *mac)
238 {
239 uint32_t mac_addr[2];
240
241 mac_addr[0] = AQ_READ_REG(hw, AQ2_FW_INTERFACE_IN_MAC_ADDRESS_REG);
242 mac_addr[1] = AQ_READ_REG(hw, AQ2_FW_INTERFACE_IN_MAC_ADDRESS_REG + 4);
243
244 if (mac_addr[0] == 0 && mac_addr[1] == 0) {
245 device_printf(hw->dev, "A2 mac address not found\n");
246 return (ENXIO);
247 }
248
249 mac_addr[0] = htole32(mac_addr[0]);
250 mac_addr[1] = htole32(mac_addr[1]);
251 memcpy(mac, (uint8_t *)mac_addr, ETHER_ADDR_LEN);
252 trace(hw, dbg_init, "aq2> MAC addr %6D", mac, ":");
253 return (0);
254 }
255
256 /* No fw_mtx: the IN window is written only from iflib-serialised paths. */
257 static int
aq2_fw_set_mode(struct aq_hw * hw,enum aq_hw_fw_mpi_state mode,enum aq_fw_link_speed speed)258 aq2_fw_set_mode(struct aq_hw *hw, enum aq_hw_fw_mpi_state mode,
259 enum aq_fw_link_speed speed)
260 {
261 uint32_t v;
262 int err;
263
264 v = AQ_READ_REG(hw, AQ2_FW_INTERFACE_IN_LINK_OPTIONS_REG);
265 v &= ~(AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_10G |
266 AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_N5G |
267 AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_5G |
268 AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_N2G5 |
269 AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_2G5 |
270 AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_1G |
271 AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_100M |
272 AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_10M |
273 AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_1G_HD |
274 AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_100M_HD |
275 AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_10M_HD);
276 v &= ~AQ2_FW_INTERFACE_IN_LINK_OPTIONS_LINK_UP;
277
278 if (mode == MPI_INIT) {
279 if (speed & aq_fw_10G)
280 v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_10G;
281 if (speed & aq_fw_5G)
282 v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_N5G |
283 AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_5G;
284 if (speed & aq_fw_2G5)
285 v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_N2G5 |
286 AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_2G5;
287 if (speed & aq_fw_1G)
288 v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_1G;
289 if (speed & aq_fw_100M)
290 v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_100M;
291 if (speed & aq_fw_10M)
292 v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_RATE_10M;
293
294 v &= ~(AQ2_FW_INTERFACE_IN_LINK_OPTIONS_PAUSE_TX |
295 AQ2_FW_INTERFACE_IN_LINK_OPTIONS_PAUSE_RX);
296 if (hw->fc.fc_tx)
297 v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_PAUSE_TX;
298 if (hw->fc.fc_rx)
299 v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_PAUSE_RX;
300
301 v |= AQ2_FW_INTERFACE_IN_LINK_OPTIONS_LINK_UP;
302 } else {
303 AQ_WRITE_REG_BIT(hw, AQ2_FW_INTERFACE_IN_LINK_CONTROL_REG,
304 AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE, 0,
305 AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE_SHUTDOWN);
306 }
307
308 trace(hw, dbg_init, "aq2> set mode %d, speed mask %#x, link options %#x",
309 mode, speed, v);
310
311 /* Options acked before ACTIVE so bring-up negotiates the new mask. */
312 AQ_WRITE_REG(hw, AQ2_FW_INTERFACE_IN_LINK_OPTIONS_REG, v);
313 if (mode == MPI_INIT) {
314 err = aq2_fw_wait_shared_ack(hw);
315 if (err != 0)
316 return (err);
317 AQ_WRITE_REG_BIT(hw, AQ2_FW_INTERFACE_IN_LINK_CONTROL_REG,
318 AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE, 0,
319 AQ2_FW_INTERFACE_IN_LINK_CONTROL_MODE_ACTIVE);
320 }
321 return (aq2_fw_wait_shared_ack(hw));
322 }
323
324 static int
aq2_fw_get_mode(struct aq_hw * hw,enum aq_hw_fw_mpi_state * modep,enum aq_fw_link_speed * speedp,enum aq_fw_link_fc * fcp)325 aq2_fw_get_mode(struct aq_hw *hw, enum aq_hw_fw_mpi_state *modep,
326 enum aq_fw_link_speed *speedp, enum aq_fw_link_fc *fcp)
327 {
328 uint32_t v;
329 enum aq_fw_link_speed speed;
330 enum aq_fw_link_fc fc = aq_fw_fc_none;
331
332 if (modep != NULL)
333 *modep = MPI_INIT;
334
335 v = AQ_READ_REG(hw, AQ2_FW_INTERFACE_OUT_LINK_STATUS_REG);
336 switch ((v & AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE) >>
337 AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_S) {
338 case AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_10G:
339 speed = aq_fw_10G;
340 break;
341 case AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_5G:
342 speed = aq_fw_5G;
343 break;
344 case AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_2G5:
345 speed = aq_fw_2G5;
346 break;
347 case AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_1G:
348 speed = aq_fw_1G;
349 break;
350 case AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_100M:
351 speed = aq_fw_100M;
352 break;
353 case AQ2_FW_INTERFACE_OUT_LINK_STATUS_RATE_10M:
354 speed = aq_fw_10M;
355 break;
356 default:
357 speed = aq_fw_none;
358 break;
359 }
360 if (speedp != NULL)
361 *speedp = speed;
362
363 if (v & AQ2_FW_INTERFACE_OUT_LINK_STATUS_PAUSE_TX)
364 fc |= aq_fw_fc_ENABLE_TX;
365 if (v & AQ2_FW_INTERFACE_OUT_LINK_STATUS_PAUSE_RX)
366 fc |= aq_fw_fc_ENABLE_RX;
367 if (fcp != NULL)
368 *fcp = fc;
369
370 trace_detail(hw, dbg_init,
371 "aq2> get mode: link status %#x, speed %d, fc %d", v, speed, fc);
372
373 return (0);
374 }
375
376 static int
aq2_fw_get_link_info(struct aq_hw * hw,struct aq_hw_link_info * info)377 aq2_fw_get_link_info(struct aq_hw *hw, struct aq_hw_link_info *info)
378 {
379 uint32_t v;
380
381 v = AQ_READ_REG(hw, AQ2_FW_INTERFACE_OUT_LINK_STATUS_REG);
382
383 info->full_duplex =
384 (v & AQ2_FW_INTERFACE_OUT_LINK_STATUS_DUPLEX) != 0;
385 info->eee = (v & AQ2_FW_INTERFACE_OUT_LINK_STATUS_EEE) != 0;
386 info->state = v & AQ2_FW_INTERFACE_OUT_LINK_STATUS_STATE;
387
388 return (0);
389 }
390
391 /* A2 firmware stats; layout depends on interface version. */
392 struct aq2_fw_statistics_a0 {
393 uint32_t link_up;
394 uint32_t link_down;
395 uint64_t tx_unicast_octets;
396 uint64_t tx_multicast_octets;
397 uint64_t tx_broadcast_octets;
398 uint64_t rx_unicast_octets;
399 uint64_t rx_multicast_octets;
400 uint64_t rx_broadcast_octets;
401 uint32_t tx_unicast_frames;
402 uint32_t tx_multicast_frames;
403 uint32_t tx_broadcast_frames;
404 uint32_t tx_errors;
405 uint32_t rx_unicast_frames;
406 uint32_t rx_multicast_frames;
407 uint32_t rx_broadcast_frames;
408 uint32_t rx_dropped_frames;
409 uint32_t rx_errors;
410 uint32_t tx_good_frames;
411 uint32_t rx_good_frames;
412 uint32_t reserved1;
413 uint32_t main_loop_cycles;
414 uint32_t reserved2;
415 };
416
417 struct aq2_fw_statistics_b0 {
418 uint64_t rx_good_octets;
419 uint64_t rx_pause_frames;
420 uint64_t rx_good_frames;
421 uint64_t rx_errors;
422 uint64_t rx_unicast_frames;
423 uint64_t rx_multicast_frames;
424 uint64_t rx_broadcast_frames;
425 uint64_t tx_good_octets;
426 uint64_t tx_pause_frames;
427 uint64_t tx_good_frames;
428 uint64_t tx_errors;
429 uint64_t tx_unicast_frames;
430 uint64_t tx_multicast_frames;
431 uint64_t tx_broadcast_frames;
432 uint32_t main_loop_cycles;
433 } __packed;
434
435 union aq2_fw_statistics {
436 struct aq2_fw_statistics_a0 a0;
437 struct aq2_fw_statistics_b0 b0;
438 };
439
440 static int
aq2_fw_get_stats(struct aq_hw * hw,struct aq_hw_stats * stats)441 aq2_fw_get_stats(struct aq_hw *hw, struct aq_hw_stats *stats)
442 {
443 union aq2_fw_statistics u;
444 int err;
445
446 memset(stats, 0, sizeof(*stats));
447
448 err = aq2_fw_interface_buffer_read(hw, AQ2_FW_INTERFACE_OUT_STATS_REG,
449 (uint32_t *)&u, sizeof(u));
450 if (err != 0) {
451 trace_error(hw, dbg_fw,
452 "aq2> statistics read FAILED, error %d", err);
453 return (err);
454 }
455
456 if (hw->aq2_iface == AQ2_FW_INTERFACE_OUT_VERSION_IFACE_VER_A0) {
457 stats->uprc = u.a0.rx_unicast_frames;
458 stats->mprc = u.a0.rx_multicast_frames;
459 stats->bprc = u.a0.rx_broadcast_frames;
460 stats->erpr = u.a0.rx_errors;
461 stats->ubrc = u.a0.rx_unicast_octets;
462 stats->mbrc = u.a0.rx_multicast_octets;
463 stats->bbrc = u.a0.rx_broadcast_octets;
464 stats->prc = u.a0.rx_good_frames;
465 stats->uptc = u.a0.tx_unicast_frames;
466 stats->mptc = u.a0.tx_multicast_frames;
467 stats->bptc = u.a0.tx_broadcast_frames;
468 stats->erpt = u.a0.tx_errors;
469 stats->ubtc = u.a0.tx_unicast_octets;
470 stats->mbtc = u.a0.tx_multicast_octets;
471 stats->bbtc = u.a0.tx_broadcast_octets;
472 stats->ptc = u.a0.tx_good_frames;
473 } else if (hw->aq2_iface == AQ2_FW_INTERFACE_OUT_VERSION_IFACE_VER_B0) {
474 /* B0 reports aggregate octets only; per-cast stays zero. */
475 stats->brc = u.b0.rx_good_octets;
476 stats->btc = u.b0.tx_good_octets;
477 stats->uprc = u.b0.rx_unicast_frames;
478 stats->mprc = u.b0.rx_multicast_frames;
479 stats->bprc = u.b0.rx_broadcast_frames;
480 stats->erpr = u.b0.rx_errors;
481 stats->prc = u.b0.rx_good_frames;
482 stats->uptc = u.b0.tx_unicast_frames;
483 stats->mptc = u.b0.tx_multicast_frames;
484 stats->bptc = u.b0.tx_broadcast_frames;
485 stats->erpt = u.b0.tx_errors;
486 stats->ptc = u.b0.tx_good_frames;
487 } else {
488 trace_warn(hw, dbg_fw,
489 "aq2> unknown F/W interface version %u, no statistics",
490 hw->aq2_iface);
491 }
492
493 return (0);
494 }
495
496 /* Only the A0 interface layout carries the link transition counters. */
497 static int
aq2_fw_get_link_counters(struct aq_hw * hw,uint32_t * up,uint32_t * down)498 aq2_fw_get_link_counters(struct aq_hw *hw, uint32_t *up, uint32_t *down)
499 {
500 union aq2_fw_statistics u;
501 int err;
502
503 if (hw->aq2_iface != AQ2_FW_INTERFACE_OUT_VERSION_IFACE_VER_A0)
504 return (ENOTSUP);
505
506 err = aq2_fw_interface_buffer_read(hw, AQ2_FW_INTERFACE_OUT_STATS_REG,
507 (uint32_t *)&u, sizeof(u));
508 if (err != 0) {
509 trace_error(hw, dbg_fw,
510 "aq2> link counter read FAILED, error %d", err);
511 return (err);
512 }
513
514 *up = u.a0.link_up;
515 *down = u.a0.link_down;
516
517 return (0);
518 }
519
520 static int
aq2_fw_get_temp(struct aq_hw * hw,int * temp_mc)521 aq2_fw_get_temp(struct aq_hw *hw, int *temp_mc)
522 {
523 uint32_t raw;
524 int err;
525
526 err = aq2_fw_interface_buffer_read(hw,
527 AQ2_FW_INTERFACE_OUT_PHY_HEALTH_MONITOR_REG, &raw, sizeof(raw));
528 if (err != 0)
529 return (err);
530
531 /* The F/W zeroes the block until the PHY reports in. */
532 if ((raw & AQ2_FW_INTERFACE_OUT_PHY_HEALTH_MONITOR_READY) == 0) {
533 trace_detail(hw, dbg_fw, "aq2> PHY health monitor not ready");
534 return (ENXIO);
535 }
536
537 /* F/W reports whole degrees Celsius, signed. */
538 *temp_mc = (int)(int8_t)((raw &
539 AQ2_FW_INTERFACE_OUT_PHY_TEMPERATURE) >>
540 AQ2_FW_INTERFACE_OUT_PHY_TEMPERATURE_S) * 1000;
541
542 return (0);
543 }
544
545 /* interface-in thermal_shutdown.shutdown_temperature, whole degC. */
546 static int
aq2_fw_get_thermal_limit(struct aq_hw * hw,int * limit_mc)547 aq2_fw_get_thermal_limit(struct aq_hw *hw, int *limit_mc)
548 {
549 uint32_t v;
550 int temp_c;
551
552 v = AQ_READ_REG(hw, AQ2_FW_INTERFACE_IN_THERMAL_SHUTDOWN_REG);
553 temp_c = (v & AQ2_FW_INTERFACE_IN_THERMAL_SHUTDOWN_TEMP) >>
554 AQ2_FW_INTERFACE_IN_THERMAL_SHUTDOWN_TEMP_S;
555 if (temp_c == 0 || temp_c == 0xff)
556 return (ENXIO);
557 *limit_mc = temp_c * 1000;
558
559 return (0);
560 }
561
562 static int
aq2_fw_get_phy_fault(struct aq_hw * hw,uint16_t * fault)563 aq2_fw_get_phy_fault(struct aq_hw *hw, uint16_t *fault)
564 {
565 uint32_t health, code;
566 int err;
567
568 err = aq2_fw_interface_buffer_read(hw,
569 AQ2_FW_INTERFACE_OUT_PHY_HEALTH_MONITOR_REG, &health,
570 sizeof(health));
571 if (err != 0)
572 return (err);
573
574 if ((health & AQ2_FW_INTERFACE_OUT_PHY_HEALTH_MONITOR_FAULT) == 0) {
575 *fault = 0;
576 return (0);
577 }
578
579 err = aq2_fw_interface_buffer_read(hw,
580 AQ2_FW_INTERFACE_OUT_PHY_FAULT_CODE_REG, &code, sizeof(code));
581 if (err != 0)
582 return (err);
583
584 *fault = (uint16_t)(code & AQ2_FW_INTERFACE_OUT_PHY_FAULT_CODE);
585
586 return (0);
587 }
588
589 static int
aq2_fw_get_phy_hot_warning(struct aq_hw * hw,bool * hot)590 aq2_fw_get_phy_hot_warning(struct aq_hw *hw, bool *hot)
591 {
592 uint32_t health;
593 int err;
594
595 err = aq2_fw_interface_buffer_read(hw,
596 AQ2_FW_INTERFACE_OUT_PHY_HEALTH_MONITOR_REG, &health,
597 sizeof(health));
598 if (err != 0)
599 return (err);
600
601 if ((health & AQ2_FW_INTERFACE_OUT_PHY_HEALTH_MONITOR_READY) == 0)
602 return (ENXIO);
603
604 *hot = (health & AQ2_FW_INTERFACE_OUT_PHY_HEALTH_MONITOR_HOT) != 0;
605
606 return (0);
607 }
608
609 const struct aq_firmware_ops aq2_fw_ops = {
610 .reset = aq2_fw_reset,
611 .set_mode = aq2_fw_set_mode,
612 .get_mode = aq2_fw_get_mode,
613 .get_link_info = aq2_fw_get_link_info,
614 .get_mac_addr = aq2_fw_get_mac_addr,
615 .get_stats = aq2_fw_get_stats,
616 .get_link_counters = aq2_fw_get_link_counters,
617 .get_temp = aq2_fw_get_temp,
618 .get_phy_fault = aq2_fw_get_phy_fault,
619 .get_phy_hot_warning = aq2_fw_get_phy_hot_warning,
620 .phy_reset = NULL, /* A2 clears thermal shutdown on its own reset */
621 .thermal_arm = NULL, /* A2 firmware ships thermal shutdown armed */
622 .get_thermal_limit = aq2_fw_get_thermal_limit,
623 .led_control = NULL,
624 };
625