]>
Commit | Line | Data |
---|---|---|
5cfd54d7 DB |
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.h" | |
b4de6c49 | 9 | #include "aq_hw_utils.h" |
5cfd54d7 DB |
10 | #include "hw_atl/hw_atl_llh.h" |
11 | #include "hw_atl2_utils.h" | |
12 | #include "hw_atl2_llh.h" | |
13 | #include "hw_atl2_internal.h" | |
14 | ||
15 | #define AQ_A2_FW_READ_TRY_MAX 1000 | |
16 | ||
17 | #define hw_atl2_shared_buffer_write(HW, ITEM, VARIABLE) \ | |
18 | hw_atl2_mif_shared_buf_write(HW,\ | |
19 | (offsetof(struct fw_interface_in, ITEM) / sizeof(u32)),\ | |
20 | (u32 *)&(VARIABLE), sizeof(VARIABLE) / sizeof(u32)) | |
21 | ||
22 | #define hw_atl2_shared_buffer_get(HW, ITEM, VARIABLE) \ | |
23 | hw_atl2_mif_shared_buf_get(HW, \ | |
24 | (offsetof(struct fw_interface_in, ITEM) / sizeof(u32)),\ | |
25 | (u32 *)&(VARIABLE), \ | |
26 | sizeof(VARIABLE) / sizeof(u32)) | |
27 | ||
28 | /* This should never be used on non atomic fields, | |
29 | * treat any > u32 read as non atomic. | |
30 | */ | |
31 | #define hw_atl2_shared_buffer_read(HW, ITEM, VARIABLE) \ | |
32 | {\ | |
33 | BUILD_BUG_ON_MSG((offsetof(struct fw_interface_out, ITEM) % \ | |
34 | sizeof(u32)) != 0,\ | |
35 | "Non aligned read " # ITEM);\ | |
36 | BUILD_BUG_ON_MSG(sizeof(VARIABLE) > sizeof(u32),\ | |
37 | "Non atomic read " # ITEM);\ | |
38 | hw_atl2_mif_shared_buf_read(HW, \ | |
39 | (offsetof(struct fw_interface_out, ITEM) / sizeof(u32)),\ | |
40 | (u32 *)&(VARIABLE), sizeof(VARIABLE) / sizeof(u32));\ | |
41 | } | |
42 | ||
43 | #define hw_atl2_shared_buffer_read_safe(HW, ITEM, DATA) \ | |
44 | hw_atl2_shared_buffer_read_block((HW), \ | |
45 | (offsetof(struct fw_interface_out, ITEM) / sizeof(u32)),\ | |
46 | sizeof(((struct fw_interface_out *)0)->ITEM) / sizeof(u32),\ | |
47 | (DATA)) | |
48 | ||
49 | static int hw_atl2_shared_buffer_read_block(struct aq_hw_s *self, | |
50 | u32 offset, u32 dwords, void *data) | |
51 | { | |
52 | struct transaction_counter_s tid1, tid2; | |
53 | int cnt = 0; | |
54 | ||
55 | do { | |
56 | do { | |
57 | hw_atl2_shared_buffer_read(self, transaction_id, tid1); | |
58 | cnt++; | |
59 | if (cnt > AQ_A2_FW_READ_TRY_MAX) | |
60 | return -ETIME; | |
61 | if (tid1.transaction_cnt_a != tid1.transaction_cnt_b) | |
62 | udelay(1); | |
63 | } while (tid1.transaction_cnt_a != tid1.transaction_cnt_b); | |
64 | ||
65 | hw_atl2_mif_shared_buf_read(self, offset, (u32 *)data, dwords); | |
66 | ||
67 | hw_atl2_shared_buffer_read(self, transaction_id, tid2); | |
68 | ||
69 | cnt++; | |
70 | if (cnt > AQ_A2_FW_READ_TRY_MAX) | |
71 | return -ETIME; | |
72 | } while (tid2.transaction_cnt_a != tid2.transaction_cnt_b || | |
73 | tid1.transaction_cnt_a != tid2.transaction_cnt_a); | |
74 | ||
75 | return 0; | |
76 | } | |
77 | ||
78 | static inline int hw_atl2_shared_buffer_finish_ack(struct aq_hw_s *self) | |
79 | { | |
80 | u32 val; | |
81 | int err; | |
82 | ||
83 | hw_atl2_mif_host_finished_write_set(self, 1U); | |
84 | err = readx_poll_timeout_atomic(hw_atl2_mif_mcp_finished_read_get, | |
85 | self, val, val == 0U, | |
86 | 100, 100000U); | |
87 | WARN(err, "hw_atl2_shared_buffer_finish_ack"); | |
88 | ||
89 | return err; | |
90 | } | |
91 | ||
92 | static int aq_a2_fw_init(struct aq_hw_s *self) | |
93 | { | |
94 | struct link_control_s link_control; | |
95 | u32 mtu; | |
96 | u32 val; | |
97 | int err; | |
98 | ||
99 | hw_atl2_shared_buffer_get(self, link_control, link_control); | |
100 | link_control.mode = AQ_HOST_MODE_ACTIVE; | |
101 | hw_atl2_shared_buffer_write(self, link_control, link_control); | |
102 | ||
103 | hw_atl2_shared_buffer_get(self, mtu, mtu); | |
104 | mtu = HW_ATL2_MTU_JUMBO; | |
105 | hw_atl2_shared_buffer_write(self, mtu, mtu); | |
106 | ||
107 | hw_atl2_mif_host_finished_write_set(self, 1U); | |
108 | err = readx_poll_timeout_atomic(hw_atl2_mif_mcp_finished_read_get, | |
109 | self, val, val == 0U, | |
110 | 100, 5000000U); | |
111 | WARN(err, "hw_atl2_shared_buffer_finish_ack"); | |
112 | ||
113 | return err; | |
114 | } | |
115 | ||
116 | static int aq_a2_fw_deinit(struct aq_hw_s *self) | |
117 | { | |
118 | struct link_control_s link_control; | |
119 | ||
120 | hw_atl2_shared_buffer_get(self, link_control, link_control); | |
121 | link_control.mode = AQ_HOST_MODE_SHUTDOWN; | |
122 | hw_atl2_shared_buffer_write(self, link_control, link_control); | |
123 | ||
124 | return hw_atl2_shared_buffer_finish_ack(self); | |
125 | } | |
126 | ||
127 | static void a2_link_speed_mask2fw(u32 speed, | |
128 | struct link_options_s *link_options) | |
129 | { | |
130 | link_options->rate_10G = !!(speed & AQ_NIC_RATE_10G); | |
131 | link_options->rate_5G = !!(speed & AQ_NIC_RATE_5G); | |
132 | link_options->rate_N5G = !!(speed & AQ_NIC_RATE_5GSR); | |
843e1396 | 133 | link_options->rate_2P5G = !!(speed & AQ_NIC_RATE_2G5); |
5cfd54d7 DB |
134 | link_options->rate_N2P5G = link_options->rate_2P5G; |
135 | link_options->rate_1G = !!(speed & AQ_NIC_RATE_1G); | |
136 | link_options->rate_100M = !!(speed & AQ_NIC_RATE_100M); | |
137 | link_options->rate_10M = !!(speed & AQ_NIC_RATE_10M); | |
071a0204 IR |
138 | |
139 | link_options->rate_1G_hd = !!(speed & AQ_NIC_RATE_1G_HALF); | |
140 | link_options->rate_100M_hd = !!(speed & AQ_NIC_RATE_100M_HALF); | |
141 | link_options->rate_10M_hd = !!(speed & AQ_NIC_RATE_10M_HALF); | |
5cfd54d7 DB |
142 | } |
143 | ||
144 | static int aq_a2_fw_set_link_speed(struct aq_hw_s *self, u32 speed) | |
145 | { | |
146 | struct link_options_s link_options; | |
147 | ||
148 | hw_atl2_shared_buffer_get(self, link_options, link_options); | |
149 | link_options.link_up = 1U; | |
150 | a2_link_speed_mask2fw(speed, &link_options); | |
151 | hw_atl2_shared_buffer_write(self, link_options, link_options); | |
152 | ||
153 | return hw_atl2_shared_buffer_finish_ack(self); | |
154 | } | |
155 | ||
156 | static int aq_a2_fw_set_state(struct aq_hw_s *self, | |
157 | enum hal_atl_utils_fw_state_e state) | |
158 | { | |
159 | struct link_options_s link_options; | |
160 | ||
161 | hw_atl2_shared_buffer_get(self, link_options, link_options); | |
162 | ||
163 | switch (state) { | |
164 | case MPI_INIT: | |
165 | link_options.link_up = 1U; | |
166 | break; | |
167 | case MPI_DEINIT: | |
168 | link_options.link_up = 0U; | |
169 | break; | |
170 | case MPI_RESET: | |
171 | case MPI_POWER: | |
172 | /* No actions */ | |
173 | break; | |
174 | } | |
175 | ||
176 | hw_atl2_shared_buffer_write(self, link_options, link_options); | |
177 | ||
178 | return hw_atl2_shared_buffer_finish_ack(self); | |
179 | } | |
180 | ||
181 | static int aq_a2_fw_update_link_status(struct aq_hw_s *self) | |
182 | { | |
183 | struct link_status_s link_status; | |
184 | ||
185 | hw_atl2_shared_buffer_read(self, link_status, link_status); | |
186 | ||
187 | switch (link_status.link_rate) { | |
188 | case AQ_A2_FW_LINK_RATE_10G: | |
189 | self->aq_link_status.mbps = 10000; | |
190 | break; | |
191 | case AQ_A2_FW_LINK_RATE_5G: | |
192 | self->aq_link_status.mbps = 5000; | |
193 | break; | |
194 | case AQ_A2_FW_LINK_RATE_2G5: | |
195 | self->aq_link_status.mbps = 2500; | |
196 | break; | |
197 | case AQ_A2_FW_LINK_RATE_1G: | |
198 | self->aq_link_status.mbps = 1000; | |
199 | break; | |
200 | case AQ_A2_FW_LINK_RATE_100M: | |
201 | self->aq_link_status.mbps = 100; | |
202 | break; | |
203 | case AQ_A2_FW_LINK_RATE_10M: | |
204 | self->aq_link_status.mbps = 10; | |
205 | break; | |
206 | default: | |
207 | self->aq_link_status.mbps = 0; | |
208 | } | |
071a0204 | 209 | self->aq_link_status.full_duplex = link_status.duplex; |
5cfd54d7 DB |
210 | |
211 | return 0; | |
212 | } | |
213 | ||
214 | static int aq_a2_fw_get_mac_permanent(struct aq_hw_s *self, u8 *mac) | |
215 | { | |
216 | struct mac_address_aligned_s mac_address; | |
217 | ||
218 | hw_atl2_shared_buffer_get(self, mac_address, mac_address); | |
219 | ether_addr_copy(mac, (u8 *)mac_address.aligned.mac_address); | |
220 | ||
5cfd54d7 DB |
221 | return 0; |
222 | } | |
223 | ||
224 | static int aq_a2_fw_update_stats(struct aq_hw_s *self) | |
225 | { | |
226 | struct hw_atl2_priv *priv = (struct hw_atl2_priv *)self->priv; | |
227 | struct statistics_s stats; | |
228 | ||
229 | hw_atl2_shared_buffer_read_safe(self, stats, &stats); | |
230 | ||
231 | #define AQ_SDELTA(_N_, _F_) (self->curr_stats._N_ += \ | |
232 | stats.msm._F_ - priv->last_stats.msm._F_) | |
233 | ||
234 | if (self->aq_link_status.mbps) { | |
235 | AQ_SDELTA(uprc, rx_unicast_frames); | |
236 | AQ_SDELTA(mprc, rx_multicast_frames); | |
237 | AQ_SDELTA(bprc, rx_broadcast_frames); | |
238 | AQ_SDELTA(erpr, rx_error_frames); | |
239 | ||
240 | AQ_SDELTA(uptc, tx_unicast_frames); | |
241 | AQ_SDELTA(mptc, tx_multicast_frames); | |
242 | AQ_SDELTA(bptc, tx_broadcast_frames); | |
243 | AQ_SDELTA(erpt, tx_errors); | |
244 | ||
245 | AQ_SDELTA(ubrc, rx_unicast_octets); | |
246 | AQ_SDELTA(ubtc, tx_unicast_octets); | |
247 | AQ_SDELTA(mbrc, rx_multicast_octets); | |
248 | AQ_SDELTA(mbtc, tx_multicast_octets); | |
249 | AQ_SDELTA(bbrc, rx_broadcast_octets); | |
250 | AQ_SDELTA(bbtc, tx_broadcast_octets); | |
251 | } | |
252 | #undef AQ_SDELTA | |
253 | self->curr_stats.dma_pkt_rc = | |
254 | hw_atl_stats_rx_dma_good_pkt_counter_get(self); | |
255 | self->curr_stats.dma_pkt_tc = | |
256 | hw_atl_stats_tx_dma_good_pkt_counter_get(self); | |
257 | self->curr_stats.dma_oct_rc = | |
258 | hw_atl_stats_rx_dma_good_octet_counter_get(self); | |
259 | self->curr_stats.dma_oct_tc = | |
260 | hw_atl_stats_tx_dma_good_octet_counter_get(self); | |
261 | self->curr_stats.dpc = hw_atl_rpb_rx_dma_drop_pkt_cnt_get(self); | |
262 | ||
263 | memcpy(&priv->last_stats, &stats, sizeof(stats)); | |
264 | ||
265 | return 0; | |
266 | } | |
267 | ||
268 | static int aq_a2_fw_renegotiate(struct aq_hw_s *self) | |
269 | { | |
270 | struct link_options_s link_options; | |
271 | int err; | |
272 | ||
273 | hw_atl2_shared_buffer_get(self, link_options, link_options); | |
274 | link_options.link_renegotiate = 1U; | |
275 | hw_atl2_shared_buffer_write(self, link_options, link_options); | |
276 | ||
277 | err = hw_atl2_shared_buffer_finish_ack(self); | |
278 | ||
279 | /* We should put renegotiate status back to zero | |
280 | * after command completes | |
281 | */ | |
282 | link_options.link_renegotiate = 0U; | |
283 | hw_atl2_shared_buffer_write(self, link_options, link_options); | |
284 | ||
285 | return err; | |
286 | } | |
287 | ||
c1be0bf0 DB |
288 | u32 hw_atl2_utils_get_fw_version(struct aq_hw_s *self) |
289 | { | |
290 | struct version_s version; | |
291 | ||
292 | hw_atl2_shared_buffer_read_safe(self, version, &version); | |
293 | ||
294 | /* A2 FW version is stored in reverse order */ | |
295 | return version.mac.major << 24 | | |
296 | version.mac.minor << 16 | | |
297 | version.mac.build; | |
298 | } | |
299 | ||
5cfd54d7 DB |
300 | int hw_atl2_utils_get_action_resolve_table_caps(struct aq_hw_s *self, |
301 | u8 *base_index, u8 *count) | |
302 | { | |
303 | struct filter_caps_s filter_caps; | |
304 | int err; | |
305 | ||
306 | err = hw_atl2_shared_buffer_read_safe(self, filter_caps, &filter_caps); | |
307 | if (err) | |
308 | return err; | |
309 | ||
310 | *base_index = filter_caps.rslv_tbl_base_index; | |
311 | *count = filter_caps.rslv_tbl_count; | |
312 | return 0; | |
313 | } | |
314 | ||
315 | const struct aq_fw_ops aq_a2_fw_ops = { | |
316 | .init = aq_a2_fw_init, | |
317 | .deinit = aq_a2_fw_deinit, | |
318 | .reset = NULL, | |
319 | .renegotiate = aq_a2_fw_renegotiate, | |
320 | .get_mac_permanent = aq_a2_fw_get_mac_permanent, | |
321 | .set_link_speed = aq_a2_fw_set_link_speed, | |
322 | .set_state = aq_a2_fw_set_state, | |
323 | .update_link_status = aq_a2_fw_update_link_status, | |
324 | .update_stats = aq_a2_fw_update_stats, | |
325 | }; |