1a57d3929SIgor Russkikh /*
2a57d3929SIgor Russkikh  * aQuantia Corporation Network Driver
3a57d3929SIgor Russkikh  * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
4a57d3929SIgor Russkikh  *
5a57d3929SIgor Russkikh  * This program is free software; you can redistribute it and/or modify it
6a57d3929SIgor Russkikh  * under the terms and conditions of the GNU General Public License,
7a57d3929SIgor Russkikh  * version 2, as published by the Free Software Foundation.
8a57d3929SIgor Russkikh  */
9a57d3929SIgor Russkikh 
10a57d3929SIgor Russkikh /* File hw_atl_utils_fw2x.c: Definition of firmware 2.x functions for
11a57d3929SIgor Russkikh  * Atlantic hardware abstraction layer.
12a57d3929SIgor Russkikh  */
13a57d3929SIgor Russkikh 
14a57d3929SIgor Russkikh #include "../aq_hw.h"
15a57d3929SIgor Russkikh #include "../aq_hw_utils.h"
16a57d3929SIgor Russkikh #include "../aq_pci_func.h"
17a57d3929SIgor Russkikh #include "../aq_ring.h"
18a57d3929SIgor Russkikh #include "../aq_vec.h"
190e1a0ddeSYana Esina #include "../aq_nic.h"
20a57d3929SIgor Russkikh #include "hw_atl_utils.h"
21a57d3929SIgor Russkikh #include "hw_atl_llh.h"
22a57d3929SIgor Russkikh 
23a57d3929SIgor Russkikh #define HW_ATL_FW2X_MPI_EFUSE_ADDR	0x364
24a57d3929SIgor Russkikh #define HW_ATL_FW2X_MPI_MBOX_ADDR	0x360
253ee5c887SYana Esina #define HW_ATL_FW2X_MPI_RPC_ADDR        0x334
26a57d3929SIgor Russkikh 
27a57d3929SIgor Russkikh #define HW_ATL_FW2X_MPI_CONTROL_ADDR	0x368
28a57d3929SIgor Russkikh #define HW_ATL_FW2X_MPI_CONTROL2_ADDR	0x36C
29a57d3929SIgor Russkikh 
30a57d3929SIgor Russkikh #define HW_ATL_FW2X_MPI_STATE_ADDR	0x370
31a57d3929SIgor Russkikh #define HW_ATL_FW2X_MPI_STATE2_ADDR	0x374
32a57d3929SIgor Russkikh 
330e1a0ddeSYana Esina #define HW_ATL_FW2X_CAP_SLEEP_PROXY      BIT(CAPS_HI_SLEEP_PROXY)
340e1a0ddeSYana Esina #define HW_ATL_FW2X_CAP_WOL              BIT(CAPS_HI_WOL)
350e1a0ddeSYana Esina 
360e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_SLEEP_PROXY      BIT(CTRL_SLEEP_PROXY)
370e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_WOL              BIT(CTRL_WOL)
380e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_LINK_DROP        BIT(CTRL_LINK_DROP)
390e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_PAUSE            BIT(CTRL_PAUSE)
400e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_ASYMMETRIC_PAUSE BIT(CTRL_ASYMMETRIC_PAUSE)
410e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_FORCE_RECONNECT  BIT(CTRL_FORCE_RECONNECT)
420e1a0ddeSYana Esina 
4392ab6407SYana Esina #define HW_ATL_FW2X_CAP_EEE_1G_MASK      BIT(CAPS_HI_1000BASET_FD_EEE)
4492ab6407SYana Esina #define HW_ATL_FW2X_CAP_EEE_2G5_MASK     BIT(CAPS_HI_2P5GBASET_FD_EEE)
4592ab6407SYana Esina #define HW_ATL_FW2X_CAP_EEE_5G_MASK      BIT(CAPS_HI_5GBASET_FD_EEE)
4692ab6407SYana Esina #define HW_ATL_FW2X_CAP_EEE_10G_MASK     BIT(CAPS_HI_10GBASET_FD_EEE)
4792ab6407SYana Esina 
480e1a0ddeSYana Esina #define HAL_ATLANTIC_WOL_FILTERS_COUNT   8
490e1a0ddeSYana Esina #define HAL_ATLANTIC_UTILS_FW2X_MSG_WOL  0x0E
500e1a0ddeSYana Esina 
510e1a0ddeSYana Esina struct __packed fw2x_msg_wol_pattern {
520e1a0ddeSYana Esina 	u8 mask[16];
530e1a0ddeSYana Esina 	u32 crc;
540e1a0ddeSYana Esina };
550e1a0ddeSYana Esina 
560e1a0ddeSYana Esina struct __packed fw2x_msg_wol {
570e1a0ddeSYana Esina 	u32 msg_id;
580e1a0ddeSYana Esina 	u8 hw_addr[ETH_ALEN];
590e1a0ddeSYana Esina 	u8 magic_packet_enabled;
600e1a0ddeSYana Esina 	u8 filter_count;
610e1a0ddeSYana Esina 	struct fw2x_msg_wol_pattern filter[HAL_ATLANTIC_WOL_FILTERS_COUNT];
620e1a0ddeSYana Esina 	u8 link_up_enabled;
630e1a0ddeSYana Esina 	u8 link_down_enabled;
640e1a0ddeSYana Esina 	u16 reserved;
650e1a0ddeSYana Esina 	u32 link_up_timeout;
660e1a0ddeSYana Esina 	u32 link_down_timeout;
670e1a0ddeSYana Esina };
680e1a0ddeSYana Esina 
6944e00dd8SIgor Russkikh static int aq_fw2x_set_link_speed(struct aq_hw_s *self, u32 speed);
7044e00dd8SIgor Russkikh static int aq_fw2x_set_state(struct aq_hw_s *self,
7144e00dd8SIgor Russkikh 			     enum hal_atl_utils_fw_state_e state);
7244e00dd8SIgor Russkikh 
73a57d3929SIgor Russkikh static int aq_fw2x_init(struct aq_hw_s *self)
74a57d3929SIgor Russkikh {
75a57d3929SIgor Russkikh 	int err = 0;
76a57d3929SIgor Russkikh 
77a57d3929SIgor Russkikh 	/* check 10 times by 1ms */
78a57d3929SIgor Russkikh 	AQ_HW_WAIT_FOR(0U != (self->mbox_addr =
79a57d3929SIgor Russkikh 		       aq_hw_read_reg(self, HW_ATL_FW2X_MPI_MBOX_ADDR)),
80a57d3929SIgor Russkikh 		       1000U, 10U);
813ee5c887SYana Esina 	AQ_HW_WAIT_FOR(0U != (self->rpc_addr =
823ee5c887SYana Esina 		       aq_hw_read_reg(self, HW_ATL_FW2X_MPI_RPC_ADDR)),
833ee5c887SYana Esina 		       1000U, 100U);
843ee5c887SYana Esina 
85a57d3929SIgor Russkikh 	return err;
86a57d3929SIgor Russkikh }
87a57d3929SIgor Russkikh 
8844e00dd8SIgor Russkikh static int aq_fw2x_deinit(struct aq_hw_s *self)
8944e00dd8SIgor Russkikh {
9044e00dd8SIgor Russkikh 	int err = aq_fw2x_set_link_speed(self, 0);
9144e00dd8SIgor Russkikh 
9244e00dd8SIgor Russkikh 	if (!err)
9344e00dd8SIgor Russkikh 		err = aq_fw2x_set_state(self, MPI_DEINIT);
9444e00dd8SIgor Russkikh 
9544e00dd8SIgor Russkikh 	return err;
9644e00dd8SIgor Russkikh }
9744e00dd8SIgor Russkikh 
98a57d3929SIgor Russkikh static enum hw_atl_fw2x_rate link_speed_mask_2fw2x_ratemask(u32 speed)
99a57d3929SIgor Russkikh {
100a57d3929SIgor Russkikh 	enum hw_atl_fw2x_rate rate = 0;
101a57d3929SIgor Russkikh 
102a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_10G)
103a57d3929SIgor Russkikh 		rate |= FW2X_RATE_10G;
104a57d3929SIgor Russkikh 
105a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_5G)
106a57d3929SIgor Russkikh 		rate |= FW2X_RATE_5G;
107a57d3929SIgor Russkikh 
108a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_5GSR)
109a57d3929SIgor Russkikh 		rate |= FW2X_RATE_5G;
110a57d3929SIgor Russkikh 
111a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_2GS)
112a57d3929SIgor Russkikh 		rate |= FW2X_RATE_2G5;
113a57d3929SIgor Russkikh 
114a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_1G)
115a57d3929SIgor Russkikh 		rate |= FW2X_RATE_1G;
116a57d3929SIgor Russkikh 
117a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_100M)
118a57d3929SIgor Russkikh 		rate |= FW2X_RATE_100M;
119a57d3929SIgor Russkikh 
120a57d3929SIgor Russkikh 	return rate;
121a57d3929SIgor Russkikh }
122a57d3929SIgor Russkikh 
12392ab6407SYana Esina static u32 fw2x_to_eee_mask(u32 speed)
12492ab6407SYana Esina {
12592ab6407SYana Esina 	u32 rate = 0;
12692ab6407SYana Esina 
12792ab6407SYana Esina 	if (speed & HW_ATL_FW2X_CAP_EEE_10G_MASK)
12892ab6407SYana Esina 		rate |= AQ_NIC_RATE_EEE_10G;
12992ab6407SYana Esina 	if (speed & HW_ATL_FW2X_CAP_EEE_5G_MASK)
13092ab6407SYana Esina 		rate |= AQ_NIC_RATE_EEE_5G;
13192ab6407SYana Esina 	if (speed & HW_ATL_FW2X_CAP_EEE_2G5_MASK)
13292ab6407SYana Esina 		rate |= AQ_NIC_RATE_EEE_2GS;
13392ab6407SYana Esina 	if (speed & HW_ATL_FW2X_CAP_EEE_1G_MASK)
13492ab6407SYana Esina 		rate |= AQ_NIC_RATE_EEE_1G;
13592ab6407SYana Esina 
13692ab6407SYana Esina 	return rate;
13792ab6407SYana Esina }
13892ab6407SYana Esina 
13992ab6407SYana Esina static u32 eee_mask_to_fw2x(u32 speed)
14092ab6407SYana Esina {
14192ab6407SYana Esina 	u32 rate = 0;
14292ab6407SYana Esina 
14392ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_10G)
14492ab6407SYana Esina 		rate |= HW_ATL_FW2X_CAP_EEE_10G_MASK;
14592ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_5G)
14692ab6407SYana Esina 		rate |= HW_ATL_FW2X_CAP_EEE_5G_MASK;
14792ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_2GS)
14892ab6407SYana Esina 		rate |= HW_ATL_FW2X_CAP_EEE_2G5_MASK;
14992ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_1G)
15092ab6407SYana Esina 		rate |= HW_ATL_FW2X_CAP_EEE_1G_MASK;
15192ab6407SYana Esina 
15292ab6407SYana Esina 	return rate;
15392ab6407SYana Esina }
15492ab6407SYana Esina 
155a57d3929SIgor Russkikh static int aq_fw2x_set_link_speed(struct aq_hw_s *self, u32 speed)
156a57d3929SIgor Russkikh {
157a57d3929SIgor Russkikh 	u32 val = link_speed_mask_2fw2x_ratemask(speed);
158a57d3929SIgor Russkikh 
159a57d3929SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL_ADDR, val);
160a57d3929SIgor Russkikh 
161a57d3929SIgor Russkikh 	return 0;
162a57d3929SIgor Russkikh }
163a57d3929SIgor Russkikh 
164288551deSIgor Russkikh static void aq_fw2x_set_mpi_flow_control(struct aq_hw_s *self, u32 *mpi_state)
165288551deSIgor Russkikh {
166288551deSIgor Russkikh 	if (self->aq_nic_cfg->flow_control & AQ_NIC_FC_RX)
167288551deSIgor Russkikh 		*mpi_state |= BIT(CAPS_HI_PAUSE);
168288551deSIgor Russkikh 	else
169288551deSIgor Russkikh 		*mpi_state &= ~BIT(CAPS_HI_PAUSE);
170288551deSIgor Russkikh 
171288551deSIgor Russkikh 	if (self->aq_nic_cfg->flow_control & AQ_NIC_FC_TX)
172288551deSIgor Russkikh 		*mpi_state |= BIT(CAPS_HI_ASYMMETRIC_PAUSE);
173288551deSIgor Russkikh 	else
174288551deSIgor Russkikh 		*mpi_state &= ~BIT(CAPS_HI_ASYMMETRIC_PAUSE);
175288551deSIgor Russkikh }
176288551deSIgor Russkikh 
17792ab6407SYana Esina static void aq_fw2x_upd_eee_rate_bits(struct aq_hw_s *self, u32 *mpi_opts,
17892ab6407SYana Esina 				      u32 eee_speeds)
17992ab6407SYana Esina {
18092ab6407SYana Esina 	*mpi_opts &= ~(HW_ATL_FW2X_CAP_EEE_1G_MASK |
18192ab6407SYana Esina 		       HW_ATL_FW2X_CAP_EEE_2G5_MASK |
18292ab6407SYana Esina 		       HW_ATL_FW2X_CAP_EEE_5G_MASK |
18392ab6407SYana Esina 		       HW_ATL_FW2X_CAP_EEE_10G_MASK);
18492ab6407SYana Esina 
18592ab6407SYana Esina 	*mpi_opts |= eee_mask_to_fw2x(eee_speeds);
18692ab6407SYana Esina }
18792ab6407SYana Esina 
188a57d3929SIgor Russkikh static int aq_fw2x_set_state(struct aq_hw_s *self,
189a57d3929SIgor Russkikh 			     enum hal_atl_utils_fw_state_e state)
190a57d3929SIgor Russkikh {
19144e00dd8SIgor Russkikh 	u32 mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
19292ab6407SYana Esina 	struct aq_nic_cfg_s *cfg = self->aq_nic_cfg;
19344e00dd8SIgor Russkikh 
19444e00dd8SIgor Russkikh 	switch (state) {
19544e00dd8SIgor Russkikh 	case MPI_INIT:
19644e00dd8SIgor Russkikh 		mpi_state &= ~BIT(CAPS_HI_LINK_DROP);
19792ab6407SYana Esina 		aq_fw2x_upd_eee_rate_bits(self, &mpi_state, cfg->eee_speeds);
198288551deSIgor Russkikh 		aq_fw2x_set_mpi_flow_control(self, &mpi_state);
19944e00dd8SIgor Russkikh 		break;
20044e00dd8SIgor Russkikh 	case MPI_DEINIT:
20144e00dd8SIgor Russkikh 		mpi_state |= BIT(CAPS_HI_LINK_DROP);
20244e00dd8SIgor Russkikh 		break;
20344e00dd8SIgor Russkikh 	case MPI_RESET:
20444e00dd8SIgor Russkikh 	case MPI_POWER:
20544e00dd8SIgor Russkikh 		/* No actions */
20644e00dd8SIgor Russkikh 		break;
20744e00dd8SIgor Russkikh 	}
20844e00dd8SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_state);
209a57d3929SIgor Russkikh 	return 0;
210a57d3929SIgor Russkikh }
211a57d3929SIgor Russkikh 
212a57d3929SIgor Russkikh static int aq_fw2x_update_link_status(struct aq_hw_s *self)
213a57d3929SIgor Russkikh {
214a57d3929SIgor Russkikh 	u32 mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE_ADDR);
215a57d3929SIgor Russkikh 	u32 speed = mpi_state & (FW2X_RATE_100M | FW2X_RATE_1G |
216a57d3929SIgor Russkikh 				 FW2X_RATE_2G5 | FW2X_RATE_5G | FW2X_RATE_10G);
217a57d3929SIgor Russkikh 	struct aq_hw_link_status_s *link_status = &self->aq_link_status;
218a57d3929SIgor Russkikh 
219a57d3929SIgor Russkikh 	if (speed) {
220a57d3929SIgor Russkikh 		if (speed & FW2X_RATE_10G)
221a57d3929SIgor Russkikh 			link_status->mbps = 10000;
222a57d3929SIgor Russkikh 		else if (speed & FW2X_RATE_5G)
223a57d3929SIgor Russkikh 			link_status->mbps = 5000;
224a57d3929SIgor Russkikh 		else if (speed & FW2X_RATE_2G5)
225a57d3929SIgor Russkikh 			link_status->mbps = 2500;
226a57d3929SIgor Russkikh 		else if (speed & FW2X_RATE_1G)
227a57d3929SIgor Russkikh 			link_status->mbps = 1000;
228a57d3929SIgor Russkikh 		else if (speed & FW2X_RATE_100M)
229a57d3929SIgor Russkikh 			link_status->mbps = 100;
230a57d3929SIgor Russkikh 		else
231a57d3929SIgor Russkikh 			link_status->mbps = 10000;
232a57d3929SIgor Russkikh 	} else {
233a57d3929SIgor Russkikh 		link_status->mbps = 0;
234a57d3929SIgor Russkikh 	}
235a57d3929SIgor Russkikh 
236a57d3929SIgor Russkikh 	return 0;
237a57d3929SIgor Russkikh }
238a57d3929SIgor Russkikh 
23976a45194SColin Ian King static int aq_fw2x_get_mac_permanent(struct aq_hw_s *self, u8 *mac)
240a57d3929SIgor Russkikh {
241a57d3929SIgor Russkikh 	int err = 0;
242a57d3929SIgor Russkikh 	u32 h = 0U;
243a57d3929SIgor Russkikh 	u32 l = 0U;
244a57d3929SIgor Russkikh 	u32 mac_addr[2] = { 0 };
245a57d3929SIgor Russkikh 	u32 efuse_addr = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_EFUSE_ADDR);
246a57d3929SIgor Russkikh 
247a57d3929SIgor Russkikh 	if (efuse_addr != 0) {
248a57d3929SIgor Russkikh 		err = hw_atl_utils_fw_downld_dwords(self,
249a57d3929SIgor Russkikh 						    efuse_addr + (40U * 4U),
250a57d3929SIgor Russkikh 						    mac_addr,
251a57d3929SIgor Russkikh 						    ARRAY_SIZE(mac_addr));
252a57d3929SIgor Russkikh 		if (err)
253a57d3929SIgor Russkikh 			return err;
254a57d3929SIgor Russkikh 		mac_addr[0] = __swab32(mac_addr[0]);
255a57d3929SIgor Russkikh 		mac_addr[1] = __swab32(mac_addr[1]);
256a57d3929SIgor Russkikh 	}
257a57d3929SIgor Russkikh 
258a57d3929SIgor Russkikh 	ether_addr_copy(mac, (u8 *)mac_addr);
259a57d3929SIgor Russkikh 
260a57d3929SIgor Russkikh 	if ((mac[0] & 0x01U) || ((mac[0] | mac[1] | mac[2]) == 0x00U)) {
261a57d3929SIgor Russkikh 		unsigned int rnd = 0;
262a57d3929SIgor Russkikh 
263a57d3929SIgor Russkikh 		get_random_bytes(&rnd, sizeof(unsigned int));
264a57d3929SIgor Russkikh 
265e9157848SNikita Danilov 		l = 0xE3000000U | (0xFFFFU & rnd) | (0x00 << 16);
266a57d3929SIgor Russkikh 		h = 0x8001300EU;
267a57d3929SIgor Russkikh 
268a57d3929SIgor Russkikh 		mac[5] = (u8)(0xFFU & l);
269a57d3929SIgor Russkikh 		l >>= 8;
270a57d3929SIgor Russkikh 		mac[4] = (u8)(0xFFU & l);
271a57d3929SIgor Russkikh 		l >>= 8;
272a57d3929SIgor Russkikh 		mac[3] = (u8)(0xFFU & l);
273a57d3929SIgor Russkikh 		l >>= 8;
274a57d3929SIgor Russkikh 		mac[2] = (u8)(0xFFU & l);
275a57d3929SIgor Russkikh 		mac[1] = (u8)(0xFFU & h);
276a57d3929SIgor Russkikh 		h >>= 8;
277a57d3929SIgor Russkikh 		mac[0] = (u8)(0xFFU & h);
278a57d3929SIgor Russkikh 	}
279a57d3929SIgor Russkikh 	return err;
280a57d3929SIgor Russkikh }
281a57d3929SIgor Russkikh 
2820ba4ad32SYueHaibing static int aq_fw2x_update_stats(struct aq_hw_s *self)
283a57d3929SIgor Russkikh {
284a57d3929SIgor Russkikh 	int err = 0;
285a57d3929SIgor Russkikh 	u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
286a57d3929SIgor Russkikh 	u32 orig_stats_val = mpi_opts & BIT(CAPS_HI_STATISTICS);
287a57d3929SIgor Russkikh 
288a57d3929SIgor Russkikh 	/* Toggle statistics bit for FW to update */
289a57d3929SIgor Russkikh 	mpi_opts = mpi_opts ^ BIT(CAPS_HI_STATISTICS);
290a57d3929SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
291a57d3929SIgor Russkikh 
292a57d3929SIgor Russkikh 	/* Wait FW to report back */
293a57d3929SIgor Russkikh 	AQ_HW_WAIT_FOR(orig_stats_val !=
294a57d3929SIgor Russkikh 		       (aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR) &
295a57d3929SIgor Russkikh 			BIT(CAPS_HI_STATISTICS)),
296a57d3929SIgor Russkikh 		       1U, 10000U);
297a57d3929SIgor Russkikh 	if (err)
298a57d3929SIgor Russkikh 		return err;
299a57d3929SIgor Russkikh 
300a57d3929SIgor Russkikh 	return hw_atl_utils_update_stats(self);
301a57d3929SIgor Russkikh }
302a57d3929SIgor Russkikh 
303a0da96c0SYana Esina static int aq_fw2x_set_sleep_proxy(struct aq_hw_s *self, u8 *mac)
304a0da96c0SYana Esina {
3058f60f762SNikita Danilov 	struct hw_atl_utils_fw_rpc *rpc = NULL;
306a0da96c0SYana Esina 	struct offload_info *cfg = NULL;
307a0da96c0SYana Esina 	unsigned int rpc_size = 0U;
308a0da96c0SYana Esina 	u32 mpi_opts;
309a0da96c0SYana Esina 	int err = 0;
310a0da96c0SYana Esina 
311a0da96c0SYana Esina 	rpc_size = sizeof(rpc->msg_id) + sizeof(*cfg);
312a0da96c0SYana Esina 
313a0da96c0SYana Esina 	err = hw_atl_utils_fw_rpc_wait(self, &rpc);
314a0da96c0SYana Esina 	if (err < 0)
315a0da96c0SYana Esina 		goto err_exit;
316a0da96c0SYana Esina 
317a0da96c0SYana Esina 	memset(rpc, 0, rpc_size);
318a0da96c0SYana Esina 	cfg = (struct offload_info *)(&rpc->msg_id + 1);
319a0da96c0SYana Esina 
320a0da96c0SYana Esina 	memcpy(cfg->mac_addr, mac, ETH_ALEN);
321a0da96c0SYana Esina 	cfg->len = sizeof(*cfg);
322a0da96c0SYana Esina 
323a0da96c0SYana Esina 	/* Clear bit 0x36C.23 and 0x36C.22 */
324a0da96c0SYana Esina 	mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
325a0da96c0SYana Esina 	mpi_opts &= ~HW_ATL_FW2X_CTRL_SLEEP_PROXY;
326a0da96c0SYana Esina 	mpi_opts &= ~HW_ATL_FW2X_CTRL_LINK_DROP;
327a0da96c0SYana Esina 
328a0da96c0SYana Esina 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
329a0da96c0SYana Esina 
330a0da96c0SYana Esina 	err = hw_atl_utils_fw_rpc_call(self, rpc_size);
331a0da96c0SYana Esina 	if (err < 0)
332a0da96c0SYana Esina 		goto err_exit;
333a0da96c0SYana Esina 
334a0da96c0SYana Esina 	/* Set bit 0x36C.23 */
335a0da96c0SYana Esina 	mpi_opts |= HW_ATL_FW2X_CTRL_SLEEP_PROXY;
336a0da96c0SYana Esina 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
337a0da96c0SYana Esina 
338a0da96c0SYana Esina 	AQ_HW_WAIT_FOR((aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR) &
339a0da96c0SYana Esina 			HW_ATL_FW2X_CTRL_SLEEP_PROXY), 1U, 10000U);
340a0da96c0SYana Esina 
341a0da96c0SYana Esina err_exit:
342a0da96c0SYana Esina 	return err;
343a0da96c0SYana Esina }
344a0da96c0SYana Esina 
345a0da96c0SYana Esina static int aq_fw2x_set_wol_params(struct aq_hw_s *self, u8 *mac)
346a0da96c0SYana Esina {
3478f60f762SNikita Danilov 	struct hw_atl_utils_fw_rpc *rpc = NULL;
348a0da96c0SYana Esina 	struct fw2x_msg_wol *msg = NULL;
349a0da96c0SYana Esina 	u32 mpi_opts;
350a0da96c0SYana Esina 	int err = 0;
351a0da96c0SYana Esina 
352a0da96c0SYana Esina 	err = hw_atl_utils_fw_rpc_wait(self, &rpc);
353a0da96c0SYana Esina 	if (err < 0)
354a0da96c0SYana Esina 		goto err_exit;
355a0da96c0SYana Esina 
356a0da96c0SYana Esina 	msg = (struct fw2x_msg_wol *)rpc;
357a0da96c0SYana Esina 
358a0da96c0SYana Esina 	msg->msg_id = HAL_ATLANTIC_UTILS_FW2X_MSG_WOL;
359a0da96c0SYana Esina 	msg->magic_packet_enabled = true;
360a0da96c0SYana Esina 	memcpy(msg->hw_addr, mac, ETH_ALEN);
361a0da96c0SYana Esina 
362a0da96c0SYana Esina 	mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
363a0da96c0SYana Esina 	mpi_opts &= ~(HW_ATL_FW2X_CTRL_SLEEP_PROXY | HW_ATL_FW2X_CTRL_WOL);
364a0da96c0SYana Esina 
365a0da96c0SYana Esina 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
366a0da96c0SYana Esina 
367a0da96c0SYana Esina 	err = hw_atl_utils_fw_rpc_call(self, sizeof(*msg));
368a0da96c0SYana Esina 	if (err < 0)
369a0da96c0SYana Esina 		goto err_exit;
370a0da96c0SYana Esina 
371a0da96c0SYana Esina 	/* Set bit 0x36C.24 */
372a0da96c0SYana Esina 	mpi_opts |= HW_ATL_FW2X_CTRL_WOL;
373a0da96c0SYana Esina 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
374a0da96c0SYana Esina 
375a0da96c0SYana Esina 	AQ_HW_WAIT_FOR((aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR) &
376a0da96c0SYana Esina 			HW_ATL_FW2X_CTRL_WOL), 1U, 10000U);
377a0da96c0SYana Esina 
378a0da96c0SYana Esina err_exit:
379a0da96c0SYana Esina 	return err;
380a0da96c0SYana Esina }
381a0da96c0SYana Esina 
382a0da96c0SYana Esina static int aq_fw2x_set_power(struct aq_hw_s *self, unsigned int power_state,
383a0da96c0SYana Esina 			     u8 *mac)
384a0da96c0SYana Esina {
385a0da96c0SYana Esina 	int err = 0;
386a0da96c0SYana Esina 
387a0da96c0SYana Esina 	if (self->aq_nic_cfg->wol & AQ_NIC_WOL_ENABLED) {
388a0da96c0SYana Esina 		err = aq_fw2x_set_sleep_proxy(self, mac);
389a0da96c0SYana Esina 		if (err < 0)
390a0da96c0SYana Esina 			goto err_exit;
391a0da96c0SYana Esina 		err = aq_fw2x_set_wol_params(self, mac);
392a0da96c0SYana Esina 	}
393a0da96c0SYana Esina 
394a0da96c0SYana Esina err_exit:
395a0da96c0SYana Esina 	return err;
396a0da96c0SYana Esina }
397a0da96c0SYana Esina 
39892ab6407SYana Esina static int aq_fw2x_set_eee_rate(struct aq_hw_s *self, u32 speed)
39992ab6407SYana Esina {
40092ab6407SYana Esina 	u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
40192ab6407SYana Esina 
40292ab6407SYana Esina 	aq_fw2x_upd_eee_rate_bits(self, &mpi_opts, speed);
40392ab6407SYana Esina 
40492ab6407SYana Esina 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
40592ab6407SYana Esina 
40692ab6407SYana Esina 	return 0;
40792ab6407SYana Esina }
40892ab6407SYana Esina 
40992ab6407SYana Esina static int aq_fw2x_get_eee_rate(struct aq_hw_s *self, u32 *rate,
41092ab6407SYana Esina 				u32 *supported_rates)
41192ab6407SYana Esina {
41292ab6407SYana Esina 	u32 mpi_state;
41392ab6407SYana Esina 	u32 caps_hi;
41492ab6407SYana Esina 	int err = 0;
4158f60f762SNikita Danilov 	u32 addr = self->mbox_addr + offsetof(struct hw_atl_utils_mbox, info) +
41692ab6407SYana Esina 		   offsetof(struct hw_aq_info, caps_hi);
41792ab6407SYana Esina 
41892ab6407SYana Esina 	err = hw_atl_utils_fw_downld_dwords(self, addr, &caps_hi,
41992ab6407SYana Esina 					    sizeof(caps_hi) / sizeof(u32));
42092ab6407SYana Esina 
42192ab6407SYana Esina 	if (err)
42292ab6407SYana Esina 		return err;
42392ab6407SYana Esina 
42492ab6407SYana Esina 	*supported_rates = fw2x_to_eee_mask(caps_hi);
42592ab6407SYana Esina 
42692ab6407SYana Esina 	mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR);
42792ab6407SYana Esina 	*rate = fw2x_to_eee_mask(mpi_state);
42892ab6407SYana Esina 
42992ab6407SYana Esina 	return err;
43092ab6407SYana Esina }
43192ab6407SYana Esina 
432b8d68b62SAnton Mikaev static int aq_fw2x_renegotiate(struct aq_hw_s *self)
433b8d68b62SAnton Mikaev {
434b8d68b62SAnton Mikaev 	u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
435b8d68b62SAnton Mikaev 
436b8d68b62SAnton Mikaev 	mpi_opts |= BIT(CTRL_FORCE_RECONNECT);
437b8d68b62SAnton Mikaev 
438b8d68b62SAnton Mikaev 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
439b8d68b62SAnton Mikaev 
440b8d68b62SAnton Mikaev 	return 0;
441b8d68b62SAnton Mikaev }
442b8d68b62SAnton Mikaev 
443288551deSIgor Russkikh static int aq_fw2x_set_flow_control(struct aq_hw_s *self)
444288551deSIgor Russkikh {
445288551deSIgor Russkikh 	u32 mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
446288551deSIgor Russkikh 
447288551deSIgor Russkikh 	aq_fw2x_set_mpi_flow_control(self, &mpi_state);
448288551deSIgor Russkikh 
449288551deSIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_state);
450288551deSIgor Russkikh 
451288551deSIgor Russkikh 	return 0;
452288551deSIgor Russkikh }
453288551deSIgor Russkikh 
454a57d3929SIgor Russkikh const struct aq_fw_ops aq_fw_2x_ops = {
455a57d3929SIgor Russkikh 	.init = aq_fw2x_init,
45644e00dd8SIgor Russkikh 	.deinit = aq_fw2x_deinit,
457a57d3929SIgor Russkikh 	.reset = NULL,
458b8d68b62SAnton Mikaev 	.renegotiate = aq_fw2x_renegotiate,
459a57d3929SIgor Russkikh 	.get_mac_permanent = aq_fw2x_get_mac_permanent,
460a57d3929SIgor Russkikh 	.set_link_speed = aq_fw2x_set_link_speed,
461a57d3929SIgor Russkikh 	.set_state = aq_fw2x_set_state,
462a57d3929SIgor Russkikh 	.update_link_status = aq_fw2x_update_link_status,
463a57d3929SIgor Russkikh 	.update_stats = aq_fw2x_update_stats,
464a0da96c0SYana Esina 	.set_power = aq_fw2x_set_power,
46592ab6407SYana Esina 	.set_eee_rate = aq_fw2x_set_eee_rate,
46692ab6407SYana Esina 	.get_eee_rate = aq_fw2x_get_eee_rate,
467288551deSIgor Russkikh 	.set_flow_control = aq_fw2x_set_flow_control,
468a57d3929SIgor Russkikh };
469