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