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