xref: /freebsd/sys/dev/aq/aq2_fw.c (revision af3f2dd124f07342c273dc017bc5e73639e446a5)
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