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