1c1be0bf0SDmitry Bogdanov // SPDX-License-Identifier: GPL-2.0-only
2c1be0bf0SDmitry Bogdanov /* Atlantic Network Driver
3c1be0bf0SDmitry Bogdanov  * Copyright (C) 2020 Marvell International Ltd.
4c1be0bf0SDmitry Bogdanov  */
5c1be0bf0SDmitry Bogdanov 
6c1be0bf0SDmitry Bogdanov #include <linux/iopoll.h>
7c1be0bf0SDmitry Bogdanov 
8c1be0bf0SDmitry Bogdanov #include "aq_hw_utils.h"
9c1be0bf0SDmitry Bogdanov #include "hw_atl/hw_atl_utils.h"
10c1be0bf0SDmitry Bogdanov #include "hw_atl2_utils.h"
11c1be0bf0SDmitry Bogdanov #include "hw_atl2_llh.h"
12c1be0bf0SDmitry Bogdanov #include "hw_atl2_llh_internal.h"
13c1be0bf0SDmitry Bogdanov 
14c1be0bf0SDmitry Bogdanov #define HW_ATL2_FW_VER_1X          0x01000000U
15c1be0bf0SDmitry Bogdanov 
16c1be0bf0SDmitry Bogdanov #define AQ_A2_BOOT_STARTED         BIT(0x18)
17c1be0bf0SDmitry Bogdanov #define AQ_A2_CRASH_INIT           BIT(0x1B)
18c1be0bf0SDmitry Bogdanov #define AQ_A2_BOOT_CODE_FAILED     BIT(0x1C)
19c1be0bf0SDmitry Bogdanov #define AQ_A2_FW_INIT_FAILED       BIT(0x1D)
20c1be0bf0SDmitry Bogdanov #define AQ_A2_FW_INIT_COMP_SUCCESS BIT(0x1F)
21c1be0bf0SDmitry Bogdanov 
22c1be0bf0SDmitry Bogdanov #define AQ_A2_FW_BOOT_FAILED_MASK (AQ_A2_CRASH_INIT | \
23c1be0bf0SDmitry Bogdanov 				   AQ_A2_BOOT_CODE_FAILED | \
24c1be0bf0SDmitry Bogdanov 				   AQ_A2_FW_INIT_FAILED)
25c1be0bf0SDmitry Bogdanov #define AQ_A2_FW_BOOT_COMPLETE_MASK (AQ_A2_FW_BOOT_FAILED_MASK | \
26c1be0bf0SDmitry Bogdanov 				     AQ_A2_FW_INIT_COMP_SUCCESS)
27c1be0bf0SDmitry Bogdanov 
28c1be0bf0SDmitry Bogdanov #define AQ_A2_FW_BOOT_REQ_REBOOT        BIT(0x0)
29c1be0bf0SDmitry Bogdanov #define AQ_A2_FW_BOOT_REQ_HOST_BOOT     BIT(0x8)
30c1be0bf0SDmitry Bogdanov #define AQ_A2_FW_BOOT_REQ_MAC_FAST_BOOT BIT(0xA)
31c1be0bf0SDmitry Bogdanov #define AQ_A2_FW_BOOT_REQ_PHY_FAST_BOOT BIT(0xB)
32c1be0bf0SDmitry Bogdanov 
33c1be0bf0SDmitry Bogdanov int hw_atl2_utils_initfw(struct aq_hw_s *self, const struct aq_fw_ops **fw_ops)
34c1be0bf0SDmitry Bogdanov {
35c1be0bf0SDmitry Bogdanov 	int err;
36c1be0bf0SDmitry Bogdanov 
37c1be0bf0SDmitry Bogdanov 	self->fw_ver_actual = hw_atl2_utils_get_fw_version(self);
38c1be0bf0SDmitry Bogdanov 
39c1be0bf0SDmitry Bogdanov 	if (hw_atl_utils_ver_match(HW_ATL2_FW_VER_1X,
40c1be0bf0SDmitry Bogdanov 				   self->fw_ver_actual) == 0) {
41c1be0bf0SDmitry Bogdanov 		*fw_ops = &aq_a2_fw_ops;
42c1be0bf0SDmitry Bogdanov 	} else {
43c1be0bf0SDmitry Bogdanov 		aq_pr_err("Bad FW version detected: %x, but continue\n",
44c1be0bf0SDmitry Bogdanov 			  self->fw_ver_actual);
45c1be0bf0SDmitry Bogdanov 		*fw_ops = &aq_a2_fw_ops;
46c1be0bf0SDmitry Bogdanov 	}
47c1be0bf0SDmitry Bogdanov 	aq_pr_trace("Detect ATL2FW %x\n", self->fw_ver_actual);
48c1be0bf0SDmitry Bogdanov 	self->aq_fw_ops = *fw_ops;
49c1be0bf0SDmitry Bogdanov 	err = self->aq_fw_ops->init(self);
50c1be0bf0SDmitry Bogdanov 
51c1be0bf0SDmitry Bogdanov 	self->chip_features |= ATL_HW_CHIP_ANTIGUA;
52c1be0bf0SDmitry Bogdanov 
53c1be0bf0SDmitry Bogdanov 	return err;
54c1be0bf0SDmitry Bogdanov }
55c1be0bf0SDmitry Bogdanov 
56c1be0bf0SDmitry Bogdanov static bool hw_atl2_mcp_boot_complete(struct aq_hw_s *self)
57c1be0bf0SDmitry Bogdanov {
58c1be0bf0SDmitry Bogdanov 	u32 rbl_status;
59c1be0bf0SDmitry Bogdanov 
60c1be0bf0SDmitry Bogdanov 	rbl_status = hw_atl2_mif_mcp_boot_reg_get(self);
61c1be0bf0SDmitry Bogdanov 	if (rbl_status & AQ_A2_FW_BOOT_COMPLETE_MASK)
62c1be0bf0SDmitry Bogdanov 		return true;
63c1be0bf0SDmitry Bogdanov 
64c1be0bf0SDmitry Bogdanov 	/* Host boot requested */
65c1be0bf0SDmitry Bogdanov 	if (hw_atl2_mif_host_req_int_get(self) & HW_ATL2_MCP_HOST_REQ_INT_READY)
66c1be0bf0SDmitry Bogdanov 		return true;
67c1be0bf0SDmitry Bogdanov 
68c1be0bf0SDmitry Bogdanov 	return false;
69c1be0bf0SDmitry Bogdanov }
70c1be0bf0SDmitry Bogdanov 
71c1be0bf0SDmitry Bogdanov int hw_atl2_utils_soft_reset(struct aq_hw_s *self)
72c1be0bf0SDmitry Bogdanov {
73c1be0bf0SDmitry Bogdanov 	bool rbl_complete = false;
74c1be0bf0SDmitry Bogdanov 	u32 rbl_status = 0;
75c1be0bf0SDmitry Bogdanov 	u32 rbl_request;
76c1be0bf0SDmitry Bogdanov 	int err;
77c1be0bf0SDmitry Bogdanov 
78c1be0bf0SDmitry Bogdanov 	err = readx_poll_timeout_atomic(hw_atl2_mif_mcp_boot_reg_get, self,
79c1be0bf0SDmitry Bogdanov 				rbl_status,
80c1be0bf0SDmitry Bogdanov 				((rbl_status & AQ_A2_BOOT_STARTED) &&
81c1be0bf0SDmitry Bogdanov 				 (rbl_status != 0xFFFFFFFFu)),
82c1be0bf0SDmitry Bogdanov 				10, 500000);
83c1be0bf0SDmitry Bogdanov 	if (err)
84c1be0bf0SDmitry Bogdanov 		aq_pr_trace("Boot code probably hanged, reboot anyway");
85c1be0bf0SDmitry Bogdanov 
86c1be0bf0SDmitry Bogdanov 	hw_atl2_mif_host_req_int_clr(self, 0x01);
87c1be0bf0SDmitry Bogdanov 	rbl_request = AQ_A2_FW_BOOT_REQ_REBOOT;
88c1be0bf0SDmitry Bogdanov #ifdef AQ_CFG_FAST_START
89c1be0bf0SDmitry Bogdanov 	rbl_request |= AQ_A2_FW_BOOT_REQ_MAC_FAST_BOOT;
90c1be0bf0SDmitry Bogdanov #endif
91c1be0bf0SDmitry Bogdanov 	hw_atl2_mif_mcp_boot_reg_set(self, rbl_request);
92c1be0bf0SDmitry Bogdanov 
93c1be0bf0SDmitry Bogdanov 	/* Wait for RBL boot */
94c1be0bf0SDmitry Bogdanov 	err = readx_poll_timeout_atomic(hw_atl2_mif_mcp_boot_reg_get, self,
95c1be0bf0SDmitry Bogdanov 				rbl_status,
96c1be0bf0SDmitry Bogdanov 				((rbl_status & AQ_A2_BOOT_STARTED) &&
97c1be0bf0SDmitry Bogdanov 				 (rbl_status != 0xFFFFFFFFu)),
98c1be0bf0SDmitry Bogdanov 				10, 200000);
99c1be0bf0SDmitry Bogdanov 	if (err) {
100c1be0bf0SDmitry Bogdanov 		aq_pr_err("Boot code hanged");
101c1be0bf0SDmitry Bogdanov 		goto err_exit;
102c1be0bf0SDmitry Bogdanov 	}
103c1be0bf0SDmitry Bogdanov 
104c1be0bf0SDmitry Bogdanov 	err = readx_poll_timeout_atomic(hw_atl2_mcp_boot_complete, self,
105c1be0bf0SDmitry Bogdanov 					rbl_complete,
106c1be0bf0SDmitry Bogdanov 					rbl_complete,
107c1be0bf0SDmitry Bogdanov 					10, 2000000);
108c1be0bf0SDmitry Bogdanov 
109c1be0bf0SDmitry Bogdanov 	if (err) {
110c1be0bf0SDmitry Bogdanov 		aq_pr_err("FW Restart timed out");
111c1be0bf0SDmitry Bogdanov 		goto err_exit;
112c1be0bf0SDmitry Bogdanov 	}
113c1be0bf0SDmitry Bogdanov 
114c1be0bf0SDmitry Bogdanov 	rbl_status = hw_atl2_mif_mcp_boot_reg_get(self);
115c1be0bf0SDmitry Bogdanov 
116c1be0bf0SDmitry Bogdanov 	if (rbl_status & AQ_A2_FW_BOOT_FAILED_MASK) {
117c1be0bf0SDmitry Bogdanov 		err = -EIO;
118c1be0bf0SDmitry Bogdanov 		aq_pr_err("FW Restart failed");
119c1be0bf0SDmitry Bogdanov 		goto err_exit;
120c1be0bf0SDmitry Bogdanov 	}
121c1be0bf0SDmitry Bogdanov 
122c1be0bf0SDmitry Bogdanov 	if (hw_atl2_mif_host_req_int_get(self) &
123c1be0bf0SDmitry Bogdanov 	    HW_ATL2_MCP_HOST_REQ_INT_READY) {
124c1be0bf0SDmitry Bogdanov 		err = -EIO;
125c1be0bf0SDmitry Bogdanov 		aq_pr_err("No FW detected. Dynamic FW load not implemented");
126c1be0bf0SDmitry Bogdanov 		goto err_exit;
127c1be0bf0SDmitry Bogdanov 	}
128c1be0bf0SDmitry Bogdanov 
129c1be0bf0SDmitry Bogdanov 	if (self->aq_fw_ops) {
130c1be0bf0SDmitry Bogdanov 		err = self->aq_fw_ops->init(self);
131c1be0bf0SDmitry Bogdanov 		if (err) {
132c1be0bf0SDmitry Bogdanov 			aq_pr_err("FW Init failed");
133c1be0bf0SDmitry Bogdanov 			goto err_exit;
134c1be0bf0SDmitry Bogdanov 		}
135c1be0bf0SDmitry Bogdanov 	}
136c1be0bf0SDmitry Bogdanov 
137c1be0bf0SDmitry Bogdanov err_exit:
138c1be0bf0SDmitry Bogdanov 	return err;
139c1be0bf0SDmitry Bogdanov }
140