]>
Commit | Line | Data |
---|---|---|
1a64821c JK |
1 | /* |
2 | * Copyright (C) 2015-2017 Netronome Systems, Inc. | |
3 | * | |
4 | * This software is dual licensed under the GNU General License Version 2, | |
5 | * June 1991 as shown in the file COPYING in the top-level directory of this | |
6 | * source tree or the BSD 2-Clause License provided below. You have the | |
7 | * option to license this software under the complete terms of either license. | |
8 | * | |
9 | * The BSD 2-Clause License: | |
10 | * | |
11 | * Redistribution and use in source and binary forms, with or | |
12 | * without modification, are permitted provided that the following | |
13 | * conditions are met: | |
14 | * | |
15 | * 1. Redistributions of source code must retain the above | |
16 | * copyright notice, this list of conditions and the following | |
17 | * disclaimer. | |
18 | * | |
19 | * 2. Redistributions in binary form must reproduce the above | |
20 | * copyright notice, this list of conditions and the following | |
21 | * disclaimer in the documentation and/or other materials | |
22 | * provided with the distribution. | |
23 | * | |
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
31 | * SOFTWARE. | |
32 | */ | |
33 | ||
34 | /* | |
35 | * nfp_nsp.c | |
36 | * Author: Jakub Kicinski <jakub.kicinski@netronome.com> | |
37 | * Jason McMullan <jason.mcmullan@netronome.com> | |
38 | */ | |
39 | ||
40 | #include <linux/bitfield.h> | |
41 | #include <linux/delay.h> | |
42 | #include <linux/firmware.h> | |
43 | #include <linux/kernel.h> | |
44 | #include <linux/kthread.h> | |
45 | #include <linux/sizes.h> | |
46 | #include <linux/slab.h> | |
47 | ||
48 | #define NFP_SUBSYS "nfp_nsp" | |
49 | ||
50 | #include "nfp.h" | |
51 | #include "nfp_cpp.h" | |
ce22f5a2 | 52 | #include "nfp_nsp.h" |
1a64821c JK |
53 | |
54 | /* Offsets relative to the CSR base */ | |
55 | #define NSP_STATUS 0x00 | |
56 | #define NSP_STATUS_MAGIC GENMASK_ULL(63, 48) | |
57 | #define NSP_STATUS_MAJOR GENMASK_ULL(47, 44) | |
58 | #define NSP_STATUS_MINOR GENMASK_ULL(43, 32) | |
59 | #define NSP_STATUS_CODE GENMASK_ULL(31, 16) | |
60 | #define NSP_STATUS_RESULT GENMASK_ULL(15, 8) | |
61 | #define NSP_STATUS_BUSY BIT_ULL(0) | |
62 | ||
63 | #define NSP_COMMAND 0x08 | |
64 | #define NSP_COMMAND_OPTION GENMASK_ULL(63, 32) | |
65 | #define NSP_COMMAND_CODE GENMASK_ULL(31, 16) | |
66 | #define NSP_COMMAND_START BIT_ULL(0) | |
67 | ||
68 | /* CPP address to retrieve the data from */ | |
69 | #define NSP_BUFFER 0x10 | |
70 | #define NSP_BUFFER_CPP GENMASK_ULL(63, 40) | |
71 | #define NSP_BUFFER_PCIE GENMASK_ULL(39, 38) | |
72 | #define NSP_BUFFER_ADDRESS GENMASK_ULL(37, 0) | |
73 | ||
74 | #define NSP_DFLT_BUFFER 0x18 | |
75 | ||
76 | #define NSP_DFLT_BUFFER_CONFIG 0x20 | |
77 | #define NSP_DFLT_BUFFER_SIZE_MB GENMASK_ULL(7, 0) | |
78 | ||
79 | #define NSP_MAGIC 0xab10 | |
80 | #define NSP_MAJOR 0 | |
81 | #define NSP_MINOR (__MAX_SPCODE - 1) | |
82 | ||
83 | #define NSP_CODE_MAJOR GENMASK(15, 12) | |
84 | #define NSP_CODE_MINOR GENMASK(11, 0) | |
85 | ||
86 | enum nfp_nsp_cmd { | |
87 | SPCODE_NOOP = 0, /* No operation */ | |
88 | SPCODE_SOFT_RESET = 1, /* Soft reset the NFP */ | |
89 | SPCODE_FW_DEFAULT = 2, /* Load default (UNDI) FW */ | |
90 | SPCODE_PHY_INIT = 3, /* Initialize the PHY */ | |
91 | SPCODE_MAC_INIT = 4, /* Initialize the MAC */ | |
92 | SPCODE_PHY_RXADAPT = 5, /* Re-run PHY RX Adaptation */ | |
93 | SPCODE_FW_LOAD = 6, /* Load fw from buffer, len in option */ | |
94 | SPCODE_ETH_RESCAN = 7, /* Rescan ETHs, write ETH_TABLE to buf */ | |
95 | SPCODE_ETH_CONTROL = 8, /* Update media config from buffer */ | |
96 | ||
97 | __MAX_SPCODE, | |
98 | }; | |
99 | ||
100 | struct nfp_nsp { | |
101 | struct nfp_cpp *cpp; | |
102 | struct nfp_resource *res; | |
368b1d1c JK |
103 | struct { |
104 | u16 major; | |
105 | u16 minor; | |
106 | } ver; | |
30a02921 JK |
107 | |
108 | /* Eth table config state */ | |
109 | bool modified; | |
110 | unsigned int idx; | |
111 | void *entries; | |
1a64821c JK |
112 | }; |
113 | ||
30a02921 JK |
114 | struct nfp_cpp *nfp_nsp_cpp(struct nfp_nsp *state) |
115 | { | |
116 | return state->cpp; | |
117 | } | |
118 | ||
119 | bool nfp_nsp_config_modified(struct nfp_nsp *state) | |
120 | { | |
121 | return state->modified; | |
122 | } | |
123 | ||
124 | void nfp_nsp_config_set_modified(struct nfp_nsp *state, bool modified) | |
125 | { | |
126 | state->modified = modified; | |
127 | } | |
128 | ||
129 | void *nfp_nsp_config_entries(struct nfp_nsp *state) | |
130 | { | |
131 | return state->entries; | |
132 | } | |
133 | ||
134 | unsigned int nfp_nsp_config_idx(struct nfp_nsp *state) | |
135 | { | |
136 | return state->idx; | |
137 | } | |
138 | ||
139 | void | |
140 | nfp_nsp_config_set_state(struct nfp_nsp *state, void *entries, unsigned int idx) | |
141 | { | |
142 | state->entries = entries; | |
143 | state->idx = idx; | |
144 | } | |
145 | ||
146 | void nfp_nsp_config_clear_state(struct nfp_nsp *state) | |
147 | { | |
148 | state->entries = NULL; | |
149 | state->idx = 0; | |
150 | } | |
151 | ||
1a64821c JK |
152 | static int nfp_nsp_check(struct nfp_nsp *state) |
153 | { | |
154 | struct nfp_cpp *cpp = state->cpp; | |
155 | u64 nsp_status, reg; | |
156 | u32 nsp_cpp; | |
157 | int err; | |
158 | ||
159 | nsp_cpp = nfp_resource_cpp_id(state->res); | |
160 | nsp_status = nfp_resource_address(state->res) + NSP_STATUS; | |
161 | ||
162 | err = nfp_cpp_readq(cpp, nsp_cpp, nsp_status, ®); | |
163 | if (err < 0) | |
164 | return err; | |
165 | ||
166 | if (FIELD_GET(NSP_STATUS_MAGIC, reg) != NSP_MAGIC) { | |
167 | nfp_err(cpp, "Cannot detect NFP Service Processor\n"); | |
168 | return -ENODEV; | |
169 | } | |
170 | ||
368b1d1c JK |
171 | state->ver.major = FIELD_GET(NSP_STATUS_MAJOR, reg); |
172 | state->ver.minor = FIELD_GET(NSP_STATUS_MINOR, reg); | |
173 | ||
174 | if (state->ver.major != NSP_MAJOR || state->ver.minor < NSP_MINOR) { | |
175 | nfp_err(cpp, "Unsupported ABI %hu.%hu\n", | |
176 | state->ver.major, state->ver.minor); | |
1a64821c JK |
177 | return -EINVAL; |
178 | } | |
179 | ||
180 | if (reg & NSP_STATUS_BUSY) { | |
181 | nfp_err(cpp, "Service processor busy!\n"); | |
182 | return -EBUSY; | |
183 | } | |
184 | ||
185 | return 0; | |
186 | } | |
187 | ||
188 | /** | |
189 | * nfp_nsp_open() - Prepare for communication and lock the NSP resource. | |
190 | * @cpp: NFP CPP Handle | |
191 | */ | |
192 | struct nfp_nsp *nfp_nsp_open(struct nfp_cpp *cpp) | |
193 | { | |
194 | struct nfp_resource *res; | |
195 | struct nfp_nsp *state; | |
196 | int err; | |
197 | ||
198 | res = nfp_resource_acquire(cpp, NFP_RESOURCE_NSP); | |
199 | if (IS_ERR(res)) | |
200 | return (void *)res; | |
201 | ||
202 | state = kzalloc(sizeof(*state), GFP_KERNEL); | |
203 | if (!state) { | |
204 | nfp_resource_release(res); | |
205 | return ERR_PTR(-ENOMEM); | |
206 | } | |
207 | state->cpp = cpp; | |
208 | state->res = res; | |
209 | ||
210 | err = nfp_nsp_check(state); | |
211 | if (err) { | |
212 | nfp_nsp_close(state); | |
213 | return ERR_PTR(err); | |
214 | } | |
215 | ||
216 | return state; | |
217 | } | |
218 | ||
219 | /** | |
220 | * nfp_nsp_close() - Clean up and unlock the NSP resource. | |
221 | * @state: NFP SP state | |
222 | */ | |
223 | void nfp_nsp_close(struct nfp_nsp *state) | |
224 | { | |
225 | nfp_resource_release(state->res); | |
226 | kfree(state); | |
227 | } | |
228 | ||
bd5ca062 JK |
229 | u16 nfp_nsp_get_abi_ver_major(struct nfp_nsp *state) |
230 | { | |
231 | return state->ver.major; | |
232 | } | |
233 | ||
234 | u16 nfp_nsp_get_abi_ver_minor(struct nfp_nsp *state) | |
235 | { | |
236 | return state->ver.minor; | |
237 | } | |
238 | ||
1a64821c JK |
239 | static int |
240 | nfp_nsp_wait_reg(struct nfp_cpp *cpp, u64 *reg, | |
241 | u32 nsp_cpp, u64 addr, u64 mask, u64 val) | |
242 | { | |
243 | const unsigned long wait_until = jiffies + 30 * HZ; | |
244 | int err; | |
245 | ||
246 | for (;;) { | |
247 | const unsigned long start_time = jiffies; | |
248 | ||
249 | err = nfp_cpp_readq(cpp, nsp_cpp, addr, reg); | |
250 | if (err < 0) | |
251 | return err; | |
252 | ||
253 | if ((*reg & mask) == val) | |
254 | return 0; | |
255 | ||
69a4aa89 JK |
256 | if (msleep_interruptible(25)) |
257 | return -ERESTARTSYS; | |
1a64821c JK |
258 | |
259 | if (time_after(start_time, wait_until)) | |
260 | return -ETIMEDOUT; | |
261 | } | |
262 | } | |
263 | ||
264 | /** | |
265 | * nfp_nsp_command() - Execute a command on the NFP Service Processor | |
266 | * @state: NFP SP state | |
267 | * @code: NFP SP Command Code | |
268 | * @option: NFP SP Command Argument | |
269 | * @buff_cpp: NFP SP Buffer CPP Address info | |
270 | * @buff_addr: NFP SP Buffer Host address | |
271 | * | |
272 | * Return: 0 for success with no result | |
273 | * | |
69a4aa89 | 274 | * positive value for NSP completion with a result code |
1a64821c JK |
275 | * |
276 | * -EAGAIN if the NSP is not yet present | |
277 | * -ENODEV if the NSP is not a supported model | |
278 | * -EBUSY if the NSP is stuck | |
279 | * -EINTR if interrupted while waiting for completion | |
280 | * -ETIMEDOUT if the NSP took longer than 30 seconds to complete | |
281 | */ | |
282 | static int nfp_nsp_command(struct nfp_nsp *state, u16 code, u32 option, | |
283 | u32 buff_cpp, u64 buff_addr) | |
284 | { | |
285 | u64 reg, nsp_base, nsp_buffer, nsp_status, nsp_command; | |
286 | struct nfp_cpp *cpp = state->cpp; | |
287 | u32 nsp_cpp; | |
288 | int err; | |
289 | ||
290 | nsp_cpp = nfp_resource_cpp_id(state->res); | |
291 | nsp_base = nfp_resource_address(state->res); | |
292 | nsp_status = nsp_base + NSP_STATUS; | |
293 | nsp_command = nsp_base + NSP_COMMAND; | |
294 | nsp_buffer = nsp_base + NSP_BUFFER; | |
295 | ||
296 | err = nfp_nsp_check(state); | |
297 | if (err) | |
298 | return err; | |
299 | ||
300 | if (!FIELD_FIT(NSP_BUFFER_CPP, buff_cpp >> 8) || | |
301 | !FIELD_FIT(NSP_BUFFER_ADDRESS, buff_addr)) { | |
302 | nfp_err(cpp, "Host buffer out of reach %08x %016llx\n", | |
303 | buff_cpp, buff_addr); | |
304 | return -EINVAL; | |
305 | } | |
306 | ||
307 | err = nfp_cpp_writeq(cpp, nsp_cpp, nsp_buffer, | |
308 | FIELD_PREP(NSP_BUFFER_CPP, buff_cpp >> 8) | | |
309 | FIELD_PREP(NSP_BUFFER_ADDRESS, buff_addr)); | |
310 | if (err < 0) | |
311 | return err; | |
312 | ||
313 | err = nfp_cpp_writeq(cpp, nsp_cpp, nsp_command, | |
314 | FIELD_PREP(NSP_COMMAND_OPTION, option) | | |
315 | FIELD_PREP(NSP_COMMAND_CODE, code) | | |
316 | FIELD_PREP(NSP_COMMAND_START, 1)); | |
317 | if (err < 0) | |
318 | return err; | |
319 | ||
320 | /* Wait for NSP_COMMAND_START to go to 0 */ | |
321 | err = nfp_nsp_wait_reg(cpp, ®, | |
322 | nsp_cpp, nsp_command, NSP_COMMAND_START, 0); | |
323 | if (err) { | |
324 | nfp_err(cpp, "Error %d waiting for code 0x%04x to start\n", | |
325 | err, code); | |
326 | return err; | |
327 | } | |
328 | ||
329 | /* Wait for NSP_STATUS_BUSY to go to 0 */ | |
330 | err = nfp_nsp_wait_reg(cpp, ®, | |
331 | nsp_cpp, nsp_status, NSP_STATUS_BUSY, 0); | |
332 | if (err) { | |
333 | nfp_err(cpp, "Error %d waiting for code 0x%04x to complete\n", | |
334 | err, code); | |
335 | return err; | |
336 | } | |
337 | ||
338 | err = FIELD_GET(NSP_STATUS_RESULT, reg); | |
339 | if (err) { | |
340 | nfp_warn(cpp, "Result (error) code set: %d command: %d\n", | |
341 | -err, code); | |
342 | return -err; | |
343 | } | |
344 | ||
345 | err = nfp_cpp_readq(cpp, nsp_cpp, nsp_command, ®); | |
346 | if (err < 0) | |
347 | return err; | |
348 | ||
349 | return FIELD_GET(NSP_COMMAND_OPTION, reg); | |
350 | } | |
351 | ||
352 | static int nfp_nsp_command_buf(struct nfp_nsp *nsp, u16 code, u32 option, | |
353 | const void *in_buf, unsigned int in_size, | |
354 | void *out_buf, unsigned int out_size) | |
355 | { | |
356 | struct nfp_cpp *cpp = nsp->cpp; | |
357 | unsigned int max_size; | |
358 | u64 reg, cpp_buf; | |
359 | int ret, err; | |
360 | u32 cpp_id; | |
361 | ||
368b1d1c JK |
362 | if (nsp->ver.minor < 13) { |
363 | nfp_err(cpp, "NSP: Code 0x%04x with buffer not supported (ABI %hu.%hu)\n", | |
364 | code, nsp->ver.major, nsp->ver.minor); | |
1a64821c JK |
365 | return -EOPNOTSUPP; |
366 | } | |
367 | ||
368 | err = nfp_cpp_readq(cpp, nfp_resource_cpp_id(nsp->res), | |
369 | nfp_resource_address(nsp->res) + | |
370 | NSP_DFLT_BUFFER_CONFIG, | |
371 | ®); | |
372 | if (err < 0) | |
373 | return err; | |
374 | ||
375 | max_size = max(in_size, out_size); | |
376 | if (FIELD_GET(NSP_DFLT_BUFFER_SIZE_MB, reg) * SZ_1M < max_size) { | |
377 | nfp_err(cpp, "NSP: default buffer too small for command 0x%04x (%llu < %u)\n", | |
378 | code, FIELD_GET(NSP_DFLT_BUFFER_SIZE_MB, reg) * SZ_1M, | |
379 | max_size); | |
380 | return -EINVAL; | |
381 | } | |
382 | ||
383 | err = nfp_cpp_readq(cpp, nfp_resource_cpp_id(nsp->res), | |
384 | nfp_resource_address(nsp->res) + | |
385 | NSP_DFLT_BUFFER, | |
386 | ®); | |
387 | if (err < 0) | |
388 | return err; | |
389 | ||
390 | cpp_id = FIELD_GET(NSP_BUFFER_CPP, reg) << 8; | |
391 | cpp_buf = FIELD_GET(NSP_BUFFER_ADDRESS, reg); | |
392 | ||
393 | if (in_buf && in_size) { | |
394 | err = nfp_cpp_write(cpp, cpp_id, cpp_buf, in_buf, in_size); | |
395 | if (err < 0) | |
396 | return err; | |
397 | } | |
398 | ||
399 | ret = nfp_nsp_command(nsp, code, option, cpp_id, cpp_buf); | |
400 | if (ret < 0) | |
401 | return ret; | |
402 | ||
403 | if (out_buf && out_size) { | |
404 | err = nfp_cpp_read(cpp, cpp_id, cpp_buf, out_buf, out_size); | |
405 | if (err < 0) | |
406 | return err; | |
407 | } | |
408 | ||
409 | return ret; | |
410 | } | |
411 | ||
412 | int nfp_nsp_wait(struct nfp_nsp *state) | |
413 | { | |
414 | const unsigned long wait_until = jiffies + 30 * HZ; | |
415 | int err; | |
416 | ||
417 | nfp_dbg(state->cpp, "Waiting for NSP to respond (30 sec max).\n"); | |
418 | ||
419 | for (;;) { | |
420 | const unsigned long start_time = jiffies; | |
421 | ||
422 | err = nfp_nsp_command(state, SPCODE_NOOP, 0, 0, 0); | |
423 | if (err != -EAGAIN) | |
424 | break; | |
425 | ||
69a4aa89 JK |
426 | if (msleep_interruptible(25)) { |
427 | err = -ERESTARTSYS; | |
1a64821c | 428 | break; |
69a4aa89 | 429 | } |
1a64821c JK |
430 | |
431 | if (time_after(start_time, wait_until)) { | |
432 | err = -ETIMEDOUT; | |
433 | break; | |
434 | } | |
435 | } | |
436 | if (err) | |
437 | nfp_err(state->cpp, "NSP failed to respond %d\n", err); | |
438 | ||
439 | return err; | |
440 | } | |
441 | ||
442 | int nfp_nsp_device_soft_reset(struct nfp_nsp *state) | |
443 | { | |
444 | int err; | |
445 | ||
446 | err = nfp_nsp_command(state, SPCODE_SOFT_RESET, 0, 0, 0); | |
447 | ||
448 | nfp_nffw_cache_flush(state->cpp); | |
449 | ||
450 | return err; | |
451 | } | |
452 | ||
453 | int nfp_nsp_load_fw(struct nfp_nsp *state, const struct firmware *fw) | |
454 | { | |
455 | return nfp_nsp_command_buf(state, SPCODE_FW_LOAD, fw->size, fw->data, | |
456 | fw->size, NULL, 0); | |
457 | } | |
458 | ||
459 | int nfp_nsp_read_eth_table(struct nfp_nsp *state, void *buf, unsigned int size) | |
460 | { | |
461 | return nfp_nsp_command_buf(state, SPCODE_ETH_RESCAN, size, NULL, 0, | |
462 | buf, size); | |
463 | } | |
464 | ||
465 | int nfp_nsp_write_eth_table(struct nfp_nsp *state, | |
466 | const void *buf, unsigned int size) | |
467 | { | |
468 | return nfp_nsp_command_buf(state, SPCODE_ETH_CONTROL, size, buf, size, | |
469 | NULL, 0); | |
470 | } |