.. | .. |
---|
2 | 2 | // Copyright 2017 IBM Corp. |
---|
3 | 3 | #include <linux/pci.h> |
---|
4 | 4 | #include <asm/pnv-ocxl.h> |
---|
5 | | -#include <misc/ocxl.h> |
---|
6 | 5 | #include <misc/ocxl-config.h> |
---|
| 6 | +#include "ocxl_internal.h" |
---|
7 | 7 | |
---|
8 | 8 | #define EXTRACT_BIT(val, bit) (!!(val & BIT(bit))) |
---|
9 | 9 | #define EXTRACT_BITS(val, s, e) ((val & GENMASK(e, s)) >> s) |
---|
.. | .. |
---|
20 | 20 | #define OCXL_DVSEC_TEMPL_MMIO_GLOBAL_SZ 0x28 |
---|
21 | 21 | #define OCXL_DVSEC_TEMPL_MMIO_PP 0x30 |
---|
22 | 22 | #define OCXL_DVSEC_TEMPL_MMIO_PP_SZ 0x38 |
---|
23 | | -#define OCXL_DVSEC_TEMPL_MEM_SZ 0x3C |
---|
24 | | -#define OCXL_DVSEC_TEMPL_WWID 0x40 |
---|
| 23 | +#define OCXL_DVSEC_TEMPL_ALL_MEM_SZ 0x3C |
---|
| 24 | +#define OCXL_DVSEC_TEMPL_LPC_MEM_START 0x40 |
---|
| 25 | +#define OCXL_DVSEC_TEMPL_WWID 0x48 |
---|
| 26 | +#define OCXL_DVSEC_TEMPL_LPC_MEM_SZ 0x58 |
---|
25 | 27 | |
---|
26 | 28 | #define OCXL_MAX_AFU_PER_FUNCTION 64 |
---|
27 | | -#define OCXL_TEMPL_LEN 0x58 |
---|
| 29 | +#define OCXL_TEMPL_LEN_1_0 0x58 |
---|
| 30 | +#define OCXL_TEMPL_LEN_1_1 0x60 |
---|
28 | 31 | #define OCXL_TEMPL_NAME_LEN 24 |
---|
29 | 32 | #define OCXL_CFG_TIMEOUT 3 |
---|
30 | 33 | |
---|
.. | .. |
---|
68 | 71 | return 0; |
---|
69 | 72 | } |
---|
70 | 73 | |
---|
71 | | -static int read_pasid(struct pci_dev *dev, struct ocxl_fn_config *fn) |
---|
| 74 | +/** |
---|
| 75 | + * get_function_0() - Find a related PCI device (function 0) |
---|
| 76 | + * @device: PCI device to match |
---|
| 77 | + * |
---|
| 78 | + * Returns a pointer to the related device, or null if not found |
---|
| 79 | + */ |
---|
| 80 | +static struct pci_dev *get_function_0(struct pci_dev *dev) |
---|
| 81 | +{ |
---|
| 82 | + unsigned int devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 0); |
---|
| 83 | + |
---|
| 84 | + return pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus), |
---|
| 85 | + dev->bus->number, devfn); |
---|
| 86 | +} |
---|
| 87 | + |
---|
| 88 | +static void read_pasid(struct pci_dev *dev, struct ocxl_fn_config *fn) |
---|
72 | 89 | { |
---|
73 | 90 | u16 val; |
---|
74 | 91 | int pos; |
---|
.. | .. |
---|
89 | 106 | out: |
---|
90 | 107 | dev_dbg(&dev->dev, "PASID capability:\n"); |
---|
91 | 108 | dev_dbg(&dev->dev, " Max PASID log = %d\n", fn->max_pasid_log); |
---|
92 | | - return 0; |
---|
93 | 109 | } |
---|
94 | 110 | |
---|
95 | 111 | static int read_dvsec_tl(struct pci_dev *dev, struct ocxl_fn_config *fn) |
---|
.. | .. |
---|
157 | 173 | static int read_dvsec_vendor(struct pci_dev *dev) |
---|
158 | 174 | { |
---|
159 | 175 | int pos; |
---|
160 | | - u32 cfg, tlx, dlx; |
---|
| 176 | + u32 cfg, tlx, dlx, reset_reload; |
---|
161 | 177 | |
---|
162 | 178 | /* |
---|
163 | | - * vendor specific DVSEC is optional |
---|
| 179 | + * vendor specific DVSEC, for IBM images only. Some older |
---|
| 180 | + * images may not have it |
---|
164 | 181 | * |
---|
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 |
---|
| 182 | + * It's only used on function 0 to specify the version of some |
---|
| 183 | + * logic blocks and to give access to special registers to |
---|
| 184 | + * enable host-based flashing. |
---|
168 | 185 | */ |
---|
169 | 186 | if (PCI_FUNC(dev->devfn) != 0) |
---|
170 | 187 | return 0; |
---|
.. | .. |
---|
176 | 193 | pci_read_config_dword(dev, pos + OCXL_DVSEC_VENDOR_CFG_VERS, &cfg); |
---|
177 | 194 | pci_read_config_dword(dev, pos + OCXL_DVSEC_VENDOR_TLX_VERS, &tlx); |
---|
178 | 195 | pci_read_config_dword(dev, pos + OCXL_DVSEC_VENDOR_DLX_VERS, &dlx); |
---|
| 196 | + pci_read_config_dword(dev, pos + OCXL_DVSEC_VENDOR_RESET_RELOAD, |
---|
| 197 | + &reset_reload); |
---|
179 | 198 | |
---|
180 | 199 | dev_dbg(&dev->dev, "Vendor specific DVSEC:\n"); |
---|
181 | 200 | dev_dbg(&dev->dev, " CFG version = 0x%x\n", cfg); |
---|
182 | 201 | dev_dbg(&dev->dev, " TLX version = 0x%x\n", tlx); |
---|
183 | 202 | dev_dbg(&dev->dev, " DLX version = 0x%x\n", dlx); |
---|
| 203 | + dev_dbg(&dev->dev, " ResetReload = 0x%x\n", reset_reload); |
---|
| 204 | + return 0; |
---|
| 205 | +} |
---|
| 206 | + |
---|
| 207 | +/** |
---|
| 208 | + * get_dvsec_vendor0() - Find a related PCI device (function 0) |
---|
| 209 | + * @dev: PCI device to match |
---|
| 210 | + * @dev0: The PCI device (function 0) found |
---|
| 211 | + * @out_pos: The position of PCI device (function 0) |
---|
| 212 | + * |
---|
| 213 | + * Returns 0 on success, negative on failure. |
---|
| 214 | + * |
---|
| 215 | + * NOTE: If it's successful, the reference of dev0 is increased, |
---|
| 216 | + * so after using it, the callers must call pci_dev_put() to give |
---|
| 217 | + * up the reference. |
---|
| 218 | + */ |
---|
| 219 | +static int get_dvsec_vendor0(struct pci_dev *dev, struct pci_dev **dev0, |
---|
| 220 | + int *out_pos) |
---|
| 221 | +{ |
---|
| 222 | + int pos; |
---|
| 223 | + |
---|
| 224 | + if (PCI_FUNC(dev->devfn) != 0) { |
---|
| 225 | + dev = get_function_0(dev); |
---|
| 226 | + if (!dev) |
---|
| 227 | + return -1; |
---|
| 228 | + } else { |
---|
| 229 | + dev = pci_dev_get(dev); |
---|
| 230 | + } |
---|
| 231 | + pos = find_dvsec(dev, OCXL_DVSEC_VENDOR_ID); |
---|
| 232 | + if (!pos) { |
---|
| 233 | + pci_dev_put(dev); |
---|
| 234 | + return -1; |
---|
| 235 | + } |
---|
| 236 | + *dev0 = dev; |
---|
| 237 | + *out_pos = pos; |
---|
| 238 | + return 0; |
---|
| 239 | +} |
---|
| 240 | + |
---|
| 241 | +int ocxl_config_get_reset_reload(struct pci_dev *dev, int *val) |
---|
| 242 | +{ |
---|
| 243 | + struct pci_dev *dev0; |
---|
| 244 | + u32 reset_reload; |
---|
| 245 | + int pos; |
---|
| 246 | + |
---|
| 247 | + if (get_dvsec_vendor0(dev, &dev0, &pos)) |
---|
| 248 | + return -1; |
---|
| 249 | + |
---|
| 250 | + pci_read_config_dword(dev0, pos + OCXL_DVSEC_VENDOR_RESET_RELOAD, |
---|
| 251 | + &reset_reload); |
---|
| 252 | + pci_dev_put(dev0); |
---|
| 253 | + *val = !!(reset_reload & BIT(0)); |
---|
| 254 | + return 0; |
---|
| 255 | +} |
---|
| 256 | + |
---|
| 257 | +int ocxl_config_set_reset_reload(struct pci_dev *dev, int val) |
---|
| 258 | +{ |
---|
| 259 | + struct pci_dev *dev0; |
---|
| 260 | + u32 reset_reload; |
---|
| 261 | + int pos; |
---|
| 262 | + |
---|
| 263 | + if (get_dvsec_vendor0(dev, &dev0, &pos)) |
---|
| 264 | + return -1; |
---|
| 265 | + |
---|
| 266 | + pci_read_config_dword(dev0, pos + OCXL_DVSEC_VENDOR_RESET_RELOAD, |
---|
| 267 | + &reset_reload); |
---|
| 268 | + if (val) |
---|
| 269 | + reset_reload |= BIT(0); |
---|
| 270 | + else |
---|
| 271 | + reset_reload &= ~BIT(0); |
---|
| 272 | + pci_write_config_dword(dev0, pos + OCXL_DVSEC_VENDOR_RESET_RELOAD, |
---|
| 273 | + reset_reload); |
---|
| 274 | + pci_dev_put(dev0); |
---|
184 | 275 | return 0; |
---|
185 | 276 | } |
---|
186 | 277 | |
---|
.. | .. |
---|
205 | 296 | { |
---|
206 | 297 | int rc; |
---|
207 | 298 | |
---|
208 | | - rc = read_pasid(dev, fn); |
---|
209 | | - if (rc) { |
---|
210 | | - dev_err(&dev->dev, "Invalid PASID configuration: %d\n", rc); |
---|
211 | | - return -ENODEV; |
---|
212 | | - } |
---|
| 299 | + read_pasid(dev, fn); |
---|
213 | 300 | |
---|
214 | 301 | rc = read_dvsec_tl(dev, fn); |
---|
215 | 302 | if (rc) { |
---|
.. | .. |
---|
274 | 361 | return 0; |
---|
275 | 362 | } |
---|
276 | 363 | |
---|
| 364 | +/** |
---|
| 365 | + * read_template_version() - Read the template version from the AFU |
---|
| 366 | + * @dev: the device for the AFU |
---|
| 367 | + * @fn: the AFU offsets |
---|
| 368 | + * @len: outputs the template length |
---|
| 369 | + * @version: outputs the major<<8,minor version |
---|
| 370 | + * |
---|
| 371 | + * Returns 0 on success, negative on failure |
---|
| 372 | + */ |
---|
| 373 | +static int read_template_version(struct pci_dev *dev, struct ocxl_fn_config *fn, |
---|
| 374 | + u16 *len, u16 *version) |
---|
| 375 | +{ |
---|
| 376 | + u32 val32; |
---|
| 377 | + u8 major, minor; |
---|
| 378 | + int rc; |
---|
| 379 | + |
---|
| 380 | + rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_VERSION, &val32); |
---|
| 381 | + if (rc) |
---|
| 382 | + return rc; |
---|
| 383 | + |
---|
| 384 | + *len = EXTRACT_BITS(val32, 16, 31); |
---|
| 385 | + major = EXTRACT_BITS(val32, 8, 15); |
---|
| 386 | + minor = EXTRACT_BITS(val32, 0, 7); |
---|
| 387 | + *version = (major << 8) + minor; |
---|
| 388 | + return 0; |
---|
| 389 | +} |
---|
| 390 | + |
---|
277 | 391 | int ocxl_config_check_afu_index(struct pci_dev *dev, |
---|
278 | 392 | struct ocxl_fn_config *fn, int afu_idx) |
---|
279 | 393 | { |
---|
280 | | - u32 val; |
---|
281 | | - int rc, templ_major, templ_minor, len; |
---|
| 394 | + int rc; |
---|
| 395 | + u16 templ_version; |
---|
| 396 | + u16 len, expected_len; |
---|
282 | 397 | |
---|
283 | 398 | pci_write_config_byte(dev, |
---|
284 | 399 | fn->dvsec_afu_info_pos + OCXL_DVSEC_AFU_INFO_AFU_IDX, |
---|
285 | 400 | afu_idx); |
---|
286 | | - rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_VERSION, &val); |
---|
| 401 | + |
---|
| 402 | + rc = read_template_version(dev, fn, &len, &templ_version); |
---|
287 | 403 | if (rc) |
---|
288 | 404 | return rc; |
---|
289 | 405 | |
---|
290 | | - /* AFU index map can have holes */ |
---|
291 | | - if (!val) |
---|
| 406 | + /* AFU index map can have holes, in which case we read all 0's */ |
---|
| 407 | + if (!templ_version && !len) |
---|
292 | 408 | return 0; |
---|
293 | 409 | |
---|
294 | | - templ_major = EXTRACT_BITS(val, 8, 15); |
---|
295 | | - templ_minor = EXTRACT_BITS(val, 0, 7); |
---|
296 | 410 | dev_dbg(&dev->dev, "AFU descriptor template version %d.%d\n", |
---|
297 | | - templ_major, templ_minor); |
---|
| 411 | + templ_version >> 8, templ_version & 0xFF); |
---|
298 | 412 | |
---|
299 | | - len = EXTRACT_BITS(val, 16, 31); |
---|
300 | | - if (len != OCXL_TEMPL_LEN) { |
---|
301 | | - dev_warn(&dev->dev, |
---|
302 | | - "Unexpected template length in AFU information (%#x)\n", |
---|
303 | | - len); |
---|
| 413 | + switch (templ_version) { |
---|
| 414 | + case 0x0005: // v0.5 was used prior to the spec approval |
---|
| 415 | + case 0x0100: |
---|
| 416 | + expected_len = OCXL_TEMPL_LEN_1_0; |
---|
| 417 | + break; |
---|
| 418 | + case 0x0101: |
---|
| 419 | + expected_len = OCXL_TEMPL_LEN_1_1; |
---|
| 420 | + break; |
---|
| 421 | + default: |
---|
| 422 | + dev_warn(&dev->dev, "Unknown AFU template version %#x\n", |
---|
| 423 | + templ_version); |
---|
| 424 | + expected_len = len; |
---|
304 | 425 | } |
---|
| 426 | + if (len != expected_len) |
---|
| 427 | + dev_warn(&dev->dev, |
---|
| 428 | + "Unexpected template length %#x in AFU information, expected %#x for version %#x\n", |
---|
| 429 | + len, expected_len, templ_version); |
---|
305 | 430 | return 1; |
---|
306 | 431 | } |
---|
307 | | -EXPORT_SYMBOL_GPL(ocxl_config_check_afu_index); |
---|
308 | 432 | |
---|
309 | 433 | static int read_afu_name(struct pci_dev *dev, struct ocxl_fn_config *fn, |
---|
310 | 434 | struct ocxl_afu_config *afu) |
---|
.. | .. |
---|
440 | 564 | return 0; |
---|
441 | 565 | } |
---|
442 | 566 | |
---|
| 567 | +/** |
---|
| 568 | + * read_afu_lpc_memory_info() - Populate AFU metadata regarding LPC memory |
---|
| 569 | + * @dev: the device for the AFU |
---|
| 570 | + * @fn: the AFU offsets |
---|
| 571 | + * @afu: the AFU struct to populate the LPC metadata into |
---|
| 572 | + * |
---|
| 573 | + * Returns 0 on success, negative on failure |
---|
| 574 | + */ |
---|
| 575 | +static int read_afu_lpc_memory_info(struct pci_dev *dev, |
---|
| 576 | + struct ocxl_fn_config *fn, |
---|
| 577 | + struct ocxl_afu_config *afu) |
---|
| 578 | +{ |
---|
| 579 | + int rc; |
---|
| 580 | + u32 val32; |
---|
| 581 | + u16 templ_version; |
---|
| 582 | + u16 templ_len; |
---|
| 583 | + u64 total_mem_size = 0; |
---|
| 584 | + u64 lpc_mem_size = 0; |
---|
| 585 | + |
---|
| 586 | + afu->lpc_mem_offset = 0; |
---|
| 587 | + afu->lpc_mem_size = 0; |
---|
| 588 | + afu->special_purpose_mem_offset = 0; |
---|
| 589 | + afu->special_purpose_mem_size = 0; |
---|
| 590 | + /* |
---|
| 591 | + * For AFUs following template v1.0, the LPC memory covers the |
---|
| 592 | + * total memory. Its size is a power of 2. |
---|
| 593 | + * |
---|
| 594 | + * For AFUs with template >= v1.01, the total memory size is |
---|
| 595 | + * still a power of 2, but it is split in 2 parts: |
---|
| 596 | + * - the LPC memory, whose size can now be anything |
---|
| 597 | + * - the remainder memory is a special purpose memory, whose |
---|
| 598 | + * definition is AFU-dependent. It is not accessible through |
---|
| 599 | + * the usual commands for LPC memory |
---|
| 600 | + */ |
---|
| 601 | + rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_ALL_MEM_SZ, &val32); |
---|
| 602 | + if (rc) |
---|
| 603 | + return rc; |
---|
| 604 | + |
---|
| 605 | + val32 = EXTRACT_BITS(val32, 0, 7); |
---|
| 606 | + if (!val32) |
---|
| 607 | + return 0; /* No LPC memory */ |
---|
| 608 | + |
---|
| 609 | + /* |
---|
| 610 | + * The configuration space spec allows for a memory size of up |
---|
| 611 | + * to 2^255 bytes. |
---|
| 612 | + * |
---|
| 613 | + * Current generation hardware uses 56-bit physical addresses, |
---|
| 614 | + * but we won't be able to get near close to that, as we won't |
---|
| 615 | + * have a hole big enough in the memory map. Let it pass in |
---|
| 616 | + * the driver for now. We'll get an error from the firmware |
---|
| 617 | + * when trying to configure something too big. |
---|
| 618 | + */ |
---|
| 619 | + total_mem_size = 1ull << val32; |
---|
| 620 | + |
---|
| 621 | + rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_LPC_MEM_START, &val32); |
---|
| 622 | + if (rc) |
---|
| 623 | + return rc; |
---|
| 624 | + |
---|
| 625 | + afu->lpc_mem_offset = val32; |
---|
| 626 | + |
---|
| 627 | + rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_LPC_MEM_START + 4, &val32); |
---|
| 628 | + if (rc) |
---|
| 629 | + return rc; |
---|
| 630 | + |
---|
| 631 | + afu->lpc_mem_offset |= (u64) val32 << 32; |
---|
| 632 | + |
---|
| 633 | + rc = read_template_version(dev, fn, &templ_len, &templ_version); |
---|
| 634 | + if (rc) |
---|
| 635 | + return rc; |
---|
| 636 | + |
---|
| 637 | + if (templ_version >= 0x0101) { |
---|
| 638 | + rc = read_afu_info(dev, fn, |
---|
| 639 | + OCXL_DVSEC_TEMPL_LPC_MEM_SZ, &val32); |
---|
| 640 | + if (rc) |
---|
| 641 | + return rc; |
---|
| 642 | + lpc_mem_size = val32; |
---|
| 643 | + |
---|
| 644 | + rc = read_afu_info(dev, fn, |
---|
| 645 | + OCXL_DVSEC_TEMPL_LPC_MEM_SZ + 4, &val32); |
---|
| 646 | + if (rc) |
---|
| 647 | + return rc; |
---|
| 648 | + lpc_mem_size |= (u64) val32 << 32; |
---|
| 649 | + } else { |
---|
| 650 | + lpc_mem_size = total_mem_size; |
---|
| 651 | + } |
---|
| 652 | + afu->lpc_mem_size = lpc_mem_size; |
---|
| 653 | + |
---|
| 654 | + if (lpc_mem_size < total_mem_size) { |
---|
| 655 | + afu->special_purpose_mem_offset = |
---|
| 656 | + afu->lpc_mem_offset + lpc_mem_size; |
---|
| 657 | + afu->special_purpose_mem_size = |
---|
| 658 | + total_mem_size - lpc_mem_size; |
---|
| 659 | + } |
---|
| 660 | + return 0; |
---|
| 661 | +} |
---|
| 662 | + |
---|
443 | 663 | int ocxl_config_read_afu(struct pci_dev *dev, struct ocxl_fn_config *fn, |
---|
444 | 664 | struct ocxl_afu_config *afu, u8 afu_idx) |
---|
445 | 665 | { |
---|
.. | .. |
---|
473 | 693 | if (rc) |
---|
474 | 694 | return rc; |
---|
475 | 695 | |
---|
476 | | - rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MEM_SZ, &val32); |
---|
| 696 | + rc = read_afu_lpc_memory_info(dev, fn, afu); |
---|
477 | 697 | if (rc) |
---|
478 | 698 | return rc; |
---|
479 | | - afu->log_mem_size = EXTRACT_BITS(val32, 0, 7); |
---|
480 | 699 | |
---|
481 | 700 | rc = read_afu_control(dev, afu); |
---|
482 | 701 | if (rc) |
---|
.. | .. |
---|
493 | 712 | dev_dbg(&dev->dev, " pp mmio bar = %hhu\n", afu->pp_mmio_bar); |
---|
494 | 713 | dev_dbg(&dev->dev, " pp mmio offset = %#llx\n", afu->pp_mmio_offset); |
---|
495 | 714 | dev_dbg(&dev->dev, " pp mmio stride = %#x\n", afu->pp_mmio_stride); |
---|
496 | | - dev_dbg(&dev->dev, " mem size (log) = %hhu\n", afu->log_mem_size); |
---|
| 715 | + dev_dbg(&dev->dev, " lpc_mem offset = %#llx\n", afu->lpc_mem_offset); |
---|
| 716 | + dev_dbg(&dev->dev, " lpc_mem size = %#llx\n", afu->lpc_mem_size); |
---|
| 717 | + dev_dbg(&dev->dev, " special purpose mem offset = %#llx\n", |
---|
| 718 | + afu->special_purpose_mem_offset); |
---|
| 719 | + dev_dbg(&dev->dev, " special purpose mem size = %#llx\n", |
---|
| 720 | + afu->special_purpose_mem_size); |
---|
497 | 721 | dev_dbg(&dev->dev, " pasid supported (log) = %u\n", |
---|
498 | 722 | afu->pasid_supported_log); |
---|
499 | 723 | dev_dbg(&dev->dev, " actag supported = %u\n", |
---|
.. | .. |
---|
540 | 764 | { |
---|
541 | 765 | return pnv_ocxl_get_pasid_count(dev, count); |
---|
542 | 766 | } |
---|
543 | | -EXPORT_SYMBOL_GPL(ocxl_config_get_pasid_info); |
---|
544 | 767 | |
---|
545 | 768 | void ocxl_config_set_afu_pasid(struct pci_dev *dev, int pos, int pasid_base, |
---|
546 | 769 | u32 pasid_count_log) |
---|