1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* Atlantic Network Driver |
3 | * |
4 | * Copyright (C) 2014-2019 aQuantia Corporation |
5 | * Copyright (C) 2019-2020 Marvell International Ltd. |
6 | */ |
7 | |
8 | /* File hw_atl_utils_fw2x.c: Definition of firmware 2.x functions for |
9 | * Atlantic hardware abstraction layer. |
10 | */ |
11 | |
12 | #include "../aq_hw.h" |
13 | #include "../aq_hw_utils.h" |
14 | #include "../aq_pci_func.h" |
15 | #include "../aq_ring.h" |
16 | #include "../aq_vec.h" |
17 | #include "../aq_nic.h" |
18 | #include "hw_atl_utils.h" |
19 | #include "hw_atl_llh.h" |
20 | |
21 | #define HW_ATL_FW2X_MPI_LED_ADDR 0x31c |
22 | #define HW_ATL_FW2X_MPI_RPC_ADDR 0x334 |
23 | |
24 | #define HW_ATL_FW2X_MPI_MBOX_ADDR 0x360 |
25 | #define HW_ATL_FW2X_MPI_EFUSE_ADDR 0x364 |
26 | #define HW_ATL_FW2X_MPI_CONTROL_ADDR 0x368 |
27 | #define HW_ATL_FW2X_MPI_CONTROL2_ADDR 0x36C |
28 | #define HW_ATL_FW2X_MPI_STATE_ADDR 0x370 |
29 | #define HW_ATL_FW2X_MPI_STATE2_ADDR 0x374 |
30 | |
31 | #define HW_ATL_FW3X_EXT_CONTROL_ADDR 0x378 |
32 | #define HW_ATL_FW3X_EXT_STATE_ADDR 0x37c |
33 | |
34 | #define HW_ATL_FW3X_PTP_ADJ_LSW_ADDR 0x50a0 |
35 | #define HW_ATL_FW3X_PTP_ADJ_MSW_ADDR 0x50a4 |
36 | |
37 | #define HW_ATL_FW2X_CAP_PAUSE BIT(CAPS_HI_PAUSE) |
38 | #define HW_ATL_FW2X_CAP_ASYM_PAUSE BIT(CAPS_HI_ASYMMETRIC_PAUSE) |
39 | #define HW_ATL_FW2X_CAP_SLEEP_PROXY BIT(CAPS_HI_SLEEP_PROXY) |
40 | #define HW_ATL_FW2X_CAP_WOL BIT(CAPS_HI_WOL) |
41 | |
42 | #define HW_ATL_FW2X_CTRL_WAKE_ON_LINK BIT(CTRL_WAKE_ON_LINK) |
43 | #define HW_ATL_FW2X_CTRL_SLEEP_PROXY BIT(CTRL_SLEEP_PROXY) |
44 | #define HW_ATL_FW2X_CTRL_WOL BIT(CTRL_WOL) |
45 | #define HW_ATL_FW2X_CTRL_LINK_DROP BIT(CTRL_LINK_DROP) |
46 | #define HW_ATL_FW2X_CTRL_PAUSE BIT(CTRL_PAUSE) |
47 | #define HW_ATL_FW2X_CTRL_TEMPERATURE BIT(CTRL_TEMPERATURE) |
48 | #define HW_ATL_FW2X_CTRL_ASYMMETRIC_PAUSE BIT(CTRL_ASYMMETRIC_PAUSE) |
49 | #define HW_ATL_FW2X_CTRL_INT_LOOPBACK BIT(CTRL_INT_LOOPBACK) |
50 | #define HW_ATL_FW2X_CTRL_EXT_LOOPBACK BIT(CTRL_EXT_LOOPBACK) |
51 | #define HW_ATL_FW2X_CTRL_DOWNSHIFT BIT(CTRL_DOWNSHIFT) |
52 | #define HW_ATL_FW2X_CTRL_FORCE_RECONNECT BIT(CTRL_FORCE_RECONNECT) |
53 | |
54 | #define HW_ATL_FW2X_CAP_EEE_1G_MASK BIT(CAPS_HI_1000BASET_FD_EEE) |
55 | #define HW_ATL_FW2X_CAP_EEE_2G5_MASK BIT(CAPS_HI_2P5GBASET_FD_EEE) |
56 | #define HW_ATL_FW2X_CAP_EEE_5G_MASK BIT(CAPS_HI_5GBASET_FD_EEE) |
57 | #define HW_ATL_FW2X_CAP_EEE_10G_MASK BIT(CAPS_HI_10GBASET_FD_EEE) |
58 | |
59 | #define HW_ATL_FW2X_CAP_MACSEC BIT(CAPS_LO_MACSEC) |
60 | |
61 | #define HAL_ATLANTIC_WOL_FILTERS_COUNT 8 |
62 | #define HAL_ATLANTIC_UTILS_FW2X_MSG_WOL 0x0E |
63 | |
64 | #define HW_ATL_FW_VER_LED 0x03010026U |
65 | #define HW_ATL_FW_VER_MEDIA_CONTROL 0x0301005aU |
66 | |
67 | struct __packed fw2x_msg_wol_pattern { |
68 | u8 mask[16]; |
69 | u32 crc; |
70 | }; |
71 | |
72 | struct __packed fw2x_msg_wol { |
73 | u32 msg_id; |
74 | u8 hw_addr[ETH_ALEN]; |
75 | u8 magic_packet_enabled; |
76 | u8 filter_count; |
77 | struct fw2x_msg_wol_pattern filter[HAL_ATLANTIC_WOL_FILTERS_COUNT]; |
78 | u8 link_up_enabled; |
79 | u8 link_down_enabled; |
80 | u16 reserved; |
81 | u32 link_up_timeout; |
82 | u32 link_down_timeout; |
83 | }; |
84 | |
85 | static int aq_fw2x_set_link_speed(struct aq_hw_s *self, u32 speed); |
86 | static int aq_fw2x_set_state(struct aq_hw_s *self, |
87 | enum hal_atl_utils_fw_state_e state); |
88 | |
89 | static u32 aq_fw2x_mbox_get(struct aq_hw_s *self); |
90 | static u32 aq_fw2x_rpc_get(struct aq_hw_s *self); |
91 | static int aq_fw2x_settings_get(struct aq_hw_s *self, u32 *addr); |
92 | static u32 aq_fw2x_state_get(struct aq_hw_s *self); |
93 | static u32 aq_fw2x_state2_get(struct aq_hw_s *self); |
94 | |
95 | static int aq_fw2x_init(struct aq_hw_s *self) |
96 | { |
97 | int err = 0; |
98 | |
99 | /* check 10 times by 1ms */ |
100 | err = readx_poll_timeout_atomic(aq_fw2x_mbox_get, |
101 | self, self->mbox_addr, |
102 | self->mbox_addr != 0U, |
103 | 1000U, 10000U); |
104 | |
105 | err = readx_poll_timeout_atomic(aq_fw2x_rpc_get, |
106 | self, self->rpc_addr, |
107 | self->rpc_addr != 0U, |
108 | 1000U, 100000U); |
109 | |
110 | err = aq_fw2x_settings_get(self, addr: &self->settings_addr); |
111 | |
112 | return err; |
113 | } |
114 | |
115 | static int aq_fw2x_deinit(struct aq_hw_s *self) |
116 | { |
117 | int err = aq_fw2x_set_link_speed(self, speed: 0); |
118 | |
119 | if (!err) |
120 | err = aq_fw2x_set_state(self, state: MPI_DEINIT); |
121 | |
122 | return err; |
123 | } |
124 | |
125 | static enum hw_atl_fw2x_rate link_speed_mask_2fw2x_ratemask(u32 speed) |
126 | { |
127 | enum hw_atl_fw2x_rate rate = 0; |
128 | |
129 | if (speed & AQ_NIC_RATE_10G) |
130 | rate |= FW2X_RATE_10G; |
131 | |
132 | if (speed & AQ_NIC_RATE_5G) |
133 | rate |= FW2X_RATE_5G; |
134 | |
135 | if (speed & AQ_NIC_RATE_2G5) |
136 | rate |= FW2X_RATE_2G5; |
137 | |
138 | if (speed & AQ_NIC_RATE_1G) |
139 | rate |= FW2X_RATE_1G; |
140 | |
141 | if (speed & AQ_NIC_RATE_100M) |
142 | rate |= FW2X_RATE_100M; |
143 | |
144 | return rate; |
145 | } |
146 | |
147 | static u32 fw2x_to_eee_mask(u32 speed) |
148 | { |
149 | u32 rate = 0; |
150 | |
151 | if (speed & HW_ATL_FW2X_CAP_EEE_10G_MASK) |
152 | rate |= AQ_NIC_RATE_EEE_10G; |
153 | if (speed & HW_ATL_FW2X_CAP_EEE_5G_MASK) |
154 | rate |= AQ_NIC_RATE_EEE_5G; |
155 | if (speed & HW_ATL_FW2X_CAP_EEE_2G5_MASK) |
156 | rate |= AQ_NIC_RATE_EEE_2G5; |
157 | if (speed & HW_ATL_FW2X_CAP_EEE_1G_MASK) |
158 | rate |= AQ_NIC_RATE_EEE_1G; |
159 | |
160 | return rate; |
161 | } |
162 | |
163 | static u32 eee_mask_to_fw2x(u32 speed) |
164 | { |
165 | u32 rate = 0; |
166 | |
167 | if (speed & AQ_NIC_RATE_EEE_10G) |
168 | rate |= HW_ATL_FW2X_CAP_EEE_10G_MASK; |
169 | if (speed & AQ_NIC_RATE_EEE_5G) |
170 | rate |= HW_ATL_FW2X_CAP_EEE_5G_MASK; |
171 | if (speed & AQ_NIC_RATE_EEE_2G5) |
172 | rate |= HW_ATL_FW2X_CAP_EEE_2G5_MASK; |
173 | if (speed & AQ_NIC_RATE_EEE_1G) |
174 | rate |= HW_ATL_FW2X_CAP_EEE_1G_MASK; |
175 | |
176 | return rate; |
177 | } |
178 | |
179 | static int aq_fw2x_set_link_speed(struct aq_hw_s *self, u32 speed) |
180 | { |
181 | u32 val = link_speed_mask_2fw2x_ratemask(speed); |
182 | |
183 | aq_hw_write_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL_ADDR, value: val); |
184 | |
185 | return 0; |
186 | } |
187 | |
188 | static void aq_fw2x_upd_flow_control_bits(struct aq_hw_s *self, |
189 | u32 *mpi_state, u32 fc) |
190 | { |
191 | *mpi_state &= ~(HW_ATL_FW2X_CTRL_PAUSE | |
192 | HW_ATL_FW2X_CTRL_ASYMMETRIC_PAUSE); |
193 | |
194 | switch (fc) { |
195 | /* There is not explicit mode of RX only pause frames, |
196 | * thus, we join this mode with FC full. |
197 | * FC full is either Rx, either Tx, or both. |
198 | */ |
199 | case AQ_NIC_FC_FULL: |
200 | case AQ_NIC_FC_RX: |
201 | *mpi_state |= HW_ATL_FW2X_CTRL_PAUSE | |
202 | HW_ATL_FW2X_CTRL_ASYMMETRIC_PAUSE; |
203 | break; |
204 | case AQ_NIC_FC_TX: |
205 | *mpi_state |= HW_ATL_FW2X_CTRL_ASYMMETRIC_PAUSE; |
206 | break; |
207 | } |
208 | } |
209 | |
210 | static void aq_fw2x_upd_eee_rate_bits(struct aq_hw_s *self, u32 *mpi_opts, |
211 | u32 eee_speeds) |
212 | { |
213 | *mpi_opts &= ~(HW_ATL_FW2X_CAP_EEE_1G_MASK | |
214 | HW_ATL_FW2X_CAP_EEE_2G5_MASK | |
215 | HW_ATL_FW2X_CAP_EEE_5G_MASK | |
216 | HW_ATL_FW2X_CAP_EEE_10G_MASK); |
217 | |
218 | *mpi_opts |= eee_mask_to_fw2x(speed: eee_speeds); |
219 | } |
220 | |
221 | static int aq_fw2x_set_state(struct aq_hw_s *self, |
222 | enum hal_atl_utils_fw_state_e state) |
223 | { |
224 | u32 mpi_state = aq_hw_read_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); |
225 | struct aq_nic_cfg_s *cfg = self->aq_nic_cfg; |
226 | |
227 | switch (state) { |
228 | case MPI_INIT: |
229 | mpi_state &= ~BIT(CAPS_HI_LINK_DROP); |
230 | aq_fw2x_upd_eee_rate_bits(self, mpi_opts: &mpi_state, eee_speeds: cfg->eee_speeds); |
231 | aq_fw2x_upd_flow_control_bits(self, mpi_state: &mpi_state, |
232 | fc: self->aq_nic_cfg->fc.req); |
233 | break; |
234 | case MPI_DEINIT: |
235 | mpi_state |= BIT(CAPS_HI_LINK_DROP); |
236 | break; |
237 | case MPI_RESET: |
238 | case MPI_POWER: |
239 | /* No actions */ |
240 | break; |
241 | } |
242 | aq_hw_write_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, value: mpi_state); |
243 | |
244 | return 0; |
245 | } |
246 | |
247 | static int aq_fw2x_update_link_status(struct aq_hw_s *self) |
248 | { |
249 | struct aq_hw_link_status_s *link_status = &self->aq_link_status; |
250 | u32 mpi_state; |
251 | u32 speed; |
252 | |
253 | mpi_state = aq_hw_read_reg(hw: self, HW_ATL_FW2X_MPI_STATE_ADDR); |
254 | speed = mpi_state & (FW2X_RATE_100M | FW2X_RATE_1G | |
255 | FW2X_RATE_2G5 | FW2X_RATE_5G | |
256 | FW2X_RATE_10G); |
257 | |
258 | if (speed) { |
259 | if (speed & FW2X_RATE_10G) |
260 | link_status->mbps = 10000; |
261 | else if (speed & FW2X_RATE_5G) |
262 | link_status->mbps = 5000; |
263 | else if (speed & FW2X_RATE_2G5) |
264 | link_status->mbps = 2500; |
265 | else if (speed & FW2X_RATE_1G) |
266 | link_status->mbps = 1000; |
267 | else if (speed & FW2X_RATE_100M) |
268 | link_status->mbps = 100; |
269 | else |
270 | link_status->mbps = 10000; |
271 | } else { |
272 | link_status->mbps = 0; |
273 | } |
274 | link_status->full_duplex = true; |
275 | |
276 | return 0; |
277 | } |
278 | |
279 | static int aq_fw2x_get_mac_permanent(struct aq_hw_s *self, u8 *mac) |
280 | { |
281 | u32 efuse_addr = aq_hw_read_reg(hw: self, HW_ATL_FW2X_MPI_EFUSE_ADDR); |
282 | u32 mac_addr[2] = { 0 }; |
283 | int err = 0; |
284 | |
285 | if (efuse_addr != 0) { |
286 | err = hw_atl_utils_fw_downld_dwords(self, |
287 | a: efuse_addr + (40U * 4U), |
288 | p: mac_addr, |
289 | ARRAY_SIZE(mac_addr)); |
290 | if (err) |
291 | return err; |
292 | mac_addr[0] = __swab32(mac_addr[0]); |
293 | mac_addr[1] = __swab32(mac_addr[1]); |
294 | } |
295 | |
296 | ether_addr_copy(dst: mac, src: (u8 *)mac_addr); |
297 | |
298 | return err; |
299 | } |
300 | |
301 | static int aq_fw2x_update_stats(struct aq_hw_s *self) |
302 | { |
303 | u32 mpi_opts = aq_hw_read_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); |
304 | u32 orig_stats_val = mpi_opts & BIT(CAPS_HI_STATISTICS); |
305 | u32 stats_val; |
306 | int err = 0; |
307 | |
308 | /* Toggle statistics bit for FW to update */ |
309 | mpi_opts = mpi_opts ^ BIT(CAPS_HI_STATISTICS); |
310 | aq_hw_write_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, value: mpi_opts); |
311 | |
312 | /* Wait FW to report back */ |
313 | err = readx_poll_timeout_atomic(aq_fw2x_state2_get, |
314 | self, stats_val, |
315 | orig_stats_val != (stats_val & |
316 | BIT(CAPS_HI_STATISTICS)), |
317 | 1U, 10000U); |
318 | if (err) |
319 | return err; |
320 | |
321 | return hw_atl_utils_update_stats(self); |
322 | } |
323 | |
324 | static int aq_fw2x_get_phy_temp(struct aq_hw_s *self, int *temp) |
325 | { |
326 | u32 mpi_opts = aq_hw_read_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); |
327 | u32 temp_val = mpi_opts & HW_ATL_FW2X_CTRL_TEMPERATURE; |
328 | u32 phy_temp_offset; |
329 | u32 temp_res; |
330 | int err = 0; |
331 | u32 val; |
332 | |
333 | phy_temp_offset = self->mbox_addr + offsetof(struct hw_atl_utils_mbox, |
334 | info.phy_temperature); |
335 | |
336 | /* Toggle statistics bit for FW to 0x36C.18 (CTRL_TEMPERATURE) */ |
337 | mpi_opts = mpi_opts ^ HW_ATL_FW2X_CTRL_TEMPERATURE; |
338 | aq_hw_write_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, value: mpi_opts); |
339 | /* Wait FW to report back */ |
340 | err = readx_poll_timeout_atomic(aq_fw2x_state2_get, self, val, |
341 | temp_val != |
342 | (val & HW_ATL_FW2X_CTRL_TEMPERATURE), |
343 | 1U, 10000U); |
344 | err = hw_atl_utils_fw_downld_dwords(self, a: phy_temp_offset, |
345 | p: &temp_res, cnt: 1); |
346 | |
347 | if (err) |
348 | return err; |
349 | |
350 | /* Convert PHY temperature from 1/256 degree Celsius |
351 | * to 1/1000 degree Celsius. |
352 | */ |
353 | *temp = (int16_t)(temp_res & 0xFFFF) * 1000 / 256; |
354 | |
355 | return 0; |
356 | } |
357 | |
358 | static int aq_fw2x_set_wol(struct aq_hw_s *self, const u8 *mac) |
359 | { |
360 | struct hw_atl_utils_fw_rpc *rpc = NULL; |
361 | struct offload_info *info = NULL; |
362 | u32 wol_bits = 0; |
363 | u32 rpc_size; |
364 | int err = 0; |
365 | u32 val; |
366 | |
367 | if (self->aq_nic_cfg->wol & WAKE_PHY) { |
368 | aq_hw_write_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, |
369 | HW_ATL_FW2X_CTRL_LINK_DROP); |
370 | readx_poll_timeout_atomic(aq_fw2x_state2_get, self, val, |
371 | (val & |
372 | HW_ATL_FW2X_CTRL_LINK_DROP) != 0, |
373 | 1000, 100000); |
374 | wol_bits |= HW_ATL_FW2X_CTRL_WAKE_ON_LINK; |
375 | } |
376 | |
377 | if (self->aq_nic_cfg->wol & WAKE_MAGIC) { |
378 | wol_bits |= HW_ATL_FW2X_CTRL_SLEEP_PROXY | |
379 | HW_ATL_FW2X_CTRL_WOL; |
380 | |
381 | err = hw_atl_utils_fw_rpc_wait(self, rpc: &rpc); |
382 | if (err < 0) |
383 | goto err_exit; |
384 | |
385 | rpc_size = sizeof(*info) + |
386 | offsetof(struct hw_atl_utils_fw_rpc, fw2x_offloads); |
387 | memset(rpc, 0, rpc_size); |
388 | info = &rpc->fw2x_offloads; |
389 | memcpy(info->mac_addr, mac, ETH_ALEN); |
390 | info->len = sizeof(*info); |
391 | |
392 | err = hw_atl_utils_fw_rpc_call(self, rpc_size); |
393 | if (err < 0) |
394 | goto err_exit; |
395 | } |
396 | |
397 | aq_hw_write_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, value: wol_bits); |
398 | |
399 | err_exit: |
400 | return err; |
401 | } |
402 | |
403 | static int aq_fw2x_set_power(struct aq_hw_s *self, unsigned int power_state, |
404 | const u8 *mac) |
405 | { |
406 | int err = 0; |
407 | |
408 | if (self->aq_nic_cfg->wol) |
409 | err = aq_fw2x_set_wol(self, mac); |
410 | |
411 | return err; |
412 | } |
413 | |
414 | static int aq_fw2x_send_fw_request(struct aq_hw_s *self, |
415 | const struct hw_fw_request_iface *fw_req, |
416 | size_t size) |
417 | { |
418 | u32 ctrl2, orig_ctrl2; |
419 | u32 dword_cnt; |
420 | int err = 0; |
421 | u32 val; |
422 | |
423 | /* Write data to drvIface Mailbox */ |
424 | dword_cnt = size / sizeof(u32); |
425 | if (size % sizeof(u32)) |
426 | dword_cnt++; |
427 | err = hw_atl_write_fwcfg_dwords(self, p: (void *)fw_req, cnt: dword_cnt); |
428 | if (err < 0) |
429 | goto err_exit; |
430 | |
431 | /* Toggle statistics bit for FW to update */ |
432 | ctrl2 = aq_hw_read_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); |
433 | orig_ctrl2 = ctrl2 & BIT(CAPS_HI_FW_REQUEST); |
434 | ctrl2 = ctrl2 ^ BIT(CAPS_HI_FW_REQUEST); |
435 | aq_hw_write_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, value: ctrl2); |
436 | |
437 | /* Wait FW to report back */ |
438 | err = readx_poll_timeout_atomic(aq_fw2x_state2_get, self, val, |
439 | orig_ctrl2 != (val & |
440 | BIT(CAPS_HI_FW_REQUEST)), |
441 | 1U, 10000U); |
442 | |
443 | err_exit: |
444 | return err; |
445 | } |
446 | |
447 | static void aq_fw3x_enable_ptp(struct aq_hw_s *self, int enable) |
448 | { |
449 | u32 ptp_opts = aq_hw_read_reg(hw: self, HW_ATL_FW3X_EXT_STATE_ADDR); |
450 | u32 all_ptp_features = BIT(CAPS_EX_PHY_PTP_EN) | |
451 | BIT(CAPS_EX_PTP_GPIO_EN); |
452 | |
453 | if (enable) |
454 | ptp_opts |= all_ptp_features; |
455 | else |
456 | ptp_opts &= ~all_ptp_features; |
457 | |
458 | aq_hw_write_reg(hw: self, HW_ATL_FW3X_EXT_CONTROL_ADDR, value: ptp_opts); |
459 | } |
460 | |
461 | static void aq_fw3x_adjust_ptp(struct aq_hw_s *self, uint64_t adj) |
462 | { |
463 | aq_hw_write_reg(hw: self, HW_ATL_FW3X_PTP_ADJ_LSW_ADDR, |
464 | value: (adj >> 0) & 0xffffffff); |
465 | aq_hw_write_reg(hw: self, HW_ATL_FW3X_PTP_ADJ_MSW_ADDR, |
466 | value: (adj >> 32) & 0xffffffff); |
467 | } |
468 | |
469 | static int aq_fw2x_led_control(struct aq_hw_s *self, u32 mode) |
470 | { |
471 | if (self->fw_ver_actual < HW_ATL_FW_VER_LED) |
472 | return -EOPNOTSUPP; |
473 | |
474 | aq_hw_write_reg(hw: self, HW_ATL_FW2X_MPI_LED_ADDR, value: mode); |
475 | |
476 | return 0; |
477 | } |
478 | |
479 | static int aq_fw2x_set_eee_rate(struct aq_hw_s *self, u32 speed) |
480 | { |
481 | u32 mpi_opts = aq_hw_read_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); |
482 | |
483 | aq_fw2x_upd_eee_rate_bits(self, mpi_opts: &mpi_opts, eee_speeds: speed); |
484 | |
485 | aq_hw_write_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, value: mpi_opts); |
486 | |
487 | return 0; |
488 | } |
489 | |
490 | static int aq_fw2x_get_eee_rate(struct aq_hw_s *self, u32 *rate, |
491 | u32 *supported_rates) |
492 | { |
493 | u32 mpi_state; |
494 | u32 caps_hi; |
495 | int err = 0; |
496 | u32 offset; |
497 | |
498 | offset = self->mbox_addr + offsetof(struct hw_atl_utils_mbox, |
499 | info.caps_hi); |
500 | |
501 | err = hw_atl_utils_fw_downld_dwords(self, a: offset, p: &caps_hi, cnt: 1); |
502 | |
503 | if (err) |
504 | return err; |
505 | |
506 | *supported_rates = fw2x_to_eee_mask(speed: caps_hi); |
507 | |
508 | mpi_state = aq_fw2x_state2_get(self); |
509 | *rate = fw2x_to_eee_mask(speed: mpi_state); |
510 | |
511 | return err; |
512 | } |
513 | |
514 | static int aq_fw2x_renegotiate(struct aq_hw_s *self) |
515 | { |
516 | u32 mpi_opts = aq_hw_read_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); |
517 | |
518 | mpi_opts |= BIT(CTRL_FORCE_RECONNECT); |
519 | |
520 | aq_hw_write_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, value: mpi_opts); |
521 | |
522 | return 0; |
523 | } |
524 | |
525 | static int aq_fw2x_set_flow_control(struct aq_hw_s *self) |
526 | { |
527 | u32 mpi_state = aq_hw_read_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); |
528 | |
529 | aq_fw2x_upd_flow_control_bits(self, mpi_state: &mpi_state, |
530 | fc: self->aq_nic_cfg->fc.req); |
531 | |
532 | aq_hw_write_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, value: mpi_state); |
533 | |
534 | return 0; |
535 | } |
536 | |
537 | static u32 aq_fw2x_get_flow_control(struct aq_hw_s *self, u32 *fcmode) |
538 | { |
539 | u32 mpi_state = aq_fw2x_state2_get(self); |
540 | *fcmode = 0; |
541 | |
542 | if (mpi_state & HW_ATL_FW2X_CAP_PAUSE) |
543 | *fcmode |= AQ_NIC_FC_RX; |
544 | |
545 | if (mpi_state & HW_ATL_FW2X_CAP_ASYM_PAUSE) |
546 | *fcmode |= AQ_NIC_FC_TX; |
547 | |
548 | return 0; |
549 | } |
550 | |
551 | static int aq_fw2x_set_phyloopback(struct aq_hw_s *self, u32 mode, bool enable) |
552 | { |
553 | u32 mpi_opts; |
554 | |
555 | switch (mode) { |
556 | case AQ_HW_LOOPBACK_PHYINT_SYS: |
557 | mpi_opts = aq_hw_read_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); |
558 | if (enable) |
559 | mpi_opts |= HW_ATL_FW2X_CTRL_INT_LOOPBACK; |
560 | else |
561 | mpi_opts &= ~HW_ATL_FW2X_CTRL_INT_LOOPBACK; |
562 | aq_hw_write_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, value: mpi_opts); |
563 | break; |
564 | case AQ_HW_LOOPBACK_PHYEXT_SYS: |
565 | mpi_opts = aq_hw_read_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); |
566 | if (enable) |
567 | mpi_opts |= HW_ATL_FW2X_CTRL_EXT_LOOPBACK; |
568 | else |
569 | mpi_opts &= ~HW_ATL_FW2X_CTRL_EXT_LOOPBACK; |
570 | aq_hw_write_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, value: mpi_opts); |
571 | break; |
572 | default: |
573 | return -EINVAL; |
574 | } |
575 | |
576 | return 0; |
577 | } |
578 | |
579 | static u32 aq_fw2x_mbox_get(struct aq_hw_s *self) |
580 | { |
581 | return aq_hw_read_reg(hw: self, HW_ATL_FW2X_MPI_MBOX_ADDR); |
582 | } |
583 | |
584 | static u32 aq_fw2x_rpc_get(struct aq_hw_s *self) |
585 | { |
586 | return aq_hw_read_reg(hw: self, HW_ATL_FW2X_MPI_RPC_ADDR); |
587 | } |
588 | |
589 | static int aq_fw2x_settings_get(struct aq_hw_s *self, u32 *addr) |
590 | { |
591 | int err = 0; |
592 | u32 offset; |
593 | |
594 | offset = self->mbox_addr + offsetof(struct hw_atl_utils_mbox, |
595 | info.setting_address); |
596 | |
597 | err = hw_atl_utils_fw_downld_dwords(self, a: offset, p: addr, cnt: 1); |
598 | |
599 | return err; |
600 | } |
601 | |
602 | static u32 aq_fw2x_state_get(struct aq_hw_s *self) |
603 | { |
604 | return aq_hw_read_reg(hw: self, HW_ATL_FW2X_MPI_STATE_ADDR); |
605 | } |
606 | |
607 | static u32 aq_fw2x_state2_get(struct aq_hw_s *self) |
608 | { |
609 | return aq_hw_read_reg(hw: self, HW_ATL_FW2X_MPI_STATE2_ADDR); |
610 | } |
611 | |
612 | static int aq_fw2x_set_downshift(struct aq_hw_s *self, u32 counter) |
613 | { |
614 | int err = 0; |
615 | u32 mpi_opts; |
616 | u32 offset; |
617 | |
618 | offset = offsetof(struct hw_atl_utils_settings, downshift_retry_count); |
619 | err = hw_atl_write_fwsettings_dwords(self, offset, p: &counter, cnt: 1); |
620 | if (err) |
621 | return err; |
622 | |
623 | mpi_opts = aq_hw_read_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR); |
624 | if (counter) |
625 | mpi_opts |= HW_ATL_FW2X_CTRL_DOWNSHIFT; |
626 | else |
627 | mpi_opts &= ~HW_ATL_FW2X_CTRL_DOWNSHIFT; |
628 | aq_hw_write_reg(hw: self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, value: mpi_opts); |
629 | |
630 | return err; |
631 | } |
632 | |
633 | static int aq_fw2x_set_media_detect(struct aq_hw_s *self, bool on) |
634 | { |
635 | u32 enable; |
636 | u32 offset; |
637 | |
638 | if (self->fw_ver_actual < HW_ATL_FW_VER_MEDIA_CONTROL) |
639 | return -EOPNOTSUPP; |
640 | |
641 | offset = offsetof(struct hw_atl_utils_settings, media_detect); |
642 | enable = on; |
643 | |
644 | return hw_atl_write_fwsettings_dwords(self, offset, p: &enable, cnt: 1); |
645 | } |
646 | |
647 | static u32 aq_fw2x_get_link_capabilities(struct aq_hw_s *self) |
648 | { |
649 | int err = 0; |
650 | u32 offset; |
651 | u32 val; |
652 | |
653 | offset = self->mbox_addr + |
654 | offsetof(struct hw_atl_utils_mbox, info.caps_lo); |
655 | |
656 | err = hw_atl_utils_fw_downld_dwords(self, a: offset, p: &val, cnt: 1); |
657 | |
658 | if (err) |
659 | return 0; |
660 | |
661 | return val; |
662 | } |
663 | |
664 | static int aq_fw2x_send_macsec_req(struct aq_hw_s *hw, |
665 | struct macsec_msg_fw_request *req, |
666 | struct macsec_msg_fw_response *response) |
667 | { |
668 | u32 low_status, low_req = 0; |
669 | u32 dword_cnt; |
670 | u32 caps_lo; |
671 | u32 offset; |
672 | int err; |
673 | |
674 | if (!req || !response) |
675 | return -EINVAL; |
676 | |
677 | caps_lo = aq_fw2x_get_link_capabilities(self: hw); |
678 | if (!(caps_lo & BIT(CAPS_LO_MACSEC))) |
679 | return -EOPNOTSUPP; |
680 | |
681 | /* Write macsec request to cfg memory */ |
682 | dword_cnt = (sizeof(*req) + sizeof(u32) - 1) / sizeof(u32); |
683 | err = hw_atl_write_fwcfg_dwords(self: hw, p: (void *)req, cnt: dword_cnt); |
684 | if (err < 0) |
685 | return err; |
686 | |
687 | /* Toggle 0x368.CAPS_LO_MACSEC bit */ |
688 | low_req = aq_hw_read_reg(hw, HW_ATL_FW2X_MPI_CONTROL_ADDR); |
689 | low_req ^= HW_ATL_FW2X_CAP_MACSEC; |
690 | aq_hw_write_reg(hw, HW_ATL_FW2X_MPI_CONTROL_ADDR, value: low_req); |
691 | |
692 | /* Wait FW to report back */ |
693 | err = readx_poll_timeout_atomic(aq_fw2x_state_get, hw, low_status, |
694 | low_req != (low_status & BIT(CAPS_LO_MACSEC)), 1U, 10000U); |
695 | if (err) |
696 | return -EIO; |
697 | |
698 | /* Read status of write operation */ |
699 | offset = hw->rpc_addr + sizeof(u32); |
700 | err = hw_atl_utils_fw_downld_dwords(self: hw, a: offset, p: (u32 *)(void *)response, |
701 | cnt: sizeof(*response) / sizeof(u32)); |
702 | |
703 | return err; |
704 | } |
705 | |
706 | const struct aq_fw_ops aq_fw_2x_ops = { |
707 | .init = aq_fw2x_init, |
708 | .deinit = aq_fw2x_deinit, |
709 | .reset = NULL, |
710 | .renegotiate = aq_fw2x_renegotiate, |
711 | .get_mac_permanent = aq_fw2x_get_mac_permanent, |
712 | .set_link_speed = aq_fw2x_set_link_speed, |
713 | .set_state = aq_fw2x_set_state, |
714 | .update_link_status = aq_fw2x_update_link_status, |
715 | .update_stats = aq_fw2x_update_stats, |
716 | .get_mac_temp = NULL, |
717 | .get_phy_temp = aq_fw2x_get_phy_temp, |
718 | .set_power = aq_fw2x_set_power, |
719 | .set_eee_rate = aq_fw2x_set_eee_rate, |
720 | .get_eee_rate = aq_fw2x_get_eee_rate, |
721 | .set_flow_control = aq_fw2x_set_flow_control, |
722 | .get_flow_control = aq_fw2x_get_flow_control, |
723 | .send_fw_request = aq_fw2x_send_fw_request, |
724 | .enable_ptp = aq_fw3x_enable_ptp, |
725 | .led_control = aq_fw2x_led_control, |
726 | .set_phyloopback = aq_fw2x_set_phyloopback, |
727 | .set_downshift = aq_fw2x_set_downshift, |
728 | .set_media_detect = aq_fw2x_set_media_detect, |
729 | .adjust_ptp = aq_fw3x_adjust_ptp, |
730 | .get_link_capabilities = aq_fw2x_get_link_capabilities, |
731 | .send_macsec_req = aq_fw2x_send_macsec_req, |
732 | }; |
733 | |