| .. | .. |
|---|
| 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 | +static int get_dvsec_vendor0(struct pci_dev *dev, struct pci_dev **dev0, |
|---|
| 208 | + int *out_pos) |
|---|
| 209 | +{ |
|---|
| 210 | + int pos; |
|---|
| 211 | + |
|---|
| 212 | + if (PCI_FUNC(dev->devfn) != 0) { |
|---|
| 213 | + dev = get_function_0(dev); |
|---|
| 214 | + if (!dev) |
|---|
| 215 | + return -1; |
|---|
| 216 | + } |
|---|
| 217 | + pos = find_dvsec(dev, OCXL_DVSEC_VENDOR_ID); |
|---|
| 218 | + if (!pos) |
|---|
| 219 | + return -1; |
|---|
| 220 | + *dev0 = dev; |
|---|
| 221 | + *out_pos = pos; |
|---|
| 222 | + return 0; |
|---|
| 223 | +} |
|---|
| 224 | + |
|---|
| 225 | +int ocxl_config_get_reset_reload(struct pci_dev *dev, int *val) |
|---|
| 226 | +{ |
|---|
| 227 | + struct pci_dev *dev0; |
|---|
| 228 | + u32 reset_reload; |
|---|
| 229 | + int pos; |
|---|
| 230 | + |
|---|
| 231 | + if (get_dvsec_vendor0(dev, &dev0, &pos)) |
|---|
| 232 | + return -1; |
|---|
| 233 | + |
|---|
| 234 | + pci_read_config_dword(dev0, pos + OCXL_DVSEC_VENDOR_RESET_RELOAD, |
|---|
| 235 | + &reset_reload); |
|---|
| 236 | + *val = !!(reset_reload & BIT(0)); |
|---|
| 237 | + return 0; |
|---|
| 238 | +} |
|---|
| 239 | + |
|---|
| 240 | +int ocxl_config_set_reset_reload(struct pci_dev *dev, int val) |
|---|
| 241 | +{ |
|---|
| 242 | + struct pci_dev *dev0; |
|---|
| 243 | + u32 reset_reload; |
|---|
| 244 | + int pos; |
|---|
| 245 | + |
|---|
| 246 | + if (get_dvsec_vendor0(dev, &dev0, &pos)) |
|---|
| 247 | + return -1; |
|---|
| 248 | + |
|---|
| 249 | + pci_read_config_dword(dev0, pos + OCXL_DVSEC_VENDOR_RESET_RELOAD, |
|---|
| 250 | + &reset_reload); |
|---|
| 251 | + if (val) |
|---|
| 252 | + reset_reload |= BIT(0); |
|---|
| 253 | + else |
|---|
| 254 | + reset_reload &= ~BIT(0); |
|---|
| 255 | + pci_write_config_dword(dev0, pos + OCXL_DVSEC_VENDOR_RESET_RELOAD, |
|---|
| 256 | + reset_reload); |
|---|
| 184 | 257 | return 0; |
|---|
| 185 | 258 | } |
|---|
| 186 | 259 | |
|---|
| .. | .. |
|---|
| 205 | 278 | { |
|---|
| 206 | 279 | int rc; |
|---|
| 207 | 280 | |
|---|
| 208 | | - rc = read_pasid(dev, fn); |
|---|
| 209 | | - if (rc) { |
|---|
| 210 | | - dev_err(&dev->dev, "Invalid PASID configuration: %d\n", rc); |
|---|
| 211 | | - return -ENODEV; |
|---|
| 212 | | - } |
|---|
| 281 | + read_pasid(dev, fn); |
|---|
| 213 | 282 | |
|---|
| 214 | 283 | rc = read_dvsec_tl(dev, fn); |
|---|
| 215 | 284 | if (rc) { |
|---|
| .. | .. |
|---|
| 274 | 343 | return 0; |
|---|
| 275 | 344 | } |
|---|
| 276 | 345 | |
|---|
| 346 | +/** |
|---|
| 347 | + * read_template_version() - Read the template version from the AFU |
|---|
| 348 | + * @dev: the device for the AFU |
|---|
| 349 | + * @fn: the AFU offsets |
|---|
| 350 | + * @len: outputs the template length |
|---|
| 351 | + * @version: outputs the major<<8,minor version |
|---|
| 352 | + * |
|---|
| 353 | + * Returns 0 on success, negative on failure |
|---|
| 354 | + */ |
|---|
| 355 | +static int read_template_version(struct pci_dev *dev, struct ocxl_fn_config *fn, |
|---|
| 356 | + u16 *len, u16 *version) |
|---|
| 357 | +{ |
|---|
| 358 | + u32 val32; |
|---|
| 359 | + u8 major, minor; |
|---|
| 360 | + int rc; |
|---|
| 361 | + |
|---|
| 362 | + rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_VERSION, &val32); |
|---|
| 363 | + if (rc) |
|---|
| 364 | + return rc; |
|---|
| 365 | + |
|---|
| 366 | + *len = EXTRACT_BITS(val32, 16, 31); |
|---|
| 367 | + major = EXTRACT_BITS(val32, 8, 15); |
|---|
| 368 | + minor = EXTRACT_BITS(val32, 0, 7); |
|---|
| 369 | + *version = (major << 8) + minor; |
|---|
| 370 | + return 0; |
|---|
| 371 | +} |
|---|
| 372 | + |
|---|
| 277 | 373 | int ocxl_config_check_afu_index(struct pci_dev *dev, |
|---|
| 278 | 374 | struct ocxl_fn_config *fn, int afu_idx) |
|---|
| 279 | 375 | { |
|---|
| 280 | | - u32 val; |
|---|
| 281 | | - int rc, templ_major, templ_minor, len; |
|---|
| 376 | + int rc; |
|---|
| 377 | + u16 templ_version; |
|---|
| 378 | + u16 len, expected_len; |
|---|
| 282 | 379 | |
|---|
| 283 | 380 | pci_write_config_byte(dev, |
|---|
| 284 | 381 | fn->dvsec_afu_info_pos + OCXL_DVSEC_AFU_INFO_AFU_IDX, |
|---|
| 285 | 382 | afu_idx); |
|---|
| 286 | | - rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_VERSION, &val); |
|---|
| 383 | + |
|---|
| 384 | + rc = read_template_version(dev, fn, &len, &templ_version); |
|---|
| 287 | 385 | if (rc) |
|---|
| 288 | 386 | return rc; |
|---|
| 289 | 387 | |
|---|
| 290 | | - /* AFU index map can have holes */ |
|---|
| 291 | | - if (!val) |
|---|
| 388 | + /* AFU index map can have holes, in which case we read all 0's */ |
|---|
| 389 | + if (!templ_version && !len) |
|---|
| 292 | 390 | return 0; |
|---|
| 293 | 391 | |
|---|
| 294 | | - templ_major = EXTRACT_BITS(val, 8, 15); |
|---|
| 295 | | - templ_minor = EXTRACT_BITS(val, 0, 7); |
|---|
| 296 | 392 | dev_dbg(&dev->dev, "AFU descriptor template version %d.%d\n", |
|---|
| 297 | | - templ_major, templ_minor); |
|---|
| 393 | + templ_version >> 8, templ_version & 0xFF); |
|---|
| 298 | 394 | |
|---|
| 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); |
|---|
| 395 | + switch (templ_version) { |
|---|
| 396 | + case 0x0005: // v0.5 was used prior to the spec approval |
|---|
| 397 | + case 0x0100: |
|---|
| 398 | + expected_len = OCXL_TEMPL_LEN_1_0; |
|---|
| 399 | + break; |
|---|
| 400 | + case 0x0101: |
|---|
| 401 | + expected_len = OCXL_TEMPL_LEN_1_1; |
|---|
| 402 | + break; |
|---|
| 403 | + default: |
|---|
| 404 | + dev_warn(&dev->dev, "Unknown AFU template version %#x\n", |
|---|
| 405 | + templ_version); |
|---|
| 406 | + expected_len = len; |
|---|
| 304 | 407 | } |
|---|
| 408 | + if (len != expected_len) |
|---|
| 409 | + dev_warn(&dev->dev, |
|---|
| 410 | + "Unexpected template length %#x in AFU information, expected %#x for version %#x\n", |
|---|
| 411 | + len, expected_len, templ_version); |
|---|
| 305 | 412 | return 1; |
|---|
| 306 | 413 | } |
|---|
| 307 | | -EXPORT_SYMBOL_GPL(ocxl_config_check_afu_index); |
|---|
| 308 | 414 | |
|---|
| 309 | 415 | static int read_afu_name(struct pci_dev *dev, struct ocxl_fn_config *fn, |
|---|
| 310 | 416 | struct ocxl_afu_config *afu) |
|---|
| .. | .. |
|---|
| 440 | 546 | return 0; |
|---|
| 441 | 547 | } |
|---|
| 442 | 548 | |
|---|
| 549 | +/** |
|---|
| 550 | + * read_afu_lpc_memory_info() - Populate AFU metadata regarding LPC memory |
|---|
| 551 | + * @dev: the device for the AFU |
|---|
| 552 | + * @fn: the AFU offsets |
|---|
| 553 | + * @afu: the AFU struct to populate the LPC metadata into |
|---|
| 554 | + * |
|---|
| 555 | + * Returns 0 on success, negative on failure |
|---|
| 556 | + */ |
|---|
| 557 | +static int read_afu_lpc_memory_info(struct pci_dev *dev, |
|---|
| 558 | + struct ocxl_fn_config *fn, |
|---|
| 559 | + struct ocxl_afu_config *afu) |
|---|
| 560 | +{ |
|---|
| 561 | + int rc; |
|---|
| 562 | + u32 val32; |
|---|
| 563 | + u16 templ_version; |
|---|
| 564 | + u16 templ_len; |
|---|
| 565 | + u64 total_mem_size = 0; |
|---|
| 566 | + u64 lpc_mem_size = 0; |
|---|
| 567 | + |
|---|
| 568 | + afu->lpc_mem_offset = 0; |
|---|
| 569 | + afu->lpc_mem_size = 0; |
|---|
| 570 | + afu->special_purpose_mem_offset = 0; |
|---|
| 571 | + afu->special_purpose_mem_size = 0; |
|---|
| 572 | + /* |
|---|
| 573 | + * For AFUs following template v1.0, the LPC memory covers the |
|---|
| 574 | + * total memory. Its size is a power of 2. |
|---|
| 575 | + * |
|---|
| 576 | + * For AFUs with template >= v1.01, the total memory size is |
|---|
| 577 | + * still a power of 2, but it is split in 2 parts: |
|---|
| 578 | + * - the LPC memory, whose size can now be anything |
|---|
| 579 | + * - the remainder memory is a special purpose memory, whose |
|---|
| 580 | + * definition is AFU-dependent. It is not accessible through |
|---|
| 581 | + * the usual commands for LPC memory |
|---|
| 582 | + */ |
|---|
| 583 | + rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_ALL_MEM_SZ, &val32); |
|---|
| 584 | + if (rc) |
|---|
| 585 | + return rc; |
|---|
| 586 | + |
|---|
| 587 | + val32 = EXTRACT_BITS(val32, 0, 7); |
|---|
| 588 | + if (!val32) |
|---|
| 589 | + return 0; /* No LPC memory */ |
|---|
| 590 | + |
|---|
| 591 | + /* |
|---|
| 592 | + * The configuration space spec allows for a memory size of up |
|---|
| 593 | + * to 2^255 bytes. |
|---|
| 594 | + * |
|---|
| 595 | + * Current generation hardware uses 56-bit physical addresses, |
|---|
| 596 | + * but we won't be able to get near close to that, as we won't |
|---|
| 597 | + * have a hole big enough in the memory map. Let it pass in |
|---|
| 598 | + * the driver for now. We'll get an error from the firmware |
|---|
| 599 | + * when trying to configure something too big. |
|---|
| 600 | + */ |
|---|
| 601 | + total_mem_size = 1ull << val32; |
|---|
| 602 | + |
|---|
| 603 | + rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_LPC_MEM_START, &val32); |
|---|
| 604 | + if (rc) |
|---|
| 605 | + return rc; |
|---|
| 606 | + |
|---|
| 607 | + afu->lpc_mem_offset = val32; |
|---|
| 608 | + |
|---|
| 609 | + rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_LPC_MEM_START + 4, &val32); |
|---|
| 610 | + if (rc) |
|---|
| 611 | + return rc; |
|---|
| 612 | + |
|---|
| 613 | + afu->lpc_mem_offset |= (u64) val32 << 32; |
|---|
| 614 | + |
|---|
| 615 | + rc = read_template_version(dev, fn, &templ_len, &templ_version); |
|---|
| 616 | + if (rc) |
|---|
| 617 | + return rc; |
|---|
| 618 | + |
|---|
| 619 | + if (templ_version >= 0x0101) { |
|---|
| 620 | + rc = read_afu_info(dev, fn, |
|---|
| 621 | + OCXL_DVSEC_TEMPL_LPC_MEM_SZ, &val32); |
|---|
| 622 | + if (rc) |
|---|
| 623 | + return rc; |
|---|
| 624 | + lpc_mem_size = val32; |
|---|
| 625 | + |
|---|
| 626 | + rc = read_afu_info(dev, fn, |
|---|
| 627 | + OCXL_DVSEC_TEMPL_LPC_MEM_SZ + 4, &val32); |
|---|
| 628 | + if (rc) |
|---|
| 629 | + return rc; |
|---|
| 630 | + lpc_mem_size |= (u64) val32 << 32; |
|---|
| 631 | + } else { |
|---|
| 632 | + lpc_mem_size = total_mem_size; |
|---|
| 633 | + } |
|---|
| 634 | + afu->lpc_mem_size = lpc_mem_size; |
|---|
| 635 | + |
|---|
| 636 | + if (lpc_mem_size < total_mem_size) { |
|---|
| 637 | + afu->special_purpose_mem_offset = |
|---|
| 638 | + afu->lpc_mem_offset + lpc_mem_size; |
|---|
| 639 | + afu->special_purpose_mem_size = |
|---|
| 640 | + total_mem_size - lpc_mem_size; |
|---|
| 641 | + } |
|---|
| 642 | + return 0; |
|---|
| 643 | +} |
|---|
| 644 | + |
|---|
| 443 | 645 | int ocxl_config_read_afu(struct pci_dev *dev, struct ocxl_fn_config *fn, |
|---|
| 444 | 646 | struct ocxl_afu_config *afu, u8 afu_idx) |
|---|
| 445 | 647 | { |
|---|
| .. | .. |
|---|
| 473 | 675 | if (rc) |
|---|
| 474 | 676 | return rc; |
|---|
| 475 | 677 | |
|---|
| 476 | | - rc = read_afu_info(dev, fn, OCXL_DVSEC_TEMPL_MEM_SZ, &val32); |
|---|
| 678 | + rc = read_afu_lpc_memory_info(dev, fn, afu); |
|---|
| 477 | 679 | if (rc) |
|---|
| 478 | 680 | return rc; |
|---|
| 479 | | - afu->log_mem_size = EXTRACT_BITS(val32, 0, 7); |
|---|
| 480 | 681 | |
|---|
| 481 | 682 | rc = read_afu_control(dev, afu); |
|---|
| 482 | 683 | if (rc) |
|---|
| .. | .. |
|---|
| 493 | 694 | dev_dbg(&dev->dev, " pp mmio bar = %hhu\n", afu->pp_mmio_bar); |
|---|
| 494 | 695 | dev_dbg(&dev->dev, " pp mmio offset = %#llx\n", afu->pp_mmio_offset); |
|---|
| 495 | 696 | 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); |
|---|
| 697 | + dev_dbg(&dev->dev, " lpc_mem offset = %#llx\n", afu->lpc_mem_offset); |
|---|
| 698 | + dev_dbg(&dev->dev, " lpc_mem size = %#llx\n", afu->lpc_mem_size); |
|---|
| 699 | + dev_dbg(&dev->dev, " special purpose mem offset = %#llx\n", |
|---|
| 700 | + afu->special_purpose_mem_offset); |
|---|
| 701 | + dev_dbg(&dev->dev, " special purpose mem size = %#llx\n", |
|---|
| 702 | + afu->special_purpose_mem_size); |
|---|
| 497 | 703 | dev_dbg(&dev->dev, " pasid supported (log) = %u\n", |
|---|
| 498 | 704 | afu->pasid_supported_log); |
|---|
| 499 | 705 | dev_dbg(&dev->dev, " actag supported = %u\n", |
|---|
| .. | .. |
|---|
| 540 | 746 | { |
|---|
| 541 | 747 | return pnv_ocxl_get_pasid_count(dev, count); |
|---|
| 542 | 748 | } |
|---|
| 543 | | -EXPORT_SYMBOL_GPL(ocxl_config_get_pasid_info); |
|---|
| 544 | 749 | |
|---|
| 545 | 750 | void ocxl_config_set_afu_pasid(struct pci_dev *dev, int pos, int pasid_base, |
|---|
| 546 | 751 | u32 pasid_count_log) |
|---|