opal-power.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * PowerNV OPAL power control for graceful shutdown handling
  4. *
  5. * Copyright 2015 IBM Corp.
  6. */
  7. #define pr_fmt(fmt) "opal-power: " fmt
  8. #include <linux/kernel.h>
  9. #include <linux/reboot.h>
  10. #include <linux/notifier.h>
  11. #include <linux/of.h>
  12. #include <asm/opal.h>
  13. #include <asm/machdep.h>
  14. #define SOFT_OFF 0x00
  15. #define SOFT_REBOOT 0x01
  16. /* Detect EPOW event */
  17. static bool detect_epow(void)
  18. {
  19. u16 epow;
  20. int i, rc;
  21. __be16 epow_classes;
  22. __be16 opal_epow_status[OPAL_SYSEPOW_MAX] = {0};
  23. /*
  24. * Check for EPOW event. Kernel sends supported EPOW classes info
  25. * to OPAL. OPAL returns EPOW info along with classes present.
  26. */
  27. epow_classes = cpu_to_be16(OPAL_SYSEPOW_MAX);
  28. rc = opal_get_epow_status(opal_epow_status, &epow_classes);
  29. if (rc != OPAL_SUCCESS) {
  30. pr_err("Failed to get EPOW event information\n");
  31. return false;
  32. }
  33. /* Look for EPOW events present */
  34. for (i = 0; i < be16_to_cpu(epow_classes); i++) {
  35. epow = be16_to_cpu(opal_epow_status[i]);
  36. /* Filter events which do not need shutdown. */
  37. if (i == OPAL_SYSEPOW_POWER)
  38. epow &= ~(OPAL_SYSPOWER_CHNG | OPAL_SYSPOWER_FAIL |
  39. OPAL_SYSPOWER_INCL);
  40. if (epow)
  41. return true;
  42. }
  43. return false;
  44. }
  45. /* Check for existing EPOW, DPO events */
  46. static bool __init poweroff_pending(void)
  47. {
  48. int rc;
  49. __be64 opal_dpo_timeout;
  50. /* Check for DPO event */
  51. rc = opal_get_dpo_status(&opal_dpo_timeout);
  52. if (rc == OPAL_SUCCESS) {
  53. pr_info("Existing DPO event detected.\n");
  54. return true;
  55. }
  56. /* Check for EPOW event */
  57. if (detect_epow()) {
  58. pr_info("Existing EPOW event detected.\n");
  59. return true;
  60. }
  61. return false;
  62. }
  63. /* OPAL power-control events notifier */
  64. static int opal_power_control_event(struct notifier_block *nb,
  65. unsigned long msg_type, void *msg)
  66. {
  67. uint64_t type;
  68. switch (msg_type) {
  69. case OPAL_MSG_EPOW:
  70. if (detect_epow()) {
  71. pr_info("EPOW msg received. Powering off system\n");
  72. orderly_poweroff(true);
  73. }
  74. break;
  75. case OPAL_MSG_DPO:
  76. pr_info("DPO msg received. Powering off system\n");
  77. orderly_poweroff(true);
  78. break;
  79. case OPAL_MSG_SHUTDOWN:
  80. type = be64_to_cpu(((struct opal_msg *)msg)->params[0]);
  81. switch (type) {
  82. case SOFT_REBOOT:
  83. pr_info("Reboot requested\n");
  84. orderly_reboot();
  85. break;
  86. case SOFT_OFF:
  87. pr_info("Poweroff requested\n");
  88. orderly_poweroff(true);
  89. break;
  90. default:
  91. pr_err("Unknown power-control type %llu\n", type);
  92. }
  93. break;
  94. default:
  95. pr_err("Unknown OPAL message type %lu\n", msg_type);
  96. }
  97. return 0;
  98. }
  99. /* OPAL EPOW event notifier block */
  100. static struct notifier_block opal_epow_nb = {
  101. .notifier_call = opal_power_control_event,
  102. .next = NULL,
  103. .priority = 0,
  104. };
  105. /* OPAL DPO event notifier block */
  106. static struct notifier_block opal_dpo_nb = {
  107. .notifier_call = opal_power_control_event,
  108. .next = NULL,
  109. .priority = 0,
  110. };
  111. /* OPAL power-control event notifier block */
  112. static struct notifier_block opal_power_control_nb = {
  113. .notifier_call = opal_power_control_event,
  114. .next = NULL,
  115. .priority = 0,
  116. };
  117. int __init opal_power_control_init(void)
  118. {
  119. int ret, supported = 0;
  120. struct device_node *np;
  121. /* Register OPAL power-control events notifier */
  122. ret = opal_message_notifier_register(OPAL_MSG_SHUTDOWN,
  123. &opal_power_control_nb);
  124. if (ret)
  125. pr_err("Failed to register SHUTDOWN notifier, ret = %d\n", ret);
  126. /* Determine OPAL EPOW, DPO support */
  127. np = of_find_node_by_path("/ibm,opal/epow");
  128. if (np) {
  129. supported = of_device_is_compatible(np, "ibm,opal-v3-epow");
  130. of_node_put(np);
  131. }
  132. if (!supported)
  133. return 0;
  134. pr_info("OPAL EPOW, DPO support detected.\n");
  135. /* Register EPOW event notifier */
  136. ret = opal_message_notifier_register(OPAL_MSG_EPOW, &opal_epow_nb);
  137. if (ret)
  138. pr_err("Failed to register EPOW notifier, ret = %d\n", ret);
  139. /* Register DPO event notifier */
  140. ret = opal_message_notifier_register(OPAL_MSG_DPO, &opal_dpo_nb);
  141. if (ret)
  142. pr_err("Failed to register DPO notifier, ret = %d\n", ret);
  143. /* Check for any pending EPOW or DPO events. */
  144. if (poweroff_pending())
  145. orderly_poweroff(true);
  146. return 0;
  147. }