198c4c201SDavid VomLehn /*
298c4c201SDavid VomLehn  * aQuantia Corporation Network Driver
398c4c201SDavid VomLehn  * Copyright (C) 2014-2017 aQuantia Corporation. All rights reserved
498c4c201SDavid VomLehn  *
598c4c201SDavid VomLehn  * This program is free software; you can redistribute it and/or modify it
698c4c201SDavid VomLehn  * under the terms and conditions of the GNU General Public License,
798c4c201SDavid VomLehn  * version 2, as published by the Free Software Foundation.
898c4c201SDavid VomLehn  */
998c4c201SDavid VomLehn 
1098c4c201SDavid VomLehn /* File hw_atl_utils.c: Definition of common functions for Atlantic hardware
1198c4c201SDavid VomLehn  * abstraction layer.
1298c4c201SDavid VomLehn  */
1398c4c201SDavid VomLehn 
141a713f87SIgor Russkikh #include "../aq_nic.h"
1598c4c201SDavid VomLehn #include "../aq_hw_utils.h"
1698c4c201SDavid VomLehn #include "hw_atl_utils.h"
1798c4c201SDavid VomLehn #include "hw_atl_llh.h"
180c58c35fSIgor Russkikh #include "hw_atl_llh_internal.h"
1998c4c201SDavid VomLehn 
2098c4c201SDavid VomLehn #include <linux/random.h>
2198c4c201SDavid VomLehn 
2298c4c201SDavid VomLehn #define HW_ATL_UCP_0X370_REG    0x0370U
2398c4c201SDavid VomLehn 
2447203b34SIgor Russkikh #define HW_ATL_MIF_CMD          0x0200U
2547203b34SIgor Russkikh #define HW_ATL_MIF_ADDR         0x0208U
2647203b34SIgor Russkikh #define HW_ATL_MIF_VAL          0x020CU
2747203b34SIgor Russkikh 
286a7f2277SNikita Danilov #define HW_ATL_RPC_CONTROL_ADR  0x0338U
296a7f2277SNikita Danilov #define HW_ATL_RPC_STATE_ADR    0x033CU
306a7f2277SNikita Danilov 
310c58c35fSIgor Russkikh #define HW_ATL_MPI_FW_VERSION	0x18
3298c4c201SDavid VomLehn #define HW_ATL_MPI_CONTROL_ADR  0x0368U
3398c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_ADR    0x036CU
3498c4c201SDavid VomLehn 
3598c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_MSK      0x00FFU
3698c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_SHIFT    0U
3744e00dd8SIgor Russkikh #define HW_ATL_MPI_SPEED_MSK      0x00FF0000U
3898c4c201SDavid VomLehn #define HW_ATL_MPI_SPEED_SHIFT    16U
3944e00dd8SIgor Russkikh #define HW_ATL_MPI_DIRTY_WAKE_MSK 0x02000000U
4098c4c201SDavid VomLehn 
41c8c82eb3SIgor Russkikh #define HW_ATL_MPI_DAISY_CHAIN_STATUS	0x704
42c8c82eb3SIgor Russkikh #define HW_ATL_MPI_BOOT_EXIT_CODE	0x388
43c8c82eb3SIgor Russkikh 
44c8c82eb3SIgor Russkikh #define HW_ATL_MAC_PHY_CONTROL	0x4000
45c8c82eb3SIgor Russkikh #define HW_ATL_MAC_PHY_MPI_RESET_BIT 0x1D
46c8c82eb3SIgor Russkikh 
470c58c35fSIgor Russkikh #define HW_ATL_FW_VER_1X 0x01050006U
48a57d3929SIgor Russkikh #define HW_ATL_FW_VER_2X 0x02000000U
49a57d3929SIgor Russkikh #define HW_ATL_FW_VER_3X 0x03000000U
500c58c35fSIgor Russkikh 
51c8c82eb3SIgor Russkikh #define FORCE_FLASHLESS 0
52c8c82eb3SIgor Russkikh 
530c58c35fSIgor Russkikh static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual);
54e9157848SNikita Danilov 
55cce96d18SIgor Russkikh static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self,
56cce96d18SIgor Russkikh 				      enum hal_atl_utils_fw_state_e state);
570c58c35fSIgor Russkikh 
586a7f2277SNikita Danilov static u32 hw_atl_utils_get_mpi_mbox_tid(struct aq_hw_s *self);
596a7f2277SNikita Danilov static u32 hw_atl_utils_mpi_get_state(struct aq_hw_s *self);
606a7f2277SNikita Danilov static u32 hw_atl_utils_mif_cmd_get(struct aq_hw_s *self);
616a7f2277SNikita Danilov static u32 hw_atl_utils_mif_addr_get(struct aq_hw_s *self);
626a7f2277SNikita Danilov static u32 hw_atl_utils_rpc_state_get(struct aq_hw_s *self);
636a7f2277SNikita Danilov 
640c58c35fSIgor Russkikh int hw_atl_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_ops)
650c58c35fSIgor Russkikh {
660c58c35fSIgor Russkikh 	int err = 0;
670c58c35fSIgor Russkikh 
68c8c82eb3SIgor Russkikh 	err = hw_atl_utils_soft_reset(self);
69c8c82eb3SIgor Russkikh 	if (err)
70c8c82eb3SIgor Russkikh 		return err;
71c8c82eb3SIgor Russkikh 
720c58c35fSIgor Russkikh 	hw_atl_utils_hw_chip_features_init(self,
730c58c35fSIgor Russkikh 					   &self->chip_features);
740c58c35fSIgor Russkikh 
750c58c35fSIgor Russkikh 	hw_atl_utils_get_fw_version(self, &self->fw_ver_actual);
760c58c35fSIgor Russkikh 
77c8c82eb3SIgor Russkikh 	if (hw_atl_utils_ver_match(HW_ATL_FW_VER_1X,
78c8c82eb3SIgor Russkikh 				   self->fw_ver_actual) == 0) {
790c58c35fSIgor Russkikh 		*fw_ops = &aq_fw_1x_ops;
80c8c82eb3SIgor Russkikh 	} else if (hw_atl_utils_ver_match(HW_ATL_FW_VER_2X,
81c8c82eb3SIgor Russkikh 					  self->fw_ver_actual) == 0) {
82a57d3929SIgor Russkikh 		*fw_ops = &aq_fw_2x_ops;
83c8c82eb3SIgor Russkikh 	} else if (hw_atl_utils_ver_match(HW_ATL_FW_VER_3X,
84c8c82eb3SIgor Russkikh 					  self->fw_ver_actual) == 0) {
85a57d3929SIgor Russkikh 		*fw_ops = &aq_fw_2x_ops;
86c8c82eb3SIgor Russkikh 	} else {
870c58c35fSIgor Russkikh 		aq_pr_err("Bad FW version detected: %x\n",
880c58c35fSIgor Russkikh 			  self->fw_ver_actual);
890c58c35fSIgor Russkikh 		return -EOPNOTSUPP;
900c58c35fSIgor Russkikh 	}
910c58c35fSIgor Russkikh 	self->aq_fw_ops = *fw_ops;
920c58c35fSIgor Russkikh 	err = self->aq_fw_ops->init(self);
930c58c35fSIgor Russkikh 	return err;
940c58c35fSIgor Russkikh }
950c58c35fSIgor Russkikh 
96c8c82eb3SIgor Russkikh static int hw_atl_utils_soft_reset_flb(struct aq_hw_s *self)
97c8c82eb3SIgor Russkikh {
981bf9a752SIgor Russkikh 	u32 gsr, val;
99c8c82eb3SIgor Russkikh 	int k = 0;
100c8c82eb3SIgor Russkikh 
101c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x40e1);
102c8c82eb3SIgor Russkikh 	AQ_HW_SLEEP(50);
103c8c82eb3SIgor Russkikh 
104c8c82eb3SIgor Russkikh 	/* Cleanup SPI */
1051bf9a752SIgor Russkikh 	val = aq_hw_read_reg(self, 0x53C);
1061bf9a752SIgor Russkikh 	aq_hw_write_reg(self, 0x53C, val | 0x10);
107c8c82eb3SIgor Russkikh 
108c8c82eb3SIgor Russkikh 	gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR);
109c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, (gsr & 0xBFFF) | 0x8000);
110c8c82eb3SIgor Russkikh 
111c8c82eb3SIgor Russkikh 	/* Kickstart MAC */
112c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x80e0);
113c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x32a8, 0x0);
114c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x520, 0x1);
1151bf9a752SIgor Russkikh 
1161bf9a752SIgor Russkikh 	/* Reset SPI again because of possible interrupted SPI burst */
1171bf9a752SIgor Russkikh 	val = aq_hw_read_reg(self, 0x53C);
1181bf9a752SIgor Russkikh 	aq_hw_write_reg(self, 0x53C, val | 0x10);
119c8c82eb3SIgor Russkikh 	AQ_HW_SLEEP(10);
1201bf9a752SIgor Russkikh 	/* Clear SPI reset state */
1211bf9a752SIgor Russkikh 	aq_hw_write_reg(self, 0x53C, val & ~0x10);
1221bf9a752SIgor Russkikh 
123c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x180e0);
124c8c82eb3SIgor Russkikh 
125c8c82eb3SIgor Russkikh 	for (k = 0; k < 1000; k++) {
126c8c82eb3SIgor Russkikh 		u32 flb_status = aq_hw_read_reg(self,
127c8c82eb3SIgor Russkikh 						HW_ATL_MPI_DAISY_CHAIN_STATUS);
128c8c82eb3SIgor Russkikh 
129c8c82eb3SIgor Russkikh 		flb_status = flb_status & 0x10;
130c8c82eb3SIgor Russkikh 		if (flb_status)
131c8c82eb3SIgor Russkikh 			break;
132c8c82eb3SIgor Russkikh 		AQ_HW_SLEEP(10);
133c8c82eb3SIgor Russkikh 	}
134c8c82eb3SIgor Russkikh 	if (k == 1000) {
135c8c82eb3SIgor Russkikh 		aq_pr_err("MAC kickstart failed\n");
136c8c82eb3SIgor Russkikh 		return -EIO;
137c8c82eb3SIgor Russkikh 	}
138c8c82eb3SIgor Russkikh 
139c8c82eb3SIgor Russkikh 	/* FW reset */
140c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x80e0);
141c8c82eb3SIgor Russkikh 	AQ_HW_SLEEP(50);
142c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x3a0, 0x1);
143c8c82eb3SIgor Russkikh 
144c8c82eb3SIgor Russkikh 	/* Kickstart PHY - skipped */
145c8c82eb3SIgor Russkikh 
146c8c82eb3SIgor Russkikh 	/* Global software reset*/
147c8c82eb3SIgor Russkikh 	hw_atl_rx_rx_reg_res_dis_set(self, 0U);
148c8c82eb3SIgor Russkikh 	hw_atl_tx_tx_reg_res_dis_set(self, 0U);
149c8c82eb3SIgor Russkikh 	aq_hw_write_reg_bit(self, HW_ATL_MAC_PHY_CONTROL,
150c8c82eb3SIgor Russkikh 			    BIT(HW_ATL_MAC_PHY_MPI_RESET_BIT),
151c8c82eb3SIgor Russkikh 			    HW_ATL_MAC_PHY_MPI_RESET_BIT, 0x0);
152c8c82eb3SIgor Russkikh 	gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR);
153c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, (gsr & 0xBFFF) | 0x8000);
154c8c82eb3SIgor Russkikh 
155c8c82eb3SIgor Russkikh 	for (k = 0; k < 1000; k++) {
156c8c82eb3SIgor Russkikh 		u32 fw_state = aq_hw_read_reg(self, HW_ATL_MPI_FW_VERSION);
157c8c82eb3SIgor Russkikh 
158c8c82eb3SIgor Russkikh 		if (fw_state)
159c8c82eb3SIgor Russkikh 			break;
160c8c82eb3SIgor Russkikh 		AQ_HW_SLEEP(10);
161c8c82eb3SIgor Russkikh 	}
162c8c82eb3SIgor Russkikh 	if (k == 1000) {
163c8c82eb3SIgor Russkikh 		aq_pr_err("FW kickstart failed\n");
164c8c82eb3SIgor Russkikh 		return -EIO;
165c8c82eb3SIgor Russkikh 	}
166d0f0fb25SIgor Russkikh 	/* Old FW requires fixed delay after init */
167d0f0fb25SIgor Russkikh 	AQ_HW_SLEEP(15);
168c8c82eb3SIgor Russkikh 
169c8c82eb3SIgor Russkikh 	return 0;
170c8c82eb3SIgor Russkikh }
171c8c82eb3SIgor Russkikh 
172c8c82eb3SIgor Russkikh static int hw_atl_utils_soft_reset_rbl(struct aq_hw_s *self)
173c8c82eb3SIgor Russkikh {
1741bf9a752SIgor Russkikh 	u32 gsr, val, rbl_status;
175c8c82eb3SIgor Russkikh 	int k;
176c8c82eb3SIgor Russkikh 
177c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x40e1);
178c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x3a0, 0x1);
179c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x32a8, 0x0);
180c8c82eb3SIgor Russkikh 
181c8c82eb3SIgor Russkikh 	/* Alter RBL status */
182c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x388, 0xDEAD);
183c8c82eb3SIgor Russkikh 
1841bf9a752SIgor Russkikh 	/* Cleanup SPI */
1851bf9a752SIgor Russkikh 	val = aq_hw_read_reg(self, 0x53C);
1861bf9a752SIgor Russkikh 	aq_hw_write_reg(self, 0x53C, val | 0x10);
1871bf9a752SIgor Russkikh 
188c8c82eb3SIgor Russkikh 	/* Global software reset*/
189c8c82eb3SIgor Russkikh 	hw_atl_rx_rx_reg_res_dis_set(self, 0U);
190c8c82eb3SIgor Russkikh 	hw_atl_tx_tx_reg_res_dis_set(self, 0U);
191c8c82eb3SIgor Russkikh 	aq_hw_write_reg_bit(self, HW_ATL_MAC_PHY_CONTROL,
192c8c82eb3SIgor Russkikh 			    BIT(HW_ATL_MAC_PHY_MPI_RESET_BIT),
193c8c82eb3SIgor Russkikh 			    HW_ATL_MAC_PHY_MPI_RESET_BIT, 0x0);
194c8c82eb3SIgor Russkikh 	gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR);
195c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR,
196c8c82eb3SIgor Russkikh 			(gsr & 0xFFFFBFFF) | 0x8000);
197c8c82eb3SIgor Russkikh 
198c8c82eb3SIgor Russkikh 	if (FORCE_FLASHLESS)
199c8c82eb3SIgor Russkikh 		aq_hw_write_reg(self, 0x534, 0x0);
200c8c82eb3SIgor Russkikh 
201c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x40e0);
202c8c82eb3SIgor Russkikh 
203c8c82eb3SIgor Russkikh 	/* Wait for RBL boot */
204c8c82eb3SIgor Russkikh 	for (k = 0; k < 1000; k++) {
205c8c82eb3SIgor Russkikh 		rbl_status = aq_hw_read_reg(self, 0x388) & 0xFFFF;
206c8c82eb3SIgor Russkikh 		if (rbl_status && rbl_status != 0xDEAD)
207c8c82eb3SIgor Russkikh 			break;
208c8c82eb3SIgor Russkikh 		AQ_HW_SLEEP(10);
209c8c82eb3SIgor Russkikh 	}
210c8c82eb3SIgor Russkikh 	if (!rbl_status || rbl_status == 0xDEAD) {
211c8c82eb3SIgor Russkikh 		aq_pr_err("RBL Restart failed");
212c8c82eb3SIgor Russkikh 		return -EIO;
213c8c82eb3SIgor Russkikh 	}
214c8c82eb3SIgor Russkikh 
215c8c82eb3SIgor Russkikh 	/* Restore NVR */
216c8c82eb3SIgor Russkikh 	if (FORCE_FLASHLESS)
217c8c82eb3SIgor Russkikh 		aq_hw_write_reg(self, 0x534, 0xA0);
218c8c82eb3SIgor Russkikh 
219c8c82eb3SIgor Russkikh 	if (rbl_status == 0xF1A7) {
220c8c82eb3SIgor Russkikh 		aq_pr_err("No FW detected. Dynamic FW load not implemented\n");
221c8c82eb3SIgor Russkikh 		return -ENOTSUPP;
222c8c82eb3SIgor Russkikh 	}
223c8c82eb3SIgor Russkikh 
224c8c82eb3SIgor Russkikh 	for (k = 0; k < 1000; k++) {
225c8c82eb3SIgor Russkikh 		u32 fw_state = aq_hw_read_reg(self, HW_ATL_MPI_FW_VERSION);
226c8c82eb3SIgor Russkikh 
227c8c82eb3SIgor Russkikh 		if (fw_state)
228c8c82eb3SIgor Russkikh 			break;
229c8c82eb3SIgor Russkikh 		AQ_HW_SLEEP(10);
230c8c82eb3SIgor Russkikh 	}
231c8c82eb3SIgor Russkikh 	if (k == 1000) {
232c8c82eb3SIgor Russkikh 		aq_pr_err("FW kickstart failed\n");
233c8c82eb3SIgor Russkikh 		return -EIO;
234c8c82eb3SIgor Russkikh 	}
235d0f0fb25SIgor Russkikh 	/* Old FW requires fixed delay after init */
236d0f0fb25SIgor Russkikh 	AQ_HW_SLEEP(15);
237c8c82eb3SIgor Russkikh 
238c8c82eb3SIgor Russkikh 	return 0;
239c8c82eb3SIgor Russkikh }
240c8c82eb3SIgor Russkikh 
241c8c82eb3SIgor Russkikh int hw_atl_utils_soft_reset(struct aq_hw_s *self)
242c8c82eb3SIgor Russkikh {
243c8c82eb3SIgor Russkikh 	int k;
244c8c82eb3SIgor Russkikh 	u32 boot_exit_code = 0;
2456a7f2277SNikita Danilov 	u32 val;
246c8c82eb3SIgor Russkikh 
247c8c82eb3SIgor Russkikh 	for (k = 0; k < 1000; ++k) {
248c8c82eb3SIgor Russkikh 		u32 flb_status = aq_hw_read_reg(self,
249c8c82eb3SIgor Russkikh 						HW_ATL_MPI_DAISY_CHAIN_STATUS);
250c8c82eb3SIgor Russkikh 		boot_exit_code = aq_hw_read_reg(self,
251c8c82eb3SIgor Russkikh 						HW_ATL_MPI_BOOT_EXIT_CODE);
252c8c82eb3SIgor Russkikh 		if (flb_status != 0x06000000 || boot_exit_code != 0)
253c8c82eb3SIgor Russkikh 			break;
254c8c82eb3SIgor Russkikh 	}
255c8c82eb3SIgor Russkikh 
256c8c82eb3SIgor Russkikh 	if (k == 1000) {
257c8c82eb3SIgor Russkikh 		aq_pr_err("Neither RBL nor FLB firmware started\n");
258c8c82eb3SIgor Russkikh 		return -EOPNOTSUPP;
259c8c82eb3SIgor Russkikh 	}
260c8c82eb3SIgor Russkikh 
261c8c82eb3SIgor Russkikh 	self->rbl_enabled = (boot_exit_code != 0);
262c8c82eb3SIgor Russkikh 
263cce96d18SIgor Russkikh 	/* FW 1.x may bootup in an invalid POWER state (WOL feature).
264cce96d18SIgor Russkikh 	 * We should work around this by forcing its state back to DEINIT
265cce96d18SIgor Russkikh 	 */
266cce96d18SIgor Russkikh 	if (!hw_atl_utils_ver_match(HW_ATL_FW_VER_1X,
267cce96d18SIgor Russkikh 				    aq_hw_read_reg(self,
268cce96d18SIgor Russkikh 						   HW_ATL_MPI_FW_VERSION))) {
269cce96d18SIgor Russkikh 		int err = 0;
270cce96d18SIgor Russkikh 
271cce96d18SIgor Russkikh 		hw_atl_utils_mpi_set_state(self, MPI_DEINIT);
2726a7f2277SNikita Danilov 		err = readx_poll_timeout_atomic(hw_atl_utils_mpi_get_state,
2736a7f2277SNikita Danilov 						self, val,
2746a7f2277SNikita Danilov 						(val & HW_ATL_MPI_STATE_MSK) ==
2756a7f2277SNikita Danilov 						 MPI_DEINIT,
2766a7f2277SNikita Danilov 						10, 10000U);
2774e3c7c00SYueHaibing 		if (err)
2784e3c7c00SYueHaibing 			return err;
279cce96d18SIgor Russkikh 	}
280cce96d18SIgor Russkikh 
281c8c82eb3SIgor Russkikh 	if (self->rbl_enabled)
282c8c82eb3SIgor Russkikh 		return hw_atl_utils_soft_reset_rbl(self);
283c8c82eb3SIgor Russkikh 	else
284c8c82eb3SIgor Russkikh 		return hw_atl_utils_soft_reset_flb(self);
285c8c82eb3SIgor Russkikh }
286c8c82eb3SIgor Russkikh 
287a57d3929SIgor Russkikh int hw_atl_utils_fw_downld_dwords(struct aq_hw_s *self, u32 a,
28898c4c201SDavid VomLehn 				  u32 *p, u32 cnt)
28998c4c201SDavid VomLehn {
29098c4c201SDavid VomLehn 	int err = 0;
2916a7f2277SNikita Danilov 	u32 val;
29298c4c201SDavid VomLehn 
2936a7f2277SNikita Danilov 	err = readx_poll_timeout_atomic(hw_atl_sem_ram_get,
2946a7f2277SNikita Danilov 					self, val, val == 1U,
29598c4c201SDavid VomLehn 					1U, 10000U);
29698c4c201SDavid VomLehn 
29798c4c201SDavid VomLehn 	if (err < 0) {
29898c4c201SDavid VomLehn 		bool is_locked;
29998c4c201SDavid VomLehn 
3008e1c072fSIgor Russkikh 		hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM);
3010b926d46SNikita Danilov 		is_locked = hw_atl_sem_ram_get(self);
30298c4c201SDavid VomLehn 		if (!is_locked) {
30398c4c201SDavid VomLehn 			err = -ETIME;
30498c4c201SDavid VomLehn 			goto err_exit;
30598c4c201SDavid VomLehn 		}
30698c4c201SDavid VomLehn 	}
30798c4c201SDavid VomLehn 
30847203b34SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_MIF_ADDR, a);
30998c4c201SDavid VomLehn 
31047203b34SIgor Russkikh 	for (++cnt; --cnt && !err;) {
31147203b34SIgor Russkikh 		aq_hw_write_reg(self, HW_ATL_MIF_CMD, 0x00008000U);
31298c4c201SDavid VomLehn 
31347203b34SIgor Russkikh 		if (IS_CHIP_FEATURE(REVISION_B1))
3146a7f2277SNikita Danilov 			err = readx_poll_timeout_atomic(hw_atl_utils_mif_addr_get,
3156a7f2277SNikita Danilov 							self, val, val != a,
3166a7f2277SNikita Danilov 							1U, 1000U);
31747203b34SIgor Russkikh 		else
3186a7f2277SNikita Danilov 			err = readx_poll_timeout_atomic(hw_atl_utils_mif_cmd_get,
3196a7f2277SNikita Danilov 							self, val,
3206a7f2277SNikita Danilov 							!(val & 0x100),
3216a7f2277SNikita Danilov 							1U, 1000U);
32298c4c201SDavid VomLehn 
32347203b34SIgor Russkikh 		*(p++) = aq_hw_read_reg(self, HW_ATL_MIF_VAL);
32447203b34SIgor Russkikh 		a += 4;
32598c4c201SDavid VomLehn 	}
32698c4c201SDavid VomLehn 
3278e1c072fSIgor Russkikh 	hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM);
32898c4c201SDavid VomLehn 
32998c4c201SDavid VomLehn err_exit:
33098c4c201SDavid VomLehn 	return err;
33198c4c201SDavid VomLehn }
33298c4c201SDavid VomLehn 
33398c4c201SDavid VomLehn static int hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 a, u32 *p,
33498c4c201SDavid VomLehn 					 u32 cnt)
33598c4c201SDavid VomLehn {
3366a7f2277SNikita Danilov 	u32 val;
33798c4c201SDavid VomLehn 	int err = 0;
33898c4c201SDavid VomLehn 	bool is_locked;
33998c4c201SDavid VomLehn 
3400b926d46SNikita Danilov 	is_locked = hw_atl_sem_ram_get(self);
34198c4c201SDavid VomLehn 	if (!is_locked) {
34298c4c201SDavid VomLehn 		err = -ETIME;
34398c4c201SDavid VomLehn 		goto err_exit;
34498c4c201SDavid VomLehn 	}
3453ee5c887SYana Esina 	if (IS_CHIP_FEATURE(REVISION_B1)) {
3463ee5c887SYana Esina 		u32 offset = 0;
34798c4c201SDavid VomLehn 
3483ee5c887SYana Esina 		for (; offset < cnt; ++offset) {
3493ee5c887SYana Esina 			aq_hw_write_reg(self, 0x328, p[offset]);
3503ee5c887SYana Esina 			aq_hw_write_reg(self, 0x32C,
3513ee5c887SYana Esina 					(0x80000000 | (0xFFFF & (offset * 4))));
3523ee5c887SYana Esina 			hw_atl_mcp_up_force_intr_set(self, 1);
3533ee5c887SYana Esina 			/* 1000 times by 10us = 10ms */
3546a7f2277SNikita Danilov 			err = readx_poll_timeout_atomic(hw_atl_scrpad12_get,
3556a7f2277SNikita Danilov 							self, val,
3566a7f2277SNikita Danilov 							(val & 0xF0000000) ==
3573ee5c887SYana Esina 							 0x80000000,
3586a7f2277SNikita Danilov 							10U, 10000U);
3593ee5c887SYana Esina 		}
3603ee5c887SYana Esina 	} else {
3613ee5c887SYana Esina 		u32 offset = 0;
36298c4c201SDavid VomLehn 
3633ee5c887SYana Esina 		aq_hw_write_reg(self, 0x208, a);
36498c4c201SDavid VomLehn 
3653ee5c887SYana Esina 		for (; offset < cnt; ++offset) {
3663ee5c887SYana Esina 			aq_hw_write_reg(self, 0x20C, p[offset]);
3673ee5c887SYana Esina 			aq_hw_write_reg(self, 0x200, 0xC000);
36898c4c201SDavid VomLehn 
3696a7f2277SNikita Danilov 			err = readx_poll_timeout_atomic(hw_atl_utils_mif_cmd_get,
3706a7f2277SNikita Danilov 							self, val,
3716a7f2277SNikita Danilov 							(val & 0x100) == 0,
3726a7f2277SNikita Danilov 							1000U, 10000U);
37398c4c201SDavid VomLehn 		}
37498c4c201SDavid VomLehn 	}
37598c4c201SDavid VomLehn 
3768e1c072fSIgor Russkikh 	hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM);
37798c4c201SDavid VomLehn 
37898c4c201SDavid VomLehn err_exit:
37998c4c201SDavid VomLehn 	return err;
38098c4c201SDavid VomLehn }
38198c4c201SDavid VomLehn 
38298c4c201SDavid VomLehn static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual)
38398c4c201SDavid VomLehn {
38498c4c201SDavid VomLehn 	int err = 0;
38598c4c201SDavid VomLehn 	const u32 dw_major_mask = 0xff000000U;
38698c4c201SDavid VomLehn 	const u32 dw_minor_mask = 0x00ffffffU;
38798c4c201SDavid VomLehn 
38898c4c201SDavid VomLehn 	err = (dw_major_mask & (ver_expected ^ ver_actual)) ? -EOPNOTSUPP : 0;
38998c4c201SDavid VomLehn 	if (err < 0)
39098c4c201SDavid VomLehn 		goto err_exit;
39198c4c201SDavid VomLehn 	err = ((dw_minor_mask & ver_expected) > (dw_minor_mask & ver_actual)) ?
39298c4c201SDavid VomLehn 		-EOPNOTSUPP : 0;
39398c4c201SDavid VomLehn err_exit:
39498c4c201SDavid VomLehn 	return err;
39598c4c201SDavid VomLehn }
39698c4c201SDavid VomLehn 
39798c4c201SDavid VomLehn static int hw_atl_utils_init_ucp(struct aq_hw_s *self,
3984cbc9f92SIgor Russkikh 				 const struct aq_hw_caps_s *aq_hw_caps)
39998c4c201SDavid VomLehn {
40098c4c201SDavid VomLehn 	int err = 0;
40198c4c201SDavid VomLehn 
40298c4c201SDavid VomLehn 	if (!aq_hw_read_reg(self, 0x370U)) {
40398c4c201SDavid VomLehn 		unsigned int rnd = 0U;
40498c4c201SDavid VomLehn 		unsigned int ucp_0x370 = 0U;
40598c4c201SDavid VomLehn 
40698c4c201SDavid VomLehn 		get_random_bytes(&rnd, sizeof(unsigned int));
40798c4c201SDavid VomLehn 
40898c4c201SDavid VomLehn 		ucp_0x370 = 0x02020202U | (0xFEFEFEFEU & rnd);
40998c4c201SDavid VomLehn 		aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370);
41098c4c201SDavid VomLehn 	}
41198c4c201SDavid VomLehn 
4128e1c072fSIgor Russkikh 	hw_atl_reg_glb_cpu_scratch_scp_set(self, 0x00000000U, 25U);
41398c4c201SDavid VomLehn 
41498c4c201SDavid VomLehn 	/* check 10 times by 1ms */
4156a7f2277SNikita Danilov 	err = readx_poll_timeout_atomic(hw_atl_scrpad25_get,
4166a7f2277SNikita Danilov 					self, self->mbox_addr,
4176a7f2277SNikita Danilov 					self->mbox_addr != 0U,
4186a7f2277SNikita Danilov 					1000U, 10000U);
41998c4c201SDavid VomLehn 
42098c4c201SDavid VomLehn 	return err;
42198c4c201SDavid VomLehn }
42298c4c201SDavid VomLehn 
42398c4c201SDavid VomLehn struct aq_hw_atl_utils_fw_rpc_tid_s {
42498c4c201SDavid VomLehn 	union {
42598c4c201SDavid VomLehn 		u32 val;
42698c4c201SDavid VomLehn 		struct {
42798c4c201SDavid VomLehn 			u16 tid;
42898c4c201SDavid VomLehn 			u16 len;
42998c4c201SDavid VomLehn 		};
43098c4c201SDavid VomLehn 	};
43198c4c201SDavid VomLehn };
43298c4c201SDavid VomLehn 
43398c4c201SDavid VomLehn #define hw_atl_utils_fw_rpc_init(_H_) hw_atl_utils_fw_rpc_wait(_H_, NULL)
43498c4c201SDavid VomLehn 
4353ee5c887SYana Esina int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size)
43698c4c201SDavid VomLehn {
43798c4c201SDavid VomLehn 	int err = 0;
43898c4c201SDavid VomLehn 	struct aq_hw_atl_utils_fw_rpc_tid_s sw;
43998c4c201SDavid VomLehn 
44098c4c201SDavid VomLehn 	if (!IS_CHIP_FEATURE(MIPS)) {
44198c4c201SDavid VomLehn 		err = -1;
44298c4c201SDavid VomLehn 		goto err_exit;
44398c4c201SDavid VomLehn 	}
4441a713f87SIgor Russkikh 	err = hw_atl_utils_fw_upload_dwords(self, self->rpc_addr,
4451a713f87SIgor Russkikh 					    (u32 *)(void *)&self->rpc,
44698c4c201SDavid VomLehn 					    (rpc_size + sizeof(u32) -
44798c4c201SDavid VomLehn 					     sizeof(u8)) / sizeof(u32));
44898c4c201SDavid VomLehn 	if (err < 0)
44998c4c201SDavid VomLehn 		goto err_exit;
45098c4c201SDavid VomLehn 
4511a713f87SIgor Russkikh 	sw.tid = 0xFFFFU & (++self->rpc_tid);
45298c4c201SDavid VomLehn 	sw.len = (u16)rpc_size;
45398c4c201SDavid VomLehn 	aq_hw_write_reg(self, HW_ATL_RPC_CONTROL_ADR, sw.val);
45498c4c201SDavid VomLehn 
45598c4c201SDavid VomLehn err_exit:
45698c4c201SDavid VomLehn 	return err;
45798c4c201SDavid VomLehn }
45898c4c201SDavid VomLehn 
4593ee5c887SYana Esina int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self,
4608f60f762SNikita Danilov 			     struct hw_atl_utils_fw_rpc **rpc)
46198c4c201SDavid VomLehn {
46298c4c201SDavid VomLehn 	int err = 0;
46398c4c201SDavid VomLehn 	struct aq_hw_atl_utils_fw_rpc_tid_s sw;
46498c4c201SDavid VomLehn 	struct aq_hw_atl_utils_fw_rpc_tid_s fw;
46598c4c201SDavid VomLehn 
46698c4c201SDavid VomLehn 	do {
46798c4c201SDavid VomLehn 		sw.val = aq_hw_read_reg(self, HW_ATL_RPC_CONTROL_ADR);
46898c4c201SDavid VomLehn 
4691a713f87SIgor Russkikh 		self->rpc_tid = sw.tid;
47098c4c201SDavid VomLehn 
4716a7f2277SNikita Danilov 		err = readx_poll_timeout_atomic(hw_atl_utils_rpc_state_get,
4726a7f2277SNikita Danilov 						self, fw.val,
4736a7f2277SNikita Danilov 						sw.tid == fw.tid,
4746a7f2277SNikita Danilov 						1000U, 100000U);
47598c4c201SDavid VomLehn 
47698c4c201SDavid VomLehn 		if (fw.len == 0xFFFFU) {
47798c4c201SDavid VomLehn 			err = hw_atl_utils_fw_rpc_call(self, sw.len);
47898c4c201SDavid VomLehn 			if (err < 0)
47998c4c201SDavid VomLehn 				goto err_exit;
48098c4c201SDavid VomLehn 		}
48198c4c201SDavid VomLehn 	} while (sw.tid != fw.tid || 0xFFFFU == fw.len);
48298c4c201SDavid VomLehn 
48398c4c201SDavid VomLehn 	if (rpc) {
48498c4c201SDavid VomLehn 		if (fw.len) {
48598c4c201SDavid VomLehn 			err =
48698c4c201SDavid VomLehn 			hw_atl_utils_fw_downld_dwords(self,
4871a713f87SIgor Russkikh 						      self->rpc_addr,
48898c4c201SDavid VomLehn 						      (u32 *)(void *)
4891a713f87SIgor Russkikh 						      &self->rpc,
49098c4c201SDavid VomLehn 						      (fw.len + sizeof(u32) -
49198c4c201SDavid VomLehn 						       sizeof(u8)) /
49298c4c201SDavid VomLehn 						      sizeof(u32));
49398c4c201SDavid VomLehn 			if (err < 0)
49498c4c201SDavid VomLehn 				goto err_exit;
49598c4c201SDavid VomLehn 		}
49698c4c201SDavid VomLehn 
4971a713f87SIgor Russkikh 		*rpc = &self->rpc;
49898c4c201SDavid VomLehn 	}
49998c4c201SDavid VomLehn 
50098c4c201SDavid VomLehn err_exit:
50198c4c201SDavid VomLehn 	return err;
50298c4c201SDavid VomLehn }
50398c4c201SDavid VomLehn 
5041a713f87SIgor Russkikh static int hw_atl_utils_mpi_create(struct aq_hw_s *self)
50598c4c201SDavid VomLehn {
50698c4c201SDavid VomLehn 	int err = 0;
50798c4c201SDavid VomLehn 
5081a713f87SIgor Russkikh 	err = hw_atl_utils_init_ucp(self, self->aq_nic_cfg->aq_hw_caps);
50998c4c201SDavid VomLehn 	if (err < 0)
51098c4c201SDavid VomLehn 		goto err_exit;
51198c4c201SDavid VomLehn 
51298c4c201SDavid VomLehn 	err = hw_atl_utils_fw_rpc_init(self);
51398c4c201SDavid VomLehn 	if (err < 0)
51498c4c201SDavid VomLehn 		goto err_exit;
51598c4c201SDavid VomLehn 
51698c4c201SDavid VomLehn err_exit:
51798c4c201SDavid VomLehn 	return err;
51898c4c201SDavid VomLehn }
51998c4c201SDavid VomLehn 
52065e665e6SIgor Russkikh int hw_atl_utils_mpi_read_mbox(struct aq_hw_s *self,
5218f60f762SNikita Danilov 			       struct hw_atl_utils_mbox_header *pmbox)
52265e665e6SIgor Russkikh {
52365e665e6SIgor Russkikh 	return hw_atl_utils_fw_downld_dwords(self,
5241a713f87SIgor Russkikh 					     self->mbox_addr,
52565e665e6SIgor Russkikh 					     (u32 *)(void *)pmbox,
52665e665e6SIgor Russkikh 					     sizeof(*pmbox) / sizeof(u32));
52765e665e6SIgor Russkikh }
52865e665e6SIgor Russkikh 
52998c4c201SDavid VomLehn void hw_atl_utils_mpi_read_stats(struct aq_hw_s *self,
5308f60f762SNikita Danilov 				 struct hw_atl_utils_mbox *pmbox)
53198c4c201SDavid VomLehn {
53298c4c201SDavid VomLehn 	int err = 0;
53398c4c201SDavid VomLehn 
53498c4c201SDavid VomLehn 	err = hw_atl_utils_fw_downld_dwords(self,
5351a713f87SIgor Russkikh 					    self->mbox_addr,
53698c4c201SDavid VomLehn 					    (u32 *)(void *)pmbox,
53798c4c201SDavid VomLehn 					    sizeof(*pmbox) / sizeof(u32));
53898c4c201SDavid VomLehn 	if (err < 0)
53998c4c201SDavid VomLehn 		goto err_exit;
54098c4c201SDavid VomLehn 
54198c4c201SDavid VomLehn 	if (IS_CHIP_FEATURE(REVISION_A0)) {
54298c4c201SDavid VomLehn 		unsigned int mtu = self->aq_nic_cfg ?
54398c4c201SDavid VomLehn 					self->aq_nic_cfg->mtu : 1514U;
54498c4c201SDavid VomLehn 		pmbox->stats.ubrc = pmbox->stats.uprc * mtu;
54598c4c201SDavid VomLehn 		pmbox->stats.ubtc = pmbox->stats.uptc * mtu;
5461a713f87SIgor Russkikh 		pmbox->stats.dpc = atomic_read(&self->dpc);
54798c4c201SDavid VomLehn 	} else {
5488e1c072fSIgor Russkikh 		pmbox->stats.dpc = hw_atl_reg_rx_dma_stat_counter7get(self);
54998c4c201SDavid VomLehn 	}
55098c4c201SDavid VomLehn 
55198c4c201SDavid VomLehn err_exit:;
55298c4c201SDavid VomLehn }
55398c4c201SDavid VomLehn 
554dfbd0749SWei Yongjun static int hw_atl_utils_mpi_set_speed(struct aq_hw_s *self, u32 speed)
55598c4c201SDavid VomLehn {
5560c58c35fSIgor Russkikh 	u32 val = aq_hw_read_reg(self, HW_ATL_MPI_CONTROL_ADR);
55798c4c201SDavid VomLehn 
55844e00dd8SIgor Russkikh 	val = val & ~HW_ATL_MPI_SPEED_MSK;
55944e00dd8SIgor Russkikh 	val |= speed << HW_ATL_MPI_SPEED_SHIFT;
5600c58c35fSIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, val);
56198c4c201SDavid VomLehn 
56298c4c201SDavid VomLehn 	return 0;
56398c4c201SDavid VomLehn }
56498c4c201SDavid VomLehn 
565dfbd0749SWei Yongjun static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self,
56644e00dd8SIgor Russkikh 				      enum hal_atl_utils_fw_state_e state)
56798c4c201SDavid VomLehn {
56898c4c201SDavid VomLehn 	int err = 0;
56998c4c201SDavid VomLehn 	u32 transaction_id = 0;
5708f60f762SNikita Danilov 	struct hw_atl_utils_mbox_header mbox;
57144e00dd8SIgor Russkikh 	u32 val = aq_hw_read_reg(self, HW_ATL_MPI_CONTROL_ADR);
57298c4c201SDavid VomLehn 
57398c4c201SDavid VomLehn 	if (state == MPI_RESET) {
57465e665e6SIgor Russkikh 		hw_atl_utils_mpi_read_mbox(self, &mbox);
57598c4c201SDavid VomLehn 
57665e665e6SIgor Russkikh 		transaction_id = mbox.transaction_id;
57798c4c201SDavid VomLehn 
5786a7f2277SNikita Danilov 		err = readx_poll_timeout_atomic(hw_atl_utils_get_mpi_mbox_tid,
5796a7f2277SNikita Danilov 						self, mbox.transaction_id,
5806a7f2277SNikita Danilov 						transaction_id !=
5816a7f2277SNikita Danilov 						mbox.transaction_id,
5826a7f2277SNikita Danilov 						1000U, 100000U);
58398c4c201SDavid VomLehn 		if (err < 0)
58498c4c201SDavid VomLehn 			goto err_exit;
58598c4c201SDavid VomLehn 	}
58644e00dd8SIgor Russkikh 	/* On interface DEINIT we disable DW (raise bit)
58744e00dd8SIgor Russkikh 	 * Otherwise enable DW (clear bit)
58844e00dd8SIgor Russkikh 	 */
58944e00dd8SIgor Russkikh 	if (state == MPI_DEINIT || state == MPI_POWER)
59044e00dd8SIgor Russkikh 		val |= HW_ATL_MPI_DIRTY_WAKE_MSK;
59144e00dd8SIgor Russkikh 	else
59244e00dd8SIgor Russkikh 		val &= ~HW_ATL_MPI_DIRTY_WAKE_MSK;
59398c4c201SDavid VomLehn 
59444e00dd8SIgor Russkikh 	/* Set new state bits */
59544e00dd8SIgor Russkikh 	val = val & ~HW_ATL_MPI_STATE_MSK;
59644e00dd8SIgor Russkikh 	val |= state & HW_ATL_MPI_STATE_MSK;
59798c4c201SDavid VomLehn 
5980c58c35fSIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, val);
59944e00dd8SIgor Russkikh err_exit:
60044e00dd8SIgor Russkikh 	return err;
6010c58c35fSIgor Russkikh }
6020c58c35fSIgor Russkikh 
603bd8ed441SPavel Belous int hw_atl_utils_mpi_get_link_status(struct aq_hw_s *self)
60498c4c201SDavid VomLehn {
6050b926d46SNikita Danilov 	u32 cp0x036C = hw_atl_utils_mpi_get_state(self);
60698c4c201SDavid VomLehn 	u32 link_speed_mask = cp0x036C >> HW_ATL_MPI_SPEED_SHIFT;
607bd8ed441SPavel Belous 	struct aq_hw_link_status_s *link_status = &self->aq_link_status;
60898c4c201SDavid VomLehn 
60998c4c201SDavid VomLehn 	if (!link_speed_mask) {
61098c4c201SDavid VomLehn 		link_status->mbps = 0U;
61198c4c201SDavid VomLehn 	} else {
61298c4c201SDavid VomLehn 		switch (link_speed_mask) {
61398c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_10G:
61498c4c201SDavid VomLehn 			link_status->mbps = 10000U;
61598c4c201SDavid VomLehn 			break;
61698c4c201SDavid VomLehn 
61798c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_5G:
61898c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_5GSR:
61998c4c201SDavid VomLehn 			link_status->mbps = 5000U;
62098c4c201SDavid VomLehn 			break;
62198c4c201SDavid VomLehn 
62298c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_2GS:
62398c4c201SDavid VomLehn 			link_status->mbps = 2500U;
62498c4c201SDavid VomLehn 			break;
62598c4c201SDavid VomLehn 
62698c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_1G:
62798c4c201SDavid VomLehn 			link_status->mbps = 1000U;
62898c4c201SDavid VomLehn 			break;
62998c4c201SDavid VomLehn 
63098c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_100M:
63198c4c201SDavid VomLehn 			link_status->mbps = 100U;
63298c4c201SDavid VomLehn 			break;
63398c4c201SDavid VomLehn 
63498c4c201SDavid VomLehn 		default:
635a7bb1beaSIgor Russkikh 			return -EBUSY;
63698c4c201SDavid VomLehn 		}
63798c4c201SDavid VomLehn 	}
63898c4c201SDavid VomLehn 
63998c4c201SDavid VomLehn 	return 0;
64098c4c201SDavid VomLehn }
64198c4c201SDavid VomLehn 
64298c4c201SDavid VomLehn int hw_atl_utils_get_mac_permanent(struct aq_hw_s *self,
64398c4c201SDavid VomLehn 				   u8 *mac)
64498c4c201SDavid VomLehn {
64598c4c201SDavid VomLehn 	int err = 0;
64698c4c201SDavid VomLehn 	u32 h = 0U;
64798c4c201SDavid VomLehn 	u32 l = 0U;
64898c4c201SDavid VomLehn 	u32 mac_addr[2];
64998c4c201SDavid VomLehn 
65098c4c201SDavid VomLehn 	if (!aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) {
65198c4c201SDavid VomLehn 		unsigned int rnd = 0;
65298c4c201SDavid VomLehn 		unsigned int ucp_0x370 = 0;
65398c4c201SDavid VomLehn 
65498c4c201SDavid VomLehn 		get_random_bytes(&rnd, sizeof(unsigned int));
65598c4c201SDavid VomLehn 
65698c4c201SDavid VomLehn 		ucp_0x370 = 0x02020202 | (0xFEFEFEFE & rnd);
65798c4c201SDavid VomLehn 		aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370);
65898c4c201SDavid VomLehn 	}
65998c4c201SDavid VomLehn 
66098c4c201SDavid VomLehn 	err = hw_atl_utils_fw_downld_dwords(self,
66198c4c201SDavid VomLehn 					    aq_hw_read_reg(self, 0x00000374U) +
66298c4c201SDavid VomLehn 					    (40U * 4U),
66398c4c201SDavid VomLehn 					    mac_addr,
66408b5cf08SIgor Russkikh 					    ARRAY_SIZE(mac_addr));
66598c4c201SDavid VomLehn 	if (err < 0) {
66698c4c201SDavid VomLehn 		mac_addr[0] = 0U;
66798c4c201SDavid VomLehn 		mac_addr[1] = 0U;
66898c4c201SDavid VomLehn 		err = 0;
66998c4c201SDavid VomLehn 	} else {
67098c4c201SDavid VomLehn 		mac_addr[0] = __swab32(mac_addr[0]);
67198c4c201SDavid VomLehn 		mac_addr[1] = __swab32(mac_addr[1]);
67298c4c201SDavid VomLehn 	}
67398c4c201SDavid VomLehn 
67498c4c201SDavid VomLehn 	ether_addr_copy(mac, (u8 *)mac_addr);
67598c4c201SDavid VomLehn 
67698c4c201SDavid VomLehn 	if ((mac[0] & 0x01U) || ((mac[0] | mac[1] | mac[2]) == 0x00U)) {
67798c4c201SDavid VomLehn 		/* chip revision */
678e9157848SNikita Danilov 		l = 0xE3000000U |
679e9157848SNikita Danilov 		    (0xFFFFU & aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) |
680e9157848SNikita Danilov 		    (0x00 << 16);
68198c4c201SDavid VomLehn 		h = 0x8001300EU;
68298c4c201SDavid VomLehn 
68398c4c201SDavid VomLehn 		mac[5] = (u8)(0xFFU & l);
68498c4c201SDavid VomLehn 		l >>= 8;
68598c4c201SDavid VomLehn 		mac[4] = (u8)(0xFFU & l);
68698c4c201SDavid VomLehn 		l >>= 8;
68798c4c201SDavid VomLehn 		mac[3] = (u8)(0xFFU & l);
68898c4c201SDavid VomLehn 		l >>= 8;
68998c4c201SDavid VomLehn 		mac[2] = (u8)(0xFFU & l);
69098c4c201SDavid VomLehn 		mac[1] = (u8)(0xFFU & h);
69198c4c201SDavid VomLehn 		h >>= 8;
69298c4c201SDavid VomLehn 		mac[0] = (u8)(0xFFU & h);
69398c4c201SDavid VomLehn 	}
69498c4c201SDavid VomLehn 
69598c4c201SDavid VomLehn 	return err;
69698c4c201SDavid VomLehn }
69798c4c201SDavid VomLehn 
69898c4c201SDavid VomLehn unsigned int hw_atl_utils_mbps_2_speed_index(unsigned int mbps)
69998c4c201SDavid VomLehn {
70098c4c201SDavid VomLehn 	unsigned int ret = 0U;
70198c4c201SDavid VomLehn 
70298c4c201SDavid VomLehn 	switch (mbps) {
70398c4c201SDavid VomLehn 	case 100U:
70498c4c201SDavid VomLehn 		ret = 5U;
70598c4c201SDavid VomLehn 		break;
70698c4c201SDavid VomLehn 
70798c4c201SDavid VomLehn 	case 1000U:
70898c4c201SDavid VomLehn 		ret = 4U;
70998c4c201SDavid VomLehn 		break;
71098c4c201SDavid VomLehn 
71198c4c201SDavid VomLehn 	case 2500U:
71298c4c201SDavid VomLehn 		ret = 3U;
71398c4c201SDavid VomLehn 		break;
71498c4c201SDavid VomLehn 
71598c4c201SDavid VomLehn 	case 5000U:
71698c4c201SDavid VomLehn 		ret = 1U;
71798c4c201SDavid VomLehn 		break;
71898c4c201SDavid VomLehn 
71998c4c201SDavid VomLehn 	case 10000U:
72098c4c201SDavid VomLehn 		ret = 0U;
72198c4c201SDavid VomLehn 		break;
72298c4c201SDavid VomLehn 
72398c4c201SDavid VomLehn 	default:
72498c4c201SDavid VomLehn 		break;
72598c4c201SDavid VomLehn 	}
72698c4c201SDavid VomLehn 	return ret;
72798c4c201SDavid VomLehn }
72898c4c201SDavid VomLehn 
72998c4c201SDavid VomLehn void hw_atl_utils_hw_chip_features_init(struct aq_hw_s *self, u32 *p)
73098c4c201SDavid VomLehn {
73198c4c201SDavid VomLehn 	u32 chip_features = 0U;
7328e1c072fSIgor Russkikh 	u32 val = hw_atl_reg_glb_mif_id_get(self);
73398c4c201SDavid VomLehn 	u32 mif_rev = val & 0xFFU;
73498c4c201SDavid VomLehn 
73547203b34SIgor Russkikh 	if ((0xFU & mif_rev) == 1U) {
73647203b34SIgor Russkikh 		chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_A0 |
73798c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_MPI_AQ |
73898c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_MIPS;
73947203b34SIgor Russkikh 	} else if ((0xFU & mif_rev) == 2U) {
74047203b34SIgor Russkikh 		chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_B0 |
74147203b34SIgor Russkikh 			HAL_ATLANTIC_UTILS_CHIP_MPI_AQ |
74247203b34SIgor Russkikh 			HAL_ATLANTIC_UTILS_CHIP_MIPS |
74347203b34SIgor Russkikh 			HAL_ATLANTIC_UTILS_CHIP_TPO2 |
74447203b34SIgor Russkikh 			HAL_ATLANTIC_UTILS_CHIP_RPF2;
74547203b34SIgor Russkikh 	} else if ((0xFU & mif_rev) == 0xAU) {
74647203b34SIgor Russkikh 		chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_B1 |
74798c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_MPI_AQ |
74898c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_MIPS |
74998c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_TPO2 |
75098c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_RPF2;
75198c4c201SDavid VomLehn 	}
75298c4c201SDavid VomLehn 
75398c4c201SDavid VomLehn 	*p = chip_features;
75498c4c201SDavid VomLehn }
75598c4c201SDavid VomLehn 
75644e00dd8SIgor Russkikh static int hw_atl_fw1x_deinit(struct aq_hw_s *self)
75798c4c201SDavid VomLehn {
75844e00dd8SIgor Russkikh 	hw_atl_utils_mpi_set_speed(self, 0);
75944e00dd8SIgor Russkikh 	hw_atl_utils_mpi_set_state(self, MPI_DEINIT);
76098c4c201SDavid VomLehn 	return 0;
76198c4c201SDavid VomLehn }
76298c4c201SDavid VomLehn 
76365e665e6SIgor Russkikh int hw_atl_utils_update_stats(struct aq_hw_s *self)
76465e665e6SIgor Russkikh {
7658f60f762SNikita Danilov 	struct hw_atl_utils_mbox mbox;
76665e665e6SIgor Russkikh 
76765e665e6SIgor Russkikh 	hw_atl_utils_mpi_read_stats(self, &mbox);
76865e665e6SIgor Russkikh 
7691a713f87SIgor Russkikh #define AQ_SDELTA(_N_) (self->curr_stats._N_ += \
7701a713f87SIgor Russkikh 			mbox.stats._N_ - self->last_stats._N_)
7711a713f87SIgor Russkikh 
772be08d839SIgor Russkikh 	if (self->aq_link_status.mbps) {
77365e665e6SIgor Russkikh 		AQ_SDELTA(uprc);
77465e665e6SIgor Russkikh 		AQ_SDELTA(mprc);
77565e665e6SIgor Russkikh 		AQ_SDELTA(bprc);
77665e665e6SIgor Russkikh 		AQ_SDELTA(erpt);
77765e665e6SIgor Russkikh 
77865e665e6SIgor Russkikh 		AQ_SDELTA(uptc);
77965e665e6SIgor Russkikh 		AQ_SDELTA(mptc);
78065e665e6SIgor Russkikh 		AQ_SDELTA(bptc);
78165e665e6SIgor Russkikh 		AQ_SDELTA(erpr);
78265e665e6SIgor Russkikh 
78365e665e6SIgor Russkikh 		AQ_SDELTA(ubrc);
78465e665e6SIgor Russkikh 		AQ_SDELTA(ubtc);
78565e665e6SIgor Russkikh 		AQ_SDELTA(mbrc);
78665e665e6SIgor Russkikh 		AQ_SDELTA(mbtc);
78765e665e6SIgor Russkikh 		AQ_SDELTA(bbrc);
78865e665e6SIgor Russkikh 		AQ_SDELTA(bbtc);
78965e665e6SIgor Russkikh 		AQ_SDELTA(dpc);
790be08d839SIgor Russkikh 	}
79165e665e6SIgor Russkikh #undef AQ_SDELTA
7928e1c072fSIgor Russkikh 	self->curr_stats.dma_pkt_rc = hw_atl_stats_rx_dma_good_pkt_counterlsw_get(self);
7938e1c072fSIgor Russkikh 	self->curr_stats.dma_pkt_tc = hw_atl_stats_tx_dma_good_pkt_counterlsw_get(self);
7948e1c072fSIgor Russkikh 	self->curr_stats.dma_oct_rc = hw_atl_stats_rx_dma_good_octet_counterlsw_get(self);
7958e1c072fSIgor Russkikh 	self->curr_stats.dma_oct_tc = hw_atl_stats_tx_dma_good_octet_counterlsw_get(self);
79665e665e6SIgor Russkikh 
7971a713f87SIgor Russkikh 	memcpy(&self->last_stats, &mbox.stats, sizeof(mbox.stats));
79865e665e6SIgor Russkikh 
79965e665e6SIgor Russkikh 	return 0;
80065e665e6SIgor Russkikh }
80165e665e6SIgor Russkikh 
802be08d839SIgor Russkikh struct aq_stats_s *hw_atl_utils_get_hw_stats(struct aq_hw_s *self)
80398c4c201SDavid VomLehn {
8041a713f87SIgor Russkikh 	return &self->curr_stats;
80598c4c201SDavid VomLehn }
80698c4c201SDavid VomLehn 
80798c4c201SDavid VomLehn static const u32 hw_atl_utils_hw_mac_regs[] = {
80898c4c201SDavid VomLehn 	0x00005580U, 0x00005590U, 0x000055B0U, 0x000055B4U,
80998c4c201SDavid VomLehn 	0x000055C0U, 0x00005B00U, 0x00005B04U, 0x00005B08U,
81098c4c201SDavid VomLehn 	0x00005B0CU, 0x00005B10U, 0x00005B14U, 0x00005B18U,
81198c4c201SDavid VomLehn 	0x00005B1CU, 0x00005B20U, 0x00005B24U, 0x00005B28U,
81298c4c201SDavid VomLehn 	0x00005B2CU, 0x00005B30U, 0x00005B34U, 0x00005B38U,
81398c4c201SDavid VomLehn 	0x00005B3CU, 0x00005B40U, 0x00005B44U, 0x00005B48U,
81498c4c201SDavid VomLehn 	0x00005B4CU, 0x00005B50U, 0x00005B54U, 0x00005B58U,
81598c4c201SDavid VomLehn 	0x00005B5CU, 0x00005B60U, 0x00005B64U, 0x00005B68U,
81698c4c201SDavid VomLehn 	0x00005B6CU, 0x00005B70U, 0x00005B74U, 0x00005B78U,
81798c4c201SDavid VomLehn 	0x00005B7CU, 0x00007C00U, 0x00007C04U, 0x00007C08U,
81898c4c201SDavid VomLehn 	0x00007C0CU, 0x00007C10U, 0x00007C14U, 0x00007C18U,
81998c4c201SDavid VomLehn 	0x00007C1CU, 0x00007C20U, 0x00007C40U, 0x00007C44U,
82098c4c201SDavid VomLehn 	0x00007C48U, 0x00007C4CU, 0x00007C50U, 0x00007C54U,
82198c4c201SDavid VomLehn 	0x00007C58U, 0x00007C5CU, 0x00007C60U, 0x00007C80U,
82298c4c201SDavid VomLehn 	0x00007C84U, 0x00007C88U, 0x00007C8CU, 0x00007C90U,
82398c4c201SDavid VomLehn 	0x00007C94U, 0x00007C98U, 0x00007C9CU, 0x00007CA0U,
82498c4c201SDavid VomLehn 	0x00007CC0U, 0x00007CC4U, 0x00007CC8U, 0x00007CCCU,
82598c4c201SDavid VomLehn 	0x00007CD0U, 0x00007CD4U, 0x00007CD8U, 0x00007CDCU,
82698c4c201SDavid VomLehn 	0x00007CE0U, 0x00000300U, 0x00000304U, 0x00000308U,
82798c4c201SDavid VomLehn 	0x0000030cU, 0x00000310U, 0x00000314U, 0x00000318U,
82898c4c201SDavid VomLehn 	0x0000031cU, 0x00000360U, 0x00000364U, 0x00000368U,
82998c4c201SDavid VomLehn 	0x0000036cU, 0x00000370U, 0x00000374U, 0x00006900U,
83098c4c201SDavid VomLehn };
83198c4c201SDavid VomLehn 
83298c4c201SDavid VomLehn int hw_atl_utils_hw_get_regs(struct aq_hw_s *self,
8334cbc9f92SIgor Russkikh 			     const struct aq_hw_caps_s *aq_hw_caps,
83498c4c201SDavid VomLehn 			     u32 *regs_buff)
83598c4c201SDavid VomLehn {
83698c4c201SDavid VomLehn 	unsigned int i = 0U;
83798c4c201SDavid VomLehn 
83898c4c201SDavid VomLehn 	for (i = 0; i < aq_hw_caps->mac_regs_count; i++)
83998c4c201SDavid VomLehn 		regs_buff[i] = aq_hw_read_reg(self,
84098c4c201SDavid VomLehn 					      hw_atl_utils_hw_mac_regs[i]);
84198c4c201SDavid VomLehn 	return 0;
84298c4c201SDavid VomLehn }
84398c4c201SDavid VomLehn 
84498c4c201SDavid VomLehn int hw_atl_utils_get_fw_version(struct aq_hw_s *self, u32 *fw_version)
84598c4c201SDavid VomLehn {
84698c4c201SDavid VomLehn 	*fw_version = aq_hw_read_reg(self, 0x18U);
84798c4c201SDavid VomLehn 	return 0;
84898c4c201SDavid VomLehn }
8490c58c35fSIgor Russkikh 
850a0da96c0SYana Esina static int aq_fw1x_set_wol(struct aq_hw_s *self, bool wol_enabled, u8 *mac)
851a0da96c0SYana Esina {
8528f60f762SNikita Danilov 	struct hw_atl_utils_fw_rpc *prpc = NULL;
853a0da96c0SYana Esina 	unsigned int rpc_size = 0U;
854a0da96c0SYana Esina 	int err = 0;
855a0da96c0SYana Esina 
856a0da96c0SYana Esina 	err = hw_atl_utils_fw_rpc_wait(self, &prpc);
857a0da96c0SYana Esina 	if (err < 0)
858a0da96c0SYana Esina 		goto err_exit;
859a0da96c0SYana Esina 
860a0da96c0SYana Esina 	memset(prpc, 0, sizeof(*prpc));
861a0da96c0SYana Esina 
862a0da96c0SYana Esina 	if (wol_enabled) {
863a0da96c0SYana Esina 		rpc_size = sizeof(prpc->msg_id) + sizeof(prpc->msg_wol);
864a0da96c0SYana Esina 
865a0da96c0SYana Esina 		prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_ADD;
866a0da96c0SYana Esina 		prpc->msg_wol.priority =
867a0da96c0SYana Esina 				HAL_ATLANTIC_UTILS_FW_MSG_WOL_PRIOR;
868a0da96c0SYana Esina 		prpc->msg_wol.pattern_id =
869a0da96c0SYana Esina 				HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN;
870a0da96c0SYana Esina 		prpc->msg_wol.wol_packet_type =
871a0da96c0SYana Esina 				HAL_ATLANTIC_UTILS_FW_MSG_WOL_MAG_PKT;
872a0da96c0SYana Esina 
873a0da96c0SYana Esina 		ether_addr_copy((u8 *)&prpc->msg_wol.wol_pattern, mac);
874a0da96c0SYana Esina 	} else {
875a0da96c0SYana Esina 		rpc_size = sizeof(prpc->msg_id) + sizeof(prpc->msg_del_id);
876a0da96c0SYana Esina 
877a0da96c0SYana Esina 		prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_DEL;
878a0da96c0SYana Esina 		prpc->msg_wol.pattern_id =
879a0da96c0SYana Esina 				HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN;
880a0da96c0SYana Esina 	}
881a0da96c0SYana Esina 
882a0da96c0SYana Esina 	err = hw_atl_utils_fw_rpc_call(self, rpc_size);
883a0da96c0SYana Esina 
884a0da96c0SYana Esina err_exit:
885a0da96c0SYana Esina 	return err;
886a0da96c0SYana Esina }
887a0da96c0SYana Esina 
8883d5537f9SWei Yongjun static int aq_fw1x_set_power(struct aq_hw_s *self, unsigned int power_state,
889a0da96c0SYana Esina 			     u8 *mac)
890a0da96c0SYana Esina {
8918f60f762SNikita Danilov 	struct hw_atl_utils_fw_rpc *prpc = NULL;
892a0da96c0SYana Esina 	unsigned int rpc_size = 0U;
893a0da96c0SYana Esina 	int err = 0;
894a0da96c0SYana Esina 
895a0da96c0SYana Esina 	if (self->aq_nic_cfg->wol & AQ_NIC_WOL_ENABLED) {
896a0da96c0SYana Esina 		err = aq_fw1x_set_wol(self, 1, mac);
897a0da96c0SYana Esina 
898a0da96c0SYana Esina 		if (err < 0)
899a0da96c0SYana Esina 			goto err_exit;
900a0da96c0SYana Esina 
901a0da96c0SYana Esina 		rpc_size = sizeof(prpc->msg_id) +
902a0da96c0SYana Esina 			   sizeof(prpc->msg_enable_wakeup);
903a0da96c0SYana Esina 
904a0da96c0SYana Esina 		err = hw_atl_utils_fw_rpc_wait(self, &prpc);
905a0da96c0SYana Esina 
906a0da96c0SYana Esina 		if (err < 0)
907a0da96c0SYana Esina 			goto err_exit;
908a0da96c0SYana Esina 
909a0da96c0SYana Esina 		memset(prpc, 0, rpc_size);
910a0da96c0SYana Esina 
911a0da96c0SYana Esina 		prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_ENABLE_WAKEUP;
912a0da96c0SYana Esina 		prpc->msg_enable_wakeup.pattern_mask = 0x00000002;
913a0da96c0SYana Esina 
914a0da96c0SYana Esina 		err = hw_atl_utils_fw_rpc_call(self, rpc_size);
915a0da96c0SYana Esina 		if (err < 0)
916a0da96c0SYana Esina 			goto err_exit;
917a0da96c0SYana Esina 	}
918a0da96c0SYana Esina 	hw_atl_utils_mpi_set_speed(self, 0);
919a0da96c0SYana Esina 	hw_atl_utils_mpi_set_state(self, MPI_POWER);
920a0da96c0SYana Esina 
921a0da96c0SYana Esina err_exit:
922a0da96c0SYana Esina 	return err;
923a0da96c0SYana Esina }
924a0da96c0SYana Esina 
9256a7f2277SNikita Danilov static u32 hw_atl_utils_get_mpi_mbox_tid(struct aq_hw_s *self)
9266a7f2277SNikita Danilov {
9276a7f2277SNikita Danilov 	struct hw_atl_utils_mbox_header mbox;
9286a7f2277SNikita Danilov 
9296a7f2277SNikita Danilov 	hw_atl_utils_mpi_read_mbox(self, &mbox);
9306a7f2277SNikita Danilov 
9316a7f2277SNikita Danilov 	return mbox.transaction_id;
9326a7f2277SNikita Danilov }
9336a7f2277SNikita Danilov 
9346a7f2277SNikita Danilov static u32 hw_atl_utils_mpi_get_state(struct aq_hw_s *self)
9356a7f2277SNikita Danilov {
9366a7f2277SNikita Danilov 	return aq_hw_read_reg(self, HW_ATL_MPI_STATE_ADR);
9376a7f2277SNikita Danilov }
9386a7f2277SNikita Danilov 
9396a7f2277SNikita Danilov static u32 hw_atl_utils_mif_cmd_get(struct aq_hw_s *self)
9406a7f2277SNikita Danilov {
9416a7f2277SNikita Danilov 	return aq_hw_read_reg(self, HW_ATL_MIF_CMD);
9426a7f2277SNikita Danilov }
9436a7f2277SNikita Danilov 
9446a7f2277SNikita Danilov static u32 hw_atl_utils_mif_addr_get(struct aq_hw_s *self)
9456a7f2277SNikita Danilov {
9466a7f2277SNikita Danilov 	return aq_hw_read_reg(self, HW_ATL_MIF_ADDR);
9476a7f2277SNikita Danilov }
9486a7f2277SNikita Danilov 
9496a7f2277SNikita Danilov static u32 hw_atl_utils_rpc_state_get(struct aq_hw_s *self)
9506a7f2277SNikita Danilov {
9516a7f2277SNikita Danilov 	return aq_hw_read_reg(self, HW_ATL_RPC_STATE_ADR);
9526a7f2277SNikita Danilov }
9536a7f2277SNikita Danilov 
9540c58c35fSIgor Russkikh const struct aq_fw_ops aq_fw_1x_ops = {
9550c58c35fSIgor Russkikh 	.init = hw_atl_utils_mpi_create,
95644e00dd8SIgor Russkikh 	.deinit = hw_atl_fw1x_deinit,
9570c58c35fSIgor Russkikh 	.reset = NULL,
9580c58c35fSIgor Russkikh 	.get_mac_permanent = hw_atl_utils_get_mac_permanent,
9590c58c35fSIgor Russkikh 	.set_link_speed = hw_atl_utils_mpi_set_speed,
9600c58c35fSIgor Russkikh 	.set_state = hw_atl_utils_mpi_set_state,
9610c58c35fSIgor Russkikh 	.update_link_status = hw_atl_utils_mpi_get_link_status,
9620c58c35fSIgor Russkikh 	.update_stats = hw_atl_utils_update_stats,
9638f894011SYana Esina 	.get_phy_temp = NULL,
964a0da96c0SYana Esina 	.set_power = aq_fw1x_set_power,
96592ab6407SYana Esina 	.set_eee_rate = NULL,
96692ab6407SYana Esina 	.get_eee_rate = NULL,
967288551deSIgor Russkikh 	.set_flow_control = NULL,
9680c58c35fSIgor Russkikh };
969