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 
2898c4c201SDavid VomLehn #define HW_ATL_FW_SM_RAM        0x2U
290c58c35fSIgor Russkikh #define HW_ATL_MPI_FW_VERSION	0x18
3098c4c201SDavid VomLehn #define HW_ATL_MPI_CONTROL_ADR  0x0368U
3198c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_ADR    0x036CU
3298c4c201SDavid VomLehn 
3398c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_MSK      0x00FFU
3498c4c201SDavid VomLehn #define HW_ATL_MPI_STATE_SHIFT    0U
3544e00dd8SIgor Russkikh #define HW_ATL_MPI_SPEED_MSK      0x00FF0000U
3698c4c201SDavid VomLehn #define HW_ATL_MPI_SPEED_SHIFT    16U
3744e00dd8SIgor Russkikh #define HW_ATL_MPI_DIRTY_WAKE_MSK 0x02000000U
3898c4c201SDavid VomLehn 
39c8c82eb3SIgor Russkikh #define HW_ATL_MPI_DAISY_CHAIN_STATUS	0x704
40c8c82eb3SIgor Russkikh #define HW_ATL_MPI_BOOT_EXIT_CODE	0x388
41c8c82eb3SIgor Russkikh 
42c8c82eb3SIgor Russkikh #define HW_ATL_MAC_PHY_CONTROL	0x4000
43c8c82eb3SIgor Russkikh #define HW_ATL_MAC_PHY_MPI_RESET_BIT 0x1D
44c8c82eb3SIgor Russkikh 
450c58c35fSIgor Russkikh #define HW_ATL_FW_VER_1X 0x01050006U
46a57d3929SIgor Russkikh #define HW_ATL_FW_VER_2X 0x02000000U
47a57d3929SIgor Russkikh #define HW_ATL_FW_VER_3X 0x03000000U
480c58c35fSIgor Russkikh 
49c8c82eb3SIgor Russkikh #define FORCE_FLASHLESS 0
50c8c82eb3SIgor Russkikh 
510c58c35fSIgor Russkikh static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual);
52e9157848SNikita Danilov 
53cce96d18SIgor Russkikh static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self,
54cce96d18SIgor Russkikh 				      enum hal_atl_utils_fw_state_e state);
550c58c35fSIgor Russkikh 
560c58c35fSIgor Russkikh int hw_atl_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_ops)
570c58c35fSIgor Russkikh {
580c58c35fSIgor Russkikh 	int err = 0;
590c58c35fSIgor Russkikh 
60c8c82eb3SIgor Russkikh 	err = hw_atl_utils_soft_reset(self);
61c8c82eb3SIgor Russkikh 	if (err)
62c8c82eb3SIgor Russkikh 		return err;
63c8c82eb3SIgor Russkikh 
640c58c35fSIgor Russkikh 	hw_atl_utils_hw_chip_features_init(self,
650c58c35fSIgor Russkikh 					   &self->chip_features);
660c58c35fSIgor Russkikh 
670c58c35fSIgor Russkikh 	hw_atl_utils_get_fw_version(self, &self->fw_ver_actual);
680c58c35fSIgor Russkikh 
69c8c82eb3SIgor Russkikh 	if (hw_atl_utils_ver_match(HW_ATL_FW_VER_1X,
70c8c82eb3SIgor Russkikh 				   self->fw_ver_actual) == 0) {
710c58c35fSIgor Russkikh 		*fw_ops = &aq_fw_1x_ops;
72c8c82eb3SIgor Russkikh 	} else if (hw_atl_utils_ver_match(HW_ATL_FW_VER_2X,
73c8c82eb3SIgor Russkikh 					  self->fw_ver_actual) == 0) {
74a57d3929SIgor Russkikh 		*fw_ops = &aq_fw_2x_ops;
75c8c82eb3SIgor Russkikh 	} else if (hw_atl_utils_ver_match(HW_ATL_FW_VER_3X,
76c8c82eb3SIgor Russkikh 					  self->fw_ver_actual) == 0) {
77a57d3929SIgor Russkikh 		*fw_ops = &aq_fw_2x_ops;
78c8c82eb3SIgor Russkikh 	} else {
790c58c35fSIgor Russkikh 		aq_pr_err("Bad FW version detected: %x\n",
800c58c35fSIgor Russkikh 			  self->fw_ver_actual);
810c58c35fSIgor Russkikh 		return -EOPNOTSUPP;
820c58c35fSIgor Russkikh 	}
830c58c35fSIgor Russkikh 	self->aq_fw_ops = *fw_ops;
840c58c35fSIgor Russkikh 	err = self->aq_fw_ops->init(self);
850c58c35fSIgor Russkikh 	return err;
860c58c35fSIgor Russkikh }
870c58c35fSIgor Russkikh 
88c8c82eb3SIgor Russkikh static int hw_atl_utils_soft_reset_flb(struct aq_hw_s *self)
89c8c82eb3SIgor Russkikh {
901bf9a752SIgor Russkikh 	u32 gsr, val;
91c8c82eb3SIgor Russkikh 	int k = 0;
92c8c82eb3SIgor Russkikh 
93c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x40e1);
94c8c82eb3SIgor Russkikh 	AQ_HW_SLEEP(50);
95c8c82eb3SIgor Russkikh 
96c8c82eb3SIgor Russkikh 	/* Cleanup SPI */
971bf9a752SIgor Russkikh 	val = aq_hw_read_reg(self, 0x53C);
981bf9a752SIgor Russkikh 	aq_hw_write_reg(self, 0x53C, val | 0x10);
99c8c82eb3SIgor Russkikh 
100c8c82eb3SIgor Russkikh 	gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR);
101c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, (gsr & 0xBFFF) | 0x8000);
102c8c82eb3SIgor Russkikh 
103c8c82eb3SIgor Russkikh 	/* Kickstart MAC */
104c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x80e0);
105c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x32a8, 0x0);
106c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x520, 0x1);
1071bf9a752SIgor Russkikh 
1081bf9a752SIgor Russkikh 	/* Reset SPI again because of possible interrupted SPI burst */
1091bf9a752SIgor Russkikh 	val = aq_hw_read_reg(self, 0x53C);
1101bf9a752SIgor Russkikh 	aq_hw_write_reg(self, 0x53C, val | 0x10);
111c8c82eb3SIgor Russkikh 	AQ_HW_SLEEP(10);
1121bf9a752SIgor Russkikh 	/* Clear SPI reset state */
1131bf9a752SIgor Russkikh 	aq_hw_write_reg(self, 0x53C, val & ~0x10);
1141bf9a752SIgor Russkikh 
115c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x180e0);
116c8c82eb3SIgor Russkikh 
117c8c82eb3SIgor Russkikh 	for (k = 0; k < 1000; k++) {
118c8c82eb3SIgor Russkikh 		u32 flb_status = aq_hw_read_reg(self,
119c8c82eb3SIgor Russkikh 						HW_ATL_MPI_DAISY_CHAIN_STATUS);
120c8c82eb3SIgor Russkikh 
121c8c82eb3SIgor Russkikh 		flb_status = flb_status & 0x10;
122c8c82eb3SIgor Russkikh 		if (flb_status)
123c8c82eb3SIgor Russkikh 			break;
124c8c82eb3SIgor Russkikh 		AQ_HW_SLEEP(10);
125c8c82eb3SIgor Russkikh 	}
126c8c82eb3SIgor Russkikh 	if (k == 1000) {
127c8c82eb3SIgor Russkikh 		aq_pr_err("MAC kickstart failed\n");
128c8c82eb3SIgor Russkikh 		return -EIO;
129c8c82eb3SIgor Russkikh 	}
130c8c82eb3SIgor Russkikh 
131c8c82eb3SIgor Russkikh 	/* FW reset */
132c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x80e0);
133c8c82eb3SIgor Russkikh 	AQ_HW_SLEEP(50);
134c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x3a0, 0x1);
135c8c82eb3SIgor Russkikh 
136c8c82eb3SIgor Russkikh 	/* Kickstart PHY - skipped */
137c8c82eb3SIgor Russkikh 
138c8c82eb3SIgor Russkikh 	/* Global software reset*/
139c8c82eb3SIgor Russkikh 	hw_atl_rx_rx_reg_res_dis_set(self, 0U);
140c8c82eb3SIgor Russkikh 	hw_atl_tx_tx_reg_res_dis_set(self, 0U);
141c8c82eb3SIgor Russkikh 	aq_hw_write_reg_bit(self, HW_ATL_MAC_PHY_CONTROL,
142c8c82eb3SIgor Russkikh 			    BIT(HW_ATL_MAC_PHY_MPI_RESET_BIT),
143c8c82eb3SIgor Russkikh 			    HW_ATL_MAC_PHY_MPI_RESET_BIT, 0x0);
144c8c82eb3SIgor Russkikh 	gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR);
145c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR, (gsr & 0xBFFF) | 0x8000);
146c8c82eb3SIgor Russkikh 
147c8c82eb3SIgor Russkikh 	for (k = 0; k < 1000; k++) {
148c8c82eb3SIgor Russkikh 		u32 fw_state = aq_hw_read_reg(self, HW_ATL_MPI_FW_VERSION);
149c8c82eb3SIgor Russkikh 
150c8c82eb3SIgor Russkikh 		if (fw_state)
151c8c82eb3SIgor Russkikh 			break;
152c8c82eb3SIgor Russkikh 		AQ_HW_SLEEP(10);
153c8c82eb3SIgor Russkikh 	}
154c8c82eb3SIgor Russkikh 	if (k == 1000) {
155c8c82eb3SIgor Russkikh 		aq_pr_err("FW kickstart failed\n");
156c8c82eb3SIgor Russkikh 		return -EIO;
157c8c82eb3SIgor Russkikh 	}
158d0f0fb25SIgor Russkikh 	/* Old FW requires fixed delay after init */
159d0f0fb25SIgor Russkikh 	AQ_HW_SLEEP(15);
160c8c82eb3SIgor Russkikh 
161c8c82eb3SIgor Russkikh 	return 0;
162c8c82eb3SIgor Russkikh }
163c8c82eb3SIgor Russkikh 
164c8c82eb3SIgor Russkikh static int hw_atl_utils_soft_reset_rbl(struct aq_hw_s *self)
165c8c82eb3SIgor Russkikh {
1661bf9a752SIgor Russkikh 	u32 gsr, val, rbl_status;
167c8c82eb3SIgor Russkikh 	int k;
168c8c82eb3SIgor Russkikh 
169c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x40e1);
170c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x3a0, 0x1);
171c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x32a8, 0x0);
172c8c82eb3SIgor Russkikh 
173c8c82eb3SIgor Russkikh 	/* Alter RBL status */
174c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x388, 0xDEAD);
175c8c82eb3SIgor Russkikh 
1761bf9a752SIgor Russkikh 	/* Cleanup SPI */
1771bf9a752SIgor Russkikh 	val = aq_hw_read_reg(self, 0x53C);
1781bf9a752SIgor Russkikh 	aq_hw_write_reg(self, 0x53C, val | 0x10);
1791bf9a752SIgor Russkikh 
180c8c82eb3SIgor Russkikh 	/* Global software reset*/
181c8c82eb3SIgor Russkikh 	hw_atl_rx_rx_reg_res_dis_set(self, 0U);
182c8c82eb3SIgor Russkikh 	hw_atl_tx_tx_reg_res_dis_set(self, 0U);
183c8c82eb3SIgor Russkikh 	aq_hw_write_reg_bit(self, HW_ATL_MAC_PHY_CONTROL,
184c8c82eb3SIgor Russkikh 			    BIT(HW_ATL_MAC_PHY_MPI_RESET_BIT),
185c8c82eb3SIgor Russkikh 			    HW_ATL_MAC_PHY_MPI_RESET_BIT, 0x0);
186c8c82eb3SIgor Russkikh 	gsr = aq_hw_read_reg(self, HW_ATL_GLB_SOFT_RES_ADR);
187c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_GLB_SOFT_RES_ADR,
188c8c82eb3SIgor Russkikh 			(gsr & 0xFFFFBFFF) | 0x8000);
189c8c82eb3SIgor Russkikh 
190c8c82eb3SIgor Russkikh 	if (FORCE_FLASHLESS)
191c8c82eb3SIgor Russkikh 		aq_hw_write_reg(self, 0x534, 0x0);
192c8c82eb3SIgor Russkikh 
193c8c82eb3SIgor Russkikh 	aq_hw_write_reg(self, 0x404, 0x40e0);
194c8c82eb3SIgor Russkikh 
195c8c82eb3SIgor Russkikh 	/* Wait for RBL boot */
196c8c82eb3SIgor Russkikh 	for (k = 0; k < 1000; k++) {
197c8c82eb3SIgor Russkikh 		rbl_status = aq_hw_read_reg(self, 0x388) & 0xFFFF;
198c8c82eb3SIgor Russkikh 		if (rbl_status && rbl_status != 0xDEAD)
199c8c82eb3SIgor Russkikh 			break;
200c8c82eb3SIgor Russkikh 		AQ_HW_SLEEP(10);
201c8c82eb3SIgor Russkikh 	}
202c8c82eb3SIgor Russkikh 	if (!rbl_status || rbl_status == 0xDEAD) {
203c8c82eb3SIgor Russkikh 		aq_pr_err("RBL Restart failed");
204c8c82eb3SIgor Russkikh 		return -EIO;
205c8c82eb3SIgor Russkikh 	}
206c8c82eb3SIgor Russkikh 
207c8c82eb3SIgor Russkikh 	/* Restore NVR */
208c8c82eb3SIgor Russkikh 	if (FORCE_FLASHLESS)
209c8c82eb3SIgor Russkikh 		aq_hw_write_reg(self, 0x534, 0xA0);
210c8c82eb3SIgor Russkikh 
211c8c82eb3SIgor Russkikh 	if (rbl_status == 0xF1A7) {
212c8c82eb3SIgor Russkikh 		aq_pr_err("No FW detected. Dynamic FW load not implemented\n");
213c8c82eb3SIgor Russkikh 		return -ENOTSUPP;
214c8c82eb3SIgor Russkikh 	}
215c8c82eb3SIgor Russkikh 
216c8c82eb3SIgor Russkikh 	for (k = 0; k < 1000; k++) {
217c8c82eb3SIgor Russkikh 		u32 fw_state = aq_hw_read_reg(self, HW_ATL_MPI_FW_VERSION);
218c8c82eb3SIgor Russkikh 
219c8c82eb3SIgor Russkikh 		if (fw_state)
220c8c82eb3SIgor Russkikh 			break;
221c8c82eb3SIgor Russkikh 		AQ_HW_SLEEP(10);
222c8c82eb3SIgor Russkikh 	}
223c8c82eb3SIgor Russkikh 	if (k == 1000) {
224c8c82eb3SIgor Russkikh 		aq_pr_err("FW kickstart failed\n");
225c8c82eb3SIgor Russkikh 		return -EIO;
226c8c82eb3SIgor Russkikh 	}
227d0f0fb25SIgor Russkikh 	/* Old FW requires fixed delay after init */
228d0f0fb25SIgor Russkikh 	AQ_HW_SLEEP(15);
229c8c82eb3SIgor Russkikh 
230c8c82eb3SIgor Russkikh 	return 0;
231c8c82eb3SIgor Russkikh }
232c8c82eb3SIgor Russkikh 
233c8c82eb3SIgor Russkikh int hw_atl_utils_soft_reset(struct aq_hw_s *self)
234c8c82eb3SIgor Russkikh {
235c8c82eb3SIgor Russkikh 	int k;
236c8c82eb3SIgor Russkikh 	u32 boot_exit_code = 0;
237c8c82eb3SIgor Russkikh 
238c8c82eb3SIgor Russkikh 	for (k = 0; k < 1000; ++k) {
239c8c82eb3SIgor Russkikh 		u32 flb_status = aq_hw_read_reg(self,
240c8c82eb3SIgor Russkikh 						HW_ATL_MPI_DAISY_CHAIN_STATUS);
241c8c82eb3SIgor Russkikh 		boot_exit_code = aq_hw_read_reg(self,
242c8c82eb3SIgor Russkikh 						HW_ATL_MPI_BOOT_EXIT_CODE);
243c8c82eb3SIgor Russkikh 		if (flb_status != 0x06000000 || boot_exit_code != 0)
244c8c82eb3SIgor Russkikh 			break;
245c8c82eb3SIgor Russkikh 	}
246c8c82eb3SIgor Russkikh 
247c8c82eb3SIgor Russkikh 	if (k == 1000) {
248c8c82eb3SIgor Russkikh 		aq_pr_err("Neither RBL nor FLB firmware started\n");
249c8c82eb3SIgor Russkikh 		return -EOPNOTSUPP;
250c8c82eb3SIgor Russkikh 	}
251c8c82eb3SIgor Russkikh 
252c8c82eb3SIgor Russkikh 	self->rbl_enabled = (boot_exit_code != 0);
253c8c82eb3SIgor Russkikh 
254cce96d18SIgor Russkikh 	/* FW 1.x may bootup in an invalid POWER state (WOL feature).
255cce96d18SIgor Russkikh 	 * We should work around this by forcing its state back to DEINIT
256cce96d18SIgor Russkikh 	 */
257cce96d18SIgor Russkikh 	if (!hw_atl_utils_ver_match(HW_ATL_FW_VER_1X,
258cce96d18SIgor Russkikh 				    aq_hw_read_reg(self,
259cce96d18SIgor Russkikh 						   HW_ATL_MPI_FW_VERSION))) {
260cce96d18SIgor Russkikh 		int err = 0;
261cce96d18SIgor Russkikh 
262cce96d18SIgor Russkikh 		hw_atl_utils_mpi_set_state(self, MPI_DEINIT);
263cce96d18SIgor Russkikh 		AQ_HW_WAIT_FOR((aq_hw_read_reg(self, HW_ATL_MPI_STATE_ADR) &
264cce96d18SIgor Russkikh 				HW_ATL_MPI_STATE_MSK) == MPI_DEINIT,
265cce96d18SIgor Russkikh 			       10, 1000U);
266cce96d18SIgor Russkikh 	}
267cce96d18SIgor Russkikh 
268c8c82eb3SIgor Russkikh 	if (self->rbl_enabled)
269c8c82eb3SIgor Russkikh 		return hw_atl_utils_soft_reset_rbl(self);
270c8c82eb3SIgor Russkikh 	else
271c8c82eb3SIgor Russkikh 		return hw_atl_utils_soft_reset_flb(self);
272c8c82eb3SIgor Russkikh }
273c8c82eb3SIgor Russkikh 
274a57d3929SIgor Russkikh int hw_atl_utils_fw_downld_dwords(struct aq_hw_s *self, u32 a,
27598c4c201SDavid VomLehn 				  u32 *p, u32 cnt)
27698c4c201SDavid VomLehn {
27798c4c201SDavid VomLehn 	int err = 0;
27898c4c201SDavid VomLehn 
2798e1c072fSIgor Russkikh 	AQ_HW_WAIT_FOR(hw_atl_reg_glb_cpu_sem_get(self,
28098c4c201SDavid VomLehn 						  HW_ATL_FW_SM_RAM) == 1U,
28198c4c201SDavid VomLehn 		       1U, 10000U);
28298c4c201SDavid VomLehn 
28398c4c201SDavid VomLehn 	if (err < 0) {
28498c4c201SDavid VomLehn 		bool is_locked;
28598c4c201SDavid VomLehn 
2868e1c072fSIgor Russkikh 		hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM);
2878e1c072fSIgor Russkikh 		is_locked = hw_atl_reg_glb_cpu_sem_get(self, HW_ATL_FW_SM_RAM);
28898c4c201SDavid VomLehn 		if (!is_locked) {
28998c4c201SDavid VomLehn 			err = -ETIME;
29098c4c201SDavid VomLehn 			goto err_exit;
29198c4c201SDavid VomLehn 		}
29298c4c201SDavid VomLehn 	}
29398c4c201SDavid VomLehn 
29447203b34SIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_MIF_ADDR, a);
29598c4c201SDavid VomLehn 
29647203b34SIgor Russkikh 	for (++cnt; --cnt && !err;) {
29747203b34SIgor Russkikh 		aq_hw_write_reg(self, HW_ATL_MIF_CMD, 0x00008000U);
29898c4c201SDavid VomLehn 
29947203b34SIgor Russkikh 		if (IS_CHIP_FEATURE(REVISION_B1))
30047203b34SIgor Russkikh 			AQ_HW_WAIT_FOR(a != aq_hw_read_reg(self,
30147203b34SIgor Russkikh 							   HW_ATL_MIF_ADDR),
30247203b34SIgor Russkikh 				       1, 1000U);
30347203b34SIgor Russkikh 		else
30447203b34SIgor Russkikh 			AQ_HW_WAIT_FOR(!(0x100 & aq_hw_read_reg(self,
30547203b34SIgor Russkikh 							   HW_ATL_MIF_CMD)),
30647203b34SIgor Russkikh 				       1, 1000U);
30798c4c201SDavid VomLehn 
30847203b34SIgor Russkikh 		*(p++) = aq_hw_read_reg(self, HW_ATL_MIF_VAL);
30947203b34SIgor Russkikh 		a += 4;
31098c4c201SDavid VomLehn 	}
31198c4c201SDavid VomLehn 
3128e1c072fSIgor Russkikh 	hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM);
31398c4c201SDavid VomLehn 
31498c4c201SDavid VomLehn err_exit:
31598c4c201SDavid VomLehn 	return err;
31698c4c201SDavid VomLehn }
31798c4c201SDavid VomLehn 
31898c4c201SDavid VomLehn static int hw_atl_utils_fw_upload_dwords(struct aq_hw_s *self, u32 a, u32 *p,
31998c4c201SDavid VomLehn 					 u32 cnt)
32098c4c201SDavid VomLehn {
32198c4c201SDavid VomLehn 	int err = 0;
32298c4c201SDavid VomLehn 	bool is_locked;
32398c4c201SDavid VomLehn 
3248e1c072fSIgor Russkikh 	is_locked = hw_atl_reg_glb_cpu_sem_get(self, HW_ATL_FW_SM_RAM);
32598c4c201SDavid VomLehn 	if (!is_locked) {
32698c4c201SDavid VomLehn 		err = -ETIME;
32798c4c201SDavid VomLehn 		goto err_exit;
32898c4c201SDavid VomLehn 	}
3293ee5c887SYana Esina 	if (IS_CHIP_FEATURE(REVISION_B1)) {
3303ee5c887SYana Esina 		u32 offset = 0;
33198c4c201SDavid VomLehn 
3323ee5c887SYana Esina 		for (; offset < cnt; ++offset) {
3333ee5c887SYana Esina 			aq_hw_write_reg(self, 0x328, p[offset]);
3343ee5c887SYana Esina 			aq_hw_write_reg(self, 0x32C,
3353ee5c887SYana Esina 					(0x80000000 | (0xFFFF & (offset * 4))));
3363ee5c887SYana Esina 			hw_atl_mcp_up_force_intr_set(self, 1);
3373ee5c887SYana Esina 			/* 1000 times by 10us = 10ms */
3383ee5c887SYana Esina 			AQ_HW_WAIT_FOR((aq_hw_read_reg(self,
3393ee5c887SYana Esina 						       0x32C) & 0xF0000000) !=
3403ee5c887SYana Esina 				       0x80000000,
3413ee5c887SYana Esina 				       10, 1000);
3423ee5c887SYana Esina 		}
3433ee5c887SYana Esina 	} else {
3443ee5c887SYana Esina 		u32 offset = 0;
34598c4c201SDavid VomLehn 
3463ee5c887SYana Esina 		aq_hw_write_reg(self, 0x208, a);
34798c4c201SDavid VomLehn 
3483ee5c887SYana Esina 		for (; offset < cnt; ++offset) {
3493ee5c887SYana Esina 			aq_hw_write_reg(self, 0x20C, p[offset]);
3503ee5c887SYana Esina 			aq_hw_write_reg(self, 0x200, 0xC000);
35198c4c201SDavid VomLehn 
3523ee5c887SYana Esina 			AQ_HW_WAIT_FOR((aq_hw_read_reg(self, 0x200U) &
3533ee5c887SYana Esina 					0x100) == 0, 10, 1000);
35498c4c201SDavid VomLehn 		}
35598c4c201SDavid VomLehn 	}
35698c4c201SDavid VomLehn 
3578e1c072fSIgor Russkikh 	hw_atl_reg_glb_cpu_sem_set(self, 1U, HW_ATL_FW_SM_RAM);
35898c4c201SDavid VomLehn 
35998c4c201SDavid VomLehn err_exit:
36098c4c201SDavid VomLehn 	return err;
36198c4c201SDavid VomLehn }
36298c4c201SDavid VomLehn 
36398c4c201SDavid VomLehn static int hw_atl_utils_ver_match(u32 ver_expected, u32 ver_actual)
36498c4c201SDavid VomLehn {
36598c4c201SDavid VomLehn 	int err = 0;
36698c4c201SDavid VomLehn 	const u32 dw_major_mask = 0xff000000U;
36798c4c201SDavid VomLehn 	const u32 dw_minor_mask = 0x00ffffffU;
36898c4c201SDavid VomLehn 
36998c4c201SDavid VomLehn 	err = (dw_major_mask & (ver_expected ^ ver_actual)) ? -EOPNOTSUPP : 0;
37098c4c201SDavid VomLehn 	if (err < 0)
37198c4c201SDavid VomLehn 		goto err_exit;
37298c4c201SDavid VomLehn 	err = ((dw_minor_mask & ver_expected) > (dw_minor_mask & ver_actual)) ?
37398c4c201SDavid VomLehn 		-EOPNOTSUPP : 0;
37498c4c201SDavid VomLehn err_exit:
37598c4c201SDavid VomLehn 	return err;
37698c4c201SDavid VomLehn }
37798c4c201SDavid VomLehn 
37898c4c201SDavid VomLehn static int hw_atl_utils_init_ucp(struct aq_hw_s *self,
3794cbc9f92SIgor Russkikh 				 const struct aq_hw_caps_s *aq_hw_caps)
38098c4c201SDavid VomLehn {
38198c4c201SDavid VomLehn 	int err = 0;
38298c4c201SDavid VomLehn 
38398c4c201SDavid VomLehn 	if (!aq_hw_read_reg(self, 0x370U)) {
38498c4c201SDavid VomLehn 		unsigned int rnd = 0U;
38598c4c201SDavid VomLehn 		unsigned int ucp_0x370 = 0U;
38698c4c201SDavid VomLehn 
38798c4c201SDavid VomLehn 		get_random_bytes(&rnd, sizeof(unsigned int));
38898c4c201SDavid VomLehn 
38998c4c201SDavid VomLehn 		ucp_0x370 = 0x02020202U | (0xFEFEFEFEU & rnd);
39098c4c201SDavid VomLehn 		aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370);
39198c4c201SDavid VomLehn 	}
39298c4c201SDavid VomLehn 
3938e1c072fSIgor Russkikh 	hw_atl_reg_glb_cpu_scratch_scp_set(self, 0x00000000U, 25U);
39498c4c201SDavid VomLehn 
39598c4c201SDavid VomLehn 	/* check 10 times by 1ms */
3961a713f87SIgor Russkikh 	AQ_HW_WAIT_FOR(0U != (self->mbox_addr =
39798c4c201SDavid VomLehn 			      aq_hw_read_reg(self, 0x360U)), 1000U, 10U);
39898c4c201SDavid VomLehn 
39998c4c201SDavid VomLehn 	return err;
40098c4c201SDavid VomLehn }
40198c4c201SDavid VomLehn 
40298c4c201SDavid VomLehn #define HW_ATL_RPC_CONTROL_ADR 0x0338U
40398c4c201SDavid VomLehn #define HW_ATL_RPC_STATE_ADR   0x033CU
40498c4c201SDavid VomLehn 
40598c4c201SDavid VomLehn struct aq_hw_atl_utils_fw_rpc_tid_s {
40698c4c201SDavid VomLehn 	union {
40798c4c201SDavid VomLehn 		u32 val;
40898c4c201SDavid VomLehn 		struct {
40998c4c201SDavid VomLehn 			u16 tid;
41098c4c201SDavid VomLehn 			u16 len;
41198c4c201SDavid VomLehn 		};
41298c4c201SDavid VomLehn 	};
41398c4c201SDavid VomLehn };
41498c4c201SDavid VomLehn 
41598c4c201SDavid VomLehn #define hw_atl_utils_fw_rpc_init(_H_) hw_atl_utils_fw_rpc_wait(_H_, NULL)
41698c4c201SDavid VomLehn 
4173ee5c887SYana Esina int hw_atl_utils_fw_rpc_call(struct aq_hw_s *self, unsigned int rpc_size)
41898c4c201SDavid VomLehn {
41998c4c201SDavid VomLehn 	int err = 0;
42098c4c201SDavid VomLehn 	struct aq_hw_atl_utils_fw_rpc_tid_s sw;
42198c4c201SDavid VomLehn 
42298c4c201SDavid VomLehn 	if (!IS_CHIP_FEATURE(MIPS)) {
42398c4c201SDavid VomLehn 		err = -1;
42498c4c201SDavid VomLehn 		goto err_exit;
42598c4c201SDavid VomLehn 	}
4261a713f87SIgor Russkikh 	err = hw_atl_utils_fw_upload_dwords(self, self->rpc_addr,
4271a713f87SIgor Russkikh 					    (u32 *)(void *)&self->rpc,
42898c4c201SDavid VomLehn 					    (rpc_size + sizeof(u32) -
42998c4c201SDavid VomLehn 					     sizeof(u8)) / sizeof(u32));
43098c4c201SDavid VomLehn 	if (err < 0)
43198c4c201SDavid VomLehn 		goto err_exit;
43298c4c201SDavid VomLehn 
4331a713f87SIgor Russkikh 	sw.tid = 0xFFFFU & (++self->rpc_tid);
43498c4c201SDavid VomLehn 	sw.len = (u16)rpc_size;
43598c4c201SDavid VomLehn 	aq_hw_write_reg(self, HW_ATL_RPC_CONTROL_ADR, sw.val);
43698c4c201SDavid VomLehn 
43798c4c201SDavid VomLehn err_exit:
43898c4c201SDavid VomLehn 	return err;
43998c4c201SDavid VomLehn }
44098c4c201SDavid VomLehn 
4413ee5c887SYana Esina int hw_atl_utils_fw_rpc_wait(struct aq_hw_s *self,
44298c4c201SDavid VomLehn 			     struct hw_aq_atl_utils_fw_rpc **rpc)
44398c4c201SDavid VomLehn {
44498c4c201SDavid VomLehn 	int err = 0;
44598c4c201SDavid VomLehn 	struct aq_hw_atl_utils_fw_rpc_tid_s sw;
44698c4c201SDavid VomLehn 	struct aq_hw_atl_utils_fw_rpc_tid_s fw;
44798c4c201SDavid VomLehn 
44898c4c201SDavid VomLehn 	do {
44998c4c201SDavid VomLehn 		sw.val = aq_hw_read_reg(self, HW_ATL_RPC_CONTROL_ADR);
45098c4c201SDavid VomLehn 
4511a713f87SIgor Russkikh 		self->rpc_tid = sw.tid;
45298c4c201SDavid VomLehn 
45398c4c201SDavid VomLehn 		AQ_HW_WAIT_FOR(sw.tid ==
45498c4c201SDavid VomLehn 			       (fw.val =
45598c4c201SDavid VomLehn 				aq_hw_read_reg(self, HW_ATL_RPC_STATE_ADR),
45698c4c201SDavid VomLehn 				fw.tid), 1000U, 100U);
45798c4c201SDavid VomLehn 		if (err < 0)
45898c4c201SDavid VomLehn 			goto err_exit;
45998c4c201SDavid VomLehn 
46098c4c201SDavid VomLehn 		if (fw.len == 0xFFFFU) {
46198c4c201SDavid VomLehn 			err = hw_atl_utils_fw_rpc_call(self, sw.len);
46298c4c201SDavid VomLehn 			if (err < 0)
46398c4c201SDavid VomLehn 				goto err_exit;
46498c4c201SDavid VomLehn 		}
46598c4c201SDavid VomLehn 	} while (sw.tid != fw.tid || 0xFFFFU == fw.len);
46698c4c201SDavid VomLehn 	if (err < 0)
46798c4c201SDavid VomLehn 		goto err_exit;
46898c4c201SDavid VomLehn 
46998c4c201SDavid VomLehn 	if (rpc) {
47098c4c201SDavid VomLehn 		if (fw.len) {
47198c4c201SDavid VomLehn 			err =
47298c4c201SDavid VomLehn 			hw_atl_utils_fw_downld_dwords(self,
4731a713f87SIgor Russkikh 						      self->rpc_addr,
47498c4c201SDavid VomLehn 						      (u32 *)(void *)
4751a713f87SIgor Russkikh 						      &self->rpc,
47698c4c201SDavid VomLehn 						      (fw.len + sizeof(u32) -
47798c4c201SDavid VomLehn 						       sizeof(u8)) /
47898c4c201SDavid VomLehn 						      sizeof(u32));
47998c4c201SDavid VomLehn 			if (err < 0)
48098c4c201SDavid VomLehn 				goto err_exit;
48198c4c201SDavid VomLehn 		}
48298c4c201SDavid VomLehn 
4831a713f87SIgor Russkikh 		*rpc = &self->rpc;
48498c4c201SDavid VomLehn 	}
48598c4c201SDavid VomLehn 
48698c4c201SDavid VomLehn err_exit:
48798c4c201SDavid VomLehn 	return err;
48898c4c201SDavid VomLehn }
48998c4c201SDavid VomLehn 
4901a713f87SIgor Russkikh static int hw_atl_utils_mpi_create(struct aq_hw_s *self)
49198c4c201SDavid VomLehn {
49298c4c201SDavid VomLehn 	int err = 0;
49398c4c201SDavid VomLehn 
4941a713f87SIgor Russkikh 	err = hw_atl_utils_init_ucp(self, self->aq_nic_cfg->aq_hw_caps);
49598c4c201SDavid VomLehn 	if (err < 0)
49698c4c201SDavid VomLehn 		goto err_exit;
49798c4c201SDavid VomLehn 
49898c4c201SDavid VomLehn 	err = hw_atl_utils_fw_rpc_init(self);
49998c4c201SDavid VomLehn 	if (err < 0)
50098c4c201SDavid VomLehn 		goto err_exit;
50198c4c201SDavid VomLehn 
50298c4c201SDavid VomLehn err_exit:
50398c4c201SDavid VomLehn 	return err;
50498c4c201SDavid VomLehn }
50598c4c201SDavid VomLehn 
50665e665e6SIgor Russkikh int hw_atl_utils_mpi_read_mbox(struct aq_hw_s *self,
50765e665e6SIgor Russkikh 			       struct hw_aq_atl_utils_mbox_header *pmbox)
50865e665e6SIgor Russkikh {
50965e665e6SIgor Russkikh 	return hw_atl_utils_fw_downld_dwords(self,
5101a713f87SIgor Russkikh 					     self->mbox_addr,
51165e665e6SIgor Russkikh 					     (u32 *)(void *)pmbox,
51265e665e6SIgor Russkikh 					     sizeof(*pmbox) / sizeof(u32));
51365e665e6SIgor Russkikh }
51465e665e6SIgor Russkikh 
51598c4c201SDavid VomLehn void hw_atl_utils_mpi_read_stats(struct aq_hw_s *self,
51698c4c201SDavid VomLehn 				 struct hw_aq_atl_utils_mbox *pmbox)
51798c4c201SDavid VomLehn {
51898c4c201SDavid VomLehn 	int err = 0;
51998c4c201SDavid VomLehn 
52098c4c201SDavid VomLehn 	err = hw_atl_utils_fw_downld_dwords(self,
5211a713f87SIgor Russkikh 					    self->mbox_addr,
52298c4c201SDavid VomLehn 					    (u32 *)(void *)pmbox,
52398c4c201SDavid VomLehn 					    sizeof(*pmbox) / sizeof(u32));
52498c4c201SDavid VomLehn 	if (err < 0)
52598c4c201SDavid VomLehn 		goto err_exit;
52698c4c201SDavid VomLehn 
52798c4c201SDavid VomLehn 	if (IS_CHIP_FEATURE(REVISION_A0)) {
52898c4c201SDavid VomLehn 		unsigned int mtu = self->aq_nic_cfg ?
52998c4c201SDavid VomLehn 					self->aq_nic_cfg->mtu : 1514U;
53098c4c201SDavid VomLehn 		pmbox->stats.ubrc = pmbox->stats.uprc * mtu;
53198c4c201SDavid VomLehn 		pmbox->stats.ubtc = pmbox->stats.uptc * mtu;
5321a713f87SIgor Russkikh 		pmbox->stats.dpc = atomic_read(&self->dpc);
53398c4c201SDavid VomLehn 	} else {
5348e1c072fSIgor Russkikh 		pmbox->stats.dpc = hw_atl_reg_rx_dma_stat_counter7get(self);
53598c4c201SDavid VomLehn 	}
53698c4c201SDavid VomLehn 
53798c4c201SDavid VomLehn err_exit:;
53898c4c201SDavid VomLehn }
53998c4c201SDavid VomLehn 
540dfbd0749SWei Yongjun static int hw_atl_utils_mpi_set_speed(struct aq_hw_s *self, u32 speed)
54198c4c201SDavid VomLehn {
5420c58c35fSIgor Russkikh 	u32 val = aq_hw_read_reg(self, HW_ATL_MPI_CONTROL_ADR);
54398c4c201SDavid VomLehn 
54444e00dd8SIgor Russkikh 	val = val & ~HW_ATL_MPI_SPEED_MSK;
54544e00dd8SIgor Russkikh 	val |= speed << HW_ATL_MPI_SPEED_SHIFT;
5460c58c35fSIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, val);
54798c4c201SDavid VomLehn 
54898c4c201SDavid VomLehn 	return 0;
54998c4c201SDavid VomLehn }
55098c4c201SDavid VomLehn 
551dfbd0749SWei Yongjun static int hw_atl_utils_mpi_set_state(struct aq_hw_s *self,
55244e00dd8SIgor Russkikh 				      enum hal_atl_utils_fw_state_e state)
55398c4c201SDavid VomLehn {
55498c4c201SDavid VomLehn 	int err = 0;
55598c4c201SDavid VomLehn 	u32 transaction_id = 0;
55665e665e6SIgor Russkikh 	struct hw_aq_atl_utils_mbox_header mbox;
55744e00dd8SIgor Russkikh 	u32 val = aq_hw_read_reg(self, HW_ATL_MPI_CONTROL_ADR);
55898c4c201SDavid VomLehn 
55998c4c201SDavid VomLehn 	if (state == MPI_RESET) {
56065e665e6SIgor Russkikh 		hw_atl_utils_mpi_read_mbox(self, &mbox);
56198c4c201SDavid VomLehn 
56265e665e6SIgor Russkikh 		transaction_id = mbox.transaction_id;
56398c4c201SDavid VomLehn 
56498c4c201SDavid VomLehn 		AQ_HW_WAIT_FOR(transaction_id !=
56565e665e6SIgor Russkikh 			       (hw_atl_utils_mpi_read_mbox(self, &mbox),
56665e665e6SIgor Russkikh 				mbox.transaction_id),
56798c4c201SDavid VomLehn 			       1000U, 100U);
56898c4c201SDavid VomLehn 		if (err < 0)
56998c4c201SDavid VomLehn 			goto err_exit;
57098c4c201SDavid VomLehn 	}
57144e00dd8SIgor Russkikh 	/* On interface DEINIT we disable DW (raise bit)
57244e00dd8SIgor Russkikh 	 * Otherwise enable DW (clear bit)
57344e00dd8SIgor Russkikh 	 */
57444e00dd8SIgor Russkikh 	if (state == MPI_DEINIT || state == MPI_POWER)
57544e00dd8SIgor Russkikh 		val |= HW_ATL_MPI_DIRTY_WAKE_MSK;
57644e00dd8SIgor Russkikh 	else
57744e00dd8SIgor Russkikh 		val &= ~HW_ATL_MPI_DIRTY_WAKE_MSK;
57898c4c201SDavid VomLehn 
57944e00dd8SIgor Russkikh 	/* Set new state bits */
58044e00dd8SIgor Russkikh 	val = val & ~HW_ATL_MPI_STATE_MSK;
58144e00dd8SIgor Russkikh 	val |= state & HW_ATL_MPI_STATE_MSK;
58298c4c201SDavid VomLehn 
5830c58c35fSIgor Russkikh 	aq_hw_write_reg(self, HW_ATL_MPI_CONTROL_ADR, val);
58444e00dd8SIgor Russkikh err_exit:
58544e00dd8SIgor Russkikh 	return err;
5860c58c35fSIgor Russkikh }
5870c58c35fSIgor Russkikh 
588bd8ed441SPavel Belous int hw_atl_utils_mpi_get_link_status(struct aq_hw_s *self)
58998c4c201SDavid VomLehn {
59098c4c201SDavid VomLehn 	u32 cp0x036C = aq_hw_read_reg(self, HW_ATL_MPI_STATE_ADR);
59198c4c201SDavid VomLehn 	u32 link_speed_mask = cp0x036C >> HW_ATL_MPI_SPEED_SHIFT;
592bd8ed441SPavel Belous 	struct aq_hw_link_status_s *link_status = &self->aq_link_status;
59398c4c201SDavid VomLehn 
59498c4c201SDavid VomLehn 	if (!link_speed_mask) {
59598c4c201SDavid VomLehn 		link_status->mbps = 0U;
59698c4c201SDavid VomLehn 	} else {
59798c4c201SDavid VomLehn 		switch (link_speed_mask) {
59898c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_10G:
59998c4c201SDavid VomLehn 			link_status->mbps = 10000U;
60098c4c201SDavid VomLehn 			break;
60198c4c201SDavid VomLehn 
60298c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_5G:
60398c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_5GSR:
60498c4c201SDavid VomLehn 			link_status->mbps = 5000U;
60598c4c201SDavid VomLehn 			break;
60698c4c201SDavid VomLehn 
60798c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_2GS:
60898c4c201SDavid VomLehn 			link_status->mbps = 2500U;
60998c4c201SDavid VomLehn 			break;
61098c4c201SDavid VomLehn 
61198c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_1G:
61298c4c201SDavid VomLehn 			link_status->mbps = 1000U;
61398c4c201SDavid VomLehn 			break;
61498c4c201SDavid VomLehn 
61598c4c201SDavid VomLehn 		case HAL_ATLANTIC_RATE_100M:
61698c4c201SDavid VomLehn 			link_status->mbps = 100U;
61798c4c201SDavid VomLehn 			break;
61898c4c201SDavid VomLehn 
61998c4c201SDavid VomLehn 		default:
620a7bb1beaSIgor Russkikh 			return -EBUSY;
62198c4c201SDavid VomLehn 		}
62298c4c201SDavid VomLehn 	}
62398c4c201SDavid VomLehn 
62498c4c201SDavid VomLehn 	return 0;
62598c4c201SDavid VomLehn }
62698c4c201SDavid VomLehn 
62798c4c201SDavid VomLehn int hw_atl_utils_get_mac_permanent(struct aq_hw_s *self,
62898c4c201SDavid VomLehn 				   u8 *mac)
62998c4c201SDavid VomLehn {
63098c4c201SDavid VomLehn 	int err = 0;
63198c4c201SDavid VomLehn 	u32 h = 0U;
63298c4c201SDavid VomLehn 	u32 l = 0U;
63398c4c201SDavid VomLehn 	u32 mac_addr[2];
63498c4c201SDavid VomLehn 
63598c4c201SDavid VomLehn 	if (!aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) {
63698c4c201SDavid VomLehn 		unsigned int rnd = 0;
63798c4c201SDavid VomLehn 		unsigned int ucp_0x370 = 0;
63898c4c201SDavid VomLehn 
63998c4c201SDavid VomLehn 		get_random_bytes(&rnd, sizeof(unsigned int));
64098c4c201SDavid VomLehn 
64198c4c201SDavid VomLehn 		ucp_0x370 = 0x02020202 | (0xFEFEFEFE & rnd);
64298c4c201SDavid VomLehn 		aq_hw_write_reg(self, HW_ATL_UCP_0X370_REG, ucp_0x370);
64398c4c201SDavid VomLehn 	}
64498c4c201SDavid VomLehn 
64598c4c201SDavid VomLehn 	err = hw_atl_utils_fw_downld_dwords(self,
64698c4c201SDavid VomLehn 					    aq_hw_read_reg(self, 0x00000374U) +
64798c4c201SDavid VomLehn 					    (40U * 4U),
64898c4c201SDavid VomLehn 					    mac_addr,
64908b5cf08SIgor Russkikh 					    ARRAY_SIZE(mac_addr));
65098c4c201SDavid VomLehn 	if (err < 0) {
65198c4c201SDavid VomLehn 		mac_addr[0] = 0U;
65298c4c201SDavid VomLehn 		mac_addr[1] = 0U;
65398c4c201SDavid VomLehn 		err = 0;
65498c4c201SDavid VomLehn 	} else {
65598c4c201SDavid VomLehn 		mac_addr[0] = __swab32(mac_addr[0]);
65698c4c201SDavid VomLehn 		mac_addr[1] = __swab32(mac_addr[1]);
65798c4c201SDavid VomLehn 	}
65898c4c201SDavid VomLehn 
65998c4c201SDavid VomLehn 	ether_addr_copy(mac, (u8 *)mac_addr);
66098c4c201SDavid VomLehn 
66198c4c201SDavid VomLehn 	if ((mac[0] & 0x01U) || ((mac[0] | mac[1] | mac[2]) == 0x00U)) {
66298c4c201SDavid VomLehn 		/* chip revision */
663e9157848SNikita Danilov 		l = 0xE3000000U |
664e9157848SNikita Danilov 		    (0xFFFFU & aq_hw_read_reg(self, HW_ATL_UCP_0X370_REG)) |
665e9157848SNikita Danilov 		    (0x00 << 16);
66698c4c201SDavid VomLehn 		h = 0x8001300EU;
66798c4c201SDavid VomLehn 
66898c4c201SDavid VomLehn 		mac[5] = (u8)(0xFFU & l);
66998c4c201SDavid VomLehn 		l >>= 8;
67098c4c201SDavid VomLehn 		mac[4] = (u8)(0xFFU & l);
67198c4c201SDavid VomLehn 		l >>= 8;
67298c4c201SDavid VomLehn 		mac[3] = (u8)(0xFFU & l);
67398c4c201SDavid VomLehn 		l >>= 8;
67498c4c201SDavid VomLehn 		mac[2] = (u8)(0xFFU & l);
67598c4c201SDavid VomLehn 		mac[1] = (u8)(0xFFU & h);
67698c4c201SDavid VomLehn 		h >>= 8;
67798c4c201SDavid VomLehn 		mac[0] = (u8)(0xFFU & h);
67898c4c201SDavid VomLehn 	}
67998c4c201SDavid VomLehn 
68098c4c201SDavid VomLehn 	return err;
68198c4c201SDavid VomLehn }
68298c4c201SDavid VomLehn 
68398c4c201SDavid VomLehn unsigned int hw_atl_utils_mbps_2_speed_index(unsigned int mbps)
68498c4c201SDavid VomLehn {
68598c4c201SDavid VomLehn 	unsigned int ret = 0U;
68698c4c201SDavid VomLehn 
68798c4c201SDavid VomLehn 	switch (mbps) {
68898c4c201SDavid VomLehn 	case 100U:
68998c4c201SDavid VomLehn 		ret = 5U;
69098c4c201SDavid VomLehn 		break;
69198c4c201SDavid VomLehn 
69298c4c201SDavid VomLehn 	case 1000U:
69398c4c201SDavid VomLehn 		ret = 4U;
69498c4c201SDavid VomLehn 		break;
69598c4c201SDavid VomLehn 
69698c4c201SDavid VomLehn 	case 2500U:
69798c4c201SDavid VomLehn 		ret = 3U;
69898c4c201SDavid VomLehn 		break;
69998c4c201SDavid VomLehn 
70098c4c201SDavid VomLehn 	case 5000U:
70198c4c201SDavid VomLehn 		ret = 1U;
70298c4c201SDavid VomLehn 		break;
70398c4c201SDavid VomLehn 
70498c4c201SDavid VomLehn 	case 10000U:
70598c4c201SDavid VomLehn 		ret = 0U;
70698c4c201SDavid VomLehn 		break;
70798c4c201SDavid VomLehn 
70898c4c201SDavid VomLehn 	default:
70998c4c201SDavid VomLehn 		break;
71098c4c201SDavid VomLehn 	}
71198c4c201SDavid VomLehn 	return ret;
71298c4c201SDavid VomLehn }
71398c4c201SDavid VomLehn 
71498c4c201SDavid VomLehn void hw_atl_utils_hw_chip_features_init(struct aq_hw_s *self, u32 *p)
71598c4c201SDavid VomLehn {
71698c4c201SDavid VomLehn 	u32 chip_features = 0U;
7178e1c072fSIgor Russkikh 	u32 val = hw_atl_reg_glb_mif_id_get(self);
71898c4c201SDavid VomLehn 	u32 mif_rev = val & 0xFFU;
71998c4c201SDavid VomLehn 
72047203b34SIgor Russkikh 	if ((0xFU & mif_rev) == 1U) {
72147203b34SIgor Russkikh 		chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_A0 |
72298c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_MPI_AQ |
72398c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_MIPS;
72447203b34SIgor Russkikh 	} else if ((0xFU & mif_rev) == 2U) {
72547203b34SIgor Russkikh 		chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_B0 |
72647203b34SIgor Russkikh 			HAL_ATLANTIC_UTILS_CHIP_MPI_AQ |
72747203b34SIgor Russkikh 			HAL_ATLANTIC_UTILS_CHIP_MIPS |
72847203b34SIgor Russkikh 			HAL_ATLANTIC_UTILS_CHIP_TPO2 |
72947203b34SIgor Russkikh 			HAL_ATLANTIC_UTILS_CHIP_RPF2;
73047203b34SIgor Russkikh 	} else if ((0xFU & mif_rev) == 0xAU) {
73147203b34SIgor Russkikh 		chip_features |= HAL_ATLANTIC_UTILS_CHIP_REVISION_B1 |
73298c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_MPI_AQ |
73398c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_MIPS |
73498c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_TPO2 |
73598c4c201SDavid VomLehn 			HAL_ATLANTIC_UTILS_CHIP_RPF2;
73698c4c201SDavid VomLehn 	}
73798c4c201SDavid VomLehn 
73898c4c201SDavid VomLehn 	*p = chip_features;
73998c4c201SDavid VomLehn }
74098c4c201SDavid VomLehn 
74144e00dd8SIgor Russkikh static int hw_atl_fw1x_deinit(struct aq_hw_s *self)
74298c4c201SDavid VomLehn {
74344e00dd8SIgor Russkikh 	hw_atl_utils_mpi_set_speed(self, 0);
74444e00dd8SIgor Russkikh 	hw_atl_utils_mpi_set_state(self, MPI_DEINIT);
74598c4c201SDavid VomLehn 	return 0;
74698c4c201SDavid VomLehn }
74798c4c201SDavid VomLehn 
74865e665e6SIgor Russkikh int hw_atl_utils_update_stats(struct aq_hw_s *self)
74965e665e6SIgor Russkikh {
75065e665e6SIgor Russkikh 	struct hw_aq_atl_utils_mbox mbox;
75165e665e6SIgor Russkikh 
75265e665e6SIgor Russkikh 	hw_atl_utils_mpi_read_stats(self, &mbox);
75365e665e6SIgor Russkikh 
7541a713f87SIgor Russkikh #define AQ_SDELTA(_N_) (self->curr_stats._N_ += \
7551a713f87SIgor Russkikh 			mbox.stats._N_ - self->last_stats._N_)
7561a713f87SIgor Russkikh 
757be08d839SIgor Russkikh 	if (self->aq_link_status.mbps) {
75865e665e6SIgor Russkikh 		AQ_SDELTA(uprc);
75965e665e6SIgor Russkikh 		AQ_SDELTA(mprc);
76065e665e6SIgor Russkikh 		AQ_SDELTA(bprc);
76165e665e6SIgor Russkikh 		AQ_SDELTA(erpt);
76265e665e6SIgor Russkikh 
76365e665e6SIgor Russkikh 		AQ_SDELTA(uptc);
76465e665e6SIgor Russkikh 		AQ_SDELTA(mptc);
76565e665e6SIgor Russkikh 		AQ_SDELTA(bptc);
76665e665e6SIgor Russkikh 		AQ_SDELTA(erpr);
76765e665e6SIgor Russkikh 
76865e665e6SIgor Russkikh 		AQ_SDELTA(ubrc);
76965e665e6SIgor Russkikh 		AQ_SDELTA(ubtc);
77065e665e6SIgor Russkikh 		AQ_SDELTA(mbrc);
77165e665e6SIgor Russkikh 		AQ_SDELTA(mbtc);
77265e665e6SIgor Russkikh 		AQ_SDELTA(bbrc);
77365e665e6SIgor Russkikh 		AQ_SDELTA(bbtc);
77465e665e6SIgor Russkikh 		AQ_SDELTA(dpc);
775be08d839SIgor Russkikh 	}
77665e665e6SIgor Russkikh #undef AQ_SDELTA
7778e1c072fSIgor Russkikh 	self->curr_stats.dma_pkt_rc = hw_atl_stats_rx_dma_good_pkt_counterlsw_get(self);
7788e1c072fSIgor Russkikh 	self->curr_stats.dma_pkt_tc = hw_atl_stats_tx_dma_good_pkt_counterlsw_get(self);
7798e1c072fSIgor Russkikh 	self->curr_stats.dma_oct_rc = hw_atl_stats_rx_dma_good_octet_counterlsw_get(self);
7808e1c072fSIgor Russkikh 	self->curr_stats.dma_oct_tc = hw_atl_stats_tx_dma_good_octet_counterlsw_get(self);
78165e665e6SIgor Russkikh 
7821a713f87SIgor Russkikh 	memcpy(&self->last_stats, &mbox.stats, sizeof(mbox.stats));
78365e665e6SIgor Russkikh 
78465e665e6SIgor Russkikh 	return 0;
78565e665e6SIgor Russkikh }
78665e665e6SIgor Russkikh 
787be08d839SIgor Russkikh struct aq_stats_s *hw_atl_utils_get_hw_stats(struct aq_hw_s *self)
78898c4c201SDavid VomLehn {
7891a713f87SIgor Russkikh 	return &self->curr_stats;
79098c4c201SDavid VomLehn }
79198c4c201SDavid VomLehn 
79298c4c201SDavid VomLehn static const u32 hw_atl_utils_hw_mac_regs[] = {
79398c4c201SDavid VomLehn 	0x00005580U, 0x00005590U, 0x000055B0U, 0x000055B4U,
79498c4c201SDavid VomLehn 	0x000055C0U, 0x00005B00U, 0x00005B04U, 0x00005B08U,
79598c4c201SDavid VomLehn 	0x00005B0CU, 0x00005B10U, 0x00005B14U, 0x00005B18U,
79698c4c201SDavid VomLehn 	0x00005B1CU, 0x00005B20U, 0x00005B24U, 0x00005B28U,
79798c4c201SDavid VomLehn 	0x00005B2CU, 0x00005B30U, 0x00005B34U, 0x00005B38U,
79898c4c201SDavid VomLehn 	0x00005B3CU, 0x00005B40U, 0x00005B44U, 0x00005B48U,
79998c4c201SDavid VomLehn 	0x00005B4CU, 0x00005B50U, 0x00005B54U, 0x00005B58U,
80098c4c201SDavid VomLehn 	0x00005B5CU, 0x00005B60U, 0x00005B64U, 0x00005B68U,
80198c4c201SDavid VomLehn 	0x00005B6CU, 0x00005B70U, 0x00005B74U, 0x00005B78U,
80298c4c201SDavid VomLehn 	0x00005B7CU, 0x00007C00U, 0x00007C04U, 0x00007C08U,
80398c4c201SDavid VomLehn 	0x00007C0CU, 0x00007C10U, 0x00007C14U, 0x00007C18U,
80498c4c201SDavid VomLehn 	0x00007C1CU, 0x00007C20U, 0x00007C40U, 0x00007C44U,
80598c4c201SDavid VomLehn 	0x00007C48U, 0x00007C4CU, 0x00007C50U, 0x00007C54U,
80698c4c201SDavid VomLehn 	0x00007C58U, 0x00007C5CU, 0x00007C60U, 0x00007C80U,
80798c4c201SDavid VomLehn 	0x00007C84U, 0x00007C88U, 0x00007C8CU, 0x00007C90U,
80898c4c201SDavid VomLehn 	0x00007C94U, 0x00007C98U, 0x00007C9CU, 0x00007CA0U,
80998c4c201SDavid VomLehn 	0x00007CC0U, 0x00007CC4U, 0x00007CC8U, 0x00007CCCU,
81098c4c201SDavid VomLehn 	0x00007CD0U, 0x00007CD4U, 0x00007CD8U, 0x00007CDCU,
81198c4c201SDavid VomLehn 	0x00007CE0U, 0x00000300U, 0x00000304U, 0x00000308U,
81298c4c201SDavid VomLehn 	0x0000030cU, 0x00000310U, 0x00000314U, 0x00000318U,
81398c4c201SDavid VomLehn 	0x0000031cU, 0x00000360U, 0x00000364U, 0x00000368U,
81498c4c201SDavid VomLehn 	0x0000036cU, 0x00000370U, 0x00000374U, 0x00006900U,
81598c4c201SDavid VomLehn };
81698c4c201SDavid VomLehn 
81798c4c201SDavid VomLehn int hw_atl_utils_hw_get_regs(struct aq_hw_s *self,
8184cbc9f92SIgor Russkikh 			     const struct aq_hw_caps_s *aq_hw_caps,
81998c4c201SDavid VomLehn 			     u32 *regs_buff)
82098c4c201SDavid VomLehn {
82198c4c201SDavid VomLehn 	unsigned int i = 0U;
82298c4c201SDavid VomLehn 
82398c4c201SDavid VomLehn 	for (i = 0; i < aq_hw_caps->mac_regs_count; i++)
82498c4c201SDavid VomLehn 		regs_buff[i] = aq_hw_read_reg(self,
82598c4c201SDavid VomLehn 					      hw_atl_utils_hw_mac_regs[i]);
82698c4c201SDavid VomLehn 	return 0;
82798c4c201SDavid VomLehn }
82898c4c201SDavid VomLehn 
82998c4c201SDavid VomLehn int hw_atl_utils_get_fw_version(struct aq_hw_s *self, u32 *fw_version)
83098c4c201SDavid VomLehn {
83198c4c201SDavid VomLehn 	*fw_version = aq_hw_read_reg(self, 0x18U);
83298c4c201SDavid VomLehn 	return 0;
83398c4c201SDavid VomLehn }
8340c58c35fSIgor Russkikh 
835a0da96c0SYana Esina static int aq_fw1x_set_wol(struct aq_hw_s *self, bool wol_enabled, u8 *mac)
836a0da96c0SYana Esina {
837a0da96c0SYana Esina 	struct hw_aq_atl_utils_fw_rpc *prpc = NULL;
838a0da96c0SYana Esina 	unsigned int rpc_size = 0U;
839a0da96c0SYana Esina 	int err = 0;
840a0da96c0SYana Esina 
841a0da96c0SYana Esina 	err = hw_atl_utils_fw_rpc_wait(self, &prpc);
842a0da96c0SYana Esina 	if (err < 0)
843a0da96c0SYana Esina 		goto err_exit;
844a0da96c0SYana Esina 
845a0da96c0SYana Esina 	memset(prpc, 0, sizeof(*prpc));
846a0da96c0SYana Esina 
847a0da96c0SYana Esina 	if (wol_enabled) {
848a0da96c0SYana Esina 		rpc_size = sizeof(prpc->msg_id) + sizeof(prpc->msg_wol);
849a0da96c0SYana Esina 
850a0da96c0SYana Esina 		prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_ADD;
851a0da96c0SYana Esina 		prpc->msg_wol.priority =
852a0da96c0SYana Esina 				HAL_ATLANTIC_UTILS_FW_MSG_WOL_PRIOR;
853a0da96c0SYana Esina 		prpc->msg_wol.pattern_id =
854a0da96c0SYana Esina 				HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN;
855a0da96c0SYana Esina 		prpc->msg_wol.wol_packet_type =
856a0da96c0SYana Esina 				HAL_ATLANTIC_UTILS_FW_MSG_WOL_MAG_PKT;
857a0da96c0SYana Esina 
858a0da96c0SYana Esina 		ether_addr_copy((u8 *)&prpc->msg_wol.wol_pattern, mac);
859a0da96c0SYana Esina 	} else {
860a0da96c0SYana Esina 		rpc_size = sizeof(prpc->msg_id) + sizeof(prpc->msg_del_id);
861a0da96c0SYana Esina 
862a0da96c0SYana Esina 		prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_WOL_DEL;
863a0da96c0SYana Esina 		prpc->msg_wol.pattern_id =
864a0da96c0SYana Esina 				HAL_ATLANTIC_UTILS_FW_MSG_WOL_PATTERN;
865a0da96c0SYana Esina 	}
866a0da96c0SYana Esina 
867a0da96c0SYana Esina 	err = hw_atl_utils_fw_rpc_call(self, rpc_size);
868a0da96c0SYana Esina 
869a0da96c0SYana Esina err_exit:
870a0da96c0SYana Esina 	return err;
871a0da96c0SYana Esina }
872a0da96c0SYana Esina 
873a0da96c0SYana Esina int aq_fw1x_set_power(struct aq_hw_s *self, unsigned int power_state,
874a0da96c0SYana Esina 		      u8 *mac)
875a0da96c0SYana Esina {
876a0da96c0SYana Esina 	struct hw_aq_atl_utils_fw_rpc *prpc = NULL;
877a0da96c0SYana Esina 	unsigned int rpc_size = 0U;
878a0da96c0SYana Esina 	int err = 0;
879a0da96c0SYana Esina 
880a0da96c0SYana Esina 	if (self->aq_nic_cfg->wol & AQ_NIC_WOL_ENABLED) {
881a0da96c0SYana Esina 		err = aq_fw1x_set_wol(self, 1, mac);
882a0da96c0SYana Esina 
883a0da96c0SYana Esina 		if (err < 0)
884a0da96c0SYana Esina 			goto err_exit;
885a0da96c0SYana Esina 
886a0da96c0SYana Esina 		rpc_size = sizeof(prpc->msg_id) +
887a0da96c0SYana Esina 			   sizeof(prpc->msg_enable_wakeup);
888a0da96c0SYana Esina 
889a0da96c0SYana Esina 		err = hw_atl_utils_fw_rpc_wait(self, &prpc);
890a0da96c0SYana Esina 
891a0da96c0SYana Esina 		if (err < 0)
892a0da96c0SYana Esina 			goto err_exit;
893a0da96c0SYana Esina 
894a0da96c0SYana Esina 		memset(prpc, 0, rpc_size);
895a0da96c0SYana Esina 
896a0da96c0SYana Esina 		prpc->msg_id = HAL_ATLANTIC_UTILS_FW_MSG_ENABLE_WAKEUP;
897a0da96c0SYana Esina 		prpc->msg_enable_wakeup.pattern_mask = 0x00000002;
898a0da96c0SYana Esina 
899a0da96c0SYana Esina 		err = hw_atl_utils_fw_rpc_call(self, rpc_size);
900a0da96c0SYana Esina 		if (err < 0)
901a0da96c0SYana Esina 			goto err_exit;
902a0da96c0SYana Esina 	}
903a0da96c0SYana Esina 	hw_atl_utils_mpi_set_speed(self, 0);
904a0da96c0SYana Esina 	hw_atl_utils_mpi_set_state(self, MPI_POWER);
905a0da96c0SYana Esina 
906a0da96c0SYana Esina err_exit:
907a0da96c0SYana Esina 	return err;
908a0da96c0SYana Esina }
909a0da96c0SYana Esina 
9100c58c35fSIgor Russkikh const struct aq_fw_ops aq_fw_1x_ops = {
9110c58c35fSIgor Russkikh 	.init = hw_atl_utils_mpi_create,
91244e00dd8SIgor Russkikh 	.deinit = hw_atl_fw1x_deinit,
9130c58c35fSIgor Russkikh 	.reset = NULL,
9140c58c35fSIgor Russkikh 	.get_mac_permanent = hw_atl_utils_get_mac_permanent,
9150c58c35fSIgor Russkikh 	.set_link_speed = hw_atl_utils_mpi_set_speed,
9160c58c35fSIgor Russkikh 	.set_state = hw_atl_utils_mpi_set_state,
9170c58c35fSIgor Russkikh 	.update_link_status = hw_atl_utils_mpi_get_link_status,
9180c58c35fSIgor Russkikh 	.update_stats = hw_atl_utils_update_stats,
919a0da96c0SYana Esina 	.set_power = aq_fw1x_set_power,
92092ab6407SYana Esina 	.set_eee_rate = NULL,
92192ab6407SYana Esina 	.get_eee_rate = NULL,
922288551deSIgor Russkikh 	.set_flow_control = NULL,
9230c58c35fSIgor Russkikh };
924