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 
233ee5c887SYana Esina #define HW_ATL_FW2X_MPI_RPC_ADDR        0x334
24a57d3929SIgor Russkikh 
256a7f2277SNikita Danilov #define HW_ATL_FW2X_MPI_MBOX_ADDR       0x360
266a7f2277SNikita Danilov #define HW_ATL_FW2X_MPI_EFUSE_ADDR	0x364
27a57d3929SIgor Russkikh #define HW_ATL_FW2X_MPI_CONTROL_ADDR	0x368
28a57d3929SIgor Russkikh #define HW_ATL_FW2X_MPI_CONTROL2_ADDR	0x36C
29a57d3929SIgor Russkikh #define HW_ATL_FW2X_MPI_STATE_ADDR	0x370
30a57d3929SIgor Russkikh #define HW_ATL_FW2X_MPI_STATE2_ADDR     0x374
31a57d3929SIgor Russkikh 
3235e8e8b4SIgor Russkikh #define HW_ATL_FW2X_CAP_PAUSE            BIT(CAPS_HI_PAUSE)
3335e8e8b4SIgor Russkikh #define HW_ATL_FW2X_CAP_ASYM_PAUSE       BIT(CAPS_HI_ASYMMETRIC_PAUSE)
340e1a0ddeSYana Esina #define HW_ATL_FW2X_CAP_SLEEP_PROXY      BIT(CAPS_HI_SLEEP_PROXY)
350e1a0ddeSYana Esina #define HW_ATL_FW2X_CAP_WOL              BIT(CAPS_HI_WOL)
360e1a0ddeSYana Esina 
370e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_SLEEP_PROXY      BIT(CTRL_SLEEP_PROXY)
380e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_WOL              BIT(CTRL_WOL)
390e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_LINK_DROP        BIT(CTRL_LINK_DROP)
400e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_PAUSE            BIT(CTRL_PAUSE)
410e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_ASYMMETRIC_PAUSE BIT(CTRL_ASYMMETRIC_PAUSE)
420e1a0ddeSYana Esina #define HW_ATL_FW2X_CTRL_FORCE_RECONNECT  BIT(CTRL_FORCE_RECONNECT)
430e1a0ddeSYana Esina 
4492ab6407SYana Esina #define HW_ATL_FW2X_CAP_EEE_1G_MASK      BIT(CAPS_HI_1000BASET_FD_EEE)
4592ab6407SYana Esina #define HW_ATL_FW2X_CAP_EEE_2G5_MASK     BIT(CAPS_HI_2P5GBASET_FD_EEE)
4692ab6407SYana Esina #define HW_ATL_FW2X_CAP_EEE_5G_MASK      BIT(CAPS_HI_5GBASET_FD_EEE)
4792ab6407SYana Esina #define HW_ATL_FW2X_CAP_EEE_10G_MASK     BIT(CAPS_HI_10GBASET_FD_EEE)
4892ab6407SYana Esina 
490e1a0ddeSYana Esina #define HAL_ATLANTIC_WOL_FILTERS_COUNT   8
500e1a0ddeSYana Esina #define HAL_ATLANTIC_UTILS_FW2X_MSG_WOL  0x0E
510e1a0ddeSYana Esina 
520e1a0ddeSYana Esina struct __packed fw2x_msg_wol_pattern {
530e1a0ddeSYana Esina 	u8 mask[16];
540e1a0ddeSYana Esina 	u32 crc;
550e1a0ddeSYana Esina };
560e1a0ddeSYana Esina 
570e1a0ddeSYana Esina struct __packed fw2x_msg_wol {
580e1a0ddeSYana Esina 	u32 msg_id;
590e1a0ddeSYana Esina 	u8 hw_addr[ETH_ALEN];
600e1a0ddeSYana Esina 	u8 magic_packet_enabled;
610e1a0ddeSYana Esina 	u8 filter_count;
620e1a0ddeSYana Esina 	struct fw2x_msg_wol_pattern filter[HAL_ATLANTIC_WOL_FILTERS_COUNT];
630e1a0ddeSYana Esina 	u8 link_up_enabled;
640e1a0ddeSYana Esina 	u8 link_down_enabled;
650e1a0ddeSYana Esina 	u16 reserved;
660e1a0ddeSYana Esina 	u32 link_up_timeout;
670e1a0ddeSYana Esina 	u32 link_down_timeout;
680e1a0ddeSYana Esina };
690e1a0ddeSYana Esina 
7044e00dd8SIgor Russkikh static int aq_fw2x_set_link_speed(struct aq_hw_s *self, u32 speed);
7144e00dd8SIgor Russkikh static int aq_fw2x_set_state(struct aq_hw_s *self,
7244e00dd8SIgor Russkikh 			     enum hal_atl_utils_fw_state_e state);
7344e00dd8SIgor Russkikh 
746a7f2277SNikita Danilov static u32 aq_fw2x_mbox_get(struct aq_hw_s *self);
756a7f2277SNikita Danilov static u32 aq_fw2x_rpc_get(struct aq_hw_s *self);
766a7f2277SNikita Danilov static u32 aq_fw2x_state2_get(struct aq_hw_s *self);
776a7f2277SNikita Danilov 
78a57d3929SIgor Russkikh static int aq_fw2x_init(struct aq_hw_s *self)
79a57d3929SIgor Russkikh {
80a57d3929SIgor Russkikh 	int err = 0;
81a57d3929SIgor Russkikh 
82a57d3929SIgor Russkikh 	/* check 10 times by 1ms */
836a7f2277SNikita Danilov 	err = readx_poll_timeout_atomic(aq_fw2x_mbox_get,
846a7f2277SNikita Danilov 					self, self->mbox_addr,
856a7f2277SNikita Danilov 					self->mbox_addr != 0U,
866a7f2277SNikita Danilov 					1000U, 10000U);
876a7f2277SNikita Danilov 
886a7f2277SNikita Danilov 	err = readx_poll_timeout_atomic(aq_fw2x_rpc_get,
896a7f2277SNikita Danilov 					self, self->rpc_addr,
906a7f2277SNikita Danilov 					self->rpc_addr != 0U,
916a7f2277SNikita Danilov 					1000U, 100000U);
923ee5c887SYana Esina 
93a57d3929SIgor Russkikh 	return err;
94a57d3929SIgor Russkikh }
95a57d3929SIgor Russkikh 
9644e00dd8SIgor Russkikh static int aq_fw2x_deinit(struct aq_hw_s *self)
9744e00dd8SIgor Russkikh {
9844e00dd8SIgor Russkikh 	int err = aq_fw2x_set_link_speed(self, 0);
9944e00dd8SIgor Russkikh 
10044e00dd8SIgor Russkikh 	if (!err)
10144e00dd8SIgor Russkikh 		err = aq_fw2x_set_state(self, MPI_DEINIT);
10244e00dd8SIgor Russkikh 
10344e00dd8SIgor Russkikh 	return err;
10444e00dd8SIgor Russkikh }
10544e00dd8SIgor Russkikh 
106a57d3929SIgor Russkikh static enum hw_atl_fw2x_rate link_speed_mask_2fw2x_ratemask(u32 speed)
107a57d3929SIgor Russkikh {
108a57d3929SIgor Russkikh 	enum hw_atl_fw2x_rate rate = 0;
109a57d3929SIgor Russkikh 
110a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_10G)
111a57d3929SIgor Russkikh 		rate |= FW2X_RATE_10G;
112a57d3929SIgor Russkikh 
113a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_5G)
114a57d3929SIgor Russkikh 		rate |= FW2X_RATE_5G;
115a57d3929SIgor Russkikh 
116a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_5GSR)
117a57d3929SIgor Russkikh 		rate |= FW2X_RATE_5G;
118a57d3929SIgor Russkikh 
119a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_2GS)
120a57d3929SIgor Russkikh 		rate |= FW2X_RATE_2G5;
121a57d3929SIgor Russkikh 
122a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_1G)
123a57d3929SIgor Russkikh 		rate |= FW2X_RATE_1G;
124a57d3929SIgor Russkikh 
125a57d3929SIgor Russkikh 	if (speed & AQ_NIC_RATE_100M)
126a57d3929SIgor Russkikh 		rate |= FW2X_RATE_100M;
127a57d3929SIgor Russkikh 
128a57d3929SIgor Russkikh 	return rate;
129a57d3929SIgor Russkikh }
130a57d3929SIgor Russkikh 
13192ab6407SYana Esina static u32 fw2x_to_eee_mask(u32 speed)
13292ab6407SYana Esina {
13392ab6407SYana Esina 	u32 rate = 0;
13492ab6407SYana Esina 
13592ab6407SYana Esina 	if (speed & HW_ATL_FW2X_CAP_EEE_10G_MASK)
13692ab6407SYana Esina 		rate |= AQ_NIC_RATE_EEE_10G;
13792ab6407SYana Esina 	if (speed & HW_ATL_FW2X_CAP_EEE_5G_MASK)
13892ab6407SYana Esina 		rate |= AQ_NIC_RATE_EEE_5G;
13992ab6407SYana Esina 	if (speed & HW_ATL_FW2X_CAP_EEE_2G5_MASK)
14092ab6407SYana Esina 		rate |= AQ_NIC_RATE_EEE_2GS;
14192ab6407SYana Esina 	if (speed & HW_ATL_FW2X_CAP_EEE_1G_MASK)
14292ab6407SYana Esina 		rate |= AQ_NIC_RATE_EEE_1G;
14392ab6407SYana Esina 
14492ab6407SYana Esina 	return rate;
14592ab6407SYana Esina }
14692ab6407SYana Esina 
14792ab6407SYana Esina static u32 eee_mask_to_fw2x(u32 speed)
14892ab6407SYana Esina {
14992ab6407SYana Esina 	u32 rate = 0;
15092ab6407SYana Esina 
15192ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_10G)
15292ab6407SYana Esina 		rate |= HW_ATL_FW2X_CAP_EEE_10G_MASK;
15392ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_5G)
15492ab6407SYana Esina 		rate |= HW_ATL_FW2X_CAP_EEE_5G_MASK;
15592ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_2GS)
15692ab6407SYana Esina 		rate |= HW_ATL_FW2X_CAP_EEE_2G5_MASK;
15792ab6407SYana Esina 	if (speed & AQ_NIC_RATE_EEE_1G)
15892ab6407SYana Esina 		rate |= HW_ATL_FW2X_CAP_EEE_1G_MASK;
15992ab6407SYana Esina 
16092ab6407SYana Esina 	return rate;
16192ab6407SYana Esina }
16292ab6407SYana Esina 
163a57d3929SIgor Russkikh static int aq_fw2x_set_link_speed(struct aq_hw_s *self, u32 speed)
164a57d3929SIgor Russkikh {
165a57d3929SIgor Russkikh 	u32 val = link_speed_mask_2fw2x_ratemask(speed);
166a57d3929SIgor Russkikh 
167a57d3929SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL_ADDR, val);
168a57d3929SIgor Russkikh 
169a57d3929SIgor Russkikh 	return 0;
170a57d3929SIgor Russkikh }
171a57d3929SIgor Russkikh 
172288551deSIgor Russkikh static void aq_fw2x_set_mpi_flow_control(struct aq_hw_s *self, u32 *mpi_state)
173288551deSIgor Russkikh {
174288551deSIgor Russkikh 	if (self->aq_nic_cfg->flow_control & AQ_NIC_FC_RX)
175288551deSIgor Russkikh 		*mpi_state |= BIT(CAPS_HI_PAUSE);
176288551deSIgor Russkikh 	else
177288551deSIgor Russkikh 		*mpi_state &= ~BIT(CAPS_HI_PAUSE);
178288551deSIgor Russkikh 
179288551deSIgor Russkikh 	if (self->aq_nic_cfg->flow_control & AQ_NIC_FC_TX)
180288551deSIgor Russkikh 		*mpi_state |= BIT(CAPS_HI_ASYMMETRIC_PAUSE);
181288551deSIgor Russkikh 	else
182288551deSIgor Russkikh 		*mpi_state &= ~BIT(CAPS_HI_ASYMMETRIC_PAUSE);
183288551deSIgor Russkikh }
184288551deSIgor Russkikh 
18592ab6407SYana Esina static void aq_fw2x_upd_eee_rate_bits(struct aq_hw_s *self, u32 *mpi_opts,
18692ab6407SYana Esina 				      u32 eee_speeds)
18792ab6407SYana Esina {
18892ab6407SYana Esina 	*mpi_opts &= ~(HW_ATL_FW2X_CAP_EEE_1G_MASK |
18992ab6407SYana Esina 		       HW_ATL_FW2X_CAP_EEE_2G5_MASK |
19092ab6407SYana Esina 		       HW_ATL_FW2X_CAP_EEE_5G_MASK |
19192ab6407SYana Esina 		       HW_ATL_FW2X_CAP_EEE_10G_MASK);
19292ab6407SYana Esina 
19392ab6407SYana Esina 	*mpi_opts |= eee_mask_to_fw2x(eee_speeds);
19492ab6407SYana Esina }
19592ab6407SYana Esina 
196a57d3929SIgor Russkikh static int aq_fw2x_set_state(struct aq_hw_s *self,
197a57d3929SIgor Russkikh 			     enum hal_atl_utils_fw_state_e state)
198a57d3929SIgor Russkikh {
19944e00dd8SIgor Russkikh 	u32 mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
20092ab6407SYana Esina 	struct aq_nic_cfg_s *cfg = self->aq_nic_cfg;
20144e00dd8SIgor Russkikh 
20244e00dd8SIgor Russkikh 	switch (state) {
20344e00dd8SIgor Russkikh 	case MPI_INIT:
20444e00dd8SIgor Russkikh 		mpi_state &= ~BIT(CAPS_HI_LINK_DROP);
20592ab6407SYana Esina 		aq_fw2x_upd_eee_rate_bits(self, &mpi_state, cfg->eee_speeds);
206288551deSIgor Russkikh 		aq_fw2x_set_mpi_flow_control(self, &mpi_state);
20744e00dd8SIgor Russkikh 		break;
20844e00dd8SIgor Russkikh 	case MPI_DEINIT:
20944e00dd8SIgor Russkikh 		mpi_state |= BIT(CAPS_HI_LINK_DROP);
21044e00dd8SIgor Russkikh 		break;
21144e00dd8SIgor Russkikh 	case MPI_RESET:
21244e00dd8SIgor Russkikh 	case MPI_POWER:
21344e00dd8SIgor Russkikh 		/* No actions */
21444e00dd8SIgor Russkikh 		break;
21544e00dd8SIgor Russkikh 	}
21644e00dd8SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_state);
217a57d3929SIgor Russkikh 	return 0;
218a57d3929SIgor Russkikh }
219a57d3929SIgor Russkikh 
220a57d3929SIgor Russkikh static int aq_fw2x_update_link_status(struct aq_hw_s *self)
221a57d3929SIgor Russkikh {
222a57d3929SIgor Russkikh 	u32 mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE_ADDR);
223a57d3929SIgor Russkikh 	u32 speed = mpi_state & (FW2X_RATE_100M | FW2X_RATE_1G |
224a57d3929SIgor Russkikh 				 FW2X_RATE_2G5 | FW2X_RATE_5G | FW2X_RATE_10G);
225a57d3929SIgor Russkikh 	struct aq_hw_link_status_s *link_status = &self->aq_link_status;
226a57d3929SIgor Russkikh 
227a57d3929SIgor Russkikh 	if (speed) {
228a57d3929SIgor Russkikh 		if (speed & FW2X_RATE_10G)
229a57d3929SIgor Russkikh 			link_status->mbps = 10000;
230a57d3929SIgor Russkikh 		else if (speed & FW2X_RATE_5G)
231a57d3929SIgor Russkikh 			link_status->mbps = 5000;
232a57d3929SIgor Russkikh 		else if (speed & FW2X_RATE_2G5)
233a57d3929SIgor Russkikh 			link_status->mbps = 2500;
234a57d3929SIgor Russkikh 		else if (speed & FW2X_RATE_1G)
235a57d3929SIgor Russkikh 			link_status->mbps = 1000;
236a57d3929SIgor Russkikh 		else if (speed & FW2X_RATE_100M)
237a57d3929SIgor Russkikh 			link_status->mbps = 100;
238a57d3929SIgor Russkikh 		else
239a57d3929SIgor Russkikh 			link_status->mbps = 10000;
240a57d3929SIgor Russkikh 	} else {
241a57d3929SIgor Russkikh 		link_status->mbps = 0;
242a57d3929SIgor Russkikh 	}
243a57d3929SIgor Russkikh 
244a57d3929SIgor Russkikh 	return 0;
245a57d3929SIgor Russkikh }
246a57d3929SIgor Russkikh 
24776a45194SColin Ian King static int aq_fw2x_get_mac_permanent(struct aq_hw_s *self, u8 *mac)
248a57d3929SIgor Russkikh {
249a57d3929SIgor Russkikh 	int err = 0;
250a57d3929SIgor Russkikh 	u32 h = 0U;
251a57d3929SIgor Russkikh 	u32 l = 0U;
252a57d3929SIgor Russkikh 	u32 mac_addr[2] = { 0 };
253a57d3929SIgor Russkikh 	u32 efuse_addr = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_EFUSE_ADDR);
254a57d3929SIgor Russkikh 
255a57d3929SIgor Russkikh 	if (efuse_addr != 0) {
256a57d3929SIgor Russkikh 		err = hw_atl_utils_fw_downld_dwords(self,
257a57d3929SIgor Russkikh 						    efuse_addr + (40U * 4U),
258a57d3929SIgor Russkikh 						    mac_addr,
259a57d3929SIgor Russkikh 						    ARRAY_SIZE(mac_addr));
260a57d3929SIgor Russkikh 		if (err)
261a57d3929SIgor Russkikh 			return err;
262a57d3929SIgor Russkikh 		mac_addr[0] = __swab32(mac_addr[0]);
263a57d3929SIgor Russkikh 		mac_addr[1] = __swab32(mac_addr[1]);
264a57d3929SIgor Russkikh 	}
265a57d3929SIgor Russkikh 
266a57d3929SIgor Russkikh 	ether_addr_copy(mac, (u8 *)mac_addr);
267a57d3929SIgor Russkikh 
268a57d3929SIgor Russkikh 	if ((mac[0] & 0x01U) || ((mac[0] | mac[1] | mac[2]) == 0x00U)) {
269a57d3929SIgor Russkikh 		unsigned int rnd = 0;
270a57d3929SIgor Russkikh 
271a57d3929SIgor Russkikh 		get_random_bytes(&rnd, sizeof(unsigned int));
272a57d3929SIgor Russkikh 
273e9157848SNikita Danilov 		l = 0xE3000000U | (0xFFFFU & rnd) | (0x00 << 16);
274a57d3929SIgor Russkikh 		h = 0x8001300EU;
275a57d3929SIgor Russkikh 
276a57d3929SIgor Russkikh 		mac[5] = (u8)(0xFFU & l);
277a57d3929SIgor Russkikh 		l >>= 8;
278a57d3929SIgor Russkikh 		mac[4] = (u8)(0xFFU & l);
279a57d3929SIgor Russkikh 		l >>= 8;
280a57d3929SIgor Russkikh 		mac[3] = (u8)(0xFFU & l);
281a57d3929SIgor Russkikh 		l >>= 8;
282a57d3929SIgor Russkikh 		mac[2] = (u8)(0xFFU & l);
283a57d3929SIgor Russkikh 		mac[1] = (u8)(0xFFU & h);
284a57d3929SIgor Russkikh 		h >>= 8;
285a57d3929SIgor Russkikh 		mac[0] = (u8)(0xFFU & h);
286a57d3929SIgor Russkikh 	}
287a57d3929SIgor Russkikh 	return err;
288a57d3929SIgor Russkikh }
289a57d3929SIgor Russkikh 
2900ba4ad32SYueHaibing static int aq_fw2x_update_stats(struct aq_hw_s *self)
291a57d3929SIgor Russkikh {
292a57d3929SIgor Russkikh 	int err = 0;
293a57d3929SIgor Russkikh 	u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
294a57d3929SIgor Russkikh 	u32 orig_stats_val = mpi_opts & BIT(CAPS_HI_STATISTICS);
2956a7f2277SNikita Danilov 	u32 stats_val;
296a57d3929SIgor Russkikh 
297a57d3929SIgor Russkikh 	/* Toggle statistics bit for FW to update */
298a57d3929SIgor Russkikh 	mpi_opts = mpi_opts ^ BIT(CAPS_HI_STATISTICS);
299a57d3929SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
300a57d3929SIgor Russkikh 
301a57d3929SIgor Russkikh 	/* Wait FW to report back */
3026a7f2277SNikita Danilov 	err = readx_poll_timeout_atomic(aq_fw2x_state2_get,
3036a7f2277SNikita Danilov 					self, stats_val,
3046a7f2277SNikita Danilov 					orig_stats_val != (stats_val &
305a57d3929SIgor Russkikh 					BIT(CAPS_HI_STATISTICS)),
306a57d3929SIgor Russkikh 					1U, 10000U);
307a57d3929SIgor Russkikh 	if (err)
308a57d3929SIgor Russkikh 		return err;
309a57d3929SIgor Russkikh 
310a57d3929SIgor Russkikh 	return hw_atl_utils_update_stats(self);
311a57d3929SIgor Russkikh }
312a57d3929SIgor Russkikh 
313a0da96c0SYana Esina static int aq_fw2x_set_sleep_proxy(struct aq_hw_s *self, u8 *mac)
314a0da96c0SYana Esina {
3158f60f762SNikita Danilov 	struct hw_atl_utils_fw_rpc *rpc = NULL;
316a0da96c0SYana Esina 	struct offload_info *cfg = NULL;
317a0da96c0SYana Esina 	unsigned int rpc_size = 0U;
318a0da96c0SYana Esina 	u32 mpi_opts;
319a0da96c0SYana Esina 	int err = 0;
3206a7f2277SNikita Danilov 	u32 val;
321a0da96c0SYana Esina 
322a0da96c0SYana Esina 	rpc_size = sizeof(rpc->msg_id) + sizeof(*cfg);
323a0da96c0SYana Esina 
324a0da96c0SYana Esina 	err = hw_atl_utils_fw_rpc_wait(self, &rpc);
325a0da96c0SYana Esina 	if (err < 0)
326a0da96c0SYana Esina 		goto err_exit;
327a0da96c0SYana Esina 
328a0da96c0SYana Esina 	memset(rpc, 0, rpc_size);
329a0da96c0SYana Esina 	cfg = (struct offload_info *)(&rpc->msg_id + 1);
330a0da96c0SYana Esina 
331a0da96c0SYana Esina 	memcpy(cfg->mac_addr, mac, ETH_ALEN);
332a0da96c0SYana Esina 	cfg->len = sizeof(*cfg);
333a0da96c0SYana Esina 
334a0da96c0SYana Esina 	/* Clear bit 0x36C.23 and 0x36C.22 */
335a0da96c0SYana Esina 	mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
336a0da96c0SYana Esina 	mpi_opts &= ~HW_ATL_FW2X_CTRL_SLEEP_PROXY;
337a0da96c0SYana Esina 	mpi_opts &= ~HW_ATL_FW2X_CTRL_LINK_DROP;
338a0da96c0SYana Esina 
339a0da96c0SYana Esina 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
340a0da96c0SYana Esina 
341a0da96c0SYana Esina 	err = hw_atl_utils_fw_rpc_call(self, rpc_size);
342a0da96c0SYana Esina 	if (err < 0)
343a0da96c0SYana Esina 		goto err_exit;
344a0da96c0SYana Esina 
345a0da96c0SYana Esina 	/* Set bit 0x36C.23 */
346a0da96c0SYana Esina 	mpi_opts |= HW_ATL_FW2X_CTRL_SLEEP_PROXY;
347a0da96c0SYana Esina 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
348a0da96c0SYana Esina 
3496a7f2277SNikita Danilov 	err = readx_poll_timeout_atomic(aq_fw2x_state2_get,
3506a7f2277SNikita Danilov 					self, val,
3516a7f2277SNikita Danilov 					val & HW_ATL_FW2X_CTRL_SLEEP_PROXY,
3526a7f2277SNikita Danilov 					1U, 10000U);
353a0da96c0SYana Esina 
354a0da96c0SYana Esina err_exit:
355a0da96c0SYana Esina 	return err;
356a0da96c0SYana Esina }
357a0da96c0SYana Esina 
358a0da96c0SYana Esina static int aq_fw2x_set_wol_params(struct aq_hw_s *self, u8 *mac)
359a0da96c0SYana Esina {
3608f60f762SNikita Danilov 	struct hw_atl_utils_fw_rpc *rpc = NULL;
361a0da96c0SYana Esina 	struct fw2x_msg_wol *msg = NULL;
362a0da96c0SYana Esina 	u32 mpi_opts;
363a0da96c0SYana Esina 	int err = 0;
3646a7f2277SNikita Danilov 	u32 val;
365a0da96c0SYana Esina 
366a0da96c0SYana Esina 	err = hw_atl_utils_fw_rpc_wait(self, &rpc);
367a0da96c0SYana Esina 	if (err < 0)
368a0da96c0SYana Esina 		goto err_exit;
369a0da96c0SYana Esina 
370a0da96c0SYana Esina 	msg = (struct fw2x_msg_wol *)rpc;
371a0da96c0SYana Esina 
372a0da96c0SYana Esina 	msg->msg_id = HAL_ATLANTIC_UTILS_FW2X_MSG_WOL;
373a0da96c0SYana Esina 	msg->magic_packet_enabled = true;
374a0da96c0SYana Esina 	memcpy(msg->hw_addr, mac, ETH_ALEN);
375a0da96c0SYana Esina 
376a0da96c0SYana Esina 	mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
377a0da96c0SYana Esina 	mpi_opts &= ~(HW_ATL_FW2X_CTRL_SLEEP_PROXY | HW_ATL_FW2X_CTRL_WOL);
378a0da96c0SYana Esina 
379a0da96c0SYana Esina 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
380a0da96c0SYana Esina 
381a0da96c0SYana Esina 	err = hw_atl_utils_fw_rpc_call(self, sizeof(*msg));
382a0da96c0SYana Esina 	if (err < 0)
383a0da96c0SYana Esina 		goto err_exit;
384a0da96c0SYana Esina 
385a0da96c0SYana Esina 	/* Set bit 0x36C.24 */
386a0da96c0SYana Esina 	mpi_opts |= HW_ATL_FW2X_CTRL_WOL;
387a0da96c0SYana Esina 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
388a0da96c0SYana Esina 
3896a7f2277SNikita Danilov 	err = readx_poll_timeout_atomic(aq_fw2x_state2_get,
3906a7f2277SNikita Danilov 					self, val, val & HW_ATL_FW2X_CTRL_WOL,
3916a7f2277SNikita Danilov 					1U, 10000U);
392a0da96c0SYana Esina 
393a0da96c0SYana Esina err_exit:
394a0da96c0SYana Esina 	return err;
395a0da96c0SYana Esina }
396a0da96c0SYana Esina 
397a0da96c0SYana Esina static int aq_fw2x_set_power(struct aq_hw_s *self, unsigned int power_state,
398a0da96c0SYana Esina 			     u8 *mac)
399a0da96c0SYana Esina {
400a0da96c0SYana Esina 	int err = 0;
401a0da96c0SYana Esina 
402a0da96c0SYana Esina 	if (self->aq_nic_cfg->wol & AQ_NIC_WOL_ENABLED) {
403a0da96c0SYana Esina 		err = aq_fw2x_set_sleep_proxy(self, mac);
404a0da96c0SYana Esina 		if (err < 0)
405a0da96c0SYana Esina 			goto err_exit;
406a0da96c0SYana Esina 		err = aq_fw2x_set_wol_params(self, mac);
407a0da96c0SYana Esina 	}
408a0da96c0SYana Esina 
409a0da96c0SYana Esina err_exit:
410a0da96c0SYana Esina 	return err;
411a0da96c0SYana Esina }
412a0da96c0SYana Esina 
41392ab6407SYana Esina static int aq_fw2x_set_eee_rate(struct aq_hw_s *self, u32 speed)
41492ab6407SYana Esina {
41592ab6407SYana Esina 	u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
41692ab6407SYana Esina 
41792ab6407SYana Esina 	aq_fw2x_upd_eee_rate_bits(self, &mpi_opts, speed);
41892ab6407SYana Esina 
41992ab6407SYana Esina 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
42092ab6407SYana Esina 
42192ab6407SYana Esina 	return 0;
42292ab6407SYana Esina }
42392ab6407SYana Esina 
42492ab6407SYana Esina static int aq_fw2x_get_eee_rate(struct aq_hw_s *self, u32 *rate,
42592ab6407SYana Esina 				u32 *supported_rates)
42692ab6407SYana Esina {
42792ab6407SYana Esina 	u32 mpi_state;
42892ab6407SYana Esina 	u32 caps_hi;
42992ab6407SYana Esina 	int err = 0;
4308f60f762SNikita Danilov 	u32 addr = self->mbox_addr + offsetof(struct hw_atl_utils_mbox, info) +
43192ab6407SYana Esina 		   offsetof(struct hw_aq_info, caps_hi);
43292ab6407SYana Esina 
43392ab6407SYana Esina 	err = hw_atl_utils_fw_downld_dwords(self, addr, &caps_hi,
43492ab6407SYana Esina 					    sizeof(caps_hi) / sizeof(u32));
43592ab6407SYana Esina 
43692ab6407SYana Esina 	if (err)
43792ab6407SYana Esina 		return err;
43892ab6407SYana Esina 
43992ab6407SYana Esina 	*supported_rates = fw2x_to_eee_mask(caps_hi);
44092ab6407SYana Esina 
44192ab6407SYana Esina 	mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR);
44292ab6407SYana Esina 	*rate = fw2x_to_eee_mask(mpi_state);
44392ab6407SYana Esina 
44492ab6407SYana Esina 	return err;
44592ab6407SYana Esina }
44692ab6407SYana Esina 
447b8d68b62SAnton Mikaev static int aq_fw2x_renegotiate(struct aq_hw_s *self)
448b8d68b62SAnton Mikaev {
449b8d68b62SAnton Mikaev 	u32 mpi_opts = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
450b8d68b62SAnton Mikaev 
451b8d68b62SAnton Mikaev 	mpi_opts |= BIT(CTRL_FORCE_RECONNECT);
452b8d68b62SAnton Mikaev 
453b8d68b62SAnton Mikaev 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_opts);
454b8d68b62SAnton Mikaev 
455b8d68b62SAnton Mikaev 	return 0;
456b8d68b62SAnton Mikaev }
457b8d68b62SAnton Mikaev 
458288551deSIgor Russkikh static int aq_fw2x_set_flow_control(struct aq_hw_s *self)
459288551deSIgor Russkikh {
460288551deSIgor Russkikh 	u32 mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR);
461288551deSIgor Russkikh 
462288551deSIgor Russkikh 	aq_fw2x_set_mpi_flow_control(self, &mpi_state);
463288551deSIgor Russkikh 
464288551deSIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_FW2X_MPI_CONTROL2_ADDR, mpi_state);
465288551deSIgor Russkikh 
466288551deSIgor Russkikh 	return 0;
467288551deSIgor Russkikh }
468288551deSIgor Russkikh 
46935e8e8b4SIgor Russkikh static u32 aq_fw2x_get_flow_control(struct aq_hw_s *self, u32 *fcmode)
47035e8e8b4SIgor Russkikh {
47135e8e8b4SIgor Russkikh 	u32 mpi_state = aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR);
47235e8e8b4SIgor Russkikh 
47335e8e8b4SIgor Russkikh 	if (mpi_state & HW_ATL_FW2X_CAP_PAUSE)
47435e8e8b4SIgor Russkikh 		if (mpi_state & HW_ATL_FW2X_CAP_ASYM_PAUSE)
47535e8e8b4SIgor Russkikh 			*fcmode = AQ_NIC_FC_RX;
47635e8e8b4SIgor Russkikh 		else
47735e8e8b4SIgor Russkikh 			*fcmode = AQ_NIC_FC_RX | AQ_NIC_FC_TX;
47835e8e8b4SIgor Russkikh 	else
47935e8e8b4SIgor Russkikh 		if (mpi_state & HW_ATL_FW2X_CAP_ASYM_PAUSE)
48035e8e8b4SIgor Russkikh 			*fcmode = AQ_NIC_FC_TX;
48135e8e8b4SIgor Russkikh 		else
48235e8e8b4SIgor Russkikh 			*fcmode = 0;
48335e8e8b4SIgor Russkikh 
48435e8e8b4SIgor Russkikh 	return 0;
48535e8e8b4SIgor Russkikh }
48635e8e8b4SIgor Russkikh 
4876a7f2277SNikita Danilov static u32 aq_fw2x_mbox_get(struct aq_hw_s *self)
4886a7f2277SNikita Danilov {
4896a7f2277SNikita Danilov 	return aq_hw_read_reg(self, HW_ATL_FW2X_MPI_MBOX_ADDR);
4906a7f2277SNikita Danilov }
4916a7f2277SNikita Danilov 
4926a7f2277SNikita Danilov static u32 aq_fw2x_rpc_get(struct aq_hw_s *self)
4936a7f2277SNikita Danilov {
4946a7f2277SNikita Danilov 	return aq_hw_read_reg(self, HW_ATL_FW2X_MPI_RPC_ADDR);
4956a7f2277SNikita Danilov }
4966a7f2277SNikita Danilov 
4976a7f2277SNikita Danilov static u32 aq_fw2x_state2_get(struct aq_hw_s *self)
4986a7f2277SNikita Danilov {
4996a7f2277SNikita Danilov 	return aq_hw_read_reg(self, HW_ATL_FW2X_MPI_STATE2_ADDR);
5006a7f2277SNikita Danilov }
5016a7f2277SNikita Danilov 
502a57d3929SIgor Russkikh const struct aq_fw_ops aq_fw_2x_ops = {
503a57d3929SIgor Russkikh 	.init = aq_fw2x_init,
50444e00dd8SIgor Russkikh 	.deinit = aq_fw2x_deinit,
505a57d3929SIgor Russkikh 	.reset = NULL,
506b8d68b62SAnton Mikaev 	.renegotiate = aq_fw2x_renegotiate,
507a57d3929SIgor Russkikh 	.get_mac_permanent = aq_fw2x_get_mac_permanent,
508a57d3929SIgor Russkikh 	.set_link_speed = aq_fw2x_set_link_speed,
509a57d3929SIgor Russkikh 	.set_state = aq_fw2x_set_state,
510a57d3929SIgor Russkikh 	.update_link_status = aq_fw2x_update_link_status,
511a57d3929SIgor Russkikh 	.update_stats = aq_fw2x_update_stats,
512a0da96c0SYana Esina 	.set_power = aq_fw2x_set_power,
51392ab6407SYana Esina 	.set_eee_rate = aq_fw2x_set_eee_rate,
51492ab6407SYana Esina 	.get_eee_rate = aq_fw2x_get_eee_rate,
515288551deSIgor Russkikh 	.set_flow_control = aq_fw2x_set_flow_control,
51635e8e8b4SIgor Russkikh 	.get_flow_control = aq_fw2x_get_flow_control
517a57d3929SIgor Russkikh };
518