diff options
author | Dan Williams <dan.j.williams@intel.com> | 2016-02-04 19:51:00 -0500 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2016-02-19 14:47:26 -0500 |
commit | 6697b2cf69d4363266ca47eaebc49ef13dabc1c9 (patch) | |
tree | 149f0f3aa05a5407491fdc568239d0b6ed5387dd /drivers/acpi | |
parent | 18558cae0272f8fd9647e69d3fec1565a7949865 (diff) |
nfit: fix multi-interface dimm handling, acpi6.1 compatibility
ACPI 6.1 clarified that multi-interface dimms require multiple control
region entries (DCRs) per dimm. Previously we were assuming that a
control region is only present when block-data-windows are present.
This implementation was done with an eye to be compatibility with the
looser ACPI 6.0 interpretation of this table.
1/ When coalescing the memory device (MEMDEV) tables for a single dimm,
coalesce on device_handle rather than control region index.
2/ Whenever we disocver a control region with non-zero block windows
re-scan for block-data-window (BDW) entries.
We may need to revisit this if a DIMM ever implements a format interface
outside of blk or pmem, but that is not on the foreseeable horizon.
Cc: <stable@vger.kernel.org>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/nfit.c | 71 |
1 files changed, 35 insertions, 36 deletions
diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index ad6d8c6b777e..424b362e8fdc 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c | |||
@@ -469,37 +469,16 @@ static void nfit_mem_find_spa_bdw(struct acpi_nfit_desc *acpi_desc, | |||
469 | nfit_mem->bdw = NULL; | 469 | nfit_mem->bdw = NULL; |
470 | } | 470 | } |
471 | 471 | ||
472 | static int nfit_mem_add(struct acpi_nfit_desc *acpi_desc, | 472 | static void nfit_mem_init_bdw(struct acpi_nfit_desc *acpi_desc, |
473 | struct nfit_mem *nfit_mem, struct acpi_nfit_system_address *spa) | 473 | struct nfit_mem *nfit_mem, struct acpi_nfit_system_address *spa) |
474 | { | 474 | { |
475 | u16 dcr = __to_nfit_memdev(nfit_mem)->region_index; | 475 | u16 dcr = __to_nfit_memdev(nfit_mem)->region_index; |
476 | struct nfit_memdev *nfit_memdev; | 476 | struct nfit_memdev *nfit_memdev; |
477 | struct nfit_flush *nfit_flush; | 477 | struct nfit_flush *nfit_flush; |
478 | struct nfit_dcr *nfit_dcr; | ||
479 | struct nfit_bdw *nfit_bdw; | 478 | struct nfit_bdw *nfit_bdw; |
480 | struct nfit_idt *nfit_idt; | 479 | struct nfit_idt *nfit_idt; |
481 | u16 idt_idx, range_index; | 480 | u16 idt_idx, range_index; |
482 | 481 | ||
483 | list_for_each_entry(nfit_dcr, &acpi_desc->dcrs, list) { | ||
484 | if (nfit_dcr->dcr->region_index != dcr) | ||
485 | continue; | ||
486 | nfit_mem->dcr = nfit_dcr->dcr; | ||
487 | break; | ||
488 | } | ||
489 | |||
490 | if (!nfit_mem->dcr) { | ||
491 | dev_dbg(acpi_desc->dev, "SPA %d missing:%s%s\n", | ||
492 | spa->range_index, __to_nfit_memdev(nfit_mem) | ||
493 | ? "" : " MEMDEV", nfit_mem->dcr ? "" : " DCR"); | ||
494 | return -ENODEV; | ||
495 | } | ||
496 | |||
497 | /* | ||
498 | * We've found enough to create an nvdimm, optionally | ||
499 | * find an associated BDW | ||
500 | */ | ||
501 | list_add(&nfit_mem->list, &acpi_desc->dimms); | ||
502 | |||
503 | list_for_each_entry(nfit_bdw, &acpi_desc->bdws, list) { | 482 | list_for_each_entry(nfit_bdw, &acpi_desc->bdws, list) { |
504 | if (nfit_bdw->bdw->region_index != dcr) | 483 | if (nfit_bdw->bdw->region_index != dcr) |
505 | continue; | 484 | continue; |
@@ -508,12 +487,12 @@ static int nfit_mem_add(struct acpi_nfit_desc *acpi_desc, | |||
508 | } | 487 | } |
509 | 488 | ||
510 | if (!nfit_mem->bdw) | 489 | if (!nfit_mem->bdw) |
511 | return 0; | 490 | return; |
512 | 491 | ||
513 | nfit_mem_find_spa_bdw(acpi_desc, nfit_mem); | 492 | nfit_mem_find_spa_bdw(acpi_desc, nfit_mem); |
514 | 493 | ||
515 | if (!nfit_mem->spa_bdw) | 494 | if (!nfit_mem->spa_bdw) |
516 | return 0; | 495 | return; |
517 | 496 | ||
518 | range_index = nfit_mem->spa_bdw->range_index; | 497 | range_index = nfit_mem->spa_bdw->range_index; |
519 | list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) { | 498 | list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) { |
@@ -538,8 +517,6 @@ static int nfit_mem_add(struct acpi_nfit_desc *acpi_desc, | |||
538 | } | 517 | } |
539 | break; | 518 | break; |
540 | } | 519 | } |
541 | |||
542 | return 0; | ||
543 | } | 520 | } |
544 | 521 | ||
545 | static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, | 522 | static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, |
@@ -548,7 +525,6 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, | |||
548 | struct nfit_mem *nfit_mem, *found; | 525 | struct nfit_mem *nfit_mem, *found; |
549 | struct nfit_memdev *nfit_memdev; | 526 | struct nfit_memdev *nfit_memdev; |
550 | int type = nfit_spa_type(spa); | 527 | int type = nfit_spa_type(spa); |
551 | u16 dcr; | ||
552 | 528 | ||
553 | switch (type) { | 529 | switch (type) { |
554 | case NFIT_SPA_DCR: | 530 | case NFIT_SPA_DCR: |
@@ -559,14 +535,18 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, | |||
559 | } | 535 | } |
560 | 536 | ||
561 | list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) { | 537 | list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) { |
562 | int rc; | 538 | struct nfit_dcr *nfit_dcr; |
539 | u32 device_handle; | ||
540 | u16 dcr; | ||
563 | 541 | ||
564 | if (nfit_memdev->memdev->range_index != spa->range_index) | 542 | if (nfit_memdev->memdev->range_index != spa->range_index) |
565 | continue; | 543 | continue; |
566 | found = NULL; | 544 | found = NULL; |
567 | dcr = nfit_memdev->memdev->region_index; | 545 | dcr = nfit_memdev->memdev->region_index; |
546 | device_handle = nfit_memdev->memdev->device_handle; | ||
568 | list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) | 547 | list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) |
569 | if (__to_nfit_memdev(nfit_mem)->region_index == dcr) { | 548 | if (__to_nfit_memdev(nfit_mem)->device_handle |
549 | == device_handle) { | ||
570 | found = nfit_mem; | 550 | found = nfit_mem; |
571 | break; | 551 | break; |
572 | } | 552 | } |
@@ -579,6 +559,31 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, | |||
579 | if (!nfit_mem) | 559 | if (!nfit_mem) |
580 | return -ENOMEM; | 560 | return -ENOMEM; |
581 | INIT_LIST_HEAD(&nfit_mem->list); | 561 | INIT_LIST_HEAD(&nfit_mem->list); |
562 | list_add(&nfit_mem->list, &acpi_desc->dimms); | ||
563 | } | ||
564 | |||
565 | list_for_each_entry(nfit_dcr, &acpi_desc->dcrs, list) { | ||
566 | if (nfit_dcr->dcr->region_index != dcr) | ||
567 | continue; | ||
568 | /* | ||
569 | * Record the control region for the dimm. For | ||
570 | * the ACPI 6.1 case, where there are separate | ||
571 | * control regions for the pmem vs blk | ||
572 | * interfaces, be sure to record the extended | ||
573 | * blk details. | ||
574 | */ | ||
575 | if (!nfit_mem->dcr) | ||
576 | nfit_mem->dcr = nfit_dcr->dcr; | ||
577 | else if (nfit_mem->dcr->windows == 0 | ||
578 | && nfit_dcr->dcr->windows) | ||
579 | nfit_mem->dcr = nfit_dcr->dcr; | ||
580 | break; | ||
581 | } | ||
582 | |||
583 | if (dcr && !nfit_mem->dcr) { | ||
584 | dev_err(acpi_desc->dev, "SPA %d missing DCR %d\n", | ||
585 | spa->range_index, dcr); | ||
586 | return -ENODEV; | ||
582 | } | 587 | } |
583 | 588 | ||
584 | if (type == NFIT_SPA_DCR) { | 589 | if (type == NFIT_SPA_DCR) { |
@@ -595,6 +600,7 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, | |||
595 | nfit_mem->idt_dcr = nfit_idt->idt; | 600 | nfit_mem->idt_dcr = nfit_idt->idt; |
596 | break; | 601 | break; |
597 | } | 602 | } |
603 | nfit_mem_init_bdw(acpi_desc, nfit_mem, spa); | ||
598 | } else { | 604 | } else { |
599 | /* | 605 | /* |
600 | * A single dimm may belong to multiple SPA-PM | 606 | * A single dimm may belong to multiple SPA-PM |
@@ -603,13 +609,6 @@ static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, | |||
603 | */ | 609 | */ |
604 | nfit_mem->memdev_pmem = nfit_memdev->memdev; | 610 | nfit_mem->memdev_pmem = nfit_memdev->memdev; |
605 | } | 611 | } |
606 | |||
607 | if (found) | ||
608 | continue; | ||
609 | |||
610 | rc = nfit_mem_add(acpi_desc, nfit_mem, spa); | ||
611 | if (rc) | ||
612 | return rc; | ||
613 | } | 612 | } |
614 | 613 | ||
615 | return 0; | 614 | return 0; |