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