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
33int 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
55static 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
70int 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
128err_exit:
129 return err;
130}
131

source code of linux/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2_utils.c