File Index
Files and directories
Sign in
FILE

hw_atl2_utils_fw.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils_fw.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/* Atlantic Network Driver
  3 * Copyright (C) 2020 Marvell International Ltd.
  4 */
  5
  6#include <linux/iopoll.h>
  7
  8#include "aq_hw.h"
  9#include "aq_hw_utils.h"
 10#include "aq_nic.h"
 11#include "hw_atl/hw_atl_llh.h"
 12#include "hw_atl2_utils.h"
 13#include "hw_atl2_llh.h"
 14#include "hw_atl2_internal.h"
 15
 16#define AQ_A2_FW_READ_TRY_MAX 1000
 17
 18#define hw_atl2_shared_buffer_write(HW, ITEM, VARIABLE) \
 19{\
 20	BUILD_BUG_ON_MSG((offsetof(struct fw_interface_in, ITEM) % \
 21			 sizeof(u32)) != 0,\
 22			 "Unaligned write " # ITEM);\
 23	BUILD_BUG_ON_MSG((sizeof(VARIABLE) %  sizeof(u32)) != 0,\
 24			 "Unaligned write length " # ITEM);\
 25	hw_atl2_mif_shared_buf_write(HW,\
 26		(offsetof(struct fw_interface_in, ITEM) / sizeof(u32)),\
 27		(u32 *)&(VARIABLE), sizeof(VARIABLE) / sizeof(u32));\
 28}
 29
 30#define hw_atl2_shared_buffer_get(HW, ITEM, VARIABLE) \
 31{\
 32	BUILD_BUG_ON_MSG((offsetof(struct fw_interface_in, ITEM) % \
 33			 sizeof(u32)) != 0,\
 34			 "Unaligned get " # ITEM);\
 35	BUILD_BUG_ON_MSG((sizeof(VARIABLE) %  sizeof(u32)) != 0,\
 36			 "Unaligned get length " # ITEM);\
 37	hw_atl2_mif_shared_buf_get(HW, \
 38		(offsetof(struct fw_interface_in, ITEM) / sizeof(u32)),\
 39		(u32 *)&(VARIABLE), \
 40		sizeof(VARIABLE) / sizeof(u32));\
 41}
 42
 43/* This should never be used on non atomic fields,
 44 * treat any > u32 read as non atomic.
 45 */
 46#define hw_atl2_shared_buffer_read(HW, ITEM, VARIABLE) \
 47{\
 48	BUILD_BUG_ON_MSG((offsetof(struct fw_interface_out, ITEM) % \
 49			 sizeof(u32)) != 0,\
 50			 "Unaligned read " # ITEM);\
 51	BUILD_BUG_ON_MSG((sizeof(VARIABLE) %  sizeof(u32)) != 0,\
 52			 "Unaligned read length " # ITEM);\
 53	BUILD_BUG_ON_MSG(sizeof(VARIABLE) > sizeof(u32),\
 54			 "Non atomic read " # ITEM);\
 55	hw_atl2_mif_shared_buf_read(HW, \
 56		(offsetof(struct fw_interface_out, ITEM) / sizeof(u32)),\
 57		(u32 *)&(VARIABLE), sizeof(VARIABLE) / sizeof(u32));\
 58}
 59
 60#define hw_atl2_shared_buffer_read_safe(HW, ITEM, DATA) \
 61({\
 62	BUILD_BUG_ON_MSG((offsetof(struct fw_interface_out, ITEM) % \
 63			 sizeof(u32)) != 0,\
 64			 "Unaligned read_safe " # ITEM);\
 65	BUILD_BUG_ON_MSG((sizeof(((struct fw_interface_out *)0)->ITEM) % \
 66			 sizeof(u32)) != 0,\
 67			 "Unaligned read_safe length " # ITEM);\
 68	hw_atl2_shared_buffer_read_block((HW), \
 69		(offsetof(struct fw_interface_out, ITEM) / sizeof(u32)),\
 70		sizeof(((struct fw_interface_out *)0)->ITEM) / sizeof(u32),\
 71		(DATA));\
 72})
 73
 74static int hw_atl2_shared_buffer_read_block(struct aq_hw_s *self,
 75					    u32 offset, u32 dwords, void *data)
 76{
 77	struct transaction_counter_s tid1, tid2;
 78	int cnt = 0;
 79
 80	do {
 81		do {
 82			hw_atl2_shared_buffer_read(self, transaction_id, tid1);
 83			cnt++;
 84			if (cnt > AQ_A2_FW_READ_TRY_MAX)
 85				return -ETIME;
 86			if (tid1.transaction_cnt_a != tid1.transaction_cnt_b)
 87				mdelay(1);
 88		} while (tid1.transaction_cnt_a != tid1.transaction_cnt_b);
 89
 90		hw_atl2_mif_shared_buf_read(self, offset, (u32 *)data, dwords);
 91
 92		hw_atl2_shared_buffer_read(self, transaction_id, tid2);
 93
 94		cnt++;
 95		if (cnt > AQ_A2_FW_READ_TRY_MAX)
 96			return -ETIME;
 97	} while (tid2.transaction_cnt_a != tid2.transaction_cnt_b ||
 98		 tid1.transaction_cnt_a != tid2.transaction_cnt_a);
 99
100	return 0;
101}
102
103static inline int hw_atl2_shared_buffer_finish_ack(struct aq_hw_s *self)
104{
105	u32 val;
106	int err;
107
108	hw_atl2_mif_host_finished_write_set(self, 1U);
109	err = readx_poll_timeout_atomic(hw_atl2_mif_mcp_finished_read_get,
110					self, val, val == 0U,
111					100, 100000U);
112	WARN(err, "hw_atl2_shared_buffer_finish_ack");
113
114	return err;
115}
116
117static int aq_a2_fw_init(struct aq_hw_s *self)
118{
119	struct link_control_s link_control;
120	u32 mtu;
121	u32 val;
122	int err;
123
124	hw_atl2_shared_buffer_get(self, link_control, link_control);
125	link_control.mode = AQ_HOST_MODE_ACTIVE;
126	hw_atl2_shared_buffer_write(self, link_control, link_control);
127
128	hw_atl2_shared_buffer_get(self, mtu, mtu);
129	mtu = HW_ATL2_MTU_JUMBO;
130	hw_atl2_shared_buffer_write(self, mtu, mtu);
131
132	hw_atl2_mif_host_finished_write_set(self, 1U);
133	err = readx_poll_timeout_atomic(hw_atl2_mif_mcp_finished_read_get,
134					self, val, val == 0U,
135					100, 5000000U);
136	WARN(err, "hw_atl2_shared_buffer_finish_ack");
137
138	return err;
139}
140
141static int aq_a2_fw_deinit(struct aq_hw_s *self)
142{
143	struct link_control_s link_control;
144
145	hw_atl2_shared_buffer_get(self, link_control, link_control);
146	link_control.mode = AQ_HOST_MODE_SHUTDOWN;
147	hw_atl2_shared_buffer_write(self, link_control, link_control);
148
149	return hw_atl2_shared_buffer_finish_ack(self);
150}
151
152static void a2_link_speed_mask2fw(u32 speed,
153				  struct link_options_s *link_options)
154{
155	link_options->rate_10G = !!(speed & AQ_NIC_RATE_10G);
156	link_options->rate_5G = !!(speed & AQ_NIC_RATE_5G);
157	link_options->rate_N5G = link_options->rate_5G;
158	link_options->rate_2P5G = !!(speed & AQ_NIC_RATE_2G5);
159	link_options->rate_N2P5G = link_options->rate_2P5G;
160	link_options->rate_1G = !!(speed & AQ_NIC_RATE_1G);
161	link_options->rate_100M = !!(speed & AQ_NIC_RATE_100M);
162	link_options->rate_10M = !!(speed & AQ_NIC_RATE_10M);
163
164	link_options->rate_1G_hd = !!(speed & AQ_NIC_RATE_1G_HALF);
165	link_options->rate_100M_hd = !!(speed & AQ_NIC_RATE_100M_HALF);
166	link_options->rate_10M_hd = !!(speed & AQ_NIC_RATE_10M_HALF);
167}
168
169static u32 a2_fw_dev_to_eee_mask(struct device_link_caps_s *device_link_caps)
170{
171	u32 rate = 0;
172
173	if (device_link_caps->eee_10G)
174		rate |= AQ_NIC_RATE_EEE_10G;
175	if (device_link_caps->eee_5G)
176		rate |= AQ_NIC_RATE_EEE_5G;
177	if (device_link_caps->eee_2P5G)
178		rate |= AQ_NIC_RATE_EEE_2G5;
179	if (device_link_caps->eee_1G)
180		rate |= AQ_NIC_RATE_EEE_1G;
181	if (device_link_caps->eee_100M)
182		rate |= AQ_NIC_RATE_EEE_100M;
183
184	return rate;
185}
186
187static u32 a2_fw_lkp_to_mask(struct lkp_link_caps_s *lkp_link_caps)
188{
189	u32 rate = 0;
190
191	if (lkp_link_caps->rate_10G)
192		rate |= AQ_NIC_RATE_10G;
193	if (lkp_link_caps->rate_5G)
194		rate |= AQ_NIC_RATE_5G;
195	if (lkp_link_caps->rate_2P5G)
196		rate |= AQ_NIC_RATE_2G5;
197	if (lkp_link_caps->rate_1G)
198		rate |= AQ_NIC_RATE_1G;
199	if (lkp_link_caps->rate_1G_hd)
200		rate |= AQ_NIC_RATE_1G_HALF;
201	if (lkp_link_caps->rate_100M)
202		rate |= AQ_NIC_RATE_100M;
203	if (lkp_link_caps->rate_100M_hd)
204		rate |= AQ_NIC_RATE_100M_HALF;
205	if (lkp_link_caps->rate_10M)
206		rate |= AQ_NIC_RATE_10M;
207	if (lkp_link_caps->rate_10M_hd)
208		rate |= AQ_NIC_RATE_10M_HALF;
209
210	if (lkp_link_caps->eee_10G)
211		rate |= AQ_NIC_RATE_EEE_10G;
212	if (lkp_link_caps->eee_5G)
213		rate |= AQ_NIC_RATE_EEE_5G;
214	if (lkp_link_caps->eee_2P5G)
215		rate |= AQ_NIC_RATE_EEE_2G5;
216	if (lkp_link_caps->eee_1G)
217		rate |= AQ_NIC_RATE_EEE_1G;
218	if (lkp_link_caps->eee_100M)
219		rate |= AQ_NIC_RATE_EEE_100M;
220
221	return rate;
222}
223
224static int aq_a2_fw_set_link_speed(struct aq_hw_s *self, u32 speed)
225{
226	struct link_options_s link_options;
227
228	hw_atl2_shared_buffer_get(self, link_options, link_options);
229	link_options.link_up = 1U;
230	a2_link_speed_mask2fw(speed, &link_options);
231	hw_atl2_shared_buffer_write(self, link_options, link_options);
232
233	return hw_atl2_shared_buffer_finish_ack(self);
234}
235
236static void aq_a2_fw_set_mpi_flow_control(struct aq_hw_s *self,
237					  struct link_options_s *link_options)
238{
239	u32 flow_control = self->aq_nic_cfg->fc.req;
240
241	link_options->pause_rx = !!(flow_control & AQ_NIC_FC_RX);
242	link_options->pause_tx = !!(flow_control & AQ_NIC_FC_TX);
243}
244
245static void aq_a2_fw_upd_eee_rate_bits(struct aq_hw_s *self,
246				       struct link_options_s *link_options,
247				       u32 eee_speeds)
248{
249	link_options->eee_10G =  !!(eee_speeds & AQ_NIC_RATE_EEE_10G);
250	link_options->eee_5G = !!(eee_speeds & AQ_NIC_RATE_EEE_5G);
251	link_options->eee_2P5G = !!(eee_speeds & AQ_NIC_RATE_EEE_2G5);
252	link_options->eee_1G = !!(eee_speeds & AQ_NIC_RATE_EEE_1G);
253	link_options->eee_100M = !!(eee_speeds & AQ_NIC_RATE_EEE_100M);
254}
255
256static int aq_a2_fw_set_state(struct aq_hw_s *self,
257			      enum hal_atl_utils_fw_state_e state)
258{
259	struct link_options_s link_options;
260
261	hw_atl2_shared_buffer_get(self, link_options, link_options);
262
263	switch (state) {
264	case MPI_INIT:
265		link_options.link_up = 1U;
266		aq_a2_fw_upd_eee_rate_bits(self, &link_options,
267					   self->aq_nic_cfg->eee_speeds);
268		aq_a2_fw_set_mpi_flow_control(self, &link_options);
269		break;
270	case MPI_DEINIT:
271		link_options.link_up = 0U;
272		break;
273	case MPI_RESET:
274	case MPI_POWER:
275		/* No actions */
276		break;
277	}
278
279	hw_atl2_shared_buffer_write(self, link_options, link_options);
280
281	return hw_atl2_shared_buffer_finish_ack(self);
282}
283
284static int aq_a2_fw_update_link_status(struct aq_hw_s *self)
285{
286	struct lkp_link_caps_s lkp_link_caps;
287	struct link_status_s link_status;
288
289	hw_atl2_shared_buffer_read(self, link_status, link_status);
290
291	switch (link_status.link_rate) {
292	case AQ_A2_FW_LINK_RATE_10G:
293		self->aq_link_status.mbps = 10000;
294		break;
295	case AQ_A2_FW_LINK_RATE_5G:
296		self->aq_link_status.mbps = 5000;
297		break;
298	case AQ_A2_FW_LINK_RATE_2G5:
299		self->aq_link_status.mbps = 2500;
300		break;
301	case AQ_A2_FW_LINK_RATE_1G:
302		self->aq_link_status.mbps = 1000;
303		break;
304	case AQ_A2_FW_LINK_RATE_100M:
305		self->aq_link_status.mbps = 100;
306		break;
307	case AQ_A2_FW_LINK_RATE_10M:
308		self->aq_link_status.mbps = 10;
309		break;
310	default:
311		self->aq_link_status.mbps = 0;
312	}
313	self->aq_link_status.full_duplex = link_status.duplex;
314
315	hw_atl2_shared_buffer_read(self, lkp_link_caps, lkp_link_caps);
316
317	self->aq_link_status.lp_link_speed_msk =
318				 a2_fw_lkp_to_mask(&lkp_link_caps);
319	self->aq_link_status.lp_flow_control =
320				((lkp_link_caps.pause_rx) ? AQ_NIC_FC_RX : 0) |
321				((lkp_link_caps.pause_tx) ? AQ_NIC_FC_TX : 0);
322
323	return 0;
324}
325
326static int aq_a2_fw_get_mac_permanent(struct aq_hw_s *self, u8 *mac)
327{
328	struct mac_address_aligned_s mac_address;
329
330	hw_atl2_shared_buffer_get(self, mac_address, mac_address);
331	ether_addr_copy(mac, (u8 *)mac_address.aligned.mac_address);
332
333	return 0;
334}
335
336static void aq_a2_fill_a0_stats(struct aq_hw_s *self,
337				struct statistics_s *stats)
338{
339	struct hw_atl2_priv *priv = self->priv;
340	struct aq_stats_s *cs = &self->curr_stats;
341	struct aq_stats_s curr_stats = *cs;
342	bool corrupted_stats = false;
343
344#define AQ_SDELTA(_N, _F)  \
345do { \
346	if (!corrupted_stats && \
347	    ((s64)(stats->a0.msm._F - priv->last_stats.a0.msm._F)) >= 0) \
348		curr_stats._N += stats->a0.msm._F - priv->last_stats.a0.msm._F;\
349	else \
350		corrupted_stats = true; \
351} while (0)
352
353	if (self->aq_link_status.mbps) {
354		AQ_SDELTA(uprc, rx_unicast_frames);
355		AQ_SDELTA(mprc, rx_multicast_frames);
356		AQ_SDELTA(bprc, rx_broadcast_frames);
357		AQ_SDELTA(erpr, rx_error_frames);
358
359		AQ_SDELTA(uptc, tx_unicast_frames);
360		AQ_SDELTA(mptc, tx_multicast_frames);
361		AQ_SDELTA(bptc, tx_broadcast_frames);
362		AQ_SDELTA(erpt, tx_errors);
363
364		AQ_SDELTA(ubrc, rx_unicast_octets);
365		AQ_SDELTA(ubtc, tx_unicast_octets);
366		AQ_SDELTA(mbrc, rx_multicast_octets);
367		AQ_SDELTA(mbtc, tx_multicast_octets);
368		AQ_SDELTA(bbrc, rx_broadcast_octets);
369		AQ_SDELTA(bbtc, tx_broadcast_octets);
370
371		if (!corrupted_stats)
372			*cs = curr_stats;
373	}
374#undef AQ_SDELTA
375
376}
377
378static void aq_a2_fill_b0_stats(struct aq_hw_s *self,
379				struct statistics_s *stats)
380{
381	struct hw_atl2_priv *priv = self->priv;
382	struct aq_stats_s *cs = &self->curr_stats;
383	struct aq_stats_s curr_stats = *cs;
384	bool corrupted_stats = false;
385
386#define AQ_SDELTA(_N, _F)  \
387do { \
388	if (!corrupted_stats && \
389	    ((s64)(stats->b0._F - priv->last_stats.b0._F)) >= 0) \
390		curr_stats._N += stats->b0._F - priv->last_stats.b0._F; \
391	else \
392		corrupted_stats = true; \
393} while (0)
394
395	if (self->aq_link_status.mbps) {
396		AQ_SDELTA(uprc, rx_unicast_frames);
397		AQ_SDELTA(mprc, rx_multicast_frames);
398		AQ_SDELTA(bprc, rx_broadcast_frames);
399		AQ_SDELTA(erpr, rx_errors);
400		AQ_SDELTA(brc, rx_good_octets);
401
402		AQ_SDELTA(uptc, tx_unicast_frames);
403		AQ_SDELTA(mptc, tx_multicast_frames);
404		AQ_SDELTA(bptc, tx_broadcast_frames);
405		AQ_SDELTA(erpt, tx_errors);
406		AQ_SDELTA(btc, tx_good_octets);
407
408		if (!corrupted_stats)
409			*cs = curr_stats;
410	}
411#undef AQ_SDELTA
412}
413
414static int aq_a2_fw_update_stats(struct aq_hw_s *self)
415{
416	struct aq_stats_s *cs = &self->curr_stats;
417	struct hw_atl2_priv *priv = self->priv;
418	struct statistics_s stats;
419	struct version_s version;
420	int err;
421
422	err = hw_atl2_shared_buffer_read_safe(self, version, &version);
423	if (err)
424		return err;
425
426	err = hw_atl2_shared_buffer_read_safe(self, stats, &stats);
427	if (err)
428		return err;
429
430	if (version.drv_iface_ver == AQ_A2_FW_INTERFACE_A0)
431		aq_a2_fill_a0_stats(self, &stats);
432	else
433		aq_a2_fill_b0_stats(self, &stats);
434
435	cs->dma_pkt_rc = hw_atl_stats_rx_dma_good_pkt_counter_get(self);
436	cs->dma_pkt_tc = hw_atl_stats_tx_dma_good_pkt_counter_get(self);
437	cs->dma_oct_rc = hw_atl_stats_rx_dma_good_octet_counter_get(self);
438	cs->dma_oct_tc = hw_atl_stats_tx_dma_good_octet_counter_get(self);
439	cs->dpc = hw_atl_rpb_rx_dma_drop_pkt_cnt_get(self);
440
441	memcpy(&priv->last_stats, &stats, sizeof(stats));
442
443	return 0;
444}
445
446static int aq_a2_fw_get_phy_temp(struct aq_hw_s *self, int *temp)
447{
448	struct phy_health_monitor_s phy_health_monitor;
449
450	hw_atl2_shared_buffer_read_safe(self, phy_health_monitor,
451					&phy_health_monitor);
452
453	*temp = (int8_t)phy_health_monitor.phy_temperature * 1000;
454	return 0;
455}
456
457static int aq_a2_fw_get_mac_temp(struct aq_hw_s *self, int *temp)
458{
459	/* There's only one temperature sensor on A2, use it for
460	 * both MAC and PHY.
461	 */
462	return aq_a2_fw_get_phy_temp(self, temp);
463}
464
465static int aq_a2_fw_set_wol_params(struct aq_hw_s *self, const u8 *mac, u32 wol)
466{
467	struct mac_address_aligned_s mac_address;
468	struct link_control_s link_control;
469	struct wake_on_lan_s wake_on_lan;
470
471	memcpy(mac_address.aligned.mac_address, mac, ETH_ALEN);
472	hw_atl2_shared_buffer_write(self, mac_address, mac_address);
473
474	memset(&wake_on_lan, 0, sizeof(wake_on_lan));
475
476	if (wol & WAKE_MAGIC)
477		wake_on_lan.wake_on_magic_packet = 1U;
478
479	if (wol & (WAKE_PHY | AQ_FW_WAKE_ON_LINK_RTPM))
480		wake_on_lan.wake_on_link_up = 1U;
481
482	hw_atl2_shared_buffer_write(self, sleep_proxy, wake_on_lan);
483
484	hw_atl2_shared_buffer_get(self, link_control, link_control);
485	link_control.mode = AQ_HOST_MODE_SLEEP_PROXY;
486	hw_atl2_shared_buffer_write(self, link_control, link_control);
487
488	return hw_atl2_shared_buffer_finish_ack(self);
489}
490
491static int aq_a2_fw_set_power(struct aq_hw_s *self, unsigned int power_state,
492			      const u8 *mac)
493{
494	u32 wol = self->aq_nic_cfg->wol;
495	int err = 0;
496
497	if (wol)
498		err = aq_a2_fw_set_wol_params(self, mac, wol);
499
500	return err;
501}
502
503static int aq_a2_fw_set_eee_rate(struct aq_hw_s *self, u32 speed)
504{
505	struct link_options_s link_options;
506
507	hw_atl2_shared_buffer_get(self, link_options, link_options);
508
509	aq_a2_fw_upd_eee_rate_bits(self, &link_options, speed);
510
511	hw_atl2_shared_buffer_write(self, link_options, link_options);
512
513	return hw_atl2_shared_buffer_finish_ack(self);
514}
515
516static int aq_a2_fw_get_eee_rate(struct aq_hw_s *self, u32 *rate,
517				 u32 *supported_rates)
518{
519	struct device_link_caps_s device_link_caps;
520	struct lkp_link_caps_s lkp_link_caps;
521
522	hw_atl2_shared_buffer_read(self, device_link_caps, device_link_caps);
523	hw_atl2_shared_buffer_read(self, lkp_link_caps, lkp_link_caps);
524
525	*supported_rates = a2_fw_dev_to_eee_mask(&device_link_caps);
526	*rate = a2_fw_lkp_to_mask(&lkp_link_caps);
527
528	return 0;
529}
530
531static int aq_a2_fw_renegotiate(struct aq_hw_s *self)
532{
533	struct link_options_s link_options;
534	int err;
535
536	hw_atl2_shared_buffer_get(self, link_options, link_options);
537	link_options.link_renegotiate = 1U;
538	hw_atl2_shared_buffer_write(self, link_options, link_options);
539
540	err = hw_atl2_shared_buffer_finish_ack(self);
541
542	/* We should put renegotiate status back to zero
543	 * after command completes
544	 */
545	link_options.link_renegotiate = 0U;
546	hw_atl2_shared_buffer_write(self, link_options, link_options);
547
548	return err;
549}
550
551static int aq_a2_fw_set_flow_control(struct aq_hw_s *self)
552{
553	struct link_options_s link_options;
554
555	hw_atl2_shared_buffer_get(self, link_options, link_options);
556
557	aq_a2_fw_set_mpi_flow_control(self, &link_options);
558
559	hw_atl2_shared_buffer_write(self, link_options, link_options);
560
561	return hw_atl2_shared_buffer_finish_ack(self);
562}
563
564static u32 aq_a2_fw_get_flow_control(struct aq_hw_s *self, u32 *fcmode)
565{
566	struct link_status_s link_status;
567
568	hw_atl2_shared_buffer_read(self, link_status, link_status);
569
570	*fcmode = ((link_status.pause_rx) ? AQ_NIC_FC_RX : 0) |
571		  ((link_status.pause_tx) ? AQ_NIC_FC_TX : 0);
572	return 0;
573}
574
575static int aq_a2_fw_set_phyloopback(struct aq_hw_s *self, u32 mode, bool enable)
576{
577	struct link_options_s link_options;
578
579	hw_atl2_shared_buffer_get(self, link_options, link_options);
580
581	switch (mode) {
582	case AQ_HW_LOOPBACK_PHYINT_SYS:
583		link_options.internal_loopback = enable;
584		break;
585	case AQ_HW_LOOPBACK_PHYEXT_SYS:
586		link_options.external_loopback = enable;
587		break;
588	default:
589		return -EINVAL;
590	}
591
592	hw_atl2_shared_buffer_write(self, link_options, link_options);
593
594	return hw_atl2_shared_buffer_finish_ack(self);
595}
596
597u32 hw_atl2_utils_get_fw_version(struct aq_hw_s *self)
598{
599	struct version_s version;
600
601	hw_atl2_shared_buffer_read_safe(self, version, &version);
602
603	/* A2 FW version is stored in reverse order */
604	return version.bundle.major << 24 |
605	       version.bundle.minor << 16 |
606	       version.bundle.build;
607}
608
609int hw_atl2_utils_get_action_resolve_table_caps(struct aq_hw_s *self,
610						u8 *base_index, u8 *count)
611{
612	struct filter_caps_s filter_caps;
613	int err;
614
615	err = hw_atl2_shared_buffer_read_safe(self, filter_caps, &filter_caps);
616	if (err)
617		return err;
618
619	*base_index = filter_caps.rslv_tbl_base_index;
620	*count = filter_caps.rslv_tbl_count;
621	return 0;
622}
623
624static int aq_a2_fw_set_downshift(struct aq_hw_s *self, u32 counter)
625{
626	struct link_options_s link_options;
627
628	hw_atl2_shared_buffer_get(self, link_options, link_options);
629	link_options.downshift = !!counter;
630	link_options.downshift_retry = counter;
631	hw_atl2_shared_buffer_write(self, link_options, link_options);
632
633	return hw_atl2_shared_buffer_finish_ack(self);
634}
635
636const struct aq_fw_ops aq_a2_fw_ops = {
637	.init               = aq_a2_fw_init,
638	.deinit             = aq_a2_fw_deinit,
639	.reset              = NULL,
640	.renegotiate        = aq_a2_fw_renegotiate,
641	.get_mac_permanent  = aq_a2_fw_get_mac_permanent,
642	.set_link_speed     = aq_a2_fw_set_link_speed,
643	.set_state          = aq_a2_fw_set_state,
644	.update_link_status = aq_a2_fw_update_link_status,
645	.update_stats       = aq_a2_fw_update_stats,
646	.set_power          = aq_a2_fw_set_power,
647	.get_mac_temp       = aq_a2_fw_get_mac_temp,
648	.get_phy_temp       = aq_a2_fw_get_phy_temp,
649	.set_eee_rate       = aq_a2_fw_set_eee_rate,
650	.get_eee_rate       = aq_a2_fw_get_eee_rate,
651	.set_flow_control   = aq_a2_fw_set_flow_control,
652	.get_flow_control   = aq_a2_fw_get_flow_control,
653	.set_phyloopback    = aq_a2_fw_set_phyloopback,
654	.set_downshift      = aq_a2_fw_set_downshift,
655};
~/ Read-only file preview.