175a6faf6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2a57d3929SIgor Russkikh /*
3a57d3929SIgor Russkikh  * aQuantia Corporation Network Driver
4910479a9SEgor Pomozov  * Copyright (C) 2014-2019 aQuantia Corporation. All rights reserved
5a57d3929SIgor Russkikh  */
6a57d3929SIgor Russkikh 
7a57d3929SIgor Russkikh /* File hw_atl_utils_fw2x.c: Definition of firmware 2.x functions for
8a57d3929SIgor Russkikh  * Atlantic hardware abstraction layer.
9a57d3929SIgor Russkikh  */
10a57d3929SIgor Russkikh 
11a57d3929SIgor Russkikh #include "../aq_hw.h"
12a57d3929SIgor Russkikh #include "../aq_hw_utils.h"
13a57d3929SIgor Russkikh #include "../aq_pci_func.h"
14a57d3929SIgor Russkikh #include "../aq_ring.h"
15a57d3929SIgor Russkikh #include "../aq_vec.h"
160e1a0ddeSYana Esina #include "../aq_nic.h"
17a57d3929SIgor Russkikh #include "hw_atl_utils.h"
18a57d3929SIgor Russkikh #include "hw_atl_llh.h"
19a57d3929SIgor Russkikh 
20d1287ce4SNikita Danilov #define HW_ATL_FW2X_MPI_LED_ADDR         0x31c
213ee5c887SYana Esina #define HW_ATL_FW2X_MPI_RPC_ADDR         0x334
22a57d3929SIgor Russkikh 
236a7f2277SNikita Danilov #define HW_ATL_FW2X_MPI_MBOX_ADDR        0x360
246a7f2277SNikita Danilov #define HW_ATL_FW2X_MPI_EFUSE_ADDR       0x364
25a57d3929SIgor Russkikh #define HW_ATL_FW2X_MPI_CONTROL_ADDR     0x368
26a57d3929SIgor Russkikh #define HW_ATL_FW2X_MPI_CONTROL2_ADDR    0x36C
27a57d3929SIgor Russkikh #define HW_ATL_FW2X_MPI_STATE_ADDR       0x370
28a57d3929SIgor Russkikh #define HW_ATL_FW2X_MPI_STATE2_ADDR      0x374
29a57d3929SIgor Russkikh 
30910479a9SEgor Pomozov #define HW_ATL_FW3X_EXT_CONTROL_ADDR     0x378
31910479a9SEgor Pomozov #define HW_ATL_FW3X_EXT_STATE_ADDR       0x37c
32910479a9SEgor Pomozov 
33f08a464cSEgor Pomozov #define HW_ATL_FW3X_PTP_ADJ_LSW_ADDR	 0x50a0
34f08a464cSEgor Pomozov #define HW_ATL_FW3X_PTP_ADJ_MSW_ADDR	 0x50a4
35f08a464cSEgor Pomozov 
3635e8e8b4SIgor Russkikh #define HW_ATL_FW2X_CAP_PAUSE            BIT(CAPS_HI_PAUSE)
3735e8e8b4SIgor Russkikh #define HW_ATL_FW2X_CAP_ASYM_PAUSE       BIT(CAPS_HI_ASYMMETRIC_PAUSE)
380e1a0ddeSYana Esina #define HW_ATL_FW2X_CAP_SLEEP_PROXY      BIT(CAPS_HI_SLEEP_PROXY)
390e1a0ddeSYana Esina #define HW_ATL_FW2X_CAP_WOL              BIT(CAPS_HI_WOL)
400e1a0ddeSYana Esina 
41837c6378SNikita Danilov #define HW_ATL_FW2X_CTRL_WAKE_ON_LINK     BIT(CTRL_WAKE_ON_LINK)
420e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_SLEEP_PROXY      BIT(CTRL_SLEEP_PROXY)
430e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_WOL              BIT(CTRL_WOL)
440e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_LINK_DROP        BIT(CTRL_LINK_DROP)
450e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_PAUSE            BIT(CTRL_PAUSE)
468f894011SYana Esina #define HW_ATL_FW2X_CTRL_TEMPERATURE      BIT(CTRL_TEMPERATURE)
470e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_ASYMMETRIC_PAUSE BIT(CTRL_ASYMMETRIC_PAUSE)
48ea4b4d7fSIgor Russkikh #define HW_ATL_FW2X_CTRL_INT_LOOPBACK     BIT(CTRL_INT_LOOPBACK)
49ea4b4d7fSIgor Russkikh #define HW_ATL_FW2X_CTRL_EXT_LOOPBACK     BIT(CTRL_EXT_LOOPBACK)
50ea4b4d7fSIgor Russkikh #define HW_ATL_FW2X_CTRL_DOWNSHIFT        BIT(CTRL_DOWNSHIFT)
510e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_FORCE_RECONNECT  BIT(CTRL_FORCE_RECONNECT)
520e1a0ddeSYana Esina 
5392ab6407SYana Esina #define HW_ATL_FW2X_CAP_EEE_1G_MASK      BIT(CAPS_HI_1000BASET_FD_EEE)
5492ab6407SYana Esina #define HW_ATL_FW2X_CAP_EEE_2G5_MASK     BIT(CAPS_HI_2P5GBASET_FD_EEE)
5592ab6407SYana Esina #define HW_ATL_FW2X_CAP_EEE_5G_MASK      BIT(CAPS_HI_5GBASET_FD_EEE)
5692ab6407SYana Esina #define HW_ATL_FW2X_CAP_EEE_10G_MASK     BIT(CAPS_HI_10GBASET_FD_EEE)
5792ab6407SYana Esina 
5862c1c2e6SDmitry Bogdanov #define HW_ATL_FW2X_CAP_MACSEC           BIT(CAPS_LO_MACSEC)
5962c1c2e6SDmitry Bogdanov 
600e1a0ddeSYana Esina #define HAL_ATLANTIC_WOL_FILTERS_COUNT   8
610e1a0ddeSYana Esina #define HAL_ATLANTIC_UTILS_FW2X_MSG_WOL  0x0E
620e1a0ddeSYana Esina 
63d1287ce4SNikita Danilov #define HW_ATL_FW_VER_LED                0x03010026U
64ea4b4d7fSIgor Russkikh #define HW_ATL_FW_VER_MEDIA_CONTROL      0x0301005aU
65d1287ce4SNikita Danilov 
660e1a0ddeSYana Esina struct __packed fw2x_msg_wol_pattern {
670e1a0ddeSYana Esina 	u8 mask[16];
680e1a0ddeSYana Esina 	u32 crc;
690e1a0ddeSYana Esina };
700e1a0ddeSYana Esina 
710e1a0ddeSYana Esina struct __packed fw2x_msg_wol {
720e1a0ddeSYana Esina 	u32 msg_id;
730e1a0ddeSYana Esina 	u8 hw_addr[ETH_ALEN];
740e1a0ddeSYana Esina 	u8 magic_packet_enabled;
750e1a0ddeSYana Esina 	u8 filter_count;
760e1a0ddeSYana Esina 	struct fw2x_msg_wol_pattern filter[HAL_ATLANTIC_WOL_FILTERS_COUNT];
770e1a0ddeSYana Esina 	u8 link_up_enabled;
780e1a0ddeSYana Esina 	u8 link_down_enabled;
790e1a0ddeSYana Esina 	u16 reserved;
800e1a0ddeSYana Esina 	u32 link_up_timeout;
810e1a0ddeSYana Esina 	u32 link_down_timeout;
820e1a0ddeSYana Esina };
830e1a0ddeSYana Esina 
8444e00dd8SIgor Russkikh static int aq_fw2x_set_link_speed(struct aq_hw_s *self, u32 speed);
8544e00dd8SIgor Russkikh static int aq_fw2x_set_state(struct aq_hw_s *self,
8644e00dd8SIgor Russkikh 			     enum hal_atl_utils_fw_state_e state);
8744e00dd8SIgor Russkikh 
886a7f2277SNikita Danilov static u32 aq_fw2x_mbox_get(struct aq_hw_s *self);
896a7f2277SNikita Danilov static u32 aq_fw2x_rpc_get(struct aq_hw_s *self);
90dc12f75aSNikita Danilov static int aq_fw2x_settings_get(struct aq_hw_s *self, u32 *addr);
9162c1c2e6SDmitry Bogdanov static u32 aq_fw2x_state_get(struct aq_hw_s *self);
926a7f2277SNikita Danilov static u32 aq_fw2x_state2_get(struct aq_hw_s *self);
936a7f2277SNikita Danilov 
94a57d3929SIgor Russkikh static int aq_fw2x_init(struct aq_hw_s *self)
95a57d3929SIgor Russkikh {
96a57d3929SIgor Russkikh 	int err = 0;
97a57d3929SIgor Russkikh 
98a57d3929SIgor Russkikh 	/* check 10 times by 1ms */
996a7f2277SNikita Danilov 	err = readx_poll_timeout_atomic(aq_fw2x_mbox_get,
1006a7f2277SNikita Danilov 					self, self->mbox_addr,
1016a7f2277SNikita Danilov 					self->mbox_addr != 0U,
1026a7f2277SNikita Danilov 					1000U, 10000U);
1036a7f2277SNikita Danilov 
1046a7f2277SNikita Danilov 	err = readx_poll_timeout_atomic(aq_fw2x_rpc_get,
1056a7f2277SNikita Danilov 					self, self->rpc_addr,
1066a7f2277SNikita Danilov 					self->rpc_addr != 0U,
1076a7f2277SNikita Danilov 					1000U, 100000U);
1083ee5c887SYana Esina 
109dc12f75aSNikita Danilov 	err = aq_fw2x_settings_get(self, &self->settings_addr);
110dc12f75aSNikita Danilov 
111a57d3929SIgor Russkikh 	return err;
112a57d3929SIgor Russkikh }
113a57d3929SIgor Russkikh 
11444e00dd8SIgor Russkikh static int aq_fw2x_deinit(struct aq_hw_s *self)
11544e00dd8SIgor Russkikh {
11644e00dd8SIgor Russkikh 	int err = aq_fw2x_set_link_speed(self, 0);
11744e00dd8SIgor Russkikh 
11844e00dd8SIgor Russkikh 	if (!err)
11944e00dd8SIgor Russkikh 		err = aq_fw2x_set_state(self, MPI_DEINIT);
12044e00dd8SIgor Russkikh 
12144e00dd8SIgor Russkikh 	return err;
12244e00dd8SIgor Russkikh }
12344e00dd8SIgor Russkikh 
124a57d3929SIgor Russkikh static enum hw_atl_fw2x_rate link_speed_mask_2fw2x_ratemask(u32 speed)
125a57d3929SIgor Russkikh {
126a57d3929SIgor Russkikh 	enum hw_atl_fw2x_rate rate = 0;
127a57d3929SIgor Russkikh 
128a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_10G)
129a57d3929SIgor Russkikh 		rate |= FW2X_RATE_10G;
130a57d3929SIgor Russkikh 
131a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_5G)
132a57d3929SIgor Russkikh 		rate |= FW2X_RATE_5G;
133a57d3929SIgor Russkikh 
134a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_5GSR)
135a57d3929SIgor Russkikh 		rate |= FW2X_RATE_5G;
136a57d3929SIgor Russkikh 
137a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_2GS)
138a57d3929SIgor Russkikh 		rate |= FW2X_RATE_2G5;
139a57d3929SIgor Russkikh 
140a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_1G)
141a57d3929SIgor Russkikh 		rate |= FW2X_RATE_1G;
142a57d3929SIgor Russkikh 
143a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_100M)
144a57d3929SIgor Russkikh 		rate |= FW2X_RATE_100M;
145a57d3929SIgor Russkikh 
146a57d3929SIgor Russkikh 	return rate;
147a57d3929SIgor Russkikh }
148a57d3929SIgor Russkikh 
14992ab6407SYana Esina static u32 fw2x_to_eee_mask(u32 speed)
15092ab6407SYana Esina {
15192ab6407SYana Esina 	u32 rate = 0;
15292ab6407SYana Esina 
15392ab6407SYana Esina 	if (speed & HW_ATL_FW2X_CAP_EEE_10G_MASK)
15492ab6407SYana Esina 		rate |= AQ_NIC_RATE_EEE_10G;
15592ab6407SYana Esina 	if (speed & HW_ATL_FW2X_CAP_EEE_5G_MASK)
15692ab6407SYana Esina 		rate |= AQ_NIC_RATE_EEE_5G;
15792ab6407SYana Esina 	if (speed & HW_ATL_FW2X_CAP_EEE_2G5_MASK)
15892ab6407SYana Esina 		rate |= AQ_NIC_RATE_EEE_2GS;
15992ab6407SYana Esina 	if (speed & HW_ATL_FW2X_CAP_EEE_1G_MASK)
16092ab6407SYana Esina 		rate |= AQ_NIC_RATE_EEE_1G;
16192ab6407SYana Esina 
16292ab6407SYana Esina 	return rate;
16392ab6407SYana Esina }
16492ab6407SYana Esina 
16592ab6407SYana Esina static u32 eee_mask_to_fw2x(u32 speed)
16692ab6407SYana Esina {
16792ab6407SYana Esina 	u32 rate = 0;
16892ab6407SYana Esina 
16992ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_10G)
17092ab6407SYana Esina 		rate |= HW_ATL_FW2X_CAP_EEE_10G_MASK;
17192ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_5G)
17292ab6407SYana Esina 		rate |= HW_ATL_FW2X_CAP_EEE_5G_MASK;
17392ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_2GS)
17492ab6407SYana Esina 		rate |= HW_ATL_FW2X_CAP_EEE_2G5_MASK;
17592ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_1G)
17692ab6407SYana Esina 		rate |= HW_ATL_FW2X_CAP_EEE_1G_MASK;
17792ab6407SYana Esina 
17892ab6407SYana Esina 	return rate;
17992ab6407SYana Esina }
18092ab6407SYana Esina 
181a57d3929SIgor Russkikh static int aq_fw2x_set_link_speed(struct aq_hw_s *self, u32 speed)
182a57d3929SIgor Russkikh {
183a57d3929SIgor Russkikh 	u32 val = link_speed_mask_2fw2x_ratemask(speed);
184a57d3929SIgor Russkikh 
185a57d3929SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL_ADDR, val);
186a57d3929SIgor Russkikh 
187a57d3929SIgor Russkikh 	return 0;
188a57d3929SIgor Russkikh }
189a57d3929SIgor Russkikh 
1908009bb19SNikita Danilov static void aq_fw2x_upd_flow_control_bits(struct aq_hw_s *self,
1918009bb19SNikita Danilov 					  u32 *mpi_state, u32 fc)
192288551deSIgor Russkikh {
1938009bb19SNikita Danilov 	*mpi_state &= ~(HW_ATL_FW2X_CTRL_PAUSE |
1948009bb19SNikita Danilov 			HW_ATL_FW2X_CTRL_ASYMMETRIC_PAUSE);
195288551deSIgor Russkikh 
1968009bb19SNikita Danilov 	switch (fc) {
1978009bb19SNikita Danilov 	/* There is not explicit mode of RX only pause frames,
1988009bb19SNikita Danilov 	 * thus, we join this mode with FC full.
1998009bb19SNikita Danilov 	 * FC full is either Rx, either Tx, or both.
2008009bb19SNikita Danilov 	 */
2018009bb19SNikita Danilov 	case AQ_NIC_FC_FULL:
2028009bb19SNikita Danilov 	case AQ_NIC_FC_RX:
2038009bb19SNikita Danilov 		*mpi_state |= HW_ATL_FW2X_CTRL_PAUSE |
2048009bb19SNikita Danilov 			      HW_ATL_FW2X_CTRL_ASYMMETRIC_PAUSE;
2058009bb19SNikita Danilov 		break;
2068009bb19SNikita Danilov 	case AQ_NIC_FC_TX:
2078009bb19SNikita Danilov 		*mpi_state |= HW_ATL_FW2X_CTRL_ASYMMETRIC_PAUSE;
2088009bb19SNikita Danilov 		break;
2098009bb19SNikita Danilov 	}
210288551deSIgor Russkikh }
211288551deSIgor Russkikh 
21292ab6407SYana Esina static void aq_fw2x_upd_eee_rate_bits(struct aq_hw_s *self, u32 *mpi_opts,
21392ab6407SYana Esina 				      u32 eee_speeds)
21492ab6407SYana Esina {
21592ab6407SYana Esina 	*mpi_opts &= ~(HW_ATL_FW2X_CAP_EEE_1G_MASK |
21692ab6407SYana Esina 		       HW_ATL_FW2X_CAP_EEE_2G5_MASK |
21792ab6407SYana Esina 		       HW_ATL_FW2X_CAP_EEE_5G_MASK |
21892ab6407SYana Esina 		       HW_ATL_FW2X_CAP_EEE_10G_MASK);
21992ab6407SYana Esina 
22092ab6407SYana Esina 	*mpi_opts |= eee_mask_to_fw2x(eee_speeds);
22192ab6407SYana Esina }
22292ab6407SYana Esina 
223a57d3929SIgor Russkikh static int aq_fw2x_set_state(struct aq_hw_s *self,
224a57d3929SIgor Russkikh 			     enum hal_atl_utils_fw_state_e state)
225a57d3929SIgor Russkikh {
22644e00dd8SIgor Russkikh 	u32 mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
22792ab6407SYana Esina 	struct aq_nic_cfg_s *cfg = self->aq_nic_cfg;
22844e00dd8SIgor Russkikh 
22944e00dd8SIgor Russkikh 	switch (state) {
23044e00dd8SIgor Russkikh 	case MPI_INIT:
23144e00dd8SIgor Russkikh 		mpi_state &= ~BIT(CAPS_HI_LINK_DROP);
23292ab6407SYana Esina 		aq_fw2x_upd_eee_rate_bits(self, &mpi_state, cfg->eee_speeds);
2338009bb19SNikita Danilov 		aq_fw2x_upd_flow_control_bits(self, &mpi_state,
2348009bb19SNikita Danilov 					      self->aq_nic_cfg->fc.req);
23544e00dd8SIgor Russkikh 		break;
23644e00dd8SIgor Russkikh 	case MPI_DEINIT:
23744e00dd8SIgor Russkikh 		mpi_state |= BIT(CAPS_HI_LINK_DROP);
23844e00dd8SIgor Russkikh 		break;
23944e00dd8SIgor Russkikh 	case MPI_RESET:
24044e00dd8SIgor Russkikh 	case MPI_POWER:
24144e00dd8SIgor Russkikh 		/* No actions */
24244e00dd8SIgor Russkikh 		break;
24344e00dd8SIgor Russkikh 	}
24444e00dd8SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_state);
2457b0c342fSNikita Danilov 
246a57d3929SIgor Russkikh 	return 0;
247a57d3929SIgor Russkikh }
248a57d3929SIgor Russkikh 
249a57d3929SIgor Russkikh static int aq_fw2x_update_link_status(struct aq_hw_s *self)
250a57d3929SIgor Russkikh {
251a57d3929SIgor Russkikh 	struct aq_hw_link_status_s *link_status = &self->aq_link_status;
2527b0c342fSNikita Danilov 	u32 mpi_state;
2537b0c342fSNikita Danilov 	u32 speed;
2547b0c342fSNikita Danilov 
2557b0c342fSNikita Danilov 	mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE_ADDR);
2567b0c342fSNikita Danilov 	speed = mpi_state & (FW2X_RATE_100M | FW2X_RATE_1G |
2577b0c342fSNikita Danilov 			     FW2X_RATE_2G5 | FW2X_RATE_5G |
2587b0c342fSNikita Danilov 			     FW2X_RATE_10G);
259a57d3929SIgor Russkikh 
260a57d3929SIgor Russkikh 	if (speed) {
261a57d3929SIgor Russkikh 		if (speed & FW2X_RATE_10G)
262a57d3929SIgor Russkikh 			link_status->mbps = 10000;
263a57d3929SIgor Russkikh 		else if (speed & FW2X_RATE_5G)
264a57d3929SIgor Russkikh 			link_status->mbps = 5000;
265a57d3929SIgor Russkikh 		else if (speed & FW2X_RATE_2G5)
266a57d3929SIgor Russkikh 			link_status->mbps = 2500;
267a57d3929SIgor Russkikh 		else if (speed & FW2X_RATE_1G)
268a57d3929SIgor Russkikh 			link_status->mbps = 1000;
269a57d3929SIgor Russkikh 		else if (speed & FW2X_RATE_100M)
270a57d3929SIgor Russkikh 			link_status->mbps = 100;
271a57d3929SIgor Russkikh 		else
272a57d3929SIgor Russkikh 			link_status->mbps = 10000;
273a57d3929SIgor Russkikh 	} else {
274a57d3929SIgor Russkikh 		link_status->mbps = 0;
275a57d3929SIgor Russkikh 	}
276a57d3929SIgor Russkikh 
277a57d3929SIgor Russkikh 	return 0;
278a57d3929SIgor Russkikh }
279a57d3929SIgor Russkikh 
28076a45194SColin Ian King static int aq_fw2x_get_mac_permanent(struct aq_hw_s *self, u8 *mac)
281a57d3929SIgor Russkikh {
2827b0c342fSNikita Danilov 	u32 efuse_addr = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_EFUSE_ADDR);
2837b0c342fSNikita Danilov 	u32 mac_addr[2] = { 0 };
284a57d3929SIgor Russkikh 	int err = 0;
285a57d3929SIgor Russkikh 	u32 h = 0U;
286a57d3929SIgor Russkikh 	u32 l = 0U;
287a57d3929SIgor Russkikh 
288a57d3929SIgor Russkikh 	if (efuse_addr != 0) {
289a57d3929SIgor Russkikh 		err = hw_atl_utils_fw_downld_dwords(self,
290a57d3929SIgor Russkikh 						    efuse_addr + (40U * 4U),
291a57d3929SIgor Russkikh 						    mac_addr,
292a57d3929SIgor Russkikh 						    ARRAY_SIZE(mac_addr));
293a57d3929SIgor Russkikh 		if (err)
294a57d3929SIgor Russkikh 			return err;
295a57d3929SIgor Russkikh 		mac_addr[0] = __swab32(mac_addr[0]);
296a57d3929SIgor Russkikh 		mac_addr[1] = __swab32(mac_addr[1]);
297a57d3929SIgor Russkikh 	}
298a57d3929SIgor Russkikh 
299a57d3929SIgor Russkikh 	ether_addr_copy(mac, (u8 *)mac_addr);
300a57d3929SIgor Russkikh 
301a57d3929SIgor Russkikh 	if ((mac[0] & 0x01U) || ((mac[0] | mac[1] | mac[2]) == 0x00U)) {
302a57d3929SIgor Russkikh 		unsigned int rnd = 0;
303a57d3929SIgor Russkikh 
304a57d3929SIgor Russkikh 		get_random_bytes(&rnd, sizeof(unsigned int));
305a57d3929SIgor Russkikh 
306e9157848SNikita Danilov 		l = 0xE3000000U | (0xFFFFU & rnd) | (0x00 << 16);
307a57d3929SIgor Russkikh 		h = 0x8001300EU;
308a57d3929SIgor Russkikh 
309a57d3929SIgor Russkikh 		mac[5] = (u8)(0xFFU & l);
310a57d3929SIgor Russkikh 		l >>= 8;
311a57d3929SIgor Russkikh 		mac[4] = (u8)(0xFFU & l);
312a57d3929SIgor Russkikh 		l >>= 8;
313a57d3929SIgor Russkikh 		mac[3] = (u8)(0xFFU & l);
314a57d3929SIgor Russkikh 		l >>= 8;
315a57d3929SIgor Russkikh 		mac[2] = (u8)(0xFFU & l);
316a57d3929SIgor Russkikh 		mac[1] = (u8)(0xFFU & h);
317a57d3929SIgor Russkikh 		h >>= 8;
318a57d3929SIgor Russkikh 		mac[0] = (u8)(0xFFU & h);
319a57d3929SIgor Russkikh 	}
3207b0c342fSNikita Danilov 
321a57d3929SIgor Russkikh 	return err;
322a57d3929SIgor Russkikh }
323a57d3929SIgor Russkikh 
3240ba4ad32SYueHaibing static int aq_fw2x_update_stats(struct aq_hw_s *self)
325a57d3929SIgor Russkikh {
326a57d3929SIgor Russkikh 	u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
327a57d3929SIgor Russkikh 	u32 orig_stats_val = mpi_opts & BIT(CAPS_HI_STATISTICS);
3286a7f2277SNikita Danilov 	u32 stats_val;
3297b0c342fSNikita Danilov 	int err = 0;
330a57d3929SIgor Russkikh 
331a57d3929SIgor Russkikh 	/* Toggle statistics bit for FW to update */
332a57d3929SIgor Russkikh 	mpi_opts = mpi_opts ^ BIT(CAPS_HI_STATISTICS);
333a57d3929SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
334a57d3929SIgor Russkikh 
335a57d3929SIgor Russkikh 	/* Wait FW to report back */
3366a7f2277SNikita Danilov 	err = readx_poll_timeout_atomic(aq_fw2x_state2_get,
3376a7f2277SNikita Danilov 					self, stats_val,
3386a7f2277SNikita Danilov 					orig_stats_val != (stats_val &
339a57d3929SIgor Russkikh 					BIT(CAPS_HI_STATISTICS)),
340a57d3929SIgor Russkikh 					1U, 10000U);
341a57d3929SIgor Russkikh 	if (err)
342a57d3929SIgor Russkikh 		return err;
343a57d3929SIgor Russkikh 
344a57d3929SIgor Russkikh 	return hw_atl_utils_update_stats(self);
345a57d3929SIgor Russkikh }
346a57d3929SIgor Russkikh 
3478f894011SYana Esina static int aq_fw2x_get_phy_temp(struct aq_hw_s *self, int *temp)
3488f894011SYana Esina {
3498f894011SYana Esina 	u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
3508f894011SYana Esina 	u32 temp_val = mpi_opts & HW_ATL_FW2X_CTRL_TEMPERATURE;
3518f894011SYana Esina 	u32 phy_temp_offset;
3528f894011SYana Esina 	u32 temp_res;
3538f894011SYana Esina 	int err = 0;
3548f894011SYana Esina 	u32 val;
3558f894011SYana Esina 
3567b0c342fSNikita Danilov 	phy_temp_offset = self->mbox_addr + offsetof(struct hw_atl_utils_mbox,
3577b0c342fSNikita Danilov 						     info.phy_temperature);
3587b0c342fSNikita Danilov 
3598f894011SYana Esina 	/* Toggle statistics bit for FW to 0x36C.18 (CTRL_TEMPERATURE) */
3608f894011SYana Esina 	mpi_opts = mpi_opts ^ HW_ATL_FW2X_CTRL_TEMPERATURE;
3618f894011SYana Esina 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
3628f894011SYana Esina 	/* Wait FW to report back */
3638f894011SYana Esina 	err = readx_poll_timeout_atomic(aq_fw2x_state2_get, self, val,
3648f894011SYana Esina 					temp_val !=
3658f894011SYana Esina 					(val & HW_ATL_FW2X_CTRL_TEMPERATURE),
3668f894011SYana Esina 					1U, 10000U);
3678f894011SYana Esina 	err = hw_atl_utils_fw_downld_dwords(self, phy_temp_offset,
3688f894011SYana Esina 					    &temp_res, 1);
3698f894011SYana Esina 
3708f894011SYana Esina 	if (err)
3718f894011SYana Esina 		return err;
3728f894011SYana Esina 
3738f894011SYana Esina 	/* Convert PHY temperature from 1/256 degree Celsius
3748f894011SYana Esina 	 * to 1/1000 degree Celsius.
3758f894011SYana Esina 	 */
37606b0d7feSIgor Russkikh 	*temp = (temp_res & 0xFFFF) * 1000 / 256;
3778f894011SYana Esina 
3788f894011SYana Esina 	return 0;
3798f894011SYana Esina }
3808f894011SYana Esina 
381837c6378SNikita Danilov static int aq_fw2x_set_wol(struct aq_hw_s *self, u8 *mac)
382a0da96c0SYana Esina {
3838f60f762SNikita Danilov 	struct hw_atl_utils_fw_rpc *rpc = NULL;
384837c6378SNikita Danilov 	struct offload_info *info = NULL;
385837c6378SNikita Danilov 	u32 wol_bits = 0;
386837c6378SNikita Danilov 	u32 rpc_size;
387a0da96c0SYana Esina 	int err = 0;
3886a7f2277SNikita Danilov 	u32 val;
389a0da96c0SYana Esina 
390837c6378SNikita Danilov 	if (self->aq_nic_cfg->wol & WAKE_PHY) {
391837c6378SNikita Danilov 		aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR,
392837c6378SNikita Danilov 				HW_ATL_FW2X_CTRL_LINK_DROP);
393837c6378SNikita Danilov 		readx_poll_timeout_atomic(aq_fw2x_state2_get, self, val,
394837c6378SNikita Danilov 					  (val &
395837c6378SNikita Danilov 					   HW_ATL_FW2X_CTRL_LINK_DROP) != 0,
396837c6378SNikita Danilov 					  1000, 100000);
397837c6378SNikita Danilov 		wol_bits |= HW_ATL_FW2X_CTRL_WAKE_ON_LINK;
398837c6378SNikita Danilov 	}
399837c6378SNikita Danilov 
400837c6378SNikita Danilov 	if (self->aq_nic_cfg->wol & WAKE_MAGIC) {
401837c6378SNikita Danilov 		wol_bits |= HW_ATL_FW2X_CTRL_SLEEP_PROXY |
402837c6378SNikita Danilov 			    HW_ATL_FW2X_CTRL_WOL;
403a0da96c0SYana Esina 
404a0da96c0SYana Esina 		err = hw_atl_utils_fw_rpc_wait(self, &rpc);
405a0da96c0SYana Esina 		if (err < 0)
406a0da96c0SYana Esina 			goto err_exit;
407a0da96c0SYana Esina 
408837c6378SNikita Danilov 		rpc_size = sizeof(*info) +
409837c6378SNikita Danilov 			   offsetof(struct hw_atl_utils_fw_rpc, fw2x_offloads);
410a0da96c0SYana Esina 		memset(rpc, 0, rpc_size);
411837c6378SNikita Danilov 		info = &rpc->fw2x_offloads;
412837c6378SNikita Danilov 		memcpy(info->mac_addr, mac, ETH_ALEN);
413837c6378SNikita Danilov 		info->len = sizeof(*info);
414a0da96c0SYana Esina 
415a0da96c0SYana Esina 		err = hw_atl_utils_fw_rpc_call(self, rpc_size);
416a0da96c0SYana Esina 		if (err < 0)
417a0da96c0SYana Esina 			goto err_exit;
418a0da96c0SYana Esina 	}
419a0da96c0SYana Esina 
420837c6378SNikita Danilov 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, wol_bits);
421a0da96c0SYana Esina 
422a0da96c0SYana Esina err_exit:
423a0da96c0SYana Esina 	return err;
424a0da96c0SYana Esina }
425a0da96c0SYana Esina 
426a0da96c0SYana Esina static int aq_fw2x_set_power(struct aq_hw_s *self, unsigned int power_state,
427a0da96c0SYana Esina 			     u8 *mac)
428a0da96c0SYana Esina {
429a0da96c0SYana Esina 	int err = 0;
430a0da96c0SYana Esina 
431837c6378SNikita Danilov 	if (self->aq_nic_cfg->wol)
432837c6378SNikita Danilov 		err = aq_fw2x_set_wol(self, mac);
433a0da96c0SYana Esina 
434a0da96c0SYana Esina 	return err;
435a0da96c0SYana Esina }
436a0da96c0SYana Esina 
437910479a9SEgor Pomozov static int aq_fw2x_send_fw_request(struct aq_hw_s *self,
438910479a9SEgor Pomozov 				   const struct hw_fw_request_iface *fw_req,
439910479a9SEgor Pomozov 				   size_t size)
440910479a9SEgor Pomozov {
441910479a9SEgor Pomozov 	u32 ctrl2, orig_ctrl2;
442910479a9SEgor Pomozov 	u32 dword_cnt;
443910479a9SEgor Pomozov 	int err = 0;
444910479a9SEgor Pomozov 	u32 val;
445910479a9SEgor Pomozov 
446910479a9SEgor Pomozov 	/* Write data to drvIface Mailbox */
447910479a9SEgor Pomozov 	dword_cnt = size / sizeof(u32);
448910479a9SEgor Pomozov 	if (size % sizeof(u32))
449910479a9SEgor Pomozov 		dword_cnt++;
450dc12f75aSNikita Danilov 	err = hw_atl_write_fwcfg_dwords(self, (void *)fw_req, dword_cnt);
451910479a9SEgor Pomozov 	if (err < 0)
452910479a9SEgor Pomozov 		goto err_exit;
453910479a9SEgor Pomozov 
454910479a9SEgor Pomozov 	/* Toggle statistics bit for FW to update */
455910479a9SEgor Pomozov 	ctrl2 = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
456910479a9SEgor Pomozov 	orig_ctrl2 = ctrl2 & BIT(CAPS_HI_FW_REQUEST);
457910479a9SEgor Pomozov 	ctrl2 = ctrl2 ^ BIT(CAPS_HI_FW_REQUEST);
458910479a9SEgor Pomozov 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, ctrl2);
459910479a9SEgor Pomozov 
460910479a9SEgor Pomozov 	/* Wait FW to report back */
461910479a9SEgor Pomozov 	err = readx_poll_timeout_atomic(aq_fw2x_state2_get, self, val,
462910479a9SEgor Pomozov 					orig_ctrl2 != (val &
463910479a9SEgor Pomozov 						       BIT(CAPS_HI_FW_REQUEST)),
464910479a9SEgor Pomozov 					1U, 10000U);
465910479a9SEgor Pomozov 
466910479a9SEgor Pomozov err_exit:
467910479a9SEgor Pomozov 	return err;
468910479a9SEgor Pomozov }
469910479a9SEgor Pomozov 
470910479a9SEgor Pomozov static void aq_fw3x_enable_ptp(struct aq_hw_s *self, int enable)
471910479a9SEgor Pomozov {
472910479a9SEgor Pomozov 	u32 ptp_opts = aq_hw_read_reg(self, HW_ATL_FW3X_EXT_STATE_ADDR);
473910479a9SEgor Pomozov 	u32 all_ptp_features = BIT(CAPS_EX_PHY_PTP_EN) |
474910479a9SEgor Pomozov 						   BIT(CAPS_EX_PTP_GPIO_EN);
475910479a9SEgor Pomozov 
476910479a9SEgor Pomozov 	if (enable)
477910479a9SEgor Pomozov 		ptp_opts |= all_ptp_features;
478910479a9SEgor Pomozov 	else
479910479a9SEgor Pomozov 		ptp_opts &= ~all_ptp_features;
480910479a9SEgor Pomozov 
481910479a9SEgor Pomozov 	aq_hw_write_reg(self, HW_ATL_FW3X_EXT_CONTROL_ADDR, ptp_opts);
482910479a9SEgor Pomozov }
483910479a9SEgor Pomozov 
484f08a464cSEgor Pomozov static void aq_fw3x_adjust_ptp(struct aq_hw_s *self, uint64_t adj)
485f08a464cSEgor Pomozov {
486f08a464cSEgor Pomozov 	aq_hw_write_reg(self, HW_ATL_FW3X_PTP_ADJ_LSW_ADDR,
487f08a464cSEgor Pomozov 			(adj >>  0) & 0xffffffff);
488f08a464cSEgor Pomozov 	aq_hw_write_reg(self, HW_ATL_FW3X_PTP_ADJ_MSW_ADDR,
489f08a464cSEgor Pomozov 			(adj >> 32) & 0xffffffff);
490f08a464cSEgor Pomozov }
491f08a464cSEgor Pomozov 
492d1287ce4SNikita Danilov static int aq_fw2x_led_control(struct aq_hw_s *self, u32 mode)
493d1287ce4SNikita Danilov {
494d1287ce4SNikita Danilov 	if (self->fw_ver_actual < HW_ATL_FW_VER_LED)
495d1287ce4SNikita Danilov 		return -EOPNOTSUPP;
496d1287ce4SNikita Danilov 
497d1287ce4SNikita Danilov 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_LED_ADDR, mode);
498d1287ce4SNikita Danilov 
499d1287ce4SNikita Danilov 	return 0;
500d1287ce4SNikita Danilov }
501d1287ce4SNikita Danilov 
50292ab6407SYana Esina static int aq_fw2x_set_eee_rate(struct aq_hw_s *self, u32 speed)
50392ab6407SYana Esina {
50492ab6407SYana Esina 	u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
50592ab6407SYana Esina 
50692ab6407SYana Esina 	aq_fw2x_upd_eee_rate_bits(self, &mpi_opts, speed);
50792ab6407SYana Esina 
50892ab6407SYana Esina 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
50992ab6407SYana Esina 
51092ab6407SYana Esina 	return 0;
51192ab6407SYana Esina }
51292ab6407SYana Esina 
51392ab6407SYana Esina static int aq_fw2x_get_eee_rate(struct aq_hw_s *self, u32 *rate,
51492ab6407SYana Esina 				u32 *supported_rates)
51592ab6407SYana Esina {
51692ab6407SYana Esina 	u32 mpi_state;
51792ab6407SYana Esina 	u32 caps_hi;
51892ab6407SYana Esina 	int err = 0;
5197b0c342fSNikita Danilov 	u32 offset;
52092ab6407SYana Esina 
5217b0c342fSNikita Danilov 	offset = self->mbox_addr + offsetof(struct hw_atl_utils_mbox,
5227b0c342fSNikita Danilov 					    info.caps_hi);
5237b0c342fSNikita Danilov 
5247b0c342fSNikita Danilov 	err = hw_atl_utils_fw_downld_dwords(self, offset, &caps_hi, 1);
52592ab6407SYana Esina 
52692ab6407SYana Esina 	if (err)
52792ab6407SYana Esina 		return err;
52892ab6407SYana Esina 
52992ab6407SYana Esina 	*supported_rates = fw2x_to_eee_mask(caps_hi);
53092ab6407SYana Esina 
5310b926d46SNikita Danilov 	mpi_state = aq_fw2x_state2_get(self);
53292ab6407SYana Esina 	*rate = fw2x_to_eee_mask(mpi_state);
53392ab6407SYana Esina 
53492ab6407SYana Esina 	return err;
53592ab6407SYana Esina }
53692ab6407SYana Esina 
537b8d68b62SAnton Mikaev static int aq_fw2x_renegotiate(struct aq_hw_s *self)
538b8d68b62SAnton Mikaev {
539b8d68b62SAnton Mikaev 	u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
540b8d68b62SAnton Mikaev 
541b8d68b62SAnton Mikaev 	mpi_opts |= BIT(CTRL_FORCE_RECONNECT);
542b8d68b62SAnton Mikaev 
543b8d68b62SAnton Mikaev 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
544b8d68b62SAnton Mikaev 
545b8d68b62SAnton Mikaev 	return 0;
546b8d68b62SAnton Mikaev }
547b8d68b62SAnton Mikaev 
548288551deSIgor Russkikh static int aq_fw2x_set_flow_control(struct aq_hw_s *self)
549288551deSIgor Russkikh {
550288551deSIgor Russkikh 	u32 mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
551288551deSIgor Russkikh 
5528009bb19SNikita Danilov 	aq_fw2x_upd_flow_control_bits(self, &mpi_state,
5538009bb19SNikita Danilov 				      self->aq_nic_cfg->fc.req);
554288551deSIgor Russkikh 
555288551deSIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_state);
556288551deSIgor Russkikh 
557288551deSIgor Russkikh 	return 0;
558288551deSIgor Russkikh }
559288551deSIgor Russkikh 
56035e8e8b4SIgor Russkikh static u32 aq_fw2x_get_flow_control(struct aq_hw_s *self, u32 *fcmode)
56135e8e8b4SIgor Russkikh {
5620b926d46SNikita Danilov 	u32 mpi_state = aq_fw2x_state2_get(self);
5638009bb19SNikita Danilov 	*fcmode = 0;
56435e8e8b4SIgor Russkikh 
56535e8e8b4SIgor Russkikh 	if (mpi_state & HW_ATL_FW2X_CAP_PAUSE)
5668009bb19SNikita Danilov 		*fcmode |= AQ_NIC_FC_RX;
5678009bb19SNikita Danilov 
56835e8e8b4SIgor Russkikh 	if (mpi_state & HW_ATL_FW2X_CAP_ASYM_PAUSE)
5698009bb19SNikita Danilov 		*fcmode |= AQ_NIC_FC_TX;
57035e8e8b4SIgor Russkikh 
57135e8e8b4SIgor Russkikh 	return 0;
57235e8e8b4SIgor Russkikh }
57335e8e8b4SIgor Russkikh 
574ea4b4d7fSIgor Russkikh static int aq_fw2x_set_phyloopback(struct aq_hw_s *self, u32 mode, bool enable)
575ea4b4d7fSIgor Russkikh {
576ea4b4d7fSIgor Russkikh 	u32 mpi_opts;
577ea4b4d7fSIgor Russkikh 
578ea4b4d7fSIgor Russkikh 	switch (mode) {
579ea4b4d7fSIgor Russkikh 	case AQ_HW_LOOPBACK_PHYINT_SYS:
580ea4b4d7fSIgor Russkikh 		mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
581ea4b4d7fSIgor Russkikh 		if (enable)
582ea4b4d7fSIgor Russkikh 			mpi_opts |= HW_ATL_FW2X_CTRL_INT_LOOPBACK;
583ea4b4d7fSIgor Russkikh 		else
584ea4b4d7fSIgor Russkikh 			mpi_opts &= ~HW_ATL_FW2X_CTRL_INT_LOOPBACK;
585ea4b4d7fSIgor Russkikh 		aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
586ea4b4d7fSIgor Russkikh 		break;
587ea4b4d7fSIgor Russkikh 	case AQ_HW_LOOPBACK_PHYEXT_SYS:
588ea4b4d7fSIgor Russkikh 		mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
589ea4b4d7fSIgor Russkikh 		if (enable)
590ea4b4d7fSIgor Russkikh 			mpi_opts |= HW_ATL_FW2X_CTRL_EXT_LOOPBACK;
591ea4b4d7fSIgor Russkikh 		else
592ea4b4d7fSIgor Russkikh 			mpi_opts &= ~HW_ATL_FW2X_CTRL_EXT_LOOPBACK;
593ea4b4d7fSIgor Russkikh 		aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
594ea4b4d7fSIgor Russkikh 		break;
595ea4b4d7fSIgor Russkikh 	default:
596ea4b4d7fSIgor Russkikh 		return -EINVAL;
597ea4b4d7fSIgor Russkikh 	}
5987b0c342fSNikita Danilov 
599ea4b4d7fSIgor Russkikh 	return 0;
600ea4b4d7fSIgor Russkikh }
601ea4b4d7fSIgor Russkikh 
6026a7f2277SNikita Danilov static u32 aq_fw2x_mbox_get(struct aq_hw_s *self)
6036a7f2277SNikita Danilov {
6046a7f2277SNikita Danilov 	return aq_hw_read_reg(self, HW_ATL_FW2X_MPI_MBOX_ADDR);
6056a7f2277SNikita Danilov }
6066a7f2277SNikita Danilov 
6076a7f2277SNikita Danilov static u32 aq_fw2x_rpc_get(struct aq_hw_s *self)
6086a7f2277SNikita Danilov {
6096a7f2277SNikita Danilov 	return aq_hw_read_reg(self, HW_ATL_FW2X_MPI_RPC_ADDR);
6106a7f2277SNikita Danilov }
6116a7f2277SNikita Danilov 
612dc12f75aSNikita Danilov static int aq_fw2x_settings_get(struct aq_hw_s *self, u32 *addr)
613dc12f75aSNikita Danilov {
614dc12f75aSNikita Danilov 	int err = 0;
615dc12f75aSNikita Danilov 	u32 offset;
616dc12f75aSNikita Danilov 
617dc12f75aSNikita Danilov 	offset = self->mbox_addr + offsetof(struct hw_atl_utils_mbox,
618dc12f75aSNikita Danilov 					    info.setting_address);
619dc12f75aSNikita Danilov 
620dc12f75aSNikita Danilov 	err = hw_atl_utils_fw_downld_dwords(self, offset, addr, 1);
621dc12f75aSNikita Danilov 
622dc12f75aSNikita Danilov 	return err;
623dc12f75aSNikita Danilov }
624dc12f75aSNikita Danilov 
62562c1c2e6SDmitry Bogdanov static u32 aq_fw2x_state_get(struct aq_hw_s *self)
62662c1c2e6SDmitry Bogdanov {
62762c1c2e6SDmitry Bogdanov 	return aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE_ADDR);
62862c1c2e6SDmitry Bogdanov }
62962c1c2e6SDmitry Bogdanov 
6306a7f2277SNikita Danilov static u32 aq_fw2x_state2_get(struct aq_hw_s *self)
6316a7f2277SNikita Danilov {
6326a7f2277SNikita Danilov 	return aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR);
6336a7f2277SNikita Danilov }
6346a7f2277SNikita Danilov 
63562c1c2e6SDmitry Bogdanov static u32 aq_fw2x_get_link_capabilities(struct aq_hw_s *self)
63662c1c2e6SDmitry Bogdanov {
63762c1c2e6SDmitry Bogdanov 	int err = 0;
63862c1c2e6SDmitry Bogdanov 	u32 offset;
63962c1c2e6SDmitry Bogdanov 	u32 val;
64062c1c2e6SDmitry Bogdanov 
64162c1c2e6SDmitry Bogdanov 	offset = self->mbox_addr +
64262c1c2e6SDmitry Bogdanov 		 offsetof(struct hw_atl_utils_mbox, info.caps_lo);
64362c1c2e6SDmitry Bogdanov 
64462c1c2e6SDmitry Bogdanov 	err = hw_atl_utils_fw_downld_dwords(self, offset, &val, 1);
64562c1c2e6SDmitry Bogdanov 
64662c1c2e6SDmitry Bogdanov 	if (err)
64762c1c2e6SDmitry Bogdanov 		return 0;
64862c1c2e6SDmitry Bogdanov 
64962c1c2e6SDmitry Bogdanov 	return val;
65062c1c2e6SDmitry Bogdanov }
65162c1c2e6SDmitry Bogdanov 
65262c1c2e6SDmitry Bogdanov static int aq_fw2x_send_macsec_req(struct aq_hw_s *hw,
65362c1c2e6SDmitry Bogdanov 				   struct macsec_msg_fw_request *req,
65462c1c2e6SDmitry Bogdanov 				   struct macsec_msg_fw_response *response)
65562c1c2e6SDmitry Bogdanov {
65662c1c2e6SDmitry Bogdanov 	u32 low_status, low_req = 0;
65762c1c2e6SDmitry Bogdanov 	u32 dword_cnt;
65862c1c2e6SDmitry Bogdanov 	u32 caps_lo;
65962c1c2e6SDmitry Bogdanov 	u32 offset;
66062c1c2e6SDmitry Bogdanov 	int err;
66162c1c2e6SDmitry Bogdanov 
66262c1c2e6SDmitry Bogdanov 	if (!req || !response)
66362c1c2e6SDmitry Bogdanov 		return -EINVAL;
66462c1c2e6SDmitry Bogdanov 
66562c1c2e6SDmitry Bogdanov 	caps_lo = aq_fw2x_get_link_capabilities(hw);
66662c1c2e6SDmitry Bogdanov 	if (!(caps_lo & BIT(CAPS_LO_MACSEC)))
66762c1c2e6SDmitry Bogdanov 		return -EOPNOTSUPP;
66862c1c2e6SDmitry Bogdanov 
66962c1c2e6SDmitry Bogdanov 	/* Write macsec request to cfg memory */
67062c1c2e6SDmitry Bogdanov 	dword_cnt = (sizeof(*req) + sizeof(u32) - 1) / sizeof(u32);
67162c1c2e6SDmitry Bogdanov 	err = hw_atl_write_fwcfg_dwords(hw, (void *)req, dword_cnt);
67262c1c2e6SDmitry Bogdanov 	if (err < 0)
67362c1c2e6SDmitry Bogdanov 		return err;
67462c1c2e6SDmitry Bogdanov 
67562c1c2e6SDmitry Bogdanov 	/* Toggle 0x368.CAPS_LO_MACSEC bit */
67662c1c2e6SDmitry Bogdanov 	low_req = aq_hw_read_reg(hw, HW_ATL_FW2X_MPI_CONTROL_ADDR);
67762c1c2e6SDmitry Bogdanov 	low_req ^= HW_ATL_FW2X_CAP_MACSEC;
67862c1c2e6SDmitry Bogdanov 	aq_hw_write_reg(hw, HW_ATL_FW2X_MPI_CONTROL_ADDR, low_req);
67962c1c2e6SDmitry Bogdanov 
68062c1c2e6SDmitry Bogdanov 	/* Wait FW to report back */
68162c1c2e6SDmitry Bogdanov 	err = readx_poll_timeout_atomic(aq_fw2x_state_get, hw, low_status,
68262c1c2e6SDmitry Bogdanov 		low_req != (low_status & BIT(CAPS_LO_MACSEC)), 1U, 10000U);
68362c1c2e6SDmitry Bogdanov 	if (err)
68462c1c2e6SDmitry Bogdanov 		return -EIO;
68562c1c2e6SDmitry Bogdanov 
68662c1c2e6SDmitry Bogdanov 	/* Read status of write operation */
68762c1c2e6SDmitry Bogdanov 	offset = hw->rpc_addr + sizeof(u32);
68862c1c2e6SDmitry Bogdanov 	err = hw_atl_utils_fw_downld_dwords(hw, offset, (u32 *)(void *)response,
68962c1c2e6SDmitry Bogdanov 					    sizeof(*response) / sizeof(u32));
69062c1c2e6SDmitry Bogdanov 
69162c1c2e6SDmitry Bogdanov 	return err;
69262c1c2e6SDmitry Bogdanov }
69362c1c2e6SDmitry Bogdanov 
694a57d3929SIgor Russkikh const struct aq_fw_ops aq_fw_2x_ops = {
695a57d3929SIgor Russkikh 	.init               = aq_fw2x_init,
69644e00dd8SIgor Russkikh 	.deinit             = aq_fw2x_deinit,
697a57d3929SIgor Russkikh 	.reset              = NULL,
698b8d68b62SAnton Mikaev 	.renegotiate        = aq_fw2x_renegotiate,
699a57d3929SIgor Russkikh 	.get_mac_permanent  = aq_fw2x_get_mac_permanent,
700a57d3929SIgor Russkikh 	.set_link_speed     = aq_fw2x_set_link_speed,
701a57d3929SIgor Russkikh 	.set_state          = aq_fw2x_set_state,
702a57d3929SIgor Russkikh 	.update_link_status = aq_fw2x_update_link_status,
703a57d3929SIgor Russkikh 	.update_stats       = aq_fw2x_update_stats,
7048f894011SYana Esina 	.get_phy_temp       = aq_fw2x_get_phy_temp,
705a0da96c0SYana Esina 	.set_power          = aq_fw2x_set_power,
70692ab6407SYana Esina 	.set_eee_rate       = aq_fw2x_set_eee_rate,
70792ab6407SYana Esina 	.get_eee_rate       = aq_fw2x_get_eee_rate,
708288551deSIgor Russkikh 	.set_flow_control   = aq_fw2x_set_flow_control,
709910479a9SEgor Pomozov 	.get_flow_control   = aq_fw2x_get_flow_control,
710910479a9SEgor Pomozov 	.send_fw_request    = aq_fw2x_send_fw_request,
711910479a9SEgor Pomozov 	.enable_ptp         = aq_fw3x_enable_ptp,
712d1287ce4SNikita Danilov 	.led_control        = aq_fw2x_led_control,
713ea4b4d7fSIgor Russkikh 	.set_phyloopback    = aq_fw2x_set_phyloopback,
714f08a464cSEgor Pomozov 	.adjust_ptp         = aq_fw3x_adjust_ptp,
71562c1c2e6SDmitry Bogdanov 	.get_link_capabilities = aq_fw2x_get_link_capabilities,
71662c1c2e6SDmitry Bogdanov 	.send_macsec_req    = aq_fw2x_send_macsec_req,
717a57d3929SIgor Russkikh };
718