macsmc-reboot.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. // SPDX-License-Identifier: GPL-2.0-only OR MIT
  2. /*
  3. * Apple SMC Reboot/Poweroff Handler
  4. * Copyright The Asahi Linux Contributors
  5. */
  6. #include <linux/delay.h>
  7. #include <linux/mfd/core.h>
  8. #include <linux/mfd/macsmc.h>
  9. #include <linux/mod_devicetable.h>
  10. #include <linux/module.h>
  11. #include <linux/nvmem-consumer.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/reboot.h>
  14. #include <linux/slab.h>
  15. struct macsmc_reboot_nvmem {
  16. struct nvmem_cell *shutdown_flag;
  17. struct nvmem_cell *boot_stage;
  18. struct nvmem_cell *boot_error_count;
  19. struct nvmem_cell *panic_count;
  20. };
  21. static const char * const nvmem_names[] = {
  22. "shutdown_flag",
  23. "boot_stage",
  24. "boot_error_count",
  25. "panic_count",
  26. };
  27. enum boot_stage {
  28. BOOT_STAGE_SHUTDOWN = 0x00, /* Clean shutdown */
  29. BOOT_STAGE_IBOOT_DONE = 0x2f, /* Last stage of bootloader */
  30. BOOT_STAGE_KERNEL_STARTED = 0x30, /* Normal OS booting */
  31. };
  32. struct macsmc_reboot {
  33. struct device *dev;
  34. struct apple_smc *smc;
  35. struct notifier_block reboot_notify;
  36. union {
  37. struct macsmc_reboot_nvmem nvm;
  38. struct nvmem_cell *nvm_cells[ARRAY_SIZE(nvmem_names)];
  39. };
  40. };
  41. /* Helpers to read/write a u8 given a struct nvmem_cell */
  42. static int nvmem_cell_get_u8(struct nvmem_cell *cell)
  43. {
  44. size_t len;
  45. void *bfr;
  46. u8 val;
  47. bfr = nvmem_cell_read(cell, &len);
  48. if (IS_ERR(bfr))
  49. return PTR_ERR(bfr);
  50. if (len < 1) {
  51. kfree(bfr);
  52. return -EINVAL;
  53. }
  54. val = *(u8 *)bfr;
  55. kfree(bfr);
  56. return val;
  57. }
  58. static int nvmem_cell_set_u8(struct nvmem_cell *cell, u8 val)
  59. {
  60. return nvmem_cell_write(cell, &val, sizeof(val));
  61. }
  62. /*
  63. * SMC 'MBSE' key actions:
  64. *
  65. * 'offw' - shutdown warning
  66. * 'slpw' - sleep warning
  67. * 'rest' - restart warning
  68. * 'off1' - shutdown (needs PMU bit set to stay on)
  69. * 'susp' - suspend
  70. * 'phra' - restart ("PE Halt Restart Action"?)
  71. * 'panb' - panic beginning
  72. * 'pane' - panic end
  73. */
  74. static int macsmc_prepare_atomic(struct sys_off_data *data)
  75. {
  76. struct macsmc_reboot *reboot = data->cb_data;
  77. dev_info(reboot->dev, "Preparing SMC for atomic mode\n");
  78. apple_smc_enter_atomic(reboot->smc);
  79. return NOTIFY_OK;
  80. }
  81. static int macsmc_power_off(struct sys_off_data *data)
  82. {
  83. struct macsmc_reboot *reboot = data->cb_data;
  84. dev_info(reboot->dev, "Issuing power off (off1)\n");
  85. if (apple_smc_write_u32_atomic(reboot->smc, SMC_KEY(MBSE), SMC_KEY(off1)) < 0) {
  86. dev_err(reboot->dev, "Failed to issue MBSE = off1 (power_off)\n");
  87. } else {
  88. mdelay(100);
  89. WARN_ONCE(1, "Unable to power off system\n");
  90. }
  91. return NOTIFY_OK;
  92. }
  93. static int macsmc_restart(struct sys_off_data *data)
  94. {
  95. struct macsmc_reboot *reboot = data->cb_data;
  96. dev_info(reboot->dev, "Issuing restart (phra)\n");
  97. if (apple_smc_write_u32_atomic(reboot->smc, SMC_KEY(MBSE), SMC_KEY(phra)) < 0) {
  98. dev_err(reboot->dev, "Failed to issue MBSE = phra (restart)\n");
  99. } else {
  100. mdelay(100);
  101. WARN_ONCE(1, "Unable to restart system\n");
  102. }
  103. return NOTIFY_OK;
  104. }
  105. static int macsmc_reboot_notify(struct notifier_block *this, unsigned long action, void *data)
  106. {
  107. struct macsmc_reboot *reboot = container_of(this, struct macsmc_reboot, reboot_notify);
  108. u8 shutdown_flag;
  109. u32 val;
  110. switch (action) {
  111. case SYS_RESTART:
  112. val = SMC_KEY(rest);
  113. shutdown_flag = 0;
  114. break;
  115. case SYS_POWER_OFF:
  116. val = SMC_KEY(offw);
  117. shutdown_flag = 1;
  118. break;
  119. default:
  120. return NOTIFY_DONE;
  121. }
  122. dev_info(reboot->dev, "Preparing for reboot (%p4ch)\n", &val);
  123. /* On the Mac Mini, this will turn off the LED for power off */
  124. if (apple_smc_write_u32(reboot->smc, SMC_KEY(MBSE), val) < 0)
  125. dev_err(reboot->dev, "Failed to issue MBSE = %p4ch (reboot_prepare)\n", &val);
  126. /* Set the boot_stage to 0, which means we're doing a clean shutdown/reboot. */
  127. if (reboot->nvm.boot_stage &&
  128. nvmem_cell_set_u8(reboot->nvm.boot_stage, BOOT_STAGE_SHUTDOWN) < 0)
  129. dev_err(reboot->dev, "Failed to write boot_stage\n");
  130. /*
  131. * Set the PMU flag to actually reboot into the off state.
  132. * Without this, the device will just reboot. We make it optional in case it is no longer
  133. * necessary on newer hardware.
  134. */
  135. if (reboot->nvm.shutdown_flag &&
  136. nvmem_cell_set_u8(reboot->nvm.shutdown_flag, shutdown_flag) < 0)
  137. dev_err(reboot->dev, "Failed to write shutdown_flag\n");
  138. return NOTIFY_OK;
  139. }
  140. static void macsmc_power_init_error_counts(struct macsmc_reboot *reboot)
  141. {
  142. int boot_error_count, panic_count;
  143. if (!reboot->nvm.boot_error_count || !reboot->nvm.panic_count)
  144. return;
  145. boot_error_count = nvmem_cell_get_u8(reboot->nvm.boot_error_count);
  146. if (boot_error_count < 0) {
  147. dev_err(reboot->dev, "Failed to read boot_error_count (%d)\n", boot_error_count);
  148. return;
  149. }
  150. panic_count = nvmem_cell_get_u8(reboot->nvm.panic_count);
  151. if (panic_count < 0) {
  152. dev_err(reboot->dev, "Failed to read panic_count (%d)\n", panic_count);
  153. return;
  154. }
  155. if (!boot_error_count && !panic_count)
  156. return;
  157. dev_warn(reboot->dev, "PMU logged %d boot error(s) and %d panic(s)\n",
  158. boot_error_count, panic_count);
  159. if (nvmem_cell_set_u8(reboot->nvm.panic_count, 0) < 0)
  160. dev_err(reboot->dev, "Failed to reset panic_count\n");
  161. if (nvmem_cell_set_u8(reboot->nvm.boot_error_count, 0) < 0)
  162. dev_err(reboot->dev, "Failed to reset boot_error_count\n");
  163. }
  164. static int macsmc_reboot_probe(struct platform_device *pdev)
  165. {
  166. struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent);
  167. struct macsmc_reboot *reboot;
  168. int ret, i;
  169. reboot = devm_kzalloc(&pdev->dev, sizeof(*reboot), GFP_KERNEL);
  170. if (!reboot)
  171. return -ENOMEM;
  172. reboot->dev = &pdev->dev;
  173. reboot->smc = smc;
  174. platform_set_drvdata(pdev, reboot);
  175. for (i = 0; i < ARRAY_SIZE(nvmem_names); i++) {
  176. struct nvmem_cell *cell;
  177. cell = devm_nvmem_cell_get(&pdev->dev,
  178. nvmem_names[i]);
  179. if (IS_ERR(cell)) {
  180. if (PTR_ERR(cell) == -EPROBE_DEFER)
  181. return -EPROBE_DEFER;
  182. dev_warn(&pdev->dev, "Missing NVMEM cell %s (%ld)\n",
  183. nvmem_names[i], PTR_ERR(cell));
  184. /* Non fatal, we'll deal with it */
  185. cell = NULL;
  186. }
  187. reboot->nvm_cells[i] = cell;
  188. }
  189. /* Set the boot_stage to indicate we're running the OS kernel */
  190. if (reboot->nvm.boot_stage &&
  191. nvmem_cell_set_u8(reboot->nvm.boot_stage, BOOT_STAGE_KERNEL_STARTED) < 0)
  192. dev_err(reboot->dev, "Failed to write boot_stage\n");
  193. /* Display and clear the error counts */
  194. macsmc_power_init_error_counts(reboot);
  195. reboot->reboot_notify.notifier_call = macsmc_reboot_notify;
  196. ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_POWER_OFF_PREPARE,
  197. SYS_OFF_PRIO_HIGH, macsmc_prepare_atomic, reboot);
  198. if (ret)
  199. return dev_err_probe(&pdev->dev, ret,
  200. "Failed to register power-off prepare handler\n");
  201. ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_HIGH,
  202. macsmc_power_off, reboot);
  203. if (ret)
  204. return dev_err_probe(&pdev->dev, ret,
  205. "Failed to register power-off handler\n");
  206. ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART_PREPARE,
  207. SYS_OFF_PRIO_HIGH, macsmc_prepare_atomic, reboot);
  208. if (ret)
  209. return dev_err_probe(&pdev->dev, ret,
  210. "Failed to register restart prepare handler\n");
  211. ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART, SYS_OFF_PRIO_HIGH,
  212. macsmc_restart, reboot);
  213. if (ret)
  214. return dev_err_probe(&pdev->dev, ret, "Failed to register restart handler\n");
  215. ret = devm_register_reboot_notifier(&pdev->dev, &reboot->reboot_notify);
  216. if (ret)
  217. return dev_err_probe(&pdev->dev, ret, "Failed to register reboot notifier\n");
  218. dev_info(&pdev->dev, "Handling reboot and poweroff requests via SMC\n");
  219. return 0;
  220. }
  221. static const struct of_device_id macsmc_reboot_of_table[] = {
  222. { .compatible = "apple,smc-reboot", },
  223. {}
  224. };
  225. MODULE_DEVICE_TABLE(of, macsmc_reboot_of_table);
  226. static struct platform_driver macsmc_reboot_driver = {
  227. .driver = {
  228. .name = "macsmc-reboot",
  229. .of_match_table = macsmc_reboot_of_table,
  230. },
  231. .probe = macsmc_reboot_probe,
  232. };
  233. module_platform_driver(macsmc_reboot_driver);
  234. MODULE_LICENSE("Dual MIT/GPL");
  235. MODULE_DESCRIPTION("Apple SMC reboot/poweroff driver");
  236. MODULE_AUTHOR("Hector Martin <marcan@marcan.st>");