1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
2 | /* Copyright (C) 2015-2018 Netronome Systems, Inc. */ |
3 | |
4 | /* |
5 | * nfp_nsp.c |
6 | * Author: Jakub Kicinski <jakub.kicinski@netronome.com> |
7 | * Jason McMullan <jason.mcmullan@netronome.com> |
8 | */ |
9 | |
10 | #include <asm/unaligned.h> |
11 | #include <linux/bitfield.h> |
12 | #include <linux/delay.h> |
13 | #include <linux/firmware.h> |
14 | #include <linux/kernel.h> |
15 | #include <linux/kthread.h> |
16 | #include <linux/overflow.h> |
17 | #include <linux/sizes.h> |
18 | #include <linux/slab.h> |
19 | |
20 | #define NFP_SUBSYS "nfp_nsp" |
21 | |
22 | #include "nfp.h" |
23 | #include "nfp_cpp.h" |
24 | #include "nfp_nsp.h" |
25 | |
26 | #define NFP_NSP_TIMEOUT_DEFAULT 30 |
27 | #define NFP_NSP_TIMEOUT_BOOT 30 |
28 | |
29 | /* Offsets relative to the CSR base */ |
30 | #define NSP_STATUS 0x00 |
31 | #define NSP_STATUS_MAGIC GENMASK_ULL(63, 48) |
32 | #define NSP_STATUS_MAJOR GENMASK_ULL(47, 44) |
33 | #define NSP_STATUS_MINOR GENMASK_ULL(43, 32) |
34 | #define NSP_STATUS_CODE GENMASK_ULL(31, 16) |
35 | #define NSP_STATUS_RESULT GENMASK_ULL(15, 8) |
36 | #define NSP_STATUS_BUSY BIT_ULL(0) |
37 | |
38 | #define NSP_COMMAND 0x08 |
39 | #define NSP_COMMAND_OPTION GENMASK_ULL(63, 32) |
40 | #define NSP_COMMAND_CODE GENMASK_ULL(31, 16) |
41 | #define NSP_COMMAND_DMA_BUF BIT_ULL(1) |
42 | #define NSP_COMMAND_START BIT_ULL(0) |
43 | |
44 | /* CPP address to retrieve the data from */ |
45 | #define NSP_BUFFER 0x10 |
46 | #define NSP_BUFFER_CPP GENMASK_ULL(63, 40) |
47 | #define NSP_BUFFER_ADDRESS GENMASK_ULL(39, 0) |
48 | |
49 | #define NSP_DFLT_BUFFER 0x18 |
50 | #define NSP_DFLT_BUFFER_CPP GENMASK_ULL(63, 40) |
51 | #define NSP_DFLT_BUFFER_ADDRESS GENMASK_ULL(39, 0) |
52 | |
53 | #define NSP_DFLT_BUFFER_CONFIG 0x20 |
54 | #define NSP_DFLT_BUFFER_DMA_CHUNK_ORDER GENMASK_ULL(63, 58) |
55 | #define NSP_DFLT_BUFFER_SIZE_4KB GENMASK_ULL(15, 8) |
56 | #define NSP_DFLT_BUFFER_SIZE_MB GENMASK_ULL(7, 0) |
57 | |
58 | #define NFP_CAP_CMD_DMA_SG 0x28 |
59 | |
60 | #define NSP_MAGIC 0xab10 |
61 | #define NSP_MAJOR 0 |
62 | #define NSP_MINOR 8 |
63 | |
64 | #define NSP_CODE_MAJOR GENMASK(15, 12) |
65 | #define NSP_CODE_MINOR GENMASK(11, 0) |
66 | |
67 | #define NFP_FW_LOAD_RET_MAJOR GENMASK(15, 8) |
68 | #define NFP_FW_LOAD_RET_MINOR GENMASK(23, 16) |
69 | |
70 | #define NFP_HWINFO_LOOKUP_SIZE GENMASK(11, 0) |
71 | |
72 | #define NFP_VERSIONS_SIZE GENMASK(11, 0) |
73 | #define NFP_VERSIONS_CNT_OFF 0 |
74 | #define NFP_VERSIONS_BSP_OFF 2 |
75 | #define NFP_VERSIONS_CPLD_OFF 6 |
76 | #define NFP_VERSIONS_APP_OFF 10 |
77 | #define NFP_VERSIONS_BUNDLE_OFF 14 |
78 | #define NFP_VERSIONS_UNDI_OFF 18 |
79 | #define NFP_VERSIONS_NCSI_OFF 22 |
80 | #define NFP_VERSIONS_CFGR_OFF 26 |
81 | |
82 | #define NSP_SFF_EEPROM_BLOCK_LEN 8 |
83 | |
84 | enum nfp_nsp_cmd { |
85 | SPCODE_NOOP = 0, /* No operation */ |
86 | SPCODE_SOFT_RESET = 1, /* Soft reset the NFP */ |
87 | SPCODE_FW_DEFAULT = 2, /* Load default (UNDI) FW */ |
88 | SPCODE_PHY_INIT = 3, /* Initialize the PHY */ |
89 | SPCODE_MAC_INIT = 4, /* Initialize the MAC */ |
90 | SPCODE_PHY_RXADAPT = 5, /* Re-run PHY RX Adaptation */ |
91 | SPCODE_FW_LOAD = 6, /* Load fw from buffer, len in option */ |
92 | SPCODE_ETH_RESCAN = 7, /* Rescan ETHs, write ETH_TABLE to buf */ |
93 | SPCODE_ETH_CONTROL = 8, /* Update media config from buffer */ |
94 | SPCODE_NSP_WRITE_FLASH = 11, /* Load and flash image from buffer */ |
95 | SPCODE_NSP_SENSORS = 12, /* Read NSP sensor(s) */ |
96 | SPCODE_NSP_IDENTIFY = 13, /* Read NSP version */ |
97 | SPCODE_FW_STORED = 16, /* If no FW loaded, load flash app FW */ |
98 | SPCODE_HWINFO_LOOKUP = 17, /* Lookup HWinfo with overwrites etc. */ |
99 | SPCODE_HWINFO_SET = 18, /* Set HWinfo entry */ |
100 | SPCODE_FW_LOADED = 19, /* Is application firmware loaded */ |
101 | SPCODE_VERSIONS = 21, /* Report FW versions */ |
102 | SPCODE_READ_SFF_EEPROM = 22, /* Read module EEPROM */ |
103 | SPCODE_READ_MEDIA = 23, /* Get either the supported or advertised media for a port */ |
104 | }; |
105 | |
106 | struct nfp_nsp_dma_buf { |
107 | __le32 chunk_cnt; |
108 | __le32 reserved[3]; |
109 | struct { |
110 | __le32 size; |
111 | __le32 reserved; |
112 | __le64 addr; |
113 | } descs[]; |
114 | }; |
115 | |
116 | static const struct { |
117 | int code; |
118 | const char *msg; |
119 | } nsp_errors[] = { |
120 | { 6010, "could not map to phy for port" }, |
121 | { 6011, "not an allowed rate/lanes for port" }, |
122 | { 6012, "not an allowed rate/lanes for port" }, |
123 | { 6013, "high/low error, change other port first" }, |
124 | { 6014, "config not found in flash" }, |
125 | }; |
126 | |
127 | struct nfp_nsp { |
128 | struct nfp_cpp *cpp; |
129 | struct nfp_resource *res; |
130 | struct { |
131 | u16 major; |
132 | u16 minor; |
133 | } ver; |
134 | |
135 | /* Eth table config state */ |
136 | bool modified; |
137 | unsigned int idx; |
138 | void *entries; |
139 | }; |
140 | |
141 | /** |
142 | * struct nfp_nsp_command_arg - NFP command argument structure |
143 | * @code: NFP SP Command Code |
144 | * @dma: @buf points to a host buffer, not NSP buffer |
145 | * @timeout_sec:Timeout value to wait for completion in seconds |
146 | * @option: NFP SP Command Argument |
147 | * @buf: NFP SP Buffer Address |
148 | * @error_cb: Callback for interpreting option if error occurred |
149 | * @error_quiet:Don't print command error/warning. Protocol errors are still |
150 | * logged. |
151 | */ |
152 | struct nfp_nsp_command_arg { |
153 | u16 code; |
154 | bool dma; |
155 | unsigned int timeout_sec; |
156 | u32 option; |
157 | u64 buf; |
158 | void (*error_cb)(struct nfp_nsp *state, u32 ret_val); |
159 | bool error_quiet; |
160 | }; |
161 | |
162 | /** |
163 | * struct nfp_nsp_command_buf_arg - NFP command with buffer argument structure |
164 | * @arg: NFP command argument structure |
165 | * @in_buf: Buffer with data for input |
166 | * @in_size: Size of @in_buf |
167 | * @out_buf: Buffer for output data |
168 | * @out_size: Size of @out_buf |
169 | */ |
170 | struct nfp_nsp_command_buf_arg { |
171 | struct nfp_nsp_command_arg arg; |
172 | const void *in_buf; |
173 | unsigned int in_size; |
174 | void *out_buf; |
175 | unsigned int out_size; |
176 | }; |
177 | |
178 | struct nfp_cpp *nfp_nsp_cpp(struct nfp_nsp *state) |
179 | { |
180 | return state->cpp; |
181 | } |
182 | |
183 | bool nfp_nsp_config_modified(struct nfp_nsp *state) |
184 | { |
185 | return state->modified; |
186 | } |
187 | |
188 | void nfp_nsp_config_set_modified(struct nfp_nsp *state, bool modified) |
189 | { |
190 | state->modified = modified; |
191 | } |
192 | |
193 | void *nfp_nsp_config_entries(struct nfp_nsp *state) |
194 | { |
195 | return state->entries; |
196 | } |
197 | |
198 | unsigned int nfp_nsp_config_idx(struct nfp_nsp *state) |
199 | { |
200 | return state->idx; |
201 | } |
202 | |
203 | void |
204 | nfp_nsp_config_set_state(struct nfp_nsp *state, void *entries, unsigned int idx) |
205 | { |
206 | state->entries = entries; |
207 | state->idx = idx; |
208 | } |
209 | |
210 | void nfp_nsp_config_clear_state(struct nfp_nsp *state) |
211 | { |
212 | state->entries = NULL; |
213 | state->idx = 0; |
214 | } |
215 | |
216 | static void nfp_nsp_print_extended_error(struct nfp_nsp *state, u32 ret_val) |
217 | { |
218 | int i; |
219 | |
220 | if (!ret_val) |
221 | return; |
222 | |
223 | for (i = 0; i < ARRAY_SIZE(nsp_errors); i++) |
224 | if (ret_val == nsp_errors[i].code) |
225 | nfp_err(state->cpp, "err msg: %s\n" , nsp_errors[i].msg); |
226 | } |
227 | |
228 | static int nfp_nsp_check(struct nfp_nsp *state) |
229 | { |
230 | struct nfp_cpp *cpp = state->cpp; |
231 | u64 nsp_status, reg; |
232 | u32 nsp_cpp; |
233 | int err; |
234 | |
235 | nsp_cpp = nfp_resource_cpp_id(res: state->res); |
236 | nsp_status = nfp_resource_address(res: state->res) + NSP_STATUS; |
237 | |
238 | err = nfp_cpp_readq(cpp, cpp_id: nsp_cpp, address: nsp_status, value: ®); |
239 | if (err < 0) |
240 | return err; |
241 | |
242 | if (FIELD_GET(NSP_STATUS_MAGIC, reg) != NSP_MAGIC) { |
243 | nfp_err(cpp, "Cannot detect NFP Service Processor\n" ); |
244 | return -ENODEV; |
245 | } |
246 | |
247 | state->ver.major = FIELD_GET(NSP_STATUS_MAJOR, reg); |
248 | state->ver.minor = FIELD_GET(NSP_STATUS_MINOR, reg); |
249 | |
250 | if (state->ver.major != NSP_MAJOR) { |
251 | nfp_err(cpp, "Unsupported ABI %hu.%hu\n" , |
252 | state->ver.major, state->ver.minor); |
253 | return -EINVAL; |
254 | } |
255 | if (state->ver.minor < NSP_MINOR) { |
256 | nfp_err(cpp, "ABI too old to support NIC operation (%u.%hu < %u.%u), please update the management FW on the flash\n" , |
257 | NSP_MAJOR, state->ver.minor, NSP_MAJOR, NSP_MINOR); |
258 | return -EINVAL; |
259 | } |
260 | |
261 | if (reg & NSP_STATUS_BUSY) { |
262 | nfp_err(cpp, "Service processor busy!\n" ); |
263 | return -EBUSY; |
264 | } |
265 | |
266 | return 0; |
267 | } |
268 | |
269 | /** |
270 | * nfp_nsp_open() - Prepare for communication and lock the NSP resource. |
271 | * @cpp: NFP CPP Handle |
272 | */ |
273 | struct nfp_nsp *nfp_nsp_open(struct nfp_cpp *cpp) |
274 | { |
275 | struct nfp_resource *res; |
276 | struct nfp_nsp *state; |
277 | int err; |
278 | |
279 | res = nfp_resource_acquire(cpp, NFP_RESOURCE_NSP); |
280 | if (IS_ERR(ptr: res)) |
281 | return (void *)res; |
282 | |
283 | state = kzalloc(size: sizeof(*state), GFP_KERNEL); |
284 | if (!state) { |
285 | nfp_resource_release(res); |
286 | return ERR_PTR(error: -ENOMEM); |
287 | } |
288 | state->cpp = cpp; |
289 | state->res = res; |
290 | |
291 | err = nfp_nsp_check(state); |
292 | if (err) { |
293 | nfp_nsp_close(state); |
294 | return ERR_PTR(error: err); |
295 | } |
296 | |
297 | return state; |
298 | } |
299 | |
300 | /** |
301 | * nfp_nsp_close() - Clean up and unlock the NSP resource. |
302 | * @state: NFP SP state |
303 | */ |
304 | void nfp_nsp_close(struct nfp_nsp *state) |
305 | { |
306 | nfp_resource_release(res: state->res); |
307 | kfree(objp: state); |
308 | } |
309 | |
310 | u16 nfp_nsp_get_abi_ver_major(struct nfp_nsp *state) |
311 | { |
312 | return state->ver.major; |
313 | } |
314 | |
315 | u16 nfp_nsp_get_abi_ver_minor(struct nfp_nsp *state) |
316 | { |
317 | return state->ver.minor; |
318 | } |
319 | |
320 | static int |
321 | nfp_nsp_wait_reg(struct nfp_cpp *cpp, u64 *reg, u32 nsp_cpp, u64 addr, |
322 | u64 mask, u64 val, u32 timeout_sec) |
323 | { |
324 | const unsigned long wait_until = jiffies + timeout_sec * HZ; |
325 | int err; |
326 | |
327 | for (;;) { |
328 | const unsigned long start_time = jiffies; |
329 | |
330 | err = nfp_cpp_readq(cpp, cpp_id: nsp_cpp, address: addr, value: reg); |
331 | if (err < 0) |
332 | return err; |
333 | |
334 | if ((*reg & mask) == val) |
335 | return 0; |
336 | |
337 | msleep(msecs: 25); |
338 | |
339 | if (time_after(start_time, wait_until)) |
340 | return -ETIMEDOUT; |
341 | } |
342 | } |
343 | |
344 | /** |
345 | * __nfp_nsp_command() - Execute a command on the NFP Service Processor |
346 | * @state: NFP SP state |
347 | * @arg: NFP command argument structure |
348 | * |
349 | * Return: 0 for success with no result |
350 | * |
351 | * positive value for NSP completion with a result code |
352 | * |
353 | * -EAGAIN if the NSP is not yet present |
354 | * -ENODEV if the NSP is not a supported model |
355 | * -EBUSY if the NSP is stuck |
356 | * -EINTR if interrupted while waiting for completion |
357 | * -ETIMEDOUT if the NSP took longer than @timeout_sec seconds to complete |
358 | */ |
359 | static int |
360 | __nfp_nsp_command(struct nfp_nsp *state, const struct nfp_nsp_command_arg *arg) |
361 | { |
362 | u64 reg, ret_val, nsp_base, nsp_buffer, nsp_status, nsp_command; |
363 | struct nfp_cpp *cpp = state->cpp; |
364 | u32 nsp_cpp; |
365 | int err; |
366 | |
367 | nsp_cpp = nfp_resource_cpp_id(res: state->res); |
368 | nsp_base = nfp_resource_address(res: state->res); |
369 | nsp_status = nsp_base + NSP_STATUS; |
370 | nsp_command = nsp_base + NSP_COMMAND; |
371 | nsp_buffer = nsp_base + NSP_BUFFER; |
372 | |
373 | err = nfp_nsp_check(state); |
374 | if (err) |
375 | return err; |
376 | |
377 | err = nfp_cpp_writeq(cpp, cpp_id: nsp_cpp, address: nsp_buffer, value: arg->buf); |
378 | if (err < 0) |
379 | return err; |
380 | |
381 | err = nfp_cpp_writeq(cpp, cpp_id: nsp_cpp, address: nsp_command, |
382 | FIELD_PREP(NSP_COMMAND_OPTION, arg->option) | |
383 | FIELD_PREP(NSP_COMMAND_CODE, arg->code) | |
384 | FIELD_PREP(NSP_COMMAND_DMA_BUF, arg->dma) | |
385 | FIELD_PREP(NSP_COMMAND_START, 1)); |
386 | if (err < 0) |
387 | return err; |
388 | |
389 | /* Wait for NSP_COMMAND_START to go to 0 */ |
390 | err = nfp_nsp_wait_reg(cpp, reg: ®, nsp_cpp, addr: nsp_command, |
391 | NSP_COMMAND_START, val: 0, NFP_NSP_TIMEOUT_DEFAULT); |
392 | if (err) { |
393 | nfp_err(cpp, "Error %d waiting for code 0x%04x to start\n" , |
394 | err, arg->code); |
395 | return err; |
396 | } |
397 | |
398 | /* Wait for NSP_STATUS_BUSY to go to 0 */ |
399 | err = nfp_nsp_wait_reg(cpp, reg: ®, nsp_cpp, addr: nsp_status, NSP_STATUS_BUSY, |
400 | val: 0, timeout_sec: arg->timeout_sec ?: NFP_NSP_TIMEOUT_DEFAULT); |
401 | if (err) { |
402 | nfp_err(cpp, "Error %d waiting for code 0x%04x to complete\n" , |
403 | err, arg->code); |
404 | return err; |
405 | } |
406 | |
407 | err = nfp_cpp_readq(cpp, cpp_id: nsp_cpp, address: nsp_command, value: &ret_val); |
408 | if (err < 0) |
409 | return err; |
410 | ret_val = FIELD_GET(NSP_COMMAND_OPTION, ret_val); |
411 | |
412 | err = FIELD_GET(NSP_STATUS_RESULT, reg); |
413 | if (err) { |
414 | if (!arg->error_quiet) |
415 | nfp_warn(cpp, "Result (error) code set: %d (%d) command: %d\n" , |
416 | -err, (int)ret_val, arg->code); |
417 | |
418 | if (arg->error_cb) |
419 | arg->error_cb(state, ret_val); |
420 | else |
421 | nfp_nsp_print_extended_error(state, ret_val); |
422 | return -err; |
423 | } |
424 | |
425 | return ret_val; |
426 | } |
427 | |
428 | static int nfp_nsp_command(struct nfp_nsp *state, u16 code) |
429 | { |
430 | const struct nfp_nsp_command_arg arg = { |
431 | .code = code, |
432 | }; |
433 | |
434 | return __nfp_nsp_command(state, arg: &arg); |
435 | } |
436 | |
437 | static int |
438 | nfp_nsp_command_buf_def(struct nfp_nsp *nsp, |
439 | struct nfp_nsp_command_buf_arg *arg) |
440 | { |
441 | struct nfp_cpp *cpp = nsp->cpp; |
442 | u64 reg, cpp_buf; |
443 | int err, ret; |
444 | u32 cpp_id; |
445 | |
446 | err = nfp_cpp_readq(cpp, cpp_id: nfp_resource_cpp_id(res: nsp->res), |
447 | address: nfp_resource_address(res: nsp->res) + |
448 | NSP_DFLT_BUFFER, |
449 | value: ®); |
450 | if (err < 0) |
451 | return err; |
452 | |
453 | cpp_id = FIELD_GET(NSP_DFLT_BUFFER_CPP, reg) << 8; |
454 | cpp_buf = FIELD_GET(NSP_DFLT_BUFFER_ADDRESS, reg); |
455 | |
456 | if (arg->in_buf && arg->in_size) { |
457 | err = nfp_cpp_write(cpp, cpp_id, address: cpp_buf, |
458 | kernel_vaddr: arg->in_buf, length: arg->in_size); |
459 | if (err < 0) |
460 | return err; |
461 | } |
462 | /* Zero out remaining part of the buffer */ |
463 | if (arg->out_buf && arg->out_size && arg->out_size > arg->in_size) { |
464 | err = nfp_cpp_write(cpp, cpp_id, address: cpp_buf + arg->in_size, |
465 | kernel_vaddr: arg->out_buf, length: arg->out_size - arg->in_size); |
466 | if (err < 0) |
467 | return err; |
468 | } |
469 | |
470 | if (!FIELD_FIT(NSP_BUFFER_CPP, cpp_id >> 8) || |
471 | !FIELD_FIT(NSP_BUFFER_ADDRESS, cpp_buf)) { |
472 | nfp_err(cpp, "Buffer out of reach %08x %016llx\n" , |
473 | cpp_id, cpp_buf); |
474 | return -EINVAL; |
475 | } |
476 | |
477 | arg->arg.buf = FIELD_PREP(NSP_BUFFER_CPP, cpp_id >> 8) | |
478 | FIELD_PREP(NSP_BUFFER_ADDRESS, cpp_buf); |
479 | ret = __nfp_nsp_command(state: nsp, arg: &arg->arg); |
480 | if (ret < 0) |
481 | return ret; |
482 | |
483 | if (arg->out_buf && arg->out_size) { |
484 | err = nfp_cpp_read(cpp, cpp_id, address: cpp_buf, |
485 | kernel_vaddr: arg->out_buf, length: arg->out_size); |
486 | if (err < 0) |
487 | return err; |
488 | } |
489 | |
490 | return ret; |
491 | } |
492 | |
493 | static int |
494 | nfp_nsp_command_buf_dma_sg(struct nfp_nsp *nsp, |
495 | struct nfp_nsp_command_buf_arg *arg, |
496 | unsigned int max_size, unsigned int chunk_order, |
497 | unsigned int dma_order) |
498 | { |
499 | struct nfp_cpp *cpp = nsp->cpp; |
500 | struct nfp_nsp_dma_buf *desc; |
501 | struct { |
502 | dma_addr_t dma_addr; |
503 | unsigned long len; |
504 | void *chunk; |
505 | } *chunks; |
506 | size_t chunk_size, dma_size; |
507 | dma_addr_t dma_desc; |
508 | struct device *dev; |
509 | unsigned long off; |
510 | int i, ret, nseg; |
511 | size_t desc_sz; |
512 | |
513 | chunk_size = BIT_ULL(chunk_order); |
514 | dma_size = BIT_ULL(dma_order); |
515 | nseg = DIV_ROUND_UP(max_size, chunk_size); |
516 | |
517 | chunks = kcalloc(n: nseg, size: sizeof(*chunks), GFP_KERNEL); |
518 | if (!chunks) |
519 | return -ENOMEM; |
520 | |
521 | off = 0; |
522 | ret = -ENOMEM; |
523 | for (i = 0; i < nseg; i++) { |
524 | unsigned long coff; |
525 | |
526 | chunks[i].chunk = kmalloc(size: chunk_size, |
527 | GFP_KERNEL | __GFP_NOWARN); |
528 | if (!chunks[i].chunk) |
529 | goto exit_free_prev; |
530 | |
531 | chunks[i].len = min_t(u64, chunk_size, max_size - off); |
532 | |
533 | coff = 0; |
534 | if (arg->in_size > off) { |
535 | coff = min_t(u64, arg->in_size - off, chunk_size); |
536 | memcpy(chunks[i].chunk, arg->in_buf + off, coff); |
537 | } |
538 | memset(chunks[i].chunk + coff, 0, chunk_size - coff); |
539 | |
540 | off += chunks[i].len; |
541 | } |
542 | |
543 | dev = nfp_cpp_device(cpp)->parent; |
544 | |
545 | for (i = 0; i < nseg; i++) { |
546 | dma_addr_t addr; |
547 | |
548 | addr = dma_map_single(dev, chunks[i].chunk, chunks[i].len, |
549 | DMA_BIDIRECTIONAL); |
550 | chunks[i].dma_addr = addr; |
551 | |
552 | ret = dma_mapping_error(dev, dma_addr: addr); |
553 | if (ret) |
554 | goto exit_unmap_prev; |
555 | |
556 | if (WARN_ONCE(round_down(addr, dma_size) != |
557 | round_down(addr + chunks[i].len - 1, dma_size), |
558 | "unaligned DMA address: %pad %lu %zd\n" , |
559 | &addr, chunks[i].len, dma_size)) { |
560 | ret = -EFAULT; |
561 | i++; |
562 | goto exit_unmap_prev; |
563 | } |
564 | } |
565 | |
566 | desc_sz = struct_size(desc, descs, nseg); |
567 | desc = kmalloc(size: desc_sz, GFP_KERNEL); |
568 | if (!desc) { |
569 | ret = -ENOMEM; |
570 | goto exit_unmap_all; |
571 | } |
572 | |
573 | desc->chunk_cnt = cpu_to_le32(nseg); |
574 | for (i = 0; i < nseg; i++) { |
575 | desc->descs[i].size = cpu_to_le32(chunks[i].len); |
576 | desc->descs[i].addr = cpu_to_le64(chunks[i].dma_addr); |
577 | } |
578 | |
579 | dma_desc = dma_map_single(dev, desc, desc_sz, DMA_TO_DEVICE); |
580 | ret = dma_mapping_error(dev, dma_addr: dma_desc); |
581 | if (ret) |
582 | goto exit_free_desc; |
583 | |
584 | arg->arg.dma = true; |
585 | arg->arg.buf = dma_desc; |
586 | ret = __nfp_nsp_command(state: nsp, arg: &arg->arg); |
587 | if (ret < 0) |
588 | goto exit_unmap_desc; |
589 | |
590 | i = 0; |
591 | off = 0; |
592 | while (off < arg->out_size) { |
593 | unsigned int len; |
594 | |
595 | len = min_t(u64, chunks[i].len, arg->out_size - off); |
596 | memcpy(arg->out_buf + off, chunks[i].chunk, len); |
597 | off += len; |
598 | i++; |
599 | } |
600 | |
601 | exit_unmap_desc: |
602 | dma_unmap_single(dev, dma_desc, desc_sz, DMA_TO_DEVICE); |
603 | exit_free_desc: |
604 | kfree(objp: desc); |
605 | exit_unmap_all: |
606 | i = nseg; |
607 | exit_unmap_prev: |
608 | while (--i >= 0) |
609 | dma_unmap_single(dev, chunks[i].dma_addr, chunks[i].len, |
610 | DMA_BIDIRECTIONAL); |
611 | i = nseg; |
612 | exit_free_prev: |
613 | while (--i >= 0) |
614 | kfree(objp: chunks[i].chunk); |
615 | kfree(objp: chunks); |
616 | if (ret < 0) |
617 | nfp_err(cpp, "NSP: SG DMA failed for command 0x%04x: %d (sz:%d cord:%d)\n" , |
618 | arg->arg.code, ret, max_size, chunk_order); |
619 | return ret; |
620 | } |
621 | |
622 | static int |
623 | nfp_nsp_command_buf_dma(struct nfp_nsp *nsp, |
624 | struct nfp_nsp_command_buf_arg *arg, |
625 | unsigned int max_size, unsigned int dma_order) |
626 | { |
627 | unsigned int chunk_order, buf_order; |
628 | struct nfp_cpp *cpp = nsp->cpp; |
629 | bool sg_ok; |
630 | u64 reg; |
631 | int err; |
632 | |
633 | buf_order = order_base_2(roundup_pow_of_two(max_size)); |
634 | |
635 | err = nfp_cpp_readq(cpp, cpp_id: nfp_resource_cpp_id(res: nsp->res), |
636 | address: nfp_resource_address(res: nsp->res) + NFP_CAP_CMD_DMA_SG, |
637 | value: ®); |
638 | if (err < 0) |
639 | return err; |
640 | sg_ok = reg & BIT_ULL(arg->arg.code - 1); |
641 | |
642 | if (!sg_ok) { |
643 | if (buf_order > dma_order) { |
644 | nfp_err(cpp, "NSP: can't service non-SG DMA for command 0x%04x\n" , |
645 | arg->arg.code); |
646 | return -ENOMEM; |
647 | } |
648 | chunk_order = buf_order; |
649 | } else { |
650 | chunk_order = min_t(unsigned int, dma_order, PAGE_SHIFT); |
651 | } |
652 | |
653 | return nfp_nsp_command_buf_dma_sg(nsp, arg, max_size, chunk_order, |
654 | dma_order); |
655 | } |
656 | |
657 | static int |
658 | nfp_nsp_command_buf(struct nfp_nsp *nsp, struct nfp_nsp_command_buf_arg *arg) |
659 | { |
660 | unsigned int dma_order, def_size, max_size; |
661 | struct nfp_cpp *cpp = nsp->cpp; |
662 | u64 reg; |
663 | int err; |
664 | |
665 | if (nsp->ver.minor < 13) { |
666 | nfp_err(cpp, "NSP: Code 0x%04x with buffer not supported (ABI %hu.%hu)\n" , |
667 | arg->arg.code, nsp->ver.major, nsp->ver.minor); |
668 | return -EOPNOTSUPP; |
669 | } |
670 | |
671 | err = nfp_cpp_readq(cpp, cpp_id: nfp_resource_cpp_id(res: nsp->res), |
672 | address: nfp_resource_address(res: nsp->res) + |
673 | NSP_DFLT_BUFFER_CONFIG, |
674 | value: ®); |
675 | if (err < 0) |
676 | return err; |
677 | |
678 | /* Zero out undefined part of the out buffer */ |
679 | if (arg->out_buf && arg->out_size && arg->out_size > arg->in_size) |
680 | memset(arg->out_buf, 0, arg->out_size - arg->in_size); |
681 | |
682 | max_size = max(arg->in_size, arg->out_size); |
683 | def_size = FIELD_GET(NSP_DFLT_BUFFER_SIZE_MB, reg) * SZ_1M + |
684 | FIELD_GET(NSP_DFLT_BUFFER_SIZE_4KB, reg) * SZ_4K; |
685 | dma_order = FIELD_GET(NSP_DFLT_BUFFER_DMA_CHUNK_ORDER, reg); |
686 | if (def_size >= max_size) { |
687 | return nfp_nsp_command_buf_def(nsp, arg); |
688 | } else if (!dma_order) { |
689 | nfp_err(cpp, "NSP: default buffer too small for command 0x%04x (%u < %u)\n" , |
690 | arg->arg.code, def_size, max_size); |
691 | return -EINVAL; |
692 | } |
693 | |
694 | return nfp_nsp_command_buf_dma(nsp, arg, max_size, dma_order); |
695 | } |
696 | |
697 | int nfp_nsp_wait(struct nfp_nsp *state) |
698 | { |
699 | const unsigned long wait_until = jiffies + NFP_NSP_TIMEOUT_BOOT * HZ; |
700 | int err; |
701 | |
702 | nfp_dbg(state->cpp, "Waiting for NSP to respond (%u sec max).\n" , |
703 | NFP_NSP_TIMEOUT_BOOT); |
704 | |
705 | for (;;) { |
706 | const unsigned long start_time = jiffies; |
707 | |
708 | err = nfp_nsp_command(state, code: SPCODE_NOOP); |
709 | if (err != -EAGAIN) |
710 | break; |
711 | |
712 | if (msleep_interruptible(msecs: 25)) { |
713 | err = -ERESTARTSYS; |
714 | break; |
715 | } |
716 | |
717 | if (time_after(start_time, wait_until)) { |
718 | err = -ETIMEDOUT; |
719 | break; |
720 | } |
721 | } |
722 | if (err) |
723 | nfp_err(state->cpp, "NSP failed to respond %d\n" , err); |
724 | |
725 | return err; |
726 | } |
727 | |
728 | int nfp_nsp_device_soft_reset(struct nfp_nsp *state) |
729 | { |
730 | return nfp_nsp_command(state, code: SPCODE_SOFT_RESET); |
731 | } |
732 | |
733 | int nfp_nsp_mac_reinit(struct nfp_nsp *state) |
734 | { |
735 | return nfp_nsp_command(state, code: SPCODE_MAC_INIT); |
736 | } |
737 | |
738 | static void nfp_nsp_load_fw_extended_msg(struct nfp_nsp *state, u32 ret_val) |
739 | { |
740 | static const char * const major_msg[] = { |
741 | /* 0 */ "Firmware from driver loaded" , |
742 | /* 1 */ "Firmware from flash loaded" , |
743 | /* 2 */ "Firmware loading failure" , |
744 | }; |
745 | static const char * const minor_msg[] = { |
746 | /* 0 */ "" , |
747 | /* 1 */ "no named partition on flash" , |
748 | /* 2 */ "error reading from flash" , |
749 | /* 3 */ "can not deflate" , |
750 | /* 4 */ "not a trusted file" , |
751 | /* 5 */ "can not parse FW file" , |
752 | /* 6 */ "MIP not found in FW file" , |
753 | /* 7 */ "null firmware name in MIP" , |
754 | /* 8 */ "FW version none" , |
755 | /* 9 */ "FW build number none" , |
756 | /* 10 */ "no FW selection policy HWInfo key found" , |
757 | /* 11 */ "static FW selection policy" , |
758 | /* 12 */ "FW version has precedence" , |
759 | /* 13 */ "different FW application load requested" , |
760 | /* 14 */ "development build" , |
761 | }; |
762 | unsigned int major, minor; |
763 | const char *level; |
764 | |
765 | major = FIELD_GET(NFP_FW_LOAD_RET_MAJOR, ret_val); |
766 | minor = FIELD_GET(NFP_FW_LOAD_RET_MINOR, ret_val); |
767 | |
768 | if (!nfp_nsp_has_stored_fw_load(state)) |
769 | return; |
770 | |
771 | /* Lower the message level in legacy case */ |
772 | if (major == 0 && (minor == 0 || minor == 10)) |
773 | level = KERN_DEBUG; |
774 | else if (major == 2) |
775 | level = KERN_ERR; |
776 | else |
777 | level = KERN_INFO; |
778 | |
779 | if (major >= ARRAY_SIZE(major_msg)) |
780 | nfp_printk(level, state->cpp, "FW loading status: %x\n" , |
781 | ret_val); |
782 | else if (minor >= ARRAY_SIZE(minor_msg)) |
783 | nfp_printk(level, state->cpp, "%s, reason code: %d\n" , |
784 | major_msg[major], minor); |
785 | else |
786 | nfp_printk(level, state->cpp, "%s%c %s\n" , |
787 | major_msg[major], minor ? ',' : '.', |
788 | minor_msg[minor]); |
789 | } |
790 | |
791 | int nfp_nsp_load_fw(struct nfp_nsp *state, const struct firmware *fw) |
792 | { |
793 | struct nfp_nsp_command_buf_arg load_fw = { |
794 | { |
795 | .code = SPCODE_FW_LOAD, |
796 | .option = fw->size, |
797 | .error_cb = nfp_nsp_load_fw_extended_msg, |
798 | }, |
799 | .in_buf = fw->data, |
800 | .in_size = fw->size, |
801 | }; |
802 | int ret; |
803 | |
804 | ret = nfp_nsp_command_buf(nsp: state, arg: &load_fw); |
805 | if (ret < 0) |
806 | return ret; |
807 | |
808 | nfp_nsp_load_fw_extended_msg(state, ret_val: ret); |
809 | return 0; |
810 | } |
811 | |
812 | int nfp_nsp_write_flash(struct nfp_nsp *state, const struct firmware *fw) |
813 | { |
814 | struct nfp_nsp_command_buf_arg write_flash = { |
815 | { |
816 | .code = SPCODE_NSP_WRITE_FLASH, |
817 | .option = fw->size, |
818 | .timeout_sec = 900, |
819 | }, |
820 | .in_buf = fw->data, |
821 | .in_size = fw->size, |
822 | }; |
823 | |
824 | return nfp_nsp_command_buf(nsp: state, arg: &write_flash); |
825 | } |
826 | |
827 | int nfp_nsp_read_eth_table(struct nfp_nsp *state, void *buf, unsigned int size) |
828 | { |
829 | struct nfp_nsp_command_buf_arg eth_rescan = { |
830 | { |
831 | .code = SPCODE_ETH_RESCAN, |
832 | .option = size, |
833 | }, |
834 | .out_buf = buf, |
835 | .out_size = size, |
836 | }; |
837 | |
838 | return nfp_nsp_command_buf(nsp: state, arg: ð_rescan); |
839 | } |
840 | |
841 | int nfp_nsp_write_eth_table(struct nfp_nsp *state, |
842 | const void *buf, unsigned int size) |
843 | { |
844 | struct nfp_nsp_command_buf_arg eth_ctrl = { |
845 | { |
846 | .code = SPCODE_ETH_CONTROL, |
847 | .option = size, |
848 | }, |
849 | .in_buf = buf, |
850 | .in_size = size, |
851 | }; |
852 | |
853 | return nfp_nsp_command_buf(nsp: state, arg: ð_ctrl); |
854 | } |
855 | |
856 | int nfp_nsp_read_identify(struct nfp_nsp *state, void *buf, unsigned int size) |
857 | { |
858 | struct nfp_nsp_command_buf_arg identify = { |
859 | { |
860 | .code = SPCODE_NSP_IDENTIFY, |
861 | .option = size, |
862 | }, |
863 | .out_buf = buf, |
864 | .out_size = size, |
865 | }; |
866 | |
867 | return nfp_nsp_command_buf(nsp: state, arg: &identify); |
868 | } |
869 | |
870 | int nfp_nsp_read_sensors(struct nfp_nsp *state, unsigned int sensor_mask, |
871 | void *buf, unsigned int size) |
872 | { |
873 | struct nfp_nsp_command_buf_arg sensors = { |
874 | { |
875 | .code = SPCODE_NSP_SENSORS, |
876 | .option = sensor_mask, |
877 | }, |
878 | .out_buf = buf, |
879 | .out_size = size, |
880 | }; |
881 | |
882 | return nfp_nsp_command_buf(nsp: state, arg: &sensors); |
883 | } |
884 | |
885 | int nfp_nsp_load_stored_fw(struct nfp_nsp *state) |
886 | { |
887 | const struct nfp_nsp_command_arg arg = { |
888 | .code = SPCODE_FW_STORED, |
889 | .error_cb = nfp_nsp_load_fw_extended_msg, |
890 | }; |
891 | int ret; |
892 | |
893 | ret = __nfp_nsp_command(state, arg: &arg); |
894 | if (ret < 0) |
895 | return ret; |
896 | |
897 | nfp_nsp_load_fw_extended_msg(state, ret_val: ret); |
898 | return 0; |
899 | } |
900 | |
901 | static int |
902 | __nfp_nsp_hwinfo_lookup(struct nfp_nsp *state, void *buf, unsigned int size, |
903 | bool optional) |
904 | { |
905 | struct nfp_nsp_command_buf_arg hwinfo_lookup = { |
906 | { |
907 | .code = SPCODE_HWINFO_LOOKUP, |
908 | .option = size, |
909 | .error_quiet = optional, |
910 | }, |
911 | .in_buf = buf, |
912 | .in_size = size, |
913 | .out_buf = buf, |
914 | .out_size = size, |
915 | }; |
916 | |
917 | return nfp_nsp_command_buf(nsp: state, arg: &hwinfo_lookup); |
918 | } |
919 | |
920 | int nfp_nsp_hwinfo_lookup(struct nfp_nsp *state, void *buf, unsigned int size) |
921 | { |
922 | int err; |
923 | |
924 | size = min_t(u32, size, NFP_HWINFO_LOOKUP_SIZE); |
925 | |
926 | err = __nfp_nsp_hwinfo_lookup(state, buf, size, optional: false); |
927 | if (err) |
928 | return err; |
929 | |
930 | if (strnlen(p: buf, maxlen: size) == size) { |
931 | nfp_err(state->cpp, "NSP HWinfo value not NULL-terminated\n" ); |
932 | return -EINVAL; |
933 | } |
934 | |
935 | return 0; |
936 | } |
937 | |
938 | int nfp_nsp_hwinfo_lookup_optional(struct nfp_nsp *state, void *buf, |
939 | unsigned int size, const char *default_val) |
940 | { |
941 | int err; |
942 | |
943 | /* Ensure that the default value is usable irrespective of whether |
944 | * it is actually going to be used. |
945 | */ |
946 | if (strnlen(p: default_val, maxlen: size) == size) |
947 | return -EINVAL; |
948 | |
949 | if (!nfp_nsp_has_hwinfo_lookup(state)) { |
950 | strcpy(p: buf, q: default_val); |
951 | return 0; |
952 | } |
953 | |
954 | size = min_t(u32, size, NFP_HWINFO_LOOKUP_SIZE); |
955 | |
956 | err = __nfp_nsp_hwinfo_lookup(state, buf, size, optional: true); |
957 | if (err) { |
958 | if (err == -ENOENT) { |
959 | strcpy(p: buf, q: default_val); |
960 | return 0; |
961 | } |
962 | |
963 | nfp_err(state->cpp, "NSP HWinfo lookup failed: %d\n" , err); |
964 | return err; |
965 | } |
966 | |
967 | if (strnlen(p: buf, maxlen: size) == size) { |
968 | nfp_err(state->cpp, "NSP HWinfo value not NULL-terminated\n" ); |
969 | return -EINVAL; |
970 | } |
971 | |
972 | return 0; |
973 | } |
974 | |
975 | int nfp_nsp_hwinfo_set(struct nfp_nsp *state, void *buf, unsigned int size) |
976 | { |
977 | struct nfp_nsp_command_buf_arg hwinfo_set = { |
978 | { |
979 | .code = SPCODE_HWINFO_SET, |
980 | .option = size, |
981 | }, |
982 | .in_buf = buf, |
983 | .in_size = size, |
984 | }; |
985 | |
986 | return nfp_nsp_command_buf(nsp: state, arg: &hwinfo_set); |
987 | } |
988 | |
989 | int nfp_nsp_fw_loaded(struct nfp_nsp *state) |
990 | { |
991 | const struct nfp_nsp_command_arg arg = { |
992 | .code = SPCODE_FW_LOADED, |
993 | }; |
994 | |
995 | return __nfp_nsp_command(state, arg: &arg); |
996 | } |
997 | |
998 | int nfp_nsp_versions(struct nfp_nsp *state, void *buf, unsigned int size) |
999 | { |
1000 | struct nfp_nsp_command_buf_arg versions = { |
1001 | { |
1002 | .code = SPCODE_VERSIONS, |
1003 | .option = min_t(u32, size, NFP_VERSIONS_SIZE), |
1004 | }, |
1005 | .out_buf = buf, |
1006 | .out_size = min_t(u32, size, NFP_VERSIONS_SIZE), |
1007 | }; |
1008 | |
1009 | return nfp_nsp_command_buf(nsp: state, arg: &versions); |
1010 | } |
1011 | |
1012 | const char *nfp_nsp_versions_get(enum nfp_nsp_versions id, bool flash, |
1013 | const u8 *buf, unsigned int size) |
1014 | { |
1015 | static const u32 id2off[] = { |
1016 | [NFP_VERSIONS_BSP] = NFP_VERSIONS_BSP_OFF, |
1017 | [NFP_VERSIONS_CPLD] = NFP_VERSIONS_CPLD_OFF, |
1018 | [NFP_VERSIONS_APP] = NFP_VERSIONS_APP_OFF, |
1019 | [NFP_VERSIONS_BUNDLE] = NFP_VERSIONS_BUNDLE_OFF, |
1020 | [NFP_VERSIONS_UNDI] = NFP_VERSIONS_UNDI_OFF, |
1021 | [NFP_VERSIONS_NCSI] = NFP_VERSIONS_NCSI_OFF, |
1022 | [NFP_VERSIONS_CFGR] = NFP_VERSIONS_CFGR_OFF, |
1023 | }; |
1024 | unsigned int field, buf_field_cnt, buf_off; |
1025 | |
1026 | if (id >= ARRAY_SIZE(id2off) || !id2off[id]) |
1027 | return ERR_PTR(error: -EINVAL); |
1028 | |
1029 | field = id * 2 + flash; |
1030 | |
1031 | buf_field_cnt = get_unaligned_le16(p: buf); |
1032 | if (buf_field_cnt <= field) |
1033 | return ERR_PTR(error: -ENOENT); |
1034 | |
1035 | buf_off = get_unaligned_le16(p: buf + id2off[id] + flash * 2); |
1036 | if (!buf_off) |
1037 | return ERR_PTR(error: -ENOENT); |
1038 | |
1039 | if (buf_off >= size) |
1040 | return ERR_PTR(error: -EINVAL); |
1041 | if (strnlen(p: &buf[buf_off], maxlen: size - buf_off) == size - buf_off) |
1042 | return ERR_PTR(error: -EINVAL); |
1043 | |
1044 | return (const char *)&buf[buf_off]; |
1045 | } |
1046 | |
1047 | static int |
1048 | __nfp_nsp_module_eeprom(struct nfp_nsp *state, void *buf, unsigned int size) |
1049 | { |
1050 | struct nfp_nsp_command_buf_arg module_eeprom = { |
1051 | { |
1052 | .code = SPCODE_READ_SFF_EEPROM, |
1053 | .option = size, |
1054 | }, |
1055 | .in_buf = buf, |
1056 | .in_size = size, |
1057 | .out_buf = buf, |
1058 | .out_size = size, |
1059 | }; |
1060 | |
1061 | return nfp_nsp_command_buf(nsp: state, arg: &module_eeprom); |
1062 | } |
1063 | |
1064 | int nfp_nsp_read_module_eeprom(struct nfp_nsp *state, int eth_index, |
1065 | unsigned int offset, void *data, |
1066 | unsigned int len, unsigned int *read_len) |
1067 | { |
1068 | struct eeprom_buf { |
1069 | u8 metalen; |
1070 | __le16 length; |
1071 | __le16 offset; |
1072 | __le16 readlen; |
1073 | u8 eth_index; |
1074 | u8 data[]; |
1075 | } __packed *buf; |
1076 | int bufsz, ret; |
1077 | |
1078 | BUILD_BUG_ON(offsetof(struct eeprom_buf, data) % 8); |
1079 | |
1080 | /* Buffer must be large enough and rounded to the next block size. */ |
1081 | bufsz = struct_size(buf, data, round_up(len, NSP_SFF_EEPROM_BLOCK_LEN)); |
1082 | buf = kzalloc(size: bufsz, GFP_KERNEL); |
1083 | if (!buf) |
1084 | return -ENOMEM; |
1085 | |
1086 | buf->metalen = |
1087 | offsetof(struct eeprom_buf, data) / NSP_SFF_EEPROM_BLOCK_LEN; |
1088 | buf->length = cpu_to_le16(len); |
1089 | buf->offset = cpu_to_le16(offset); |
1090 | buf->eth_index = eth_index; |
1091 | |
1092 | ret = __nfp_nsp_module_eeprom(state, buf, size: bufsz); |
1093 | |
1094 | *read_len = min_t(unsigned int, len, le16_to_cpu(buf->readlen)); |
1095 | if (*read_len) |
1096 | memcpy(data, buf->data, *read_len); |
1097 | |
1098 | if (!ret && *read_len < len) |
1099 | ret = -EIO; |
1100 | |
1101 | kfree(objp: buf); |
1102 | |
1103 | return ret; |
1104 | }; |
1105 | |
1106 | int nfp_nsp_read_media(struct nfp_nsp *state, void *buf, unsigned int size) |
1107 | { |
1108 | struct nfp_nsp_command_buf_arg media = { |
1109 | { |
1110 | .code = SPCODE_READ_MEDIA, |
1111 | .option = size, |
1112 | }, |
1113 | .in_buf = buf, |
1114 | .in_size = size, |
1115 | .out_buf = buf, |
1116 | .out_size = size, |
1117 | }; |
1118 | |
1119 | return nfp_nsp_command_buf(nsp: state, arg: &media); |
1120 | } |
1121 | |