175a6faf6SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2843e1396SMark Starovoytov /* Atlantic Network Driver
3843e1396SMark Starovoytov  *
4843e1396SMark Starovoytov  * Copyright (C) 2014-2019 aQuantia Corporation
5843e1396SMark Starovoytov  * Copyright (C) 2019-2020 Marvell International Ltd.
698c4c201SDavid VomLehn  */
798c4c201SDavid VomLehn 
898c4c201SDavid VomLehn /* File hw_atl_utils.c: Definition of common functions for Atlantic hardware
998c4c201SDavid VomLehn  * abstraction layer.
1098c4c201SDavid VomLehn  */
1198c4c201SDavid VomLehn 
121a713f87SIgor Russkikh #include "../aq_nic.h"
1398c4c201SDavid VomLehn #include "../aq_hw_utils.h"
1498c4c201SDavid VomLehn #include "hw_atl_utils.h"
1598c4c201SDavid VomLehn #include "hw_atl_llh.h"
160c58c35fSIgor Russkikh #include "hw_atl_llh_internal.h"
1798c4c201SDavid VomLehn 
1898c4c201SDavid VomLehn #include <linux/random.h>
1998c4c201SDavid VomLehn 
2098c4c201SDavid VomLehn #define HW_ATL_UCP_0X370_REG    0x0370U
2198c4c201SDavid VomLehn 
2247203b34SIgor Russkikh #define HW_ATL_MIF_CMD          0x0200U
2347203b34SIgor Russkikh #define HW_ATL_MIF_ADDR         0x0208U
2447203b34SIgor Russkikh #define HW_ATL_MIF_VAL          0x020CU
2547203b34SIgor Russkikh 
26e7b5f97eSIgor Russkikh #define HW_ATL_MPI_RPC_ADDR     0x0334U
276a7f2277SNikita Danilov #define HW_ATL_RPC_CONTROL_ADR  0x0338U
286a7f2277SNikita Danilov #define HW_ATL_RPC_STATE_ADR    0x033CU
296a7f2277SNikita Danilov 
300c58c35fSIgor Russkikh #define HW_ATL_MPI_FW_VERSION	0x18
3198c4c201SDavid VomLehn #define HW_ATL_MPI_CONTROL_ADR  0x0368U
3298c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_ADR    0x036CU
3398c4c201SDavid VomLehn 
3498c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_MSK      0x00FFU
3598c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_SHIFT    0U
3644e00dd8SIgor Russkikh #define HW_ATL_MPI_SPEED_MSK      0x00FF0000U
3798c4c201SDavid VomLehn #define HW_ATL_MPI_SPEED_SHIFT    16U
3844e00dd8SIgor Russkikh #define HW_ATL_MPI_DIRTY_WAKE_MSK 0x02000000U
3998c4c201SDavid VomLehn 
40c8c82eb3SIgor Russkikh #define HW_ATL_MPI_DAISY_CHAIN_STATUS	0x704
41c8c82eb3SIgor Russkikh #define HW_ATL_MPI_BOOT_EXIT_CODE	0x388
42c8c82eb3SIgor Russkikh 
43c8c82eb3SIgor Russkikh #define HW_ATL_MAC_PHY_CONTROL	0x4000
44c8c82eb3SIgor Russkikh #define HW_ATL_MAC_PHY_MPI_RESET_BIT 0x1D
45c8c82eb3SIgor Russkikh 
460c58c35fSIgor Russkikh #define HW_ATL_FW_VER_1X 0x01050006U
47a57d3929SIgor Russkikh #define HW_ATL_FW_VER_2X 0x02000000U
48a57d3929SIgor Russkikh #define HW_ATL_FW_VER_3X 0x03000000U
490044b1e1SDmitry Bogdanov #define HW_ATL_FW_VER_4X 0x04000000U
500c58c35fSIgor Russkikh 
51c8c82eb3SIgor Russkikh #define FORCE_FLASHLESS 0
52c8c82eb3SIgor Russkikh 
53dc12f75aSNikita Danilov enum mcp_area {
54dc12f75aSNikita Danilov 	MCP_AREA_CONFIG = 0x80000000,
55dc12f75aSNikita Danilov 	MCP_AREA_SETTINGS = 0x20000000,
56dc12f75aSNikita Danilov };
57dc12f75aSNikita Danilov 
58cce96d18SIgor Russkikh static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self,
59cce96d18SIgor Russkikh 				      enum hal_atl_utils_fw_state_e state);
606a7f2277SNikita Danilov static u32 hw_atl_utils_get_mpi_mbox_tid(struct aq_hw_s *self);
616a7f2277SNikita Danilov static u32 hw_atl_utils_mpi_get_state(struct aq_hw_s *self);
626a7f2277SNikita Danilov static u32 hw_atl_utils_mif_cmd_get(struct aq_hw_s *self);
636a7f2277SNikita Danilov static u32 hw_atl_utils_mif_addr_get(struct aq_hw_s *self);
646a7f2277SNikita Danilov static u32 hw_atl_utils_rpc_state_get(struct aq_hw_s *self);
65e7b5f97eSIgor Russkikh static u32 aq_fw1x_rpc_get(struct aq_hw_s *self);
666a7f2277SNikita Danilov 
hw_atl_utils_initfw(struct aq_hw_s * self,const struct aq_fw_ops ** fw_ops)670c58c35fSIgor Russkikh int hw_atl_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_ops)
680c58c35fSIgor Russkikh {
690c58c35fSIgor Russkikh 	int err = 0;
700c58c35fSIgor Russkikh 
710c58c35fSIgor Russkikh 	hw_atl_utils_hw_chip_features_init(self,
720c58c35fSIgor Russkikh 					   &self->chip_features);
730c58c35fSIgor Russkikh 
7436e90a52SNikita Danilov 	self->fw_ver_actual = hw_atl_utils_get_fw_version(self);
750c58c35fSIgor Russkikh 
76b567edbfSMark Starovoytov 	if (hw_atl_utils_ver_match(HW_ATL_FW_VER_1X, self->fw_ver_actual)) {
770c58c35fSIgor Russkikh 		*fw_ops = &aq_fw_1x_ops;
78b567edbfSMark Starovoytov 	} else if (hw_atl_utils_ver_match(HW_ATL_FW_VER_2X, self->fw_ver_actual)) {
79a57d3929SIgor Russkikh 		*fw_ops = &aq_fw_2x_ops;
80b567edbfSMark Starovoytov 	} else if (hw_atl_utils_ver_match(HW_ATL_FW_VER_3X, self->fw_ver_actual)) {
81a57d3929SIgor Russkikh 		*fw_ops = &aq_fw_2x_ops;
820044b1e1SDmitry Bogdanov 	} else if (hw_atl_utils_ver_match(HW_ATL_FW_VER_4X, self->fw_ver_actual)) {
830044b1e1SDmitry Bogdanov 		*fw_ops = &aq_fw_2x_ops;
84c8c82eb3SIgor Russkikh 	} else {
850c58c35fSIgor Russkikh 		aq_pr_err("Bad FW version detected: %x\n",
860c58c35fSIgor Russkikh 			  self->fw_ver_actual);
870c58c35fSIgor Russkikh 		return -EOPNOTSUPP;
880c58c35fSIgor Russkikh 	}
890c58c35fSIgor Russkikh 	self->aq_fw_ops = *fw_ops;
900c58c35fSIgor Russkikh 	err = self->aq_fw_ops->init(self);
917b0c342fSNikita Danilov 
920c58c35fSIgor Russkikh 	return err;
930c58c35fSIgor Russkikh }
940c58c35fSIgor Russkikh 
hw_atl_utils_soft_reset_flb(struct aq_hw_s * self)95c8c82eb3SIgor Russkikh static int hw_atl_utils_soft_reset_flb(struct aq_hw_s *self)
96c8c82eb3SIgor Russkikh {
971bf9a752SIgor Russkikh 	u32 gsr, val;
98c8c82eb3SIgor Russkikh 	int k = 0;
99c8c82eb3SIgor Russkikh 
100c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x40e1);
101c8c82eb3SIgor Russkikh 	AQ_HW_SLEEP(50);
102c8c82eb3SIgor Russkikh 
103c8c82eb3SIgor Russkikh 	/* Cleanup SPI */
1041bf9a752SIgor Russkikh 	val = aq_hw_read_reg(self, 0x53C);
1051bf9a752SIgor Russkikh 	aq_hw_write_reg(self, 0x53C, val | 0x10);
106c8c82eb3SIgor Russkikh 
107c8c82eb3SIgor Russkikh 	gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR);
108c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, (gsr & 0xBFFF) | 0x8000);
109c8c82eb3SIgor Russkikh 
110c8c82eb3SIgor Russkikh 	/* Kickstart MAC */
111c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x80e0);
112c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x32a8, 0x0);
113c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x520, 0x1);
1141bf9a752SIgor Russkikh 
1151bf9a752SIgor Russkikh 	/* Reset SPI again because of possible interrupted SPI burst */
1161bf9a752SIgor Russkikh 	val = aq_hw_read_reg(self, 0x53C);
1171bf9a752SIgor Russkikh 	aq_hw_write_reg(self, 0x53C, val | 0x10);
118c8c82eb3SIgor Russkikh 	AQ_HW_SLEEP(10);
1191bf9a752SIgor Russkikh 	/* Clear SPI reset state */
1201bf9a752SIgor Russkikh 	aq_hw_write_reg(self, 0x53C, val & ~0x10);
1211bf9a752SIgor Russkikh 
122c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x180e0);
123c8c82eb3SIgor Russkikh 
124c8c82eb3SIgor Russkikh 	for (k = 0; k < 1000; k++) {
125c8c82eb3SIgor Russkikh 		u32 flb_status = aq_hw_read_reg(self,
126c8c82eb3SIgor Russkikh 						HW_ATL_MPI_DAISY_CHAIN_STATUS);
127c8c82eb3SIgor Russkikh 
128c8c82eb3SIgor Russkikh 		flb_status = flb_status & 0x10;
129c8c82eb3SIgor Russkikh 		if (flb_status)
130c8c82eb3SIgor Russkikh 			break;
131c8c82eb3SIgor Russkikh 		AQ_HW_SLEEP(10);
132c8c82eb3SIgor Russkikh 	}
133c8c82eb3SIgor Russkikh 	if (k == 1000) {
134c8c82eb3SIgor Russkikh 		aq_pr_err("MAC kickstart failed\n");
135c8c82eb3SIgor Russkikh 		return -EIO;
136c8c82eb3SIgor Russkikh 	}
137c8c82eb3SIgor Russkikh 
138c8c82eb3SIgor Russkikh 	/* FW reset */
139c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x80e0);
140c8c82eb3SIgor Russkikh 	AQ_HW_SLEEP(50);
141c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x3a0, 0x1);
142c8c82eb3SIgor Russkikh 
143c8c82eb3SIgor Russkikh 	/* Kickstart PHY - skipped */
144c8c82eb3SIgor Russkikh 
145c8c82eb3SIgor Russkikh 	/* Global software reset*/
146c8c82eb3SIgor Russkikh 	hw_atl_rx_rx_reg_res_dis_set(self, 0U);
147c8c82eb3SIgor Russkikh 	hw_atl_tx_tx_reg_res_dis_set(self, 0U);
148c8c82eb3SIgor Russkikh 	aq_hw_write_reg_bit(self, HW_ATL_MAC_PHY_CONTROL,
149c8c82eb3SIgor Russkikh 			    BIT(HW_ATL_MAC_PHY_MPI_RESET_BIT),
150c8c82eb3SIgor Russkikh 			    HW_ATL_MAC_PHY_MPI_RESET_BIT, 0x0);
151c8c82eb3SIgor Russkikh 	gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR);
152c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, (gsr & 0xBFFF) | 0x8000);
153c8c82eb3SIgor Russkikh 
154c8c82eb3SIgor Russkikh 	for (k = 0; k < 1000; k++) {
155c8c82eb3SIgor Russkikh 		u32 fw_state = aq_hw_read_reg(self, HW_ATL_MPI_FW_VERSION);
156c8c82eb3SIgor Russkikh 
157c8c82eb3SIgor Russkikh 		if (fw_state)
158c8c82eb3SIgor Russkikh 			break;
159c8c82eb3SIgor Russkikh 		AQ_HW_SLEEP(10);
160c8c82eb3SIgor Russkikh 	}
161c8c82eb3SIgor Russkikh 	if (k == 1000) {
162c8c82eb3SIgor Russkikh 		aq_pr_err("FW kickstart failed\n");
163c8c82eb3SIgor Russkikh 		return -EIO;
164c8c82eb3SIgor Russkikh 	}
165d0f0fb25SIgor Russkikh 	/* Old FW requires fixed delay after init */
166d0f0fb25SIgor Russkikh 	AQ_HW_SLEEP(15);
167c8c82eb3SIgor Russkikh 
168c8c82eb3SIgor Russkikh 	return 0;
169c8c82eb3SIgor Russkikh }
170c8c82eb3SIgor Russkikh 
hw_atl_utils_soft_reset_rbl(struct aq_hw_s * self)171c8c82eb3SIgor Russkikh static int hw_atl_utils_soft_reset_rbl(struct aq_hw_s *self)
172c8c82eb3SIgor Russkikh {
1731bf9a752SIgor Russkikh 	u32 gsr, val, rbl_status;
174c8c82eb3SIgor Russkikh 	int k;
175c8c82eb3SIgor Russkikh 
176c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x40e1);
177c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x3a0, 0x1);
178c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x32a8, 0x0);
179c8c82eb3SIgor Russkikh 
180c8c82eb3SIgor Russkikh 	/* Alter RBL status */
181c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x388, 0xDEAD);
182c8c82eb3SIgor Russkikh 
1831bf9a752SIgor Russkikh 	/* Cleanup SPI */
1841bf9a752SIgor Russkikh 	val = aq_hw_read_reg(self, 0x53C);
1851bf9a752SIgor Russkikh 	aq_hw_write_reg(self, 0x53C, val | 0x10);
1861bf9a752SIgor Russkikh 
187c8c82eb3SIgor Russkikh 	/* Global software reset*/
188c8c82eb3SIgor Russkikh 	hw_atl_rx_rx_reg_res_dis_set(self, 0U);
189c8c82eb3SIgor Russkikh 	hw_atl_tx_tx_reg_res_dis_set(self, 0U);
190c8c82eb3SIgor Russkikh 	aq_hw_write_reg_bit(self, HW_ATL_MAC_PHY_CONTROL,
191c8c82eb3SIgor Russkikh 			    BIT(HW_ATL_MAC_PHY_MPI_RESET_BIT),
192c8c82eb3SIgor Russkikh 			    HW_ATL_MAC_PHY_MPI_RESET_BIT, 0x0);
193c8c82eb3SIgor Russkikh 	gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR);
194c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR,
195c8c82eb3SIgor Russkikh 			(gsr & 0xFFFFBFFF) | 0x8000);
196c8c82eb3SIgor Russkikh 
197c8c82eb3SIgor Russkikh 	if (FORCE_FLASHLESS)
198c8c82eb3SIgor Russkikh 		aq_hw_write_reg(self, 0x534, 0x0);
199c8c82eb3SIgor Russkikh 
200c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x40e0);
201c8c82eb3SIgor Russkikh 
202c8c82eb3SIgor Russkikh 	/* Wait for RBL boot */
203c8c82eb3SIgor Russkikh 	for (k = 0; k < 1000; k++) {
204c8c82eb3SIgor Russkikh 		rbl_status = aq_hw_read_reg(self, 0x388) & 0xFFFF;
205c8c82eb3SIgor Russkikh 		if (rbl_status && rbl_status != 0xDEAD)
206c8c82eb3SIgor Russkikh 			break;
207c8c82eb3SIgor Russkikh 		AQ_HW_SLEEP(10);
208c8c82eb3SIgor Russkikh 	}
209c8c82eb3SIgor Russkikh 	if (!rbl_status || rbl_status == 0xDEAD) {
210c8c82eb3SIgor Russkikh 		aq_pr_err("RBL Restart failed");
211c8c82eb3SIgor Russkikh 		return -EIO;
212c8c82eb3SIgor Russkikh 	}
213c8c82eb3SIgor Russkikh 
214c8c82eb3SIgor Russkikh 	/* Restore NVR */
215c8c82eb3SIgor Russkikh 	if (FORCE_FLASHLESS)
216c8c82eb3SIgor Russkikh 		aq_hw_write_reg(self, 0x534, 0xA0);
217c8c82eb3SIgor Russkikh 
218c8c82eb3SIgor Russkikh 	if (rbl_status == 0xF1A7) {
219c8c82eb3SIgor Russkikh 		aq_pr_err("No FW detected. Dynamic FW load not implemented\n");
220e35df218SMark Starovoytov 		return -EOPNOTSUPP;
221c8c82eb3SIgor Russkikh 	}
222c8c82eb3SIgor Russkikh 
223c8c82eb3SIgor Russkikh 	for (k = 0; k < 1000; k++) {
224c8c82eb3SIgor Russkikh 		u32 fw_state = aq_hw_read_reg(self, HW_ATL_MPI_FW_VERSION);
225c8c82eb3SIgor Russkikh 
226c8c82eb3SIgor Russkikh 		if (fw_state)
227c8c82eb3SIgor Russkikh 			break;
228c8c82eb3SIgor Russkikh 		AQ_HW_SLEEP(10);
229c8c82eb3SIgor Russkikh 	}
230c8c82eb3SIgor Russkikh 	if (k == 1000) {
231c8c82eb3SIgor Russkikh 		aq_pr_err("FW kickstart failed\n");
232c8c82eb3SIgor Russkikh 		return -EIO;
233c8c82eb3SIgor Russkikh 	}
234d0f0fb25SIgor Russkikh 	/* Old FW requires fixed delay after init */
235d0f0fb25SIgor Russkikh 	AQ_HW_SLEEP(15);
236c8c82eb3SIgor Russkikh 
237c8c82eb3SIgor Russkikh 	return 0;
238c8c82eb3SIgor Russkikh }
239c8c82eb3SIgor Russkikh 
hw_atl_utils_soft_reset(struct aq_hw_s * self)240c8c82eb3SIgor Russkikh int hw_atl_utils_soft_reset(struct aq_hw_s *self)
241c8c82eb3SIgor Russkikh {
2420044b1e1SDmitry Bogdanov 	int ver = hw_atl_utils_get_fw_version(self);
243c8c82eb3SIgor Russkikh 	u32 boot_exit_code = 0;
2446a7f2277SNikita Danilov 	u32 val;
2457b0c342fSNikita Danilov 	int k;
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 
2630044b1e1SDmitry Bogdanov 	if (hw_atl_utils_ver_match(HW_ATL_FW_VER_1X, ver)) {
2640044b1e1SDmitry Bogdanov 		int err = 0;
2650044b1e1SDmitry Bogdanov 
266cce96d18SIgor Russkikh 		/* FW 1.x may bootup in an invalid POWER state (WOL feature).
267cce96d18SIgor Russkikh 		 * We should work around this by forcing its state back to DEINIT
268cce96d18SIgor Russkikh 		 */
269cce96d18SIgor Russkikh 		hw_atl_utils_mpi_set_state(self, MPI_DEINIT);
2706a7f2277SNikita Danilov 		err = readx_poll_timeout_atomic(hw_atl_utils_mpi_get_state,
2716a7f2277SNikita Danilov 						self, val,
2726a7f2277SNikita Danilov 						(val & HW_ATL_MPI_STATE_MSK) ==
2736a7f2277SNikita Danilov 						 MPI_DEINIT,
2746a7f2277SNikita Danilov 						10, 10000U);
2754e3c7c00SYueHaibing 		if (err)
2764e3c7c00SYueHaibing 			return err;
2770044b1e1SDmitry Bogdanov 	} else if (hw_atl_utils_ver_match(HW_ATL_FW_VER_4X, ver)) {
2780044b1e1SDmitry Bogdanov 		u64 sem_timeout = aq_hw_read_reg(self, HW_ATL_MIF_RESET_TIMEOUT_ADR);
2790044b1e1SDmitry Bogdanov 
2800044b1e1SDmitry Bogdanov 		/* Acquire 2 semaphores before issuing reset for FW 4.x */
2810044b1e1SDmitry Bogdanov 		if (sem_timeout > 3000)
2820044b1e1SDmitry Bogdanov 			sem_timeout = 3000;
2830044b1e1SDmitry Bogdanov 		sem_timeout = sem_timeout * 1000;
2840044b1e1SDmitry Bogdanov 
2850044b1e1SDmitry Bogdanov 		if (sem_timeout != 0) {
2860044b1e1SDmitry Bogdanov 			int err;
2870044b1e1SDmitry Bogdanov 
2880044b1e1SDmitry Bogdanov 			err = readx_poll_timeout_atomic(hw_atl_sem_reset1_get, self, val,
2890044b1e1SDmitry Bogdanov 							val == 1U, 1U, sem_timeout);
2900044b1e1SDmitry Bogdanov 			if (err)
2910044b1e1SDmitry Bogdanov 				aq_pr_err("reset sema1 timeout");
2920044b1e1SDmitry Bogdanov 
2930044b1e1SDmitry Bogdanov 			err = readx_poll_timeout_atomic(hw_atl_sem_reset2_get, self, val,
2940044b1e1SDmitry Bogdanov 							val == 1U, 1U, sem_timeout);
2950044b1e1SDmitry Bogdanov 			if (err)
2960044b1e1SDmitry Bogdanov 				aq_pr_err("reset sema2 timeout");
2970044b1e1SDmitry Bogdanov 		}
298cce96d18SIgor Russkikh 	}
299cce96d18SIgor Russkikh 
300c8c82eb3SIgor Russkikh 	if (self->rbl_enabled)
301c8c82eb3SIgor Russkikh 		return hw_atl_utils_soft_reset_rbl(self);
302c8c82eb3SIgor Russkikh 	else
303c8c82eb3SIgor Russkikh 		return hw_atl_utils_soft_reset_flb(self);
304c8c82eb3SIgor Russkikh }
305c8c82eb3SIgor Russkikh 
hw_atl_utils_fw_downld_dwords(struct aq_hw_s * self,u32 a,u32 * p,u32 cnt)306a57d3929SIgor Russkikh int hw_atl_utils_fw_downld_dwords(struct aq_hw_s *self, u32 a,
30798c4c201SDavid VomLehn 				  u32 *p, u32 cnt)
30898c4c201SDavid VomLehn {
30998c4c201SDavid VomLehn 	int err = 0;
3106a7f2277SNikita Danilov 	u32 val;
31198c4c201SDavid VomLehn 
3126a7f2277SNikita Danilov 	err = readx_poll_timeout_atomic(hw_atl_sem_ram_get,
3136a7f2277SNikita Danilov 					self, val, val == 1U,
31498c4c201SDavid VomLehn 					1U, 10000U);
31598c4c201SDavid VomLehn 
31698c4c201SDavid VomLehn 	if (err < 0) {
31798c4c201SDavid VomLehn 		bool is_locked;
31898c4c201SDavid VomLehn 
3198e1c072fSIgor Russkikh 		hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM);
3200b926d46SNikita Danilov 		is_locked = hw_atl_sem_ram_get(self);
32198c4c201SDavid VomLehn 		if (!is_locked) {
32298c4c201SDavid VomLehn 			err = -ETIME;
32398c4c201SDavid VomLehn 			goto err_exit;
32498c4c201SDavid VomLehn 		}
32598c4c201SDavid VomLehn 	}
32698c4c201SDavid VomLehn 
32747203b34SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_MIF_ADDR, a);
32898c4c201SDavid VomLehn 
32947203b34SIgor Russkikh 	for (++cnt; --cnt && !err;) {
33047203b34SIgor Russkikh 		aq_hw_write_reg(self, HW_ATL_MIF_CMD, 0x00008000U);
33198c4c201SDavid VomLehn 
332d1ad88feSMark Starovoytov 		if (ATL_HW_IS_CHIP_FEATURE(self, REVISION_B1))
3336a7f2277SNikita Danilov 			err = readx_poll_timeout_atomic(hw_atl_utils_mif_addr_get,
3346a7f2277SNikita Danilov 							self, val, val != a,
3356a7f2277SNikita Danilov 							1U, 1000U);
33647203b34SIgor Russkikh 		else
3376a7f2277SNikita Danilov 			err = readx_poll_timeout_atomic(hw_atl_utils_mif_cmd_get,
3386a7f2277SNikita Danilov 							self, val,
3396a7f2277SNikita Danilov 							!(val & 0x100),
3406a7f2277SNikita Danilov 							1U, 1000U);
34198c4c201SDavid VomLehn 
34247203b34SIgor Russkikh 		*(p++) = aq_hw_read_reg(self, HW_ATL_MIF_VAL);
34347203b34SIgor Russkikh 		a += 4;
34498c4c201SDavid VomLehn 	}
34598c4c201SDavid VomLehn 
3468e1c072fSIgor Russkikh 	hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM);
34798c4c201SDavid VomLehn 
34898c4c201SDavid VomLehn err_exit:
34998c4c201SDavid VomLehn 	return err;
35098c4c201SDavid VomLehn }
35198c4c201SDavid VomLehn 
hw_atl_utils_write_b1_mbox(struct aq_hw_s * self,u32 addr,u32 * p,u32 cnt,enum mcp_area area)352dc12f75aSNikita Danilov static int hw_atl_utils_write_b1_mbox(struct aq_hw_s *self, u32 addr,
353dc12f75aSNikita Danilov 				      u32 *p, u32 cnt, enum mcp_area area)
35498c4c201SDavid VomLehn {
355dc12f75aSNikita Danilov 	u32 data_offset = 0;
356dc12f75aSNikita Danilov 	u32 offset = addr;
35798c4c201SDavid VomLehn 	int err = 0;
358dc12f75aSNikita Danilov 	u32 val;
35998c4c201SDavid VomLehn 
360dc12f75aSNikita Danilov 	switch (area) {
361dc12f75aSNikita Danilov 	case MCP_AREA_CONFIG:
362dc12f75aSNikita Danilov 		offset -= self->rpc_addr;
363dc12f75aSNikita Danilov 		break;
364930b9a05SNikita Danilov 
365dc12f75aSNikita Danilov 	case MCP_AREA_SETTINGS:
366dc12f75aSNikita Danilov 		offset -= self->settings_addr;
367dc12f75aSNikita Danilov 		break;
368dc12f75aSNikita Danilov 	}
36998c4c201SDavid VomLehn 
370dc12f75aSNikita Danilov 	offset = offset / sizeof(u32);
371dc12f75aSNikita Danilov 
372dc12f75aSNikita Danilov 	for (; data_offset < cnt; ++data_offset, ++offset) {
373dc12f75aSNikita Danilov 		aq_hw_write_reg(self, 0x328, p[data_offset]);
3743ee5c887SYana Esina 		aq_hw_write_reg(self, 0x32C,
375dc12f75aSNikita Danilov 				(area | (0xFFFF & (offset * 4))));
3763ee5c887SYana Esina 		hw_atl_mcp_up_force_intr_set(self, 1);
3773ee5c887SYana Esina 		/* 1000 times by 10us = 10ms */
3786a7f2277SNikita Danilov 		err = readx_poll_timeout_atomic(hw_atl_scrpad12_get,
3796a7f2277SNikita Danilov 						self, val,
380930b9a05SNikita Danilov 						(val & 0xF0000000) !=
381dc12f75aSNikita Danilov 						area,
3826a7f2277SNikita Danilov 						10U, 10000U);
38398c4c201SDavid VomLehn 
384dc12f75aSNikita Danilov 		if (err < 0)
385dc12f75aSNikita Danilov 			break;
386dc12f75aSNikita Danilov 	}
387dc12f75aSNikita Danilov 
388dc12f75aSNikita Danilov 	return err;
389dc12f75aSNikita Danilov }
390dc12f75aSNikita Danilov 
hw_atl_utils_write_b0_mbox(struct aq_hw_s * self,u32 addr,u32 * p,u32 cnt)391dc12f75aSNikita Danilov static int hw_atl_utils_write_b0_mbox(struct aq_hw_s *self, u32 addr,
392dc12f75aSNikita Danilov 				      u32 *p, u32 cnt)
393dc12f75aSNikita Danilov {
394dc12f75aSNikita Danilov 	u32 offset = 0;
395dc12f75aSNikita Danilov 	int err = 0;
396dc12f75aSNikita Danilov 	u32 val;
397dc12f75aSNikita Danilov 
398dc12f75aSNikita Danilov 	aq_hw_write_reg(self, 0x208, addr);
39998c4c201SDavid VomLehn 
4003ee5c887SYana Esina 	for (; offset < cnt; ++offset) {
4013ee5c887SYana Esina 		aq_hw_write_reg(self, 0x20C, p[offset]);
4023ee5c887SYana Esina 		aq_hw_write_reg(self, 0x200, 0xC000);
40398c4c201SDavid VomLehn 
4046a7f2277SNikita Danilov 		err = readx_poll_timeout_atomic(hw_atl_utils_mif_cmd_get,
4056a7f2277SNikita Danilov 						self, val,
406dc12f75aSNikita Danilov 						(val & 0x100) == 0U,
407dc12f75aSNikita Danilov 						10U, 10000U);
408dc12f75aSNikita Danilov 
409dc12f75aSNikita Danilov 		if (err < 0)
410dc12f75aSNikita Danilov 			break;
41198c4c201SDavid VomLehn 	}
412dc12f75aSNikita Danilov 
413dc12f75aSNikita Danilov 	return err;
41498c4c201SDavid VomLehn }
41598c4c201SDavid VomLehn 
hw_atl_utils_fw_upload_dwords(struct aq_hw_s * self,u32 addr,u32 * p,u32 cnt,enum mcp_area area)416dc12f75aSNikita Danilov static int hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 addr, u32 *p,
417dc12f75aSNikita Danilov 					 u32 cnt, enum mcp_area area)
418dc12f75aSNikita Danilov {
419dc12f75aSNikita Danilov 	int err = 0;
420dc12f75aSNikita Danilov 	u32 val;
421dc12f75aSNikita Danilov 
422dc12f75aSNikita Danilov 	err = readx_poll_timeout_atomic(hw_atl_sem_ram_get, self,
423dc12f75aSNikita Danilov 					val, val == 1U,
424dc12f75aSNikita Danilov 					10U, 100000U);
425dc12f75aSNikita Danilov 	if (err < 0)
426dc12f75aSNikita Danilov 		goto err_exit;
427dc12f75aSNikita Danilov 
428d1ad88feSMark Starovoytov 	if (ATL_HW_IS_CHIP_FEATURE(self, REVISION_B1))
429dc12f75aSNikita Danilov 		err = hw_atl_utils_write_b1_mbox(self, addr, p, cnt, area);
430dc12f75aSNikita Danilov 	else
431dc12f75aSNikita Danilov 		err = hw_atl_utils_write_b0_mbox(self, addr, p, cnt);
432dc12f75aSNikita Danilov 
4338e1c072fSIgor Russkikh 	hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM);
43498c4c201SDavid VomLehn 
435dc12f75aSNikita Danilov 	if (err < 0)
436dc12f75aSNikita Danilov 		goto err_exit;
437dc12f75aSNikita Danilov 
438dc12f75aSNikita Danilov 	err = aq_hw_err_from_flags(self);
439dc12f75aSNikita Danilov 
44098c4c201SDavid VomLehn err_exit:
44198c4c201SDavid VomLehn 	return err;
44298c4c201SDavid VomLehn }
44398c4c201SDavid VomLehn 
hw_atl_write_fwcfg_dwords(struct aq_hw_s * self,u32 * p,u32 cnt)444dc12f75aSNikita Danilov int hw_atl_write_fwcfg_dwords(struct aq_hw_s *self, u32 *p, u32 cnt)
445dc12f75aSNikita Danilov {
446dc12f75aSNikita Danilov 	return hw_atl_utils_fw_upload_dwords(self, self->rpc_addr, p,
447dc12f75aSNikita Danilov 					     cnt, MCP_AREA_CONFIG);
448dc12f75aSNikita Danilov }
449dc12f75aSNikita Danilov 
hw_atl_write_fwsettings_dwords(struct aq_hw_s * self,u32 offset,u32 * p,u32 cnt)450dc12f75aSNikita Danilov int hw_atl_write_fwsettings_dwords(struct aq_hw_s *self, u32 offset, u32 *p,
451dc12f75aSNikita Danilov 				   u32 cnt)
452dc12f75aSNikita Danilov {
453dc12f75aSNikita Danilov 	return hw_atl_utils_fw_upload_dwords(self, self->settings_addr + offset,
454dc12f75aSNikita Danilov 					     p, cnt, MCP_AREA_SETTINGS);
455dc12f75aSNikita Danilov }
456dc12f75aSNikita Danilov 
hw_atl_utils_ver_match(u32 ver_expected,u32 ver_actual)457b567edbfSMark Starovoytov bool hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual)
45898c4c201SDavid VomLehn {
45998c4c201SDavid VomLehn 	const u32 dw_major_mask = 0xff000000U;
46098c4c201SDavid VomLehn 	const u32 dw_minor_mask = 0x00ffffffU;
461b567edbfSMark Starovoytov 	bool ver_match;
46298c4c201SDavid VomLehn 
463b567edbfSMark Starovoytov 	ver_match = (dw_major_mask & (ver_expected ^ ver_actual)) ? false : true;
464b567edbfSMark Starovoytov 	if (!ver_match)
46598c4c201SDavid VomLehn 		goto err_exit;
466b567edbfSMark Starovoytov 	ver_match = ((dw_minor_mask & ver_expected) > (dw_minor_mask & ver_actual)) ?
467b567edbfSMark Starovoytov 		false : true;
4687b0c342fSNikita Danilov 
46998c4c201SDavid VomLehn err_exit:
470b567edbfSMark Starovoytov 	return ver_match;
47198c4c201SDavid VomLehn }
47298c4c201SDavid VomLehn 
hw_atl_utils_init_ucp(struct aq_hw_s * self,const struct aq_hw_caps_s * aq_hw_caps)47398c4c201SDavid VomLehn static int hw_atl_utils_init_ucp(struct aq_hw_s *self,
4744cbc9f92SIgor Russkikh 				 const struct aq_hw_caps_s *aq_hw_caps)
47598c4c201SDavid VomLehn {
47698c4c201SDavid VomLehn 	int err = 0;
47798c4c201SDavid VomLehn 
47898c4c201SDavid VomLehn 	if (!aq_hw_read_reg(self, 0x370U)) {
47998c4c201SDavid VomLehn 		unsigned int rnd = 0U;
48098c4c201SDavid VomLehn 		unsigned int ucp_0x370 = 0U;
48198c4c201SDavid VomLehn 
48298c4c201SDavid VomLehn 		get_random_bytes(&rnd, sizeof(unsigned int));
48398c4c201SDavid VomLehn 
48498c4c201SDavid VomLehn 		ucp_0x370 = 0x02020202U | (0xFEFEFEFEU & rnd);
48598c4c201SDavid VomLehn 		aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370);
48698c4c201SDavid VomLehn 	}
48798c4c201SDavid VomLehn 
4888e1c072fSIgor Russkikh 	hw_atl_reg_glb_cpu_scratch_scp_set(self, 0x00000000U, 25U);
48998c4c201SDavid VomLehn 
49098c4c201SDavid VomLehn 	/* check 10 times by 1ms */
4916a7f2277SNikita Danilov 	err = readx_poll_timeout_atomic(hw_atl_scrpad25_get,
4926a7f2277SNikita Danilov 					self, self->mbox_addr,
4936a7f2277SNikita Danilov 					self->mbox_addr != 0U,
4946a7f2277SNikita Danilov 					1000U, 10000U);
495e7b5f97eSIgor Russkikh 	err = readx_poll_timeout_atomic(aq_fw1x_rpc_get, self,
496e7b5f97eSIgor Russkikh 					self->rpc_addr,
497e7b5f97eSIgor Russkikh 					self->rpc_addr != 0U,
498e7b5f97eSIgor Russkikh 					1000U, 100000U);
49998c4c201SDavid VomLehn 
50098c4c201SDavid VomLehn 	return err;
50198c4c201SDavid VomLehn }
50298c4c201SDavid VomLehn 
50398c4c201SDavid VomLehn struct aq_hw_atl_utils_fw_rpc_tid_s {
50498c4c201SDavid VomLehn 	union {
50598c4c201SDavid VomLehn 		u32 val;
50698c4c201SDavid VomLehn 		struct {
50798c4c201SDavid VomLehn 			u16 tid;
50898c4c201SDavid VomLehn 			u16 len;
50998c4c201SDavid VomLehn 		};
51098c4c201SDavid VomLehn 	};
51198c4c201SDavid VomLehn };
51298c4c201SDavid VomLehn 
51398c4c201SDavid VomLehn #define hw_atl_utils_fw_rpc_init(_H_) hw_atl_utils_fw_rpc_wait(_H_, NULL)
51498c4c201SDavid VomLehn 
hw_atl_utils_fw_rpc_call(struct aq_hw_s * self,unsigned int rpc_size)5153ee5c887SYana Esina int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size)
51698c4c201SDavid VomLehn {
51798c4c201SDavid VomLehn 	struct aq_hw_atl_utils_fw_rpc_tid_s sw;
5187b0c342fSNikita Danilov 	int err = 0;
51998c4c201SDavid VomLehn 
520d1ad88feSMark Starovoytov 	if (!ATL_HW_IS_CHIP_FEATURE(self, MIPS)) {
52198c4c201SDavid VomLehn 		err = -1;
52298c4c201SDavid VomLehn 		goto err_exit;
52398c4c201SDavid VomLehn 	}
524dc12f75aSNikita Danilov 	err = hw_atl_write_fwcfg_dwords(self, (u32 *)(void *)&self->rpc,
52598c4c201SDavid VomLehn 					(rpc_size + sizeof(u32) -
52698c4c201SDavid VomLehn 					 sizeof(u8)) / sizeof(u32));
52798c4c201SDavid VomLehn 	if (err < 0)
52898c4c201SDavid VomLehn 		goto err_exit;
52998c4c201SDavid VomLehn 
5301a713f87SIgor Russkikh 	sw.tid = 0xFFFFU & (++self->rpc_tid);
53198c4c201SDavid VomLehn 	sw.len = (u16)rpc_size;
53298c4c201SDavid VomLehn 	aq_hw_write_reg(self, HW_ATL_RPC_CONTROL_ADR, sw.val);
53398c4c201SDavid VomLehn 
53498c4c201SDavid VomLehn err_exit:
53598c4c201SDavid VomLehn 	return err;
53698c4c201SDavid VomLehn }
53798c4c201SDavid VomLehn 
hw_atl_utils_fw_rpc_wait(struct aq_hw_s * self,struct hw_atl_utils_fw_rpc ** rpc)5383ee5c887SYana Esina int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self,
5398f60f762SNikita Danilov 			     struct hw_atl_utils_fw_rpc **rpc)
54098c4c201SDavid VomLehn {
54198c4c201SDavid VomLehn 	struct aq_hw_atl_utils_fw_rpc_tid_s sw;
54298c4c201SDavid VomLehn 	struct aq_hw_atl_utils_fw_rpc_tid_s fw;
5437b0c342fSNikita Danilov 	int err = 0;
54498c4c201SDavid VomLehn 
54598c4c201SDavid VomLehn 	do {
54698c4c201SDavid VomLehn 		sw.val = aq_hw_read_reg(self, HW_ATL_RPC_CONTROL_ADR);
54798c4c201SDavid VomLehn 
5481a713f87SIgor Russkikh 		self->rpc_tid = sw.tid;
54998c4c201SDavid VomLehn 
5506a7f2277SNikita Danilov 		err = readx_poll_timeout_atomic(hw_atl_utils_rpc_state_get,
5516a7f2277SNikita Danilov 						self, fw.val,
5526a7f2277SNikita Danilov 						sw.tid == fw.tid,
5536a7f2277SNikita Danilov 						1000U, 100000U);
554e7b5f97eSIgor Russkikh 		if (err < 0)
555e7b5f97eSIgor Russkikh 			goto err_exit;
556e7b5f97eSIgor Russkikh 
557e7b5f97eSIgor Russkikh 		err = aq_hw_err_from_flags(self);
558e7b5f97eSIgor Russkikh 		if (err < 0)
559e7b5f97eSIgor Russkikh 			goto err_exit;
56098c4c201SDavid VomLehn 
56198c4c201SDavid VomLehn 		if (fw.len == 0xFFFFU) {
562b922f622SZekun Shen 			if (sw.len > sizeof(self->rpc)) {
563b922f622SZekun Shen 				printk(KERN_INFO "Invalid sw len: %x\n", sw.len);
564b922f622SZekun Shen 				err = -EINVAL;
565b922f622SZekun Shen 				goto err_exit;
566b922f622SZekun Shen 			}
56798c4c201SDavid VomLehn 			err = hw_atl_utils_fw_rpc_call(self, sw.len);
56898c4c201SDavid VomLehn 			if (err < 0)
56998c4c201SDavid VomLehn 				goto err_exit;
57098c4c201SDavid VomLehn 		}
57198c4c201SDavid VomLehn 	} while (sw.tid != fw.tid || 0xFFFFU == fw.len);
57298c4c201SDavid VomLehn 
57398c4c201SDavid VomLehn 	if (rpc) {
57498c4c201SDavid VomLehn 		if (fw.len) {
575b922f622SZekun Shen 			if (fw.len > sizeof(self->rpc)) {
576b922f622SZekun Shen 				printk(KERN_INFO "Invalid fw len: %x\n", fw.len);
577b922f622SZekun Shen 				err = -EINVAL;
578b922f622SZekun Shen 				goto err_exit;
579b922f622SZekun Shen 			}
58098c4c201SDavid VomLehn 			err =
58198c4c201SDavid VomLehn 			hw_atl_utils_fw_downld_dwords(self,
5821a713f87SIgor Russkikh 						      self->rpc_addr,
58398c4c201SDavid VomLehn 						      (u32 *)(void *)
5841a713f87SIgor Russkikh 						      &self->rpc,
58598c4c201SDavid VomLehn 						      (fw.len + sizeof(u32) -
58698c4c201SDavid VomLehn 						       sizeof(u8)) /
58798c4c201SDavid VomLehn 						      sizeof(u32));
58898c4c201SDavid VomLehn 			if (err < 0)
58998c4c201SDavid VomLehn 				goto err_exit;
59098c4c201SDavid VomLehn 		}
59198c4c201SDavid VomLehn 
5921a713f87SIgor Russkikh 		*rpc = &self->rpc;
59398c4c201SDavid VomLehn 	}
59498c4c201SDavid VomLehn 
59598c4c201SDavid VomLehn err_exit:
59698c4c201SDavid VomLehn 	return err;
59798c4c201SDavid VomLehn }
59898c4c201SDavid VomLehn 
hw_atl_utils_mpi_create(struct aq_hw_s * self)5991a713f87SIgor Russkikh static int hw_atl_utils_mpi_create(struct aq_hw_s *self)
60098c4c201SDavid VomLehn {
60198c4c201SDavid VomLehn 	int err = 0;
60298c4c201SDavid VomLehn 
6031a713f87SIgor Russkikh 	err = hw_atl_utils_init_ucp(self, self->aq_nic_cfg->aq_hw_caps);
60498c4c201SDavid VomLehn 	if (err < 0)
60598c4c201SDavid VomLehn 		goto err_exit;
60698c4c201SDavid VomLehn 
60798c4c201SDavid VomLehn 	err = hw_atl_utils_fw_rpc_init(self);
60898c4c201SDavid VomLehn 	if (err < 0)
60998c4c201SDavid VomLehn 		goto err_exit;
61098c4c201SDavid VomLehn 
61198c4c201SDavid VomLehn err_exit:
61298c4c201SDavid VomLehn 	return err;
61398c4c201SDavid VomLehn }
61498c4c201SDavid VomLehn 
hw_atl_utils_mpi_read_mbox(struct aq_hw_s * self,struct hw_atl_utils_mbox_header * pmbox)61565e665e6SIgor Russkikh int hw_atl_utils_mpi_read_mbox(struct aq_hw_s *self,
6168f60f762SNikita Danilov 			       struct hw_atl_utils_mbox_header *pmbox)
61765e665e6SIgor Russkikh {
61865e665e6SIgor Russkikh 	return hw_atl_utils_fw_downld_dwords(self,
6191a713f87SIgor Russkikh 					     self->mbox_addr,
62065e665e6SIgor Russkikh 					     (u32 *)(void *)pmbox,
62165e665e6SIgor Russkikh 					     sizeof(*pmbox) / sizeof(u32));
62265e665e6SIgor Russkikh }
62365e665e6SIgor Russkikh 
hw_atl_utils_mpi_read_stats(struct aq_hw_s * self,struct hw_atl_utils_mbox * pmbox)62498c4c201SDavid VomLehn void hw_atl_utils_mpi_read_stats(struct aq_hw_s *self,
6258f60f762SNikita Danilov 				 struct hw_atl_utils_mbox *pmbox)
62698c4c201SDavid VomLehn {
62798c4c201SDavid VomLehn 	int err = 0;
62898c4c201SDavid VomLehn 
62998c4c201SDavid VomLehn 	err = hw_atl_utils_fw_downld_dwords(self,
6301a713f87SIgor Russkikh 					    self->mbox_addr,
63198c4c201SDavid VomLehn 					    (u32 *)(void *)pmbox,
63298c4c201SDavid VomLehn 					    sizeof(*pmbox) / sizeof(u32));
63398c4c201SDavid VomLehn 	if (err < 0)
63498c4c201SDavid VomLehn 		goto err_exit;
63598c4c201SDavid VomLehn 
636d1ad88feSMark Starovoytov 	if (ATL_HW_IS_CHIP_FEATURE(self, REVISION_A0)) {
63798c4c201SDavid VomLehn 		unsigned int mtu = self->aq_nic_cfg ?
63898c4c201SDavid VomLehn 					self->aq_nic_cfg->mtu : 1514U;
63998c4c201SDavid VomLehn 		pmbox->stats.ubrc = pmbox->stats.uprc * mtu;
64098c4c201SDavid VomLehn 		pmbox->stats.ubtc = pmbox->stats.uptc * mtu;
6411a713f87SIgor Russkikh 		pmbox->stats.dpc = atomic_read(&self->dpc);
64298c4c201SDavid VomLehn 	} else {
643ce4cdbe4SDmitry Bogdanov 		pmbox->stats.dpc = hw_atl_rpb_rx_dma_drop_pkt_cnt_get(self);
64498c4c201SDavid VomLehn 	}
64598c4c201SDavid VomLehn 
64698c4c201SDavid VomLehn err_exit:;
64798c4c201SDavid VomLehn }
64898c4c201SDavid VomLehn 
hw_atl_utils_mpi_set_speed(struct aq_hw_s * self,u32 speed)649dfbd0749SWei Yongjun static int hw_atl_utils_mpi_set_speed(struct aq_hw_s *self, u32 speed)
65098c4c201SDavid VomLehn {
6510c58c35fSIgor Russkikh 	u32 val = aq_hw_read_reg(self, HW_ATL_MPI_CONTROL_ADR);
65298c4c201SDavid VomLehn 
65344e00dd8SIgor Russkikh 	val = val & ~HW_ATL_MPI_SPEED_MSK;
65444e00dd8SIgor Russkikh 	val |= speed << HW_ATL_MPI_SPEED_SHIFT;
6550c58c35fSIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, val);
65698c4c201SDavid VomLehn 
65798c4c201SDavid VomLehn 	return 0;
65898c4c201SDavid VomLehn }
65998c4c201SDavid VomLehn 
hw_atl_utils_mpi_set_state(struct aq_hw_s * self,enum hal_atl_utils_fw_state_e state)660dfbd0749SWei Yongjun static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self,
66144e00dd8SIgor Russkikh 				      enum hal_atl_utils_fw_state_e state)
66298c4c201SDavid VomLehn {
66344e00dd8SIgor Russkikh 	u32 val = aq_hw_read_reg(self, HW_ATL_MPI_CONTROL_ADR);
6647b0c342fSNikita Danilov 	struct hw_atl_utils_mbox_header mbox;
6657b0c342fSNikita Danilov 	u32 transaction_id = 0;
6667b0c342fSNikita Danilov 	int err = 0;
66798c4c201SDavid VomLehn 
66898c4c201SDavid VomLehn 	if (state == MPI_RESET) {
66965e665e6SIgor Russkikh 		hw_atl_utils_mpi_read_mbox(self, &mbox);
67098c4c201SDavid VomLehn 
67165e665e6SIgor Russkikh 		transaction_id = mbox.transaction_id;
67298c4c201SDavid VomLehn 
6736a7f2277SNikita Danilov 		err = readx_poll_timeout_atomic(hw_atl_utils_get_mpi_mbox_tid,
6746a7f2277SNikita Danilov 						self, mbox.transaction_id,
6756a7f2277SNikita Danilov 						transaction_id !=
6766a7f2277SNikita Danilov 						mbox.transaction_id,
6776a7f2277SNikita Danilov 						1000U, 100000U);
67898c4c201SDavid VomLehn 		if (err < 0)
67998c4c201SDavid VomLehn 			goto err_exit;
68098c4c201SDavid VomLehn 	}
68144e00dd8SIgor Russkikh 	/* On interface DEINIT we disable DW (raise bit)
68244e00dd8SIgor Russkikh 	 * Otherwise enable DW (clear bit)
68344e00dd8SIgor Russkikh 	 */
68444e00dd8SIgor Russkikh 	if (state == MPI_DEINIT || state == MPI_POWER)
68544e00dd8SIgor Russkikh 		val |= HW_ATL_MPI_DIRTY_WAKE_MSK;
68644e00dd8SIgor Russkikh 	else
68744e00dd8SIgor Russkikh 		val &= ~HW_ATL_MPI_DIRTY_WAKE_MSK;
68898c4c201SDavid VomLehn 
68944e00dd8SIgor Russkikh 	/* Set new state bits */
69044e00dd8SIgor Russkikh 	val = val & ~HW_ATL_MPI_STATE_MSK;
69144e00dd8SIgor Russkikh 	val |= state & HW_ATL_MPI_STATE_MSK;
69298c4c201SDavid VomLehn 
6930c58c35fSIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, val);
6947b0c342fSNikita Danilov 
69544e00dd8SIgor Russkikh err_exit:
69644e00dd8SIgor Russkikh 	return err;
6970c58c35fSIgor Russkikh }
6980c58c35fSIgor Russkikh 
hw_atl_utils_mpi_get_link_status(struct aq_hw_s * self)699bd8ed441SPavel Belous int hw_atl_utils_mpi_get_link_status(struct aq_hw_s *self)
70098c4c201SDavid VomLehn {
701bd8ed441SPavel Belous 	struct aq_hw_link_status_s *link_status = &self->aq_link_status;
7027b0c342fSNikita Danilov 	u32 mpi_state;
7037b0c342fSNikita Danilov 	u32 speed;
70498c4c201SDavid VomLehn 
7057b0c342fSNikita Danilov 	mpi_state = hw_atl_utils_mpi_get_state(self);
706ac70957eSIgor Russkikh 	speed = mpi_state >> HW_ATL_MPI_SPEED_SHIFT;
7077b0c342fSNikita Danilov 
7087b0c342fSNikita Danilov 	if (!speed) {
70998c4c201SDavid VomLehn 		link_status->mbps = 0U;
71098c4c201SDavid VomLehn 	} else {
7117b0c342fSNikita Danilov 		switch (speed) {
71298c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_10G:
71398c4c201SDavid VomLehn 			link_status->mbps = 10000U;
71498c4c201SDavid VomLehn 			break;
71598c4c201SDavid VomLehn 
71698c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_5G:
71798c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_5GSR:
71898c4c201SDavid VomLehn 			link_status->mbps = 5000U;
71998c4c201SDavid VomLehn 			break;
72098c4c201SDavid VomLehn 
721843e1396SMark Starovoytov 		case HAL_ATLANTIC_RATE_2G5:
72298c4c201SDavid VomLehn 			link_status->mbps = 2500U;
72398c4c201SDavid VomLehn 			break;
72498c4c201SDavid VomLehn 
72598c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_1G:
72698c4c201SDavid VomLehn 			link_status->mbps = 1000U;
72798c4c201SDavid VomLehn 			break;
72898c4c201SDavid VomLehn 
72998c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_100M:
73098c4c201SDavid VomLehn 			link_status->mbps = 100U;
73198c4c201SDavid VomLehn 			break;
73298c4c201SDavid VomLehn 
73398c4c201SDavid VomLehn 		default:
734a7bb1beaSIgor Russkikh 			return -EBUSY;
73598c4c201SDavid VomLehn 		}
73698c4c201SDavid VomLehn 	}
737071a0204SIgor Russkikh 	link_status->full_duplex = true;
73898c4c201SDavid VomLehn 
73998c4c201SDavid VomLehn 	return 0;
74098c4c201SDavid VomLehn }
74198c4c201SDavid VomLehn 
hw_atl_utils_get_mac_permanent(struct aq_hw_s * self,u8 * mac)74298c4c201SDavid VomLehn int hw_atl_utils_get_mac_permanent(struct aq_hw_s *self,
74398c4c201SDavid VomLehn 				   u8 *mac)
74498c4c201SDavid VomLehn {
7457b0c342fSNikita Danilov 	u32 mac_addr[2];
7467b0c342fSNikita Danilov 	u32 efuse_addr;
74798c4c201SDavid VomLehn 	int err = 0;
74898c4c201SDavid VomLehn 	u32 h = 0U;
74998c4c201SDavid VomLehn 	u32 l = 0U;
75098c4c201SDavid VomLehn 
75198c4c201SDavid VomLehn 	if (!aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) {
75298c4c201SDavid VomLehn 		unsigned int ucp_0x370 = 0;
7537b0c342fSNikita Danilov 		unsigned int rnd = 0;
75498c4c201SDavid VomLehn 
75598c4c201SDavid VomLehn 		get_random_bytes(&rnd, sizeof(unsigned int));
75698c4c201SDavid VomLehn 
75798c4c201SDavid VomLehn 		ucp_0x370 = 0x02020202 | (0xFEFEFEFE & rnd);
75898c4c201SDavid VomLehn 		aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370);
75998c4c201SDavid VomLehn 	}
76098c4c201SDavid VomLehn 
7617b0c342fSNikita Danilov 	efuse_addr = aq_hw_read_reg(self, 0x00000374U);
7627b0c342fSNikita Danilov 
7637b0c342fSNikita Danilov 	err = hw_atl_utils_fw_downld_dwords(self, efuse_addr + (40U * 4U),
7647b0c342fSNikita Danilov 					    mac_addr, ARRAY_SIZE(mac_addr));
76598c4c201SDavid VomLehn 	if (err < 0) {
76698c4c201SDavid VomLehn 		mac_addr[0] = 0U;
76798c4c201SDavid VomLehn 		mac_addr[1] = 0U;
76898c4c201SDavid VomLehn 		err = 0;
76998c4c201SDavid VomLehn 	} else {
77098c4c201SDavid VomLehn 		mac_addr[0] = __swab32(mac_addr[0]);
77198c4c201SDavid VomLehn 		mac_addr[1] = __swab32(mac_addr[1]);
77298c4c201SDavid VomLehn 	}
77398c4c201SDavid VomLehn 
77498c4c201SDavid VomLehn 	ether_addr_copy(mac, (u8 *)mac_addr);
77598c4c201SDavid VomLehn 
77698c4c201SDavid VomLehn 	if ((mac[0] & 0x01U) || ((mac[0] | mac[1] | mac[2]) == 0x00U)) {
77798c4c201SDavid VomLehn 		/* chip revision */
778e9157848SNikita Danilov 		l = 0xE3000000U |
779e9157848SNikita Danilov 		    (0xFFFFU & aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) |
780e9157848SNikita Danilov 		    (0x00 << 16);
78198c4c201SDavid VomLehn 		h = 0x8001300EU;
78298c4c201SDavid VomLehn 
78398c4c201SDavid VomLehn 		mac[5] = (u8)(0xFFU & l);
78498c4c201SDavid VomLehn 		l >>= 8;
78598c4c201SDavid VomLehn 		mac[4] = (u8)(0xFFU & l);
78698c4c201SDavid VomLehn 		l >>= 8;
78798c4c201SDavid VomLehn 		mac[3] = (u8)(0xFFU & l);
78898c4c201SDavid VomLehn 		l >>= 8;
78998c4c201SDavid VomLehn 		mac[2] = (u8)(0xFFU & l);
79098c4c201SDavid VomLehn 		mac[1] = (u8)(0xFFU & h);
79198c4c201SDavid VomLehn 		h >>= 8;
79298c4c201SDavid VomLehn 		mac[0] = (u8)(0xFFU & h);
79398c4c201SDavid VomLehn 	}
79498c4c201SDavid VomLehn 
79598c4c201SDavid VomLehn 	return err;
79698c4c201SDavid VomLehn }
79798c4c201SDavid VomLehn 
hw_atl_utils_mbps_2_speed_index(unsigned int mbps)79898c4c201SDavid VomLehn unsigned int hw_atl_utils_mbps_2_speed_index(unsigned int mbps)
79998c4c201SDavid VomLehn {
80098c4c201SDavid VomLehn 	unsigned int ret = 0U;
80198c4c201SDavid VomLehn 
80298c4c201SDavid VomLehn 	switch (mbps) {
80398c4c201SDavid VomLehn 	case 100U:
80498c4c201SDavid VomLehn 		ret = 5U;
80598c4c201SDavid VomLehn 		break;
80698c4c201SDavid VomLehn 
80798c4c201SDavid VomLehn 	case 1000U:
80898c4c201SDavid VomLehn 		ret = 4U;
80998c4c201SDavid VomLehn 		break;
81098c4c201SDavid VomLehn 
81198c4c201SDavid VomLehn 	case 2500U:
81298c4c201SDavid VomLehn 		ret = 3U;
81398c4c201SDavid VomLehn 		break;
81498c4c201SDavid VomLehn 
81598c4c201SDavid VomLehn 	case 5000U:
81698c4c201SDavid VomLehn 		ret = 1U;
81798c4c201SDavid VomLehn 		break;
81898c4c201SDavid VomLehn 
81998c4c201SDavid VomLehn 	case 10000U:
82098c4c201SDavid VomLehn 		ret = 0U;
82198c4c201SDavid VomLehn 		break;
82298c4c201SDavid VomLehn 
82398c4c201SDavid VomLehn 	default:
82498c4c201SDavid VomLehn 		break;
82598c4c201SDavid VomLehn 	}
8267b0c342fSNikita Danilov 
82798c4c201SDavid VomLehn 	return ret;
82898c4c201SDavid VomLehn }
82998c4c201SDavid VomLehn 
hw_atl_utils_hw_chip_features_init(struct aq_hw_s * self,u32 * p)83098c4c201SDavid VomLehn void hw_atl_utils_hw_chip_features_init(struct aq_hw_s *self, u32 *p)
83198c4c201SDavid VomLehn {
8328e1c072fSIgor Russkikh 	u32 val = hw_atl_reg_glb_mif_id_get(self);
83398c4c201SDavid VomLehn 	u32 mif_rev = val & 0xFFU;
8347b0c342fSNikita Danilov 	u32 chip_features = 0U;
83598c4c201SDavid VomLehn 
836d1ad88feSMark Starovoytov 	chip_features |= ATL_HW_CHIP_ATLANTIC;
837d1ad88feSMark Starovoytov 
83847203b34SIgor Russkikh 	if ((0xFU & mif_rev) == 1U) {
839d1ad88feSMark Starovoytov 		chip_features |= ATL_HW_CHIP_REVISION_A0 |
840d1ad88feSMark Starovoytov 			ATL_HW_CHIP_MPI_AQ |
841d1ad88feSMark Starovoytov 			ATL_HW_CHIP_MIPS;
84247203b34SIgor Russkikh 	} else if ((0xFU & mif_rev) == 2U) {
843d1ad88feSMark Starovoytov 		chip_features |= ATL_HW_CHIP_REVISION_B0 |
844d1ad88feSMark Starovoytov 			ATL_HW_CHIP_MPI_AQ |
845d1ad88feSMark Starovoytov 			ATL_HW_CHIP_MIPS |
846d1ad88feSMark Starovoytov 			ATL_HW_CHIP_TPO2 |
847d1ad88feSMark Starovoytov 			ATL_HW_CHIP_RPF2;
84847203b34SIgor Russkikh 	} else if ((0xFU & mif_rev) == 0xAU) {
849d1ad88feSMark Starovoytov 		chip_features |= ATL_HW_CHIP_REVISION_B1 |
850d1ad88feSMark Starovoytov 			ATL_HW_CHIP_MPI_AQ |
851d1ad88feSMark Starovoytov 			ATL_HW_CHIP_MIPS |
852d1ad88feSMark Starovoytov 			ATL_HW_CHIP_TPO2 |
853d1ad88feSMark Starovoytov 			ATL_HW_CHIP_RPF2;
85498c4c201SDavid VomLehn 	}
85598c4c201SDavid VomLehn 
85698c4c201SDavid VomLehn 	*p = chip_features;
85798c4c201SDavid VomLehn }
85898c4c201SDavid VomLehn 
hw_atl_fw1x_deinit(struct aq_hw_s * self)85944e00dd8SIgor Russkikh static int hw_atl_fw1x_deinit(struct aq_hw_s *self)
86098c4c201SDavid VomLehn {
86144e00dd8SIgor Russkikh 	hw_atl_utils_mpi_set_speed(self, 0);
86244e00dd8SIgor Russkikh 	hw_atl_utils_mpi_set_state(self, MPI_DEINIT);
8637b0c342fSNikita Danilov 
86498c4c201SDavid VomLehn 	return 0;
86598c4c201SDavid VomLehn }
86698c4c201SDavid VomLehn 
hw_atl_utils_update_stats(struct aq_hw_s * self)86765e665e6SIgor Russkikh int hw_atl_utils_update_stats(struct aq_hw_s *self)
86865e665e6SIgor Russkikh {
869ce4cdbe4SDmitry Bogdanov 	struct aq_stats_s *cs = &self->curr_stats;
870*2087ced0SDmitry Bogdanov 	struct aq_stats_s curr_stats = *cs;
8717b0c342fSNikita Danilov 	struct hw_atl_utils_mbox mbox;
872*2087ced0SDmitry Bogdanov 	bool corrupted_stats = false;
87365e665e6SIgor Russkikh 
87465e665e6SIgor Russkikh 	hw_atl_utils_mpi_read_stats(self, &mbox);
87565e665e6SIgor Russkikh 
876*2087ced0SDmitry Bogdanov #define AQ_SDELTA(_N_)  \
877*2087ced0SDmitry Bogdanov do { \
878*2087ced0SDmitry Bogdanov 	if (!corrupted_stats && \
879*2087ced0SDmitry Bogdanov 	    ((s64)(mbox.stats._N_ - self->last_stats._N_)) >= 0) \
880*2087ced0SDmitry Bogdanov 		curr_stats._N_ += mbox.stats._N_ - self->last_stats._N_; \
881*2087ced0SDmitry Bogdanov 	else \
882*2087ced0SDmitry Bogdanov 		corrupted_stats = true; \
883*2087ced0SDmitry Bogdanov } while (0)
8841a713f87SIgor Russkikh 
885be08d839SIgor Russkikh 	if (self->aq_link_status.mbps) {
88665e665e6SIgor Russkikh 		AQ_SDELTA(uprc);
88765e665e6SIgor Russkikh 		AQ_SDELTA(mprc);
88865e665e6SIgor Russkikh 		AQ_SDELTA(bprc);
88965e665e6SIgor Russkikh 		AQ_SDELTA(erpt);
89065e665e6SIgor Russkikh 
89165e665e6SIgor Russkikh 		AQ_SDELTA(uptc);
89265e665e6SIgor Russkikh 		AQ_SDELTA(mptc);
89365e665e6SIgor Russkikh 		AQ_SDELTA(bptc);
89465e665e6SIgor Russkikh 		AQ_SDELTA(erpr);
89565e665e6SIgor Russkikh 
89665e665e6SIgor Russkikh 		AQ_SDELTA(ubrc);
89765e665e6SIgor Russkikh 		AQ_SDELTA(ubtc);
89865e665e6SIgor Russkikh 		AQ_SDELTA(mbrc);
89965e665e6SIgor Russkikh 		AQ_SDELTA(mbtc);
90065e665e6SIgor Russkikh 		AQ_SDELTA(bbrc);
90165e665e6SIgor Russkikh 		AQ_SDELTA(bbtc);
90265e665e6SIgor Russkikh 		AQ_SDELTA(dpc);
903*2087ced0SDmitry Bogdanov 
904*2087ced0SDmitry Bogdanov 		if (!corrupted_stats)
905*2087ced0SDmitry Bogdanov 			*cs = curr_stats;
906be08d839SIgor Russkikh 	}
90765e665e6SIgor Russkikh #undef AQ_SDELTA
908ce4cdbe4SDmitry Bogdanov 
909ce4cdbe4SDmitry Bogdanov 	cs->dma_pkt_rc = hw_atl_stats_rx_dma_good_pkt_counter_get(self);
910ce4cdbe4SDmitry Bogdanov 	cs->dma_pkt_tc = hw_atl_stats_tx_dma_good_pkt_counter_get(self);
911ce4cdbe4SDmitry Bogdanov 	cs->dma_oct_rc = hw_atl_stats_rx_dma_good_octet_counter_get(self);
912ce4cdbe4SDmitry Bogdanov 	cs->dma_oct_tc = hw_atl_stats_tx_dma_good_octet_counter_get(self);
91365e665e6SIgor Russkikh 
9141a713f87SIgor Russkikh 	memcpy(&self->last_stats, &mbox.stats, sizeof(mbox.stats));
91565e665e6SIgor Russkikh 
91665e665e6SIgor Russkikh 	return 0;
91765e665e6SIgor Russkikh }
91865e665e6SIgor Russkikh 
hw_atl_utils_get_hw_stats(struct aq_hw_s * self)919be08d839SIgor Russkikh struct aq_stats_s *hw_atl_utils_get_hw_stats(struct aq_hw_s *self)
92098c4c201SDavid VomLehn {
9211a713f87SIgor Russkikh 	return &self->curr_stats;
92298c4c201SDavid VomLehn }
92398c4c201SDavid VomLehn 
92498c4c201SDavid VomLehn static const u32 hw_atl_utils_hw_mac_regs[] = {
92598c4c201SDavid VomLehn 	0x00005580U, 0x00005590U, 0x000055B0U, 0x000055B4U,
92698c4c201SDavid VomLehn 	0x000055C0U, 0x00005B00U, 0x00005B04U, 0x00005B08U,
92798c4c201SDavid VomLehn 	0x00005B0CU, 0x00005B10U, 0x00005B14U, 0x00005B18U,
92898c4c201SDavid VomLehn 	0x00005B1CU, 0x00005B20U, 0x00005B24U, 0x00005B28U,
92998c4c201SDavid VomLehn 	0x00005B2CU, 0x00005B30U, 0x00005B34U, 0x00005B38U,
93098c4c201SDavid VomLehn 	0x00005B3CU, 0x00005B40U, 0x00005B44U, 0x00005B48U,
93198c4c201SDavid VomLehn 	0x00005B4CU, 0x00005B50U, 0x00005B54U, 0x00005B58U,
93298c4c201SDavid VomLehn 	0x00005B5CU, 0x00005B60U, 0x00005B64U, 0x00005B68U,
93398c4c201SDavid VomLehn 	0x00005B6CU, 0x00005B70U, 0x00005B74U, 0x00005B78U,
93498c4c201SDavid VomLehn 	0x00005B7CU, 0x00007C00U, 0x00007C04U, 0x00007C08U,
93598c4c201SDavid VomLehn 	0x00007C0CU, 0x00007C10U, 0x00007C14U, 0x00007C18U,
93698c4c201SDavid VomLehn 	0x00007C1CU, 0x00007C20U, 0x00007C40U, 0x00007C44U,
93798c4c201SDavid VomLehn 	0x00007C48U, 0x00007C4CU, 0x00007C50U, 0x00007C54U,
93898c4c201SDavid VomLehn 	0x00007C58U, 0x00007C5CU, 0x00007C60U, 0x00007C80U,
93998c4c201SDavid VomLehn 	0x00007C84U, 0x00007C88U, 0x00007C8CU, 0x00007C90U,
94098c4c201SDavid VomLehn 	0x00007C94U, 0x00007C98U, 0x00007C9CU, 0x00007CA0U,
94198c4c201SDavid VomLehn 	0x00007CC0U, 0x00007CC4U, 0x00007CC8U, 0x00007CCCU,
94298c4c201SDavid VomLehn 	0x00007CD0U, 0x00007CD4U, 0x00007CD8U, 0x00007CDCU,
94398c4c201SDavid VomLehn 	0x00007CE0U, 0x00000300U, 0x00000304U, 0x00000308U,
94498c4c201SDavid VomLehn 	0x0000030cU, 0x00000310U, 0x00000314U, 0x00000318U,
94598c4c201SDavid VomLehn 	0x0000031cU, 0x00000360U, 0x00000364U, 0x00000368U,
94698c4c201SDavid VomLehn 	0x0000036cU, 0x00000370U, 0x00000374U, 0x00006900U,
94798c4c201SDavid VomLehn };
94898c4c201SDavid VomLehn 
hw_atl_utils_hw_get_regs(struct aq_hw_s * self,const struct aq_hw_caps_s * aq_hw_caps,u32 * regs_buff)94998c4c201SDavid VomLehn int hw_atl_utils_hw_get_regs(struct aq_hw_s *self,
9504cbc9f92SIgor Russkikh 			     const struct aq_hw_caps_s *aq_hw_caps,
95198c4c201SDavid VomLehn 			     u32 *regs_buff)
95298c4c201SDavid VomLehn {
95398c4c201SDavid VomLehn 	unsigned int i = 0U;
95498c4c201SDavid VomLehn 
95598c4c201SDavid VomLehn 	for (i = 0; i < aq_hw_caps->mac_regs_count; i++)
95698c4c201SDavid VomLehn 		regs_buff[i] = aq_hw_read_reg(self,
95798c4c201SDavid VomLehn 					      hw_atl_utils_hw_mac_regs[i]);
9587b0c342fSNikita Danilov 
95998c4c201SDavid VomLehn 	return 0;
96098c4c201SDavid VomLehn }
96198c4c201SDavid VomLehn 
hw_atl_utils_get_fw_version(struct aq_hw_s * self)96236e90a52SNikita Danilov u32 hw_atl_utils_get_fw_version(struct aq_hw_s *self)
96398c4c201SDavid VomLehn {
96436e90a52SNikita Danilov 	return aq_hw_read_reg(self, HW_ATL_MPI_FW_VERSION);
96598c4c201SDavid VomLehn }
9660c58c35fSIgor Russkikh 
aq_fw1x_set_wake_magic(struct aq_hw_s * self,bool wol_enabled,const u8 * mac)967837c6378SNikita Danilov static int aq_fw1x_set_wake_magic(struct aq_hw_s *self, bool wol_enabled,
96876660757SJakub Kicinski 				  const u8 *mac)
969a0da96c0SYana Esina {
9708f60f762SNikita Danilov 	struct hw_atl_utils_fw_rpc *prpc = NULL;
971a0da96c0SYana Esina 	unsigned int rpc_size = 0U;
972a0da96c0SYana Esina 	int err = 0;
973a0da96c0SYana Esina 
974a0da96c0SYana Esina 	err = hw_atl_utils_fw_rpc_wait(self, &prpc);
975a0da96c0SYana Esina 	if (err < 0)
976a0da96c0SYana Esina 		goto err_exit;
977a0da96c0SYana Esina 
978a0da96c0SYana Esina 	memset(prpc, 0, sizeof(*prpc));
979a0da96c0SYana Esina 
980a0da96c0SYana Esina 	if (wol_enabled) {
981d993e14bSNikita Danilov 		rpc_size = offsetof(struct hw_atl_utils_fw_rpc, msg_wol_add) +
982d993e14bSNikita Danilov 			   sizeof(prpc->msg_wol_add);
983d993e14bSNikita Danilov 
984a0da96c0SYana Esina 
985a0da96c0SYana Esina 		prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_ADD;
986d993e14bSNikita Danilov 		prpc->msg_wol_add.priority =
987a0da96c0SYana Esina 				HAL_ATLANTIC_UTILS_FW_MSG_WOL_PRIOR;
988d993e14bSNikita Danilov 		prpc->msg_wol_add.pattern_id =
989a0da96c0SYana Esina 				HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN;
990d993e14bSNikita Danilov 		prpc->msg_wol_add.packet_type =
991a0da96c0SYana Esina 				HAL_ATLANTIC_UTILS_FW_MSG_WOL_MAG_PKT;
992a0da96c0SYana Esina 
993d993e14bSNikita Danilov 		ether_addr_copy((u8 *)&prpc->msg_wol_add.magic_packet_pattern,
994d993e14bSNikita Danilov 				mac);
995a0da96c0SYana Esina 	} else {
996d993e14bSNikita Danilov 		rpc_size = sizeof(prpc->msg_wol_remove) +
997d993e14bSNikita Danilov 			   offsetof(struct hw_atl_utils_fw_rpc, msg_wol_remove);
998a0da96c0SYana Esina 
999a0da96c0SYana Esina 		prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_DEL;
1000d993e14bSNikita Danilov 		prpc->msg_wol_add.pattern_id =
1001a0da96c0SYana Esina 				HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN;
1002a0da96c0SYana Esina 	}
1003a0da96c0SYana Esina 
1004a0da96c0SYana Esina 	err = hw_atl_utils_fw_rpc_call(self, rpc_size);
1005a0da96c0SYana Esina 
1006a0da96c0SYana Esina err_exit:
1007a0da96c0SYana Esina 	return err;
1008a0da96c0SYana Esina }
1009a0da96c0SYana Esina 
aq_fw1x_set_power(struct aq_hw_s * self,unsigned int power_state,const u8 * mac)10103d5537f9SWei Yongjun static int aq_fw1x_set_power(struct aq_hw_s *self, unsigned int power_state,
101176660757SJakub Kicinski 			     const u8 *mac)
1012a0da96c0SYana Esina {
10138f60f762SNikita Danilov 	struct hw_atl_utils_fw_rpc *prpc = NULL;
1014a0da96c0SYana Esina 	unsigned int rpc_size = 0U;
1015a0da96c0SYana Esina 	int err = 0;
1016a0da96c0SYana Esina 
1017837c6378SNikita Danilov 	if (self->aq_nic_cfg->wol & WAKE_MAGIC) {
1018837c6378SNikita Danilov 		err = aq_fw1x_set_wake_magic(self, 1, mac);
1019a0da96c0SYana Esina 
1020a0da96c0SYana Esina 		if (err < 0)
1021a0da96c0SYana Esina 			goto err_exit;
1022a0da96c0SYana Esina 
1023a0da96c0SYana Esina 		rpc_size = sizeof(prpc->msg_id) +
1024a0da96c0SYana Esina 			   sizeof(prpc->msg_enable_wakeup);
1025a0da96c0SYana Esina 
1026a0da96c0SYana Esina 		err = hw_atl_utils_fw_rpc_wait(self, &prpc);
1027a0da96c0SYana Esina 
1028a0da96c0SYana Esina 		if (err < 0)
1029a0da96c0SYana Esina 			goto err_exit;
1030a0da96c0SYana Esina 
1031a0da96c0SYana Esina 		memset(prpc, 0, rpc_size);
1032a0da96c0SYana Esina 
1033a0da96c0SYana Esina 		prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_ENABLE_WAKEUP;
1034a0da96c0SYana Esina 		prpc->msg_enable_wakeup.pattern_mask = 0x00000002;
1035a0da96c0SYana Esina 
1036a0da96c0SYana Esina 		err = hw_atl_utils_fw_rpc_call(self, rpc_size);
1037a0da96c0SYana Esina 		if (err < 0)
1038a0da96c0SYana Esina 			goto err_exit;
1039a0da96c0SYana Esina 	}
1040a0da96c0SYana Esina 	hw_atl_utils_mpi_set_speed(self, 0);
1041a0da96c0SYana Esina 	hw_atl_utils_mpi_set_state(self, MPI_POWER);
1042a0da96c0SYana Esina 
1043a0da96c0SYana Esina err_exit:
1044a0da96c0SYana Esina 	return err;
1045a0da96c0SYana Esina }
1046a0da96c0SYana Esina 
hw_atl_utils_get_mpi_mbox_tid(struct aq_hw_s * self)10476a7f2277SNikita Danilov static u32 hw_atl_utils_get_mpi_mbox_tid(struct aq_hw_s *self)
10486a7f2277SNikita Danilov {
10496a7f2277SNikita Danilov 	struct hw_atl_utils_mbox_header mbox;
10506a7f2277SNikita Danilov 
10516a7f2277SNikita Danilov 	hw_atl_utils_mpi_read_mbox(self, &mbox);
10526a7f2277SNikita Danilov 
10536a7f2277SNikita Danilov 	return mbox.transaction_id;
10546a7f2277SNikita Danilov }
10556a7f2277SNikita Danilov 
hw_atl_utils_mpi_get_state(struct aq_hw_s * self)10566a7f2277SNikita Danilov static u32 hw_atl_utils_mpi_get_state(struct aq_hw_s *self)
10576a7f2277SNikita Danilov {
10586a7f2277SNikita Danilov 	return aq_hw_read_reg(self, HW_ATL_MPI_STATE_ADR);
10596a7f2277SNikita Danilov }
10606a7f2277SNikita Danilov 
hw_atl_utils_mif_cmd_get(struct aq_hw_s * self)10616a7f2277SNikita Danilov static u32 hw_atl_utils_mif_cmd_get(struct aq_hw_s *self)
10626a7f2277SNikita Danilov {
10636a7f2277SNikita Danilov 	return aq_hw_read_reg(self, HW_ATL_MIF_CMD);
10646a7f2277SNikita Danilov }
10656a7f2277SNikita Danilov 
hw_atl_utils_mif_addr_get(struct aq_hw_s * self)10666a7f2277SNikita Danilov static u32 hw_atl_utils_mif_addr_get(struct aq_hw_s *self)
10676a7f2277SNikita Danilov {
10686a7f2277SNikita Danilov 	return aq_hw_read_reg(self, HW_ATL_MIF_ADDR);
10696a7f2277SNikita Danilov }
10706a7f2277SNikita Danilov 
hw_atl_utils_rpc_state_get(struct aq_hw_s * self)10716a7f2277SNikita Danilov static u32 hw_atl_utils_rpc_state_get(struct aq_hw_s *self)
10726a7f2277SNikita Danilov {
10736a7f2277SNikita Danilov 	return aq_hw_read_reg(self, HW_ATL_RPC_STATE_ADR);
10746a7f2277SNikita Danilov }
10756a7f2277SNikita Danilov 
aq_fw1x_rpc_get(struct aq_hw_s * self)1076e7b5f97eSIgor Russkikh static u32 aq_fw1x_rpc_get(struct aq_hw_s *self)
1077e7b5f97eSIgor Russkikh {
1078e7b5f97eSIgor Russkikh 	return aq_hw_read_reg(self, HW_ATL_MPI_RPC_ADDR);
1079e7b5f97eSIgor Russkikh }
1080e7b5f97eSIgor Russkikh 
10810c58c35fSIgor Russkikh const struct aq_fw_ops aq_fw_1x_ops = {
10820c58c35fSIgor Russkikh 	.init = hw_atl_utils_mpi_create,
108344e00dd8SIgor Russkikh 	.deinit = hw_atl_fw1x_deinit,
10840c58c35fSIgor Russkikh 	.reset = NULL,
10850c58c35fSIgor Russkikh 	.get_mac_permanent = hw_atl_utils_get_mac_permanent,
10860c58c35fSIgor Russkikh 	.set_link_speed = hw_atl_utils_mpi_set_speed,
10870c58c35fSIgor Russkikh 	.set_state = hw_atl_utils_mpi_set_state,
10880c58c35fSIgor Russkikh 	.update_link_status = hw_atl_utils_mpi_get_link_status,
10890c58c35fSIgor Russkikh 	.update_stats = hw_atl_utils_update_stats,
10908dcf2ad3SMark Starovoytov 	.get_mac_temp = NULL,
10918f894011SYana Esina 	.get_phy_temp = NULL,
1092a0da96c0SYana Esina 	.set_power = aq_fw1x_set_power,
109392ab6407SYana Esina 	.set_eee_rate = NULL,
109492ab6407SYana Esina 	.get_eee_rate = NULL,
1095288551deSIgor Russkikh 	.set_flow_control = NULL,
1096910479a9SEgor Pomozov 	.send_fw_request = NULL,
1097910479a9SEgor Pomozov 	.enable_ptp = NULL,
1098d1287ce4SNikita Danilov 	.led_control = NULL,
10990c58c35fSIgor Russkikh };
1100