pmem.c 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Handles hot and cold plug of persistent memory regions on pseries.
  4. */
  5. #define pr_fmt(fmt) "pseries-pmem: " fmt
  6. #include <linux/kernel.h>
  7. #include <linux/interrupt.h>
  8. #include <linux/delay.h>
  9. #include <linux/sched.h> /* for idle_task_exit */
  10. #include <linux/sched/hotplug.h>
  11. #include <linux/cpu.h>
  12. #include <linux/of.h>
  13. #include <linux/of_platform.h>
  14. #include <linux/slab.h>
  15. #include <asm/rtas.h>
  16. #include <asm/firmware.h>
  17. #include <asm/machdep.h>
  18. #include <asm/vdso_datapage.h>
  19. #include <asm/plpar_wrappers.h>
  20. #include <asm/topology.h>
  21. #include "pseries.h"
  22. static struct device_node *pmem_node;
  23. static ssize_t pmem_drc_add_node(u32 drc_index)
  24. {
  25. struct device_node *dn;
  26. int rc;
  27. pr_debug("Attempting to add pmem node, drc index: %x\n", drc_index);
  28. rc = dlpar_acquire_drc(drc_index);
  29. if (rc) {
  30. pr_err("Failed to acquire DRC, rc: %d, drc index: %x\n",
  31. rc, drc_index);
  32. return -EINVAL;
  33. }
  34. dn = dlpar_configure_connector(cpu_to_be32(drc_index), pmem_node);
  35. if (!dn) {
  36. pr_err("configure-connector failed for drc %x\n", drc_index);
  37. dlpar_release_drc(drc_index);
  38. return -EINVAL;
  39. }
  40. /* NB: The of reconfig notifier creates platform device from the node */
  41. rc = dlpar_attach_node(dn, pmem_node);
  42. if (rc) {
  43. pr_err("Failed to attach node %pOF, rc: %d, drc index: %x\n",
  44. dn, rc, drc_index);
  45. if (dlpar_release_drc(drc_index))
  46. dlpar_free_cc_nodes(dn);
  47. return rc;
  48. }
  49. pr_info("Successfully added %pOF, drc index: %x\n", dn, drc_index);
  50. return 0;
  51. }
  52. static ssize_t pmem_drc_remove_node(u32 drc_index)
  53. {
  54. struct device_node *dn;
  55. uint32_t index;
  56. int rc;
  57. for_each_child_of_node(pmem_node, dn) {
  58. if (of_property_read_u32(dn, "ibm,my-drc-index", &index))
  59. continue;
  60. if (index == drc_index)
  61. break;
  62. }
  63. if (!dn) {
  64. pr_err("Attempting to remove unused DRC index %x\n", drc_index);
  65. return -ENODEV;
  66. }
  67. pr_debug("Attempting to remove %pOF, drc index: %x\n", dn, drc_index);
  68. /* * NB: tears down the ibm,pmemory device as a side-effect */
  69. rc = dlpar_detach_node(dn);
  70. if (rc)
  71. return rc;
  72. rc = dlpar_release_drc(drc_index);
  73. if (rc) {
  74. pr_err("Failed to release drc (%x) for CPU %pOFn, rc: %d\n",
  75. drc_index, dn, rc);
  76. dlpar_attach_node(dn, pmem_node);
  77. return rc;
  78. }
  79. pr_info("Successfully removed PMEM with drc index: %x\n", drc_index);
  80. return 0;
  81. }
  82. int dlpar_hp_pmem(struct pseries_hp_errorlog *hp_elog)
  83. {
  84. u32 drc_index;
  85. int rc;
  86. /* slim chance, but we might get a hotplug event while booting */
  87. if (!pmem_node)
  88. pmem_node = of_find_node_by_type(NULL, "ibm,persistent-memory");
  89. if (!pmem_node) {
  90. pr_err("Hotplug event for a pmem device, but none exists\n");
  91. return -ENODEV;
  92. }
  93. if (hp_elog->id_type != PSERIES_HP_ELOG_ID_DRC_INDEX) {
  94. pr_err("Unsupported hotplug event type %d\n",
  95. hp_elog->id_type);
  96. return -EINVAL;
  97. }
  98. drc_index = hp_elog->_drc_u.drc_index;
  99. lock_device_hotplug();
  100. if (hp_elog->action == PSERIES_HP_ELOG_ACTION_ADD) {
  101. rc = pmem_drc_add_node(drc_index);
  102. } else if (hp_elog->action == PSERIES_HP_ELOG_ACTION_REMOVE) {
  103. rc = pmem_drc_remove_node(drc_index);
  104. } else {
  105. pr_err("Unsupported hotplug action (%d)\n", hp_elog->action);
  106. rc = -EINVAL;
  107. }
  108. unlock_device_hotplug();
  109. return rc;
  110. }
  111. static const struct of_device_id drc_pmem_match[] = {
  112. { .type = "ibm,persistent-memory", },
  113. {}
  114. };
  115. static int pseries_pmem_init(void)
  116. {
  117. /*
  118. * Only supported on POWER8 and above.
  119. */
  120. if (!cpu_has_feature(CPU_FTR_ARCH_207S))
  121. return 0;
  122. pmem_node = of_find_node_by_type(NULL, "ibm,persistent-memory");
  123. if (!pmem_node)
  124. return 0;
  125. /*
  126. * The generic OF bus probe/populate handles creating platform devices
  127. * from the child (ibm,pmemory) nodes. The generic code registers an of
  128. * reconfig notifier to handle the hot-add/remove cases too.
  129. */
  130. of_platform_bus_probe(pmem_node, drc_pmem_match, NULL);
  131. return 0;
  132. }
  133. machine_arch_initcall(pseries, pseries_pmem_init);