aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2016-02-04 19:51:00 -0500
committerDan Williams <dan.j.williams@intel.com>2016-02-19 14:47:26 -0500
commit6697b2cf69d4363266ca47eaebc49ef13dabc1c9 (patch)
tree149f0f3aa05a5407491fdc568239d0b6ed5387dd /drivers/acpi
parent18558cae0272f8fd9647e69d3fec1565a7949865 (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.c71
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
472static int nfit_mem_add(struct acpi_nfit_desc *acpi_desc, 472static 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
545static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, 522static 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;