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 | |
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, ver_actual: 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 | |
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(aq_hw: 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(aq_hw: self) & HW_ATL2_MCP_HOST_REQ_INT_READY) |
65 | return true; |
66 | |
67 | return false; |
68 | } |
69 | |
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(aq_hw: self, val: 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(aq_hw: self, val: 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(aq_hw: 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(aq_hw: 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 | |