]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | /* SPDX-License-Identifier: BSD-3-Clause |
2 | * | |
f67539c2 TL |
3 | * Copyright(c) 2019-2020 Xilinx, Inc. |
4 | * Copyright(c) 2012-2019 Solarflare Communications Inc. | |
11fdf7f2 TL |
5 | */ |
6 | ||
7 | #include "efx.h" | |
8 | #include "efx_impl.h" | |
9 | ||
10 | ||
9f95a23c | 11 | #if EFX_OPTS_EF10() |
11fdf7f2 TL |
12 | |
13 | #if EFSYS_OPT_MCDI | |
14 | ||
15 | #ifndef WITH_MCDI_V2 | |
16 | #error "WITH_MCDI_V2 required for EF10 MCDIv2 commands." | |
17 | #endif | |
18 | ||
19 | ||
20 | __checkReturn efx_rc_t | |
21 | ef10_mcdi_init( | |
22 | __in efx_nic_t *enp, | |
23 | __in const efx_mcdi_transport_t *emtp) | |
24 | { | |
25 | efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); | |
26 | efsys_mem_t *esmp = emtp->emt_dma_mem; | |
27 | efx_dword_t dword; | |
28 | efx_rc_t rc; | |
29 | ||
9f95a23c | 30 | EFSYS_ASSERT(EFX_FAMILY_IS_EF10(enp)); |
11fdf7f2 TL |
31 | EFSYS_ASSERT(enp->en_features & EFX_FEATURE_MCDI_DMA); |
32 | ||
33 | /* | |
34 | * All EF10 firmware supports MCDIv2 and MCDIv1. | |
35 | * Medford BootROM supports MCDIv2 and MCDIv1. | |
36 | * Huntington BootROM supports MCDIv1 only. | |
37 | */ | |
38 | emip->emi_max_version = 2; | |
39 | ||
40 | /* A host DMA buffer is required for EF10 MCDI */ | |
41 | if (esmp == NULL) { | |
42 | rc = EINVAL; | |
43 | goto fail1; | |
44 | } | |
45 | ||
46 | /* | |
47 | * Ensure that the MC doorbell is in a known state before issuing MCDI | |
48 | * commands. The recovery algorithm requires that the MC command buffer | |
49 | * must be 256 byte aligned. See bug24769. | |
50 | */ | |
51 | if ((EFSYS_MEM_ADDR(esmp) & 0xFF) != 0) { | |
52 | rc = EINVAL; | |
53 | goto fail2; | |
54 | } | |
55 | EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 1); | |
56 | EFX_BAR_WRITED(enp, ER_DZ_MC_DB_HWRD_REG, &dword, B_FALSE); | |
57 | ||
58 | /* Save initial MC reboot status */ | |
59 | (void) ef10_mcdi_poll_reboot(enp); | |
60 | ||
61 | /* Start a new epoch (allow fresh MCDI requests to succeed) */ | |
62 | efx_mcdi_new_epoch(enp); | |
63 | ||
64 | return (0); | |
65 | ||
66 | fail2: | |
67 | EFSYS_PROBE(fail2); | |
68 | fail1: | |
69 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
70 | ||
71 | return (rc); | |
72 | } | |
73 | ||
74 | void | |
75 | ef10_mcdi_fini( | |
76 | __in efx_nic_t *enp) | |
77 | { | |
78 | efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); | |
79 | ||
80 | emip->emi_new_epoch = B_FALSE; | |
81 | } | |
82 | ||
83 | /* | |
84 | * In older firmware all commands are processed in a single thread, so a long | |
85 | * running command for one PCIe function can block processing for another | |
86 | * function (see bug 61269). | |
87 | * | |
88 | * In newer firmware that supports multithreaded MCDI processing, we can extend | |
89 | * the timeout for long-running requests which we know firmware may choose to | |
90 | * process in a background thread. | |
91 | */ | |
92 | #define EF10_MCDI_CMD_TIMEOUT_US (10 * 1000 * 1000) | |
93 | #define EF10_MCDI_CMD_LONG_TIMEOUT_US (60 * 1000 * 1000) | |
94 | ||
95 | void | |
96 | ef10_mcdi_get_timeout( | |
97 | __in efx_nic_t *enp, | |
98 | __in efx_mcdi_req_t *emrp, | |
99 | __out uint32_t *timeoutp) | |
100 | { | |
101 | efx_nic_cfg_t *encp = &(enp->en_nic_cfg); | |
102 | ||
103 | switch (emrp->emr_cmd) { | |
104 | case MC_CMD_POLL_BIST: | |
105 | case MC_CMD_NVRAM_ERASE: | |
106 | case MC_CMD_LICENSING_V3: | |
107 | case MC_CMD_NVRAM_UPDATE_FINISH: | |
108 | if (encp->enc_nvram_update_verify_result_supported != B_FALSE) { | |
109 | /* | |
110 | * Potentially longer running commands, which firmware | |
111 | * may choose to process in a background thread. | |
112 | */ | |
113 | *timeoutp = EF10_MCDI_CMD_LONG_TIMEOUT_US; | |
114 | break; | |
115 | } | |
116 | /* FALLTHRU */ | |
117 | default: | |
118 | *timeoutp = EF10_MCDI_CMD_TIMEOUT_US; | |
119 | break; | |
120 | } | |
121 | } | |
122 | ||
123 | void | |
124 | ef10_mcdi_send_request( | |
125 | __in efx_nic_t *enp, | |
126 | __in_bcount(hdr_len) void *hdrp, | |
127 | __in size_t hdr_len, | |
128 | __in_bcount(sdu_len) void *sdup, | |
129 | __in size_t sdu_len) | |
130 | { | |
131 | const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp; | |
132 | efsys_mem_t *esmp = emtp->emt_dma_mem; | |
133 | efx_dword_t dword; | |
134 | unsigned int pos; | |
135 | ||
9f95a23c | 136 | EFSYS_ASSERT(EFX_FAMILY_IS_EF10(enp)); |
11fdf7f2 TL |
137 | |
138 | /* Write the header */ | |
139 | for (pos = 0; pos < hdr_len; pos += sizeof (efx_dword_t)) { | |
140 | dword = *(efx_dword_t *)((uint8_t *)hdrp + pos); | |
141 | EFSYS_MEM_WRITED(esmp, pos, &dword); | |
142 | } | |
143 | ||
144 | /* Write the payload */ | |
145 | for (pos = 0; pos < sdu_len; pos += sizeof (efx_dword_t)) { | |
146 | dword = *(efx_dword_t *)((uint8_t *)sdup + pos); | |
147 | EFSYS_MEM_WRITED(esmp, hdr_len + pos, &dword); | |
148 | } | |
149 | ||
150 | /* Guarantee ordering of memory (MCDI request) and PIO (MC doorbell) */ | |
151 | EFSYS_DMA_SYNC_FOR_DEVICE(esmp, 0, hdr_len + sdu_len); | |
152 | EFSYS_PIO_WRITE_BARRIER(); | |
153 | ||
154 | /* Ring the doorbell to post the command DMA address to the MC */ | |
155 | EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, | |
156 | EFSYS_MEM_ADDR(esmp) >> 32); | |
157 | EFX_BAR_WRITED(enp, ER_DZ_MC_DB_LWRD_REG, &dword, B_FALSE); | |
158 | ||
159 | EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, | |
160 | EFSYS_MEM_ADDR(esmp) & 0xffffffff); | |
161 | EFX_BAR_WRITED(enp, ER_DZ_MC_DB_HWRD_REG, &dword, B_FALSE); | |
162 | } | |
163 | ||
164 | __checkReturn boolean_t | |
165 | ef10_mcdi_poll_response( | |
166 | __in efx_nic_t *enp) | |
167 | { | |
168 | const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp; | |
169 | efsys_mem_t *esmp = emtp->emt_dma_mem; | |
170 | efx_dword_t hdr; | |
171 | ||
172 | EFSYS_MEM_READD(esmp, 0, &hdr); | |
173 | EFSYS_MEM_READ_BARRIER(); | |
174 | ||
175 | return (EFX_DWORD_FIELD(hdr, MCDI_HEADER_RESPONSE) ? B_TRUE : B_FALSE); | |
176 | } | |
177 | ||
178 | void | |
179 | ef10_mcdi_read_response( | |
180 | __in efx_nic_t *enp, | |
181 | __out_bcount(length) void *bufferp, | |
182 | __in size_t offset, | |
183 | __in size_t length) | |
184 | { | |
185 | const efx_mcdi_transport_t *emtp = enp->en_mcdi.em_emtp; | |
186 | efsys_mem_t *esmp = emtp->emt_dma_mem; | |
187 | unsigned int pos = 0; | |
188 | efx_dword_t data; | |
189 | size_t remaining = length; | |
190 | ||
191 | while (remaining > 0) { | |
192 | size_t chunk = MIN(remaining, sizeof (data)); | |
193 | ||
194 | EFSYS_MEM_READD(esmp, offset + pos, &data); | |
195 | memcpy((uint8_t *)bufferp + pos, &data, chunk); | |
196 | pos += chunk; | |
197 | remaining -= chunk; | |
198 | } | |
199 | } | |
200 | ||
201 | efx_rc_t | |
202 | ef10_mcdi_poll_reboot( | |
203 | __in efx_nic_t *enp) | |
204 | { | |
205 | efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); | |
206 | efx_dword_t dword; | |
207 | uint32_t old_status; | |
208 | uint32_t new_status; | |
209 | efx_rc_t rc; | |
210 | ||
211 | old_status = emip->emi_mc_reboot_status; | |
212 | ||
213 | /* Update MC reboot status word */ | |
214 | EFX_BAR_TBL_READD(enp, ER_DZ_BIU_MC_SFT_STATUS_REG, 0, &dword, B_FALSE); | |
215 | new_status = dword.ed_u32[0]; | |
216 | ||
217 | /* MC has rebooted if the value has changed */ | |
218 | if (new_status != old_status) { | |
219 | emip->emi_mc_reboot_status = new_status; | |
220 | ||
221 | /* | |
222 | * FIXME: Ignore detected MC REBOOT for now. | |
223 | * | |
224 | * The Siena support for checking for MC reboot from status | |
225 | * flags is broken - see comments in siena_mcdi_poll_reboot(). | |
226 | * As the generic MCDI code is shared the EF10 reboot | |
227 | * detection suffers similar problems. | |
228 | * | |
229 | * Do not report an error when the boot status changes until | |
230 | * this can be handled by common code drivers (and reworked to | |
231 | * support Siena too). | |
232 | */ | |
233 | _NOTE(CONSTANTCONDITION) | |
234 | if (B_FALSE) { | |
235 | rc = EIO; | |
236 | goto fail1; | |
237 | } | |
238 | } | |
239 | ||
240 | return (0); | |
241 | ||
242 | fail1: | |
243 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
244 | ||
245 | return (rc); | |
246 | } | |
247 | ||
248 | __checkReturn efx_rc_t | |
249 | ef10_mcdi_feature_supported( | |
250 | __in efx_nic_t *enp, | |
251 | __in efx_mcdi_feature_id_t id, | |
252 | __out boolean_t *supportedp) | |
253 | { | |
254 | efx_nic_cfg_t *encp = &(enp->en_nic_cfg); | |
255 | uint32_t privilege_mask = encp->enc_privilege_mask; | |
256 | efx_rc_t rc; | |
257 | ||
9f95a23c | 258 | EFSYS_ASSERT(EFX_FAMILY_IS_EF10(enp)); |
11fdf7f2 TL |
259 | |
260 | /* | |
261 | * Use privilege mask state at MCDI attach. | |
262 | */ | |
263 | ||
264 | switch (id) { | |
265 | case EFX_MCDI_FEATURE_FW_UPDATE: | |
266 | /* | |
267 | * Admin privilege must be used prior to introduction of | |
268 | * specific flag. | |
269 | */ | |
270 | *supportedp = | |
271 | EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, ADMIN); | |
272 | break; | |
273 | case EFX_MCDI_FEATURE_LINK_CONTROL: | |
274 | /* | |
275 | * Admin privilege used prior to introduction of | |
276 | * specific flag. | |
277 | */ | |
278 | *supportedp = | |
279 | EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, LINK) || | |
280 | EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, ADMIN); | |
281 | break; | |
282 | case EFX_MCDI_FEATURE_MACADDR_CHANGE: | |
283 | /* | |
284 | * Admin privilege must be used prior to introduction of | |
285 | * mac spoofing privilege (at v4.6), which is used up to | |
286 | * introduction of change mac spoofing privilege (at v4.7) | |
287 | */ | |
288 | *supportedp = | |
289 | EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, CHANGE_MAC) || | |
290 | EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, MAC_SPOOFING) || | |
291 | EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, ADMIN); | |
292 | break; | |
293 | case EFX_MCDI_FEATURE_MAC_SPOOFING: | |
294 | /* | |
295 | * Admin privilege must be used prior to introduction of | |
296 | * mac spoofing privilege (at v4.6), which is used up to | |
297 | * introduction of mac spoofing TX privilege (at v4.7) | |
298 | */ | |
299 | *supportedp = | |
300 | EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, MAC_SPOOFING_TX) || | |
301 | EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, MAC_SPOOFING) || | |
302 | EFX_MCDI_HAVE_PRIVILEGE(privilege_mask, ADMIN); | |
303 | break; | |
304 | default: | |
305 | rc = ENOTSUP; | |
306 | goto fail1; | |
307 | } | |
308 | ||
309 | return (0); | |
310 | ||
311 | fail1: | |
312 | EFSYS_PROBE1(fail1, efx_rc_t, rc); | |
313 | ||
314 | return (rc); | |
315 | } | |
316 | ||
317 | #endif /* EFSYS_OPT_MCDI */ | |
318 | ||
9f95a23c | 319 | #endif /* EFX_OPTS_EF10() */ |