diff options
Diffstat (limited to 'drivers/misc/ocxl/config.c')
-rw-r--r-- | drivers/misc/ocxl/config.c | 712 |
1 files changed, 712 insertions, 0 deletions
diff --git a/drivers/misc/ocxl/config.c b/drivers/misc/ocxl/config.c new file mode 100644 index 000000000000..ea8cca50ea06 --- /dev/null +++ b/drivers/misc/ocxl/config.c | |||
@@ -0,0 +1,712 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0+ | ||
2 | // Copyright 2017 IBM Corp. | ||
3 | #include <linux/pci.h> | ||
4 | #include <asm/pnv-ocxl.h> | ||
5 | #include <misc/ocxl-config.h> | ||
6 | #include "ocxl_internal.h" | ||
7 | |||
8 | #define EXTRACT_BIT(val, bit) (!!(val & BIT(bit))) | ||
9 | #define EXTRACT_BITS(val, s, e) ((val & GENMASK(e, s)) >> s) | ||
10 | |||
11 | #define OCXL_DVSEC_AFU_IDX_MASK GENMASK(5, 0) | ||
12 | #define OCXL_DVSEC_ACTAG_MASK GENMASK(11, 0) | ||
13 | #define OCXL_DVSEC_PASID_MASK GENMASK(19, 0) | ||
14 | #define OCXL_DVSEC_PASID_LOG_MASK GENMASK(4, 0) | ||
15 | |||
16 | #define OCXL_DVSEC_TEMPL_VERSION 0x0 | ||
17 | #define OCXL_DVSEC_TEMPL_NAME 0x4 | ||
18 | #define OCXL_DVSEC_TEMPL_AFU_VERSION 0x1C | ||
19 | #define OCXL_DVSEC_TEMPL_MMIO_GLOBAL 0x20 | ||
20 | #define OCXL_DVSEC_TEMPL_MMIO_GLOBAL_SZ 0x28 | ||
21 | #define OCXL_DVSEC_TEMPL_MMIO_PP 0x30 | ||
22 | #define OCXL_DVSEC_TEMPL_MMIO_PP_SZ 0x38 | ||
23 | #define OCXL_DVSEC_TEMPL_MEM_SZ 0x3C | ||
24 | #define OCXL_DVSEC_TEMPL_WWID 0x40 | ||
25 | |||
26 | #define OCXL_MAX_AFU_PER_FUNCTION 64 | ||
27 | #define OCXL_TEMPL_LEN 0x58 | ||
28 | #define OCXL_TEMPL_NAME_LEN 24 | ||
29 | #define OCXL_CFG_TIMEOUT 3 | ||
30 | |||
31 | static int find_dvsec(struct pci_dev *dev, int dvsec_id) | ||
32 | { | ||
33 | int vsec = 0; | ||
34 | u16 vendor, id; | ||
35 | |||
36 | while ((vsec = pci_find_next_ext_capability(dev, vsec, | ||
37 | OCXL_EXT_CAP_ID_DVSEC))) { | ||
38 | pci_read_config_word(dev, vsec + OCXL_DVSEC_VENDOR_OFFSET, | ||
39 | &vendor); | ||
40 | pci_read_config_word(dev, vsec + OCXL_DVSEC_ID_OFFSET, &id); | ||
41 | if (vendor == PCI_VENDOR_ID_IBM && id == dvsec_id) | ||
42 | return vsec; | ||
43 | } | ||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | static int find_dvsec_afu_ctrl(struct pci_dev *dev, u8 afu_idx) | ||
48 | { | ||
49 | int vsec = 0; | ||
50 | u16 vendor, id; | ||
51 | u8 idx; | ||
52 | |||
53 | while ((vsec = pci_find_next_ext_capability(dev, vsec, | ||
54 | OCXL_EXT_CAP_ID_DVSEC))) { | ||
55 | pci_read_config_word(dev, vsec + OCXL_DVSEC_VENDOR_OFFSET, | ||
56 | &vendor); | ||
57 | pci_read_config_word(dev, vsec + OCXL_DVSEC_ID_OFFSET, &id); | ||
58 | |||
59 | if (vendor == PCI_VENDOR_ID_IBM && | ||
60 | id == OCXL_DVSEC_AFU_CTRL_ID) { | ||
61 | pci_read_config_byte(dev, | ||
62 | vsec + OCXL_DVSEC_AFU_CTRL_AFU_IDX, | ||
63 | &idx); | ||
64 | if (idx == afu_idx) | ||
65 | return vsec; | ||
66 | } | ||
67 | } | ||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | static int read_pasid(struct pci_dev *dev, struct ocxl_fn_config *fn) | ||
72 | { | ||
73 | u16 val; | ||
74 | int pos; | ||
75 | |||
76 | pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_PASID); | ||
77 | if (!pos) { | ||
78 | /* | ||
79 | * PASID capability is not mandatory, but there | ||
80 | * shouldn't be any AFU | ||
81 | */ | ||
82 | dev_dbg(&dev->dev, "Function doesn't require any PASID\n"); | ||
83 | fn->max_pasid_log = -1; | ||
84 | goto out; | ||
85 | } | ||
86 | pci_read_config_word(dev, pos + PCI_PASID_CAP, &val); | ||
87 | fn->max_pasid_log = EXTRACT_BITS(val, 8, 12); | ||
88 | |||
89 | out: | ||
90 | dev_dbg(&dev->dev, "PASID capability:\n"); | ||
91 | dev_dbg(&dev->dev, " Max PASID log = %d\n", fn->max_pasid_log); | ||
92 | return 0; | ||
93 | } | ||
94 | |||
95 | static int read_dvsec_tl(struct pci_dev *dev, struct ocxl_fn_config *fn) | ||
96 | { | ||
97 | int pos; | ||
98 | |||
99 | pos = find_dvsec(dev, OCXL_DVSEC_TL_ID); | ||
100 | if (!pos && PCI_FUNC(dev->devfn) == 0) { | ||
101 | dev_err(&dev->dev, "Can't find TL DVSEC\n"); | ||
102 | return -ENODEV; | ||
103 | } | ||
104 | if (pos && PCI_FUNC(dev->devfn) != 0) { | ||
105 | dev_err(&dev->dev, "TL DVSEC is only allowed on function 0\n"); | ||
106 | return -ENODEV; | ||
107 | } | ||
108 | fn->dvsec_tl_pos = pos; | ||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static int read_dvsec_function(struct pci_dev *dev, struct ocxl_fn_config *fn) | ||
113 | { | ||
114 | int pos, afu_present; | ||
115 | u32 val; | ||
116 | |||
117 | pos = find_dvsec(dev, OCXL_DVSEC_FUNC_ID); | ||
118 | if (!pos) { | ||
119 | dev_err(&dev->dev, "Can't find function DVSEC\n"); | ||
120 | return -ENODEV; | ||
121 | } | ||
122 | fn->dvsec_function_pos = pos; | ||
123 | |||
124 | pci_read_config_dword(dev, pos + OCXL_DVSEC_FUNC_OFF_INDEX, &val); | ||
125 | afu_present = EXTRACT_BIT(val, 31); | ||
126 | if (!afu_present) { | ||
127 | fn->max_afu_index = -1; | ||
128 | dev_dbg(&dev->dev, "Function doesn't define any AFU\n"); | ||
129 | goto out; | ||
130 | } | ||
131 | fn->max_afu_index = EXTRACT_BITS(val, 24, 29); | ||
132 | |||
133 | out: | ||
134 | dev_dbg(&dev->dev, "Function DVSEC:\n"); | ||
135 | dev_dbg(&dev->dev, " Max AFU index = %d\n", fn->max_afu_index); | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | static int read_dvsec_afu_info(struct pci_dev *dev, struct ocxl_fn_config *fn) | ||
140 | { | ||
141 | int pos; | ||
142 | |||
143 | if (fn->max_afu_index < 0) { | ||
144 | fn->dvsec_afu_info_pos = -1; | ||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | pos = find_dvsec(dev, OCXL_DVSEC_AFU_INFO_ID); | ||
149 | if (!pos) { | ||
150 | dev_err(&dev->dev, "Can't find AFU information DVSEC\n"); | ||
151 | return -ENODEV; | ||
152 | } | ||
153 | fn->dvsec_afu_info_pos = pos; | ||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | static int read_dvsec_vendor(struct pci_dev *dev) | ||
158 | { | ||
159 | int pos; | ||
160 | u32 cfg, tlx, dlx; | ||
161 | |||
162 | /* | ||
163 | * vendor specific DVSEC is optional | ||
164 | * | ||
165 | * It's currently only used on function 0 to specify the | ||
166 | * version of some logic blocks. Some older images may not | ||
167 | * even have it so we ignore any errors | ||
168 | */ | ||
169 | if (PCI_FUNC(dev->devfn) != 0) | ||
170 | return 0; | ||
171 | |||
172 | pos = find_dvsec(dev, OCXL_DVSEC_VENDOR_ID); | ||
173 | if (!pos) | ||
174 | return 0; | ||
175 | |||
176 | pci_read_config_dword(dev, pos + OCXL_DVSEC_VENDOR_CFG_VERS, &cfg); | ||
177 | pci_read_config_dword(dev, pos + OCXL_DVSEC_VENDOR_TLX_VERS, &tlx); | ||
178 | pci_read_config_dword(dev, pos + OCXL_DVSEC_VENDOR_DLX_VERS, &dlx); | ||
179 | |||
180 | dev_dbg(&dev->dev, "Vendor specific DVSEC:\n"); | ||
181 | dev_dbg(&dev->dev, " CFG version = 0x%x\n", cfg); | ||
182 | dev_dbg(&dev->dev, " TLX version = 0x%x\n", tlx); | ||
183 | dev_dbg(&dev->dev, " DLX version = 0x%x\n", dlx); | ||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static int validate_function(struct pci_dev *dev, struct ocxl_fn_config *fn) | ||
188 | { | ||
189 | if (fn->max_pasid_log == -1 && fn->max_afu_index >= 0) { | ||
190 | dev_err(&dev->dev, | ||
191 | "AFUs are defined but no PASIDs are requested\n"); | ||
192 | return -EINVAL; | ||
193 | } | ||
194 | |||
195 | if (fn->max_afu_index > OCXL_MAX_AFU_PER_FUNCTION) { | ||
196 | dev_err(&dev->dev, | ||
197 | "Max AFU index out of architectural limit (%d vs %d)\n", | ||
198 | fn->max_afu_index, OCXL_MAX_AFU_PER_FUNCTION); | ||
199 | return -EINVAL; | ||
200 | } | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | int ocxl_config_read_function(struct pci_dev *dev, struct ocxl_fn_config *fn) | ||
205 | { | ||
206 | int rc; | ||
207 | |||
208 | rc = read_pasid(dev, fn); | ||
209 | if (rc) { | ||
210 | dev_err(&dev->dev, "Invalid PASID configuration: %d\n", rc); | ||
211 | return -ENODEV; | ||
212 | } | ||
213 | |||
214 | rc = read_dvsec_tl(dev, fn); | ||
215 | if (rc) { | ||
216 | dev_err(&dev->dev, | ||
217 | "Invalid Transaction Layer DVSEC configuration: %d\n", | ||
218 | rc); | ||
219 | return -ENODEV; | ||
220 | } | ||
221 | |||
222 | rc = read_dvsec_function(dev, fn); | ||
223 | if (rc) { | ||
224 | dev_err(&dev->dev, | ||
225 | "Invalid Function DVSEC configuration: %d\n", rc); | ||
226 | return -ENODEV; | ||
227 | } | ||
228 | |||
229 | rc = read_dvsec_afu_info(dev, fn); | ||
230 | if (rc) { | ||
231 | dev_err(&dev->dev, "Invalid AFU configuration: %d\n", rc); | ||
232 | return -ENODEV; | ||
233 | } | ||
234 | |||
235 | rc = read_dvsec_vendor(dev); | ||
236 | if (rc) { | ||
237 | dev_err(&dev->dev, | ||
238 | "Invalid vendor specific DVSEC configuration: %d\n", | ||
239 | rc); | ||
240 | return -ENODEV; | ||
241 | } | ||
242 | |||
243 | rc = validate_function(dev, fn); | ||
244 | return rc; | ||
245 | } | ||
246 | |||
247 | static int read_afu_info(struct pci_dev *dev, struct ocxl_fn_config *fn, | ||
248 | int offset, u32 *data) | ||
249 | { | ||
250 | u32 val; | ||
251 | unsigned long timeout = jiffies + (HZ * OCXL_CFG_TIMEOUT); | ||
252 | int pos = fn->dvsec_afu_info_pos; | ||
253 | |||
254 | /* Protect 'data valid' bit */ | ||
255 | if (EXTRACT_BIT(offset, 31)) { | ||
256 | dev_err(&dev->dev, "Invalid offset in AFU info DVSEC\n"); | ||
257 | return -EINVAL; | ||
258 | } | ||
259 | |||
260 | pci_write_config_dword(dev, pos + OCXL_DVSEC_AFU_INFO_OFF, offset); | ||
261 | pci_read_config_dword(dev, pos + OCXL_DVSEC_AFU_INFO_OFF, &val); | ||
262 | while (!EXTRACT_BIT(val, 31)) { | ||
263 | if (time_after_eq(jiffies, timeout)) { | ||
264 | dev_err(&dev->dev, | ||
265 | "Timeout while reading AFU info DVSEC (offset=%d)\n", | ||
266 | offset); | ||
267 | return -EBUSY; | ||
268 | } | ||
269 | cpu_relax(); | ||
270 | pci_read_config_dword(dev, pos + OCXL_DVSEC_AFU_INFO_OFF, &val); | ||
271 | } | ||
272 | pci_read_config_dword(dev, pos + OCXL_DVSEC_AFU_INFO_DATA, data); | ||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | int ocxl_config_check_afu_index(struct pci_dev *dev, | ||
277 | struct ocxl_fn_config *fn, int afu_idx) | ||
278 | { | ||
279 | u32 val; | ||
280 | int rc, templ_major, templ_minor, len; | ||
281 | |||
282 | pci_write_config_word(dev, fn->dvsec_afu_info_pos, afu_idx); | ||
283 | rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_VERSION, &val); | ||
284 | if (rc) | ||
285 | return rc; | ||
286 | |||
287 | /* AFU index map can have holes */ | ||
288 | if (!val) | ||
289 | return 0; | ||
290 | |||
291 | templ_major = EXTRACT_BITS(val, 8, 15); | ||
292 | templ_minor = EXTRACT_BITS(val, 0, 7); | ||
293 | dev_dbg(&dev->dev, "AFU descriptor template version %d.%d\n", | ||
294 | templ_major, templ_minor); | ||
295 | |||
296 | len = EXTRACT_BITS(val, 16, 31); | ||
297 | if (len != OCXL_TEMPL_LEN) { | ||
298 | dev_warn(&dev->dev, | ||
299 | "Unexpected template length in AFU information (%#x)\n", | ||
300 | len); | ||
301 | } | ||
302 | return 1; | ||
303 | } | ||
304 | |||
305 | static int read_afu_name(struct pci_dev *dev, struct ocxl_fn_config *fn, | ||
306 | struct ocxl_afu_config *afu) | ||
307 | { | ||
308 | int i, rc; | ||
309 | u32 val, *ptr; | ||
310 | |||
311 | BUILD_BUG_ON(OCXL_AFU_NAME_SZ < OCXL_TEMPL_NAME_LEN); | ||
312 | for (i = 0; i < OCXL_TEMPL_NAME_LEN; i += 4) { | ||
313 | rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_NAME + i, &val); | ||
314 | if (rc) | ||
315 | return rc; | ||
316 | ptr = (u32 *) &afu->name[i]; | ||
317 | *ptr = val; | ||
318 | } | ||
319 | afu->name[OCXL_AFU_NAME_SZ - 1] = '\0'; /* play safe */ | ||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | static int read_afu_mmio(struct pci_dev *dev, struct ocxl_fn_config *fn, | ||
324 | struct ocxl_afu_config *afu) | ||
325 | { | ||
326 | int rc; | ||
327 | u32 val; | ||
328 | |||
329 | /* | ||
330 | * Global MMIO | ||
331 | */ | ||
332 | rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_GLOBAL, &val); | ||
333 | if (rc) | ||
334 | return rc; | ||
335 | afu->global_mmio_bar = EXTRACT_BITS(val, 0, 2); | ||
336 | afu->global_mmio_offset = EXTRACT_BITS(val, 16, 31) << 16; | ||
337 | |||
338 | rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_GLOBAL + 4, &val); | ||
339 | if (rc) | ||
340 | return rc; | ||
341 | afu->global_mmio_offset += (u64) val << 32; | ||
342 | |||
343 | rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_GLOBAL_SZ, &val); | ||
344 | if (rc) | ||
345 | return rc; | ||
346 | afu->global_mmio_size = val; | ||
347 | |||
348 | /* | ||
349 | * Per-process MMIO | ||
350 | */ | ||
351 | rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_PP, &val); | ||
352 | if (rc) | ||
353 | return rc; | ||
354 | afu->pp_mmio_bar = EXTRACT_BITS(val, 0, 2); | ||
355 | afu->pp_mmio_offset = EXTRACT_BITS(val, 16, 31) << 16; | ||
356 | |||
357 | rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_PP + 4, &val); | ||
358 | if (rc) | ||
359 | return rc; | ||
360 | afu->pp_mmio_offset += (u64) val << 32; | ||
361 | |||
362 | rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MMIO_PP_SZ, &val); | ||
363 | if (rc) | ||
364 | return rc; | ||
365 | afu->pp_mmio_stride = val; | ||
366 | |||
367 | return 0; | ||
368 | } | ||
369 | |||
370 | static int read_afu_control(struct pci_dev *dev, struct ocxl_afu_config *afu) | ||
371 | { | ||
372 | int pos; | ||
373 | u8 val8; | ||
374 | u16 val16; | ||
375 | |||
376 | pos = find_dvsec_afu_ctrl(dev, afu->idx); | ||
377 | if (!pos) { | ||
378 | dev_err(&dev->dev, "Can't find AFU control DVSEC for AFU %d\n", | ||
379 | afu->idx); | ||
380 | return -ENODEV; | ||
381 | } | ||
382 | afu->dvsec_afu_control_pos = pos; | ||
383 | |||
384 | pci_read_config_byte(dev, pos + OCXL_DVSEC_AFU_CTRL_PASID_SUP, &val8); | ||
385 | afu->pasid_supported_log = EXTRACT_BITS(val8, 0, 4); | ||
386 | |||
387 | pci_read_config_word(dev, pos + OCXL_DVSEC_AFU_CTRL_ACTAG_SUP, &val16); | ||
388 | afu->actag_supported = EXTRACT_BITS(val16, 0, 11); | ||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | static bool char_allowed(int c) | ||
393 | { | ||
394 | /* | ||
395 | * Permitted Characters : Alphanumeric, hyphen, underscore, comma | ||
396 | */ | ||
397 | if ((c >= 0x30 && c <= 0x39) /* digits */ || | ||
398 | (c >= 0x41 && c <= 0x5A) /* upper case */ || | ||
399 | (c >= 0x61 && c <= 0x7A) /* lower case */ || | ||
400 | c == 0 /* NULL */ || | ||
401 | c == 0x2D /* - */ || | ||
402 | c == 0x5F /* _ */ || | ||
403 | c == 0x2C /* , */) | ||
404 | return true; | ||
405 | return false; | ||
406 | } | ||
407 | |||
408 | static int validate_afu(struct pci_dev *dev, struct ocxl_afu_config *afu) | ||
409 | { | ||
410 | int i; | ||
411 | |||
412 | if (!afu->name[0]) { | ||
413 | dev_err(&dev->dev, "Empty AFU name\n"); | ||
414 | return -EINVAL; | ||
415 | } | ||
416 | for (i = 0; i < OCXL_TEMPL_NAME_LEN; i++) { | ||
417 | if (!char_allowed(afu->name[i])) { | ||
418 | dev_err(&dev->dev, | ||
419 | "Invalid character in AFU name\n"); | ||
420 | return -EINVAL; | ||
421 | } | ||
422 | } | ||
423 | |||
424 | if (afu->global_mmio_bar != 0 && | ||
425 | afu->global_mmio_bar != 2 && | ||
426 | afu->global_mmio_bar != 4) { | ||
427 | dev_err(&dev->dev, "Invalid global MMIO bar number\n"); | ||
428 | return -EINVAL; | ||
429 | } | ||
430 | if (afu->pp_mmio_bar != 0 && | ||
431 | afu->pp_mmio_bar != 2 && | ||
432 | afu->pp_mmio_bar != 4) { | ||
433 | dev_err(&dev->dev, "Invalid per-process MMIO bar number\n"); | ||
434 | return -EINVAL; | ||
435 | } | ||
436 | return 0; | ||
437 | } | ||
438 | |||
439 | int ocxl_config_read_afu(struct pci_dev *dev, struct ocxl_fn_config *fn, | ||
440 | struct ocxl_afu_config *afu, u8 afu_idx) | ||
441 | { | ||
442 | int rc; | ||
443 | u32 val32; | ||
444 | |||
445 | /* | ||
446 | * First, we need to write the AFU idx for the AFU we want to | ||
447 | * access. | ||
448 | */ | ||
449 | WARN_ON((afu_idx & OCXL_DVSEC_AFU_IDX_MASK) != afu_idx); | ||
450 | afu->idx = afu_idx; | ||
451 | pci_write_config_byte(dev, | ||
452 | fn->dvsec_afu_info_pos + OCXL_DVSEC_AFU_INFO_AFU_IDX, | ||
453 | afu->idx); | ||
454 | |||
455 | rc = read_afu_name(dev, fn, afu); | ||
456 | if (rc) | ||
457 | return rc; | ||
458 | |||
459 | rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_AFU_VERSION, &val32); | ||
460 | if (rc) | ||
461 | return rc; | ||
462 | afu->version_major = EXTRACT_BITS(val32, 24, 31); | ||
463 | afu->version_minor = EXTRACT_BITS(val32, 16, 23); | ||
464 | afu->afuc_type = EXTRACT_BITS(val32, 14, 15); | ||
465 | afu->afum_type = EXTRACT_BITS(val32, 12, 13); | ||
466 | afu->profile = EXTRACT_BITS(val32, 0, 7); | ||
467 | |||
468 | rc = read_afu_mmio(dev, fn, afu); | ||
469 | if (rc) | ||
470 | return rc; | ||
471 | |||
472 | rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MEM_SZ, &val32); | ||
473 | if (rc) | ||
474 | return rc; | ||
475 | afu->log_mem_size = EXTRACT_BITS(val32, 0, 7); | ||
476 | |||
477 | rc = read_afu_control(dev, afu); | ||
478 | if (rc) | ||
479 | return rc; | ||
480 | |||
481 | dev_dbg(&dev->dev, "AFU configuration:\n"); | ||
482 | dev_dbg(&dev->dev, " name = %s\n", afu->name); | ||
483 | dev_dbg(&dev->dev, " version = %d.%d\n", afu->version_major, | ||
484 | afu->version_minor); | ||
485 | dev_dbg(&dev->dev, " global mmio bar = %hhu\n", afu->global_mmio_bar); | ||
486 | dev_dbg(&dev->dev, " global mmio offset = %#llx\n", | ||
487 | afu->global_mmio_offset); | ||
488 | dev_dbg(&dev->dev, " global mmio size = %#x\n", afu->global_mmio_size); | ||
489 | dev_dbg(&dev->dev, " pp mmio bar = %hhu\n", afu->pp_mmio_bar); | ||
490 | dev_dbg(&dev->dev, " pp mmio offset = %#llx\n", afu->pp_mmio_offset); | ||
491 | dev_dbg(&dev->dev, " pp mmio stride = %#x\n", afu->pp_mmio_stride); | ||
492 | dev_dbg(&dev->dev, " mem size (log) = %hhu\n", afu->log_mem_size); | ||
493 | dev_dbg(&dev->dev, " pasid supported (log) = %u\n", | ||
494 | afu->pasid_supported_log); | ||
495 | dev_dbg(&dev->dev, " actag supported = %u\n", | ||
496 | afu->actag_supported); | ||
497 | |||
498 | rc = validate_afu(dev, afu); | ||
499 | return rc; | ||
500 | } | ||
501 | |||
502 | int ocxl_config_get_actag_info(struct pci_dev *dev, u16 *base, u16 *enabled, | ||
503 | u16 *supported) | ||
504 | { | ||
505 | int rc; | ||
506 | |||
507 | /* | ||
508 | * This is really a simple wrapper for the kernel API, to | ||
509 | * avoid an external driver using ocxl as a library to call | ||
510 | * platform-dependent code | ||
511 | */ | ||
512 | rc = pnv_ocxl_get_actag(dev, base, enabled, supported); | ||
513 | if (rc) { | ||
514 | dev_err(&dev->dev, "Can't get actag for device: %d\n", rc); | ||
515 | return rc; | ||
516 | } | ||
517 | return 0; | ||
518 | } | ||
519 | |||
520 | void ocxl_config_set_afu_actag(struct pci_dev *dev, int pos, int actag_base, | ||
521 | int actag_count) | ||
522 | { | ||
523 | u16 val; | ||
524 | |||
525 | val = actag_count & OCXL_DVSEC_ACTAG_MASK; | ||
526 | pci_write_config_byte(dev, pos + OCXL_DVSEC_AFU_CTRL_ACTAG_EN, val); | ||
527 | |||
528 | val = actag_base & OCXL_DVSEC_ACTAG_MASK; | ||
529 | pci_write_config_dword(dev, pos + OCXL_DVSEC_AFU_CTRL_ACTAG_BASE, val); | ||
530 | } | ||
531 | |||
532 | int ocxl_config_get_pasid_info(struct pci_dev *dev, int *count) | ||
533 | { | ||
534 | return pnv_ocxl_get_pasid_count(dev, count); | ||
535 | } | ||
536 | |||
537 | void ocxl_config_set_afu_pasid(struct pci_dev *dev, int pos, int pasid_base, | ||
538 | u32 pasid_count_log) | ||
539 | { | ||
540 | u8 val8; | ||
541 | u32 val32; | ||
542 | |||
543 | val8 = pasid_count_log & OCXL_DVSEC_PASID_LOG_MASK; | ||
544 | pci_write_config_byte(dev, pos + OCXL_DVSEC_AFU_CTRL_PASID_EN, val8); | ||
545 | |||
546 | pci_read_config_dword(dev, pos + OCXL_DVSEC_AFU_CTRL_PASID_BASE, | ||
547 | &val32); | ||
548 | val32 &= ~OCXL_DVSEC_PASID_MASK; | ||
549 | val32 |= pasid_base & OCXL_DVSEC_PASID_MASK; | ||
550 | pci_write_config_dword(dev, pos + OCXL_DVSEC_AFU_CTRL_PASID_BASE, | ||
551 | val32); | ||
552 | } | ||
553 | |||
554 | void ocxl_config_set_afu_state(struct pci_dev *dev, int pos, int enable) | ||
555 | { | ||
556 | u8 val; | ||
557 | |||
558 | pci_read_config_byte(dev, pos + OCXL_DVSEC_AFU_CTRL_ENABLE, &val); | ||
559 | if (enable) | ||
560 | val |= 1; | ||
561 | else | ||
562 | val &= 0xFE; | ||
563 | pci_write_config_byte(dev, pos + OCXL_DVSEC_AFU_CTRL_ENABLE, val); | ||
564 | } | ||
565 | |||
566 | int ocxl_config_set_TL(struct pci_dev *dev, int tl_dvsec) | ||
567 | { | ||
568 | u32 val; | ||
569 | __be32 *be32ptr; | ||
570 | u8 timers; | ||
571 | int i, rc; | ||
572 | long recv_cap; | ||
573 | char *recv_rate; | ||
574 | |||
575 | /* | ||
576 | * Skip on function != 0, as the TL can only be defined on 0 | ||
577 | */ | ||
578 | if (PCI_FUNC(dev->devfn) != 0) | ||
579 | return 0; | ||
580 | |||
581 | recv_rate = kzalloc(PNV_OCXL_TL_RATE_BUF_SIZE, GFP_KERNEL); | ||
582 | if (!recv_rate) | ||
583 | return -ENOMEM; | ||
584 | /* | ||
585 | * The spec defines 64 templates for messages in the | ||
586 | * Transaction Layer (TL). | ||
587 | * | ||
588 | * The host and device each support a subset, so we need to | ||
589 | * configure the transmitters on each side to send only | ||
590 | * templates the receiver understands, at a rate the receiver | ||
591 | * can process. Per the spec, template 0 must be supported by | ||
592 | * everybody. That's the template which has been used by the | ||
593 | * host and device so far. | ||
594 | * | ||
595 | * The sending rate limit must be set before the template is | ||
596 | * enabled. | ||
597 | */ | ||
598 | |||
599 | /* | ||
600 | * Device -> host | ||
601 | */ | ||
602 | rc = pnv_ocxl_get_tl_cap(dev, &recv_cap, recv_rate, | ||
603 | PNV_OCXL_TL_RATE_BUF_SIZE); | ||
604 | if (rc) | ||
605 | goto out; | ||
606 | |||
607 | for (i = 0; i < PNV_OCXL_TL_RATE_BUF_SIZE; i += 4) { | ||
608 | be32ptr = (__be32 *) &recv_rate[i]; | ||
609 | pci_write_config_dword(dev, | ||
610 | tl_dvsec + OCXL_DVSEC_TL_SEND_RATE + i, | ||
611 | be32_to_cpu(*be32ptr)); | ||
612 | } | ||
613 | val = recv_cap >> 32; | ||
614 | pci_write_config_dword(dev, tl_dvsec + OCXL_DVSEC_TL_SEND_CAP, val); | ||
615 | val = recv_cap & GENMASK(31, 0); | ||
616 | pci_write_config_dword(dev, tl_dvsec + OCXL_DVSEC_TL_SEND_CAP + 4, val); | ||
617 | |||
618 | /* | ||
619 | * Host -> device | ||
620 | */ | ||
621 | for (i = 0; i < PNV_OCXL_TL_RATE_BUF_SIZE; i += 4) { | ||
622 | pci_read_config_dword(dev, | ||
623 | tl_dvsec + OCXL_DVSEC_TL_RECV_RATE + i, | ||
624 | &val); | ||
625 | be32ptr = (__be32 *) &recv_rate[i]; | ||
626 | *be32ptr = cpu_to_be32(val); | ||
627 | } | ||
628 | pci_read_config_dword(dev, tl_dvsec + OCXL_DVSEC_TL_RECV_CAP, &val); | ||
629 | recv_cap = (long) val << 32; | ||
630 | pci_read_config_dword(dev, tl_dvsec + OCXL_DVSEC_TL_RECV_CAP + 4, &val); | ||
631 | recv_cap |= val; | ||
632 | |||
633 | rc = pnv_ocxl_set_tl_conf(dev, recv_cap, __pa(recv_rate), | ||
634 | PNV_OCXL_TL_RATE_BUF_SIZE); | ||
635 | if (rc) | ||
636 | goto out; | ||
637 | |||
638 | /* | ||
639 | * Opencapi commands needing to be retried are classified per | ||
640 | * the TL in 2 groups: short and long commands. | ||
641 | * | ||
642 | * The short back off timer it not used for now. It will be | ||
643 | * for opencapi 4.0. | ||
644 | * | ||
645 | * The long back off timer is typically used when an AFU hits | ||
646 | * a page fault but the NPU is already processing one. So the | ||
647 | * AFU needs to wait before it can resubmit. Having a value | ||
648 | * too low doesn't break anything, but can generate extra | ||
649 | * traffic on the link. | ||
650 | * We set it to 1.6 us for now. It's shorter than, but in the | ||
651 | * same order of magnitude as the time spent to process a page | ||
652 | * fault. | ||
653 | */ | ||
654 | timers = 0x2 << 4; /* long timer = 1.6 us */ | ||
655 | pci_write_config_byte(dev, tl_dvsec + OCXL_DVSEC_TL_BACKOFF_TIMERS, | ||
656 | timers); | ||
657 | |||
658 | rc = 0; | ||
659 | out: | ||
660 | kfree(recv_rate); | ||
661 | return rc; | ||
662 | } | ||
663 | |||
664 | int ocxl_config_terminate_pasid(struct pci_dev *dev, int afu_control, int pasid) | ||
665 | { | ||
666 | u32 val; | ||
667 | unsigned long timeout; | ||
668 | |||
669 | pci_read_config_dword(dev, afu_control + OCXL_DVSEC_AFU_CTRL_TERM_PASID, | ||
670 | &val); | ||
671 | if (EXTRACT_BIT(val, 20)) { | ||
672 | dev_err(&dev->dev, | ||
673 | "Can't terminate PASID %#x, previous termination didn't complete\n", | ||
674 | pasid); | ||
675 | return -EBUSY; | ||
676 | } | ||
677 | |||
678 | val &= ~OCXL_DVSEC_PASID_MASK; | ||
679 | val |= pasid & OCXL_DVSEC_PASID_MASK; | ||
680 | val |= BIT(20); | ||
681 | pci_write_config_dword(dev, | ||
682 | afu_control + OCXL_DVSEC_AFU_CTRL_TERM_PASID, | ||
683 | val); | ||
684 | |||
685 | timeout = jiffies + (HZ * OCXL_CFG_TIMEOUT); | ||
686 | pci_read_config_dword(dev, afu_control + OCXL_DVSEC_AFU_CTRL_TERM_PASID, | ||
687 | &val); | ||
688 | while (EXTRACT_BIT(val, 20)) { | ||
689 | if (time_after_eq(jiffies, timeout)) { | ||
690 | dev_err(&dev->dev, | ||
691 | "Timeout while waiting for AFU to terminate PASID %#x\n", | ||
692 | pasid); | ||
693 | return -EBUSY; | ||
694 | } | ||
695 | cpu_relax(); | ||
696 | pci_read_config_dword(dev, | ||
697 | afu_control + OCXL_DVSEC_AFU_CTRL_TERM_PASID, | ||
698 | &val); | ||
699 | } | ||
700 | return 0; | ||
701 | } | ||
702 | |||
703 | void ocxl_config_set_actag(struct pci_dev *dev, int func_dvsec, u32 tag_first, | ||
704 | u32 tag_count) | ||
705 | { | ||
706 | u32 val; | ||
707 | |||
708 | val = (tag_first & OCXL_DVSEC_ACTAG_MASK) << 16; | ||
709 | val |= tag_count & OCXL_DVSEC_ACTAG_MASK; | ||
710 | pci_write_config_dword(dev, func_dvsec + OCXL_DVSEC_FUNC_OFF_ACTAG, | ||
711 | val); | ||
712 | } | ||