pvpanic.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Pvpanic Device Support
  4. *
  5. * Copyright (C) 2013 Fujitsu.
  6. * Copyright (C) 2018 ZTE.
  7. */
  8. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  9. #include <linux/acpi.h>
  10. #include <linux/kernel.h>
  11. #include <linux/kexec.h>
  12. #include <linux/module.h>
  13. #include <linux/of.h>
  14. #include <linux/of_address.h>
  15. #include <linux/platform_device.h>
  16. #include <linux/types.h>
  17. #include <uapi/misc/pvpanic.h>
  18. static void __iomem *base;
  19. MODULE_AUTHOR("Hu Tao <hutao@cn.fujitsu.com>");
  20. MODULE_DESCRIPTION("pvpanic device driver");
  21. MODULE_LICENSE("GPL");
  22. static void
  23. pvpanic_send_event(unsigned int event)
  24. {
  25. iowrite8(event, base);
  26. }
  27. static int
  28. pvpanic_panic_notify(struct notifier_block *nb, unsigned long code,
  29. void *unused)
  30. {
  31. unsigned int event = PVPANIC_PANICKED;
  32. if (kexec_crash_loaded())
  33. event = PVPANIC_CRASH_LOADED;
  34. pvpanic_send_event(event);
  35. return NOTIFY_DONE;
  36. }
  37. static struct notifier_block pvpanic_panic_nb = {
  38. .notifier_call = pvpanic_panic_notify,
  39. .priority = 1, /* let this called before broken drm_fb_helper */
  40. };
  41. #ifdef CONFIG_ACPI
  42. static int pvpanic_add(struct acpi_device *device);
  43. static int pvpanic_remove(struct acpi_device *device);
  44. static const struct acpi_device_id pvpanic_device_ids[] = {
  45. { "QEMU0001", 0 },
  46. { "", 0 }
  47. };
  48. MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids);
  49. static struct acpi_driver pvpanic_driver = {
  50. .name = "pvpanic",
  51. .class = "QEMU",
  52. .ids = pvpanic_device_ids,
  53. .ops = {
  54. .add = pvpanic_add,
  55. .remove = pvpanic_remove,
  56. },
  57. .owner = THIS_MODULE,
  58. };
  59. static acpi_status
  60. pvpanic_walk_resources(struct acpi_resource *res, void *context)
  61. {
  62. struct resource r;
  63. if (acpi_dev_resource_io(res, &r)) {
  64. #ifdef CONFIG_HAS_IOPORT_MAP
  65. base = ioport_map(r.start, resource_size(&r));
  66. return AE_OK;
  67. #else
  68. return AE_ERROR;
  69. #endif
  70. } else if (acpi_dev_resource_memory(res, &r)) {
  71. base = ioremap(r.start, resource_size(&r));
  72. return AE_OK;
  73. }
  74. return AE_ERROR;
  75. }
  76. static int pvpanic_add(struct acpi_device *device)
  77. {
  78. int ret;
  79. ret = acpi_bus_get_status(device);
  80. if (ret < 0)
  81. return ret;
  82. if (!device->status.enabled || !device->status.functional)
  83. return -ENODEV;
  84. acpi_walk_resources(device->handle, METHOD_NAME__CRS,
  85. pvpanic_walk_resources, NULL);
  86. if (!base)
  87. return -ENODEV;
  88. atomic_notifier_chain_register(&panic_notifier_list,
  89. &pvpanic_panic_nb);
  90. return 0;
  91. }
  92. static int pvpanic_remove(struct acpi_device *device)
  93. {
  94. atomic_notifier_chain_unregister(&panic_notifier_list,
  95. &pvpanic_panic_nb);
  96. iounmap(base);
  97. return 0;
  98. }
  99. static int pvpanic_register_acpi_driver(void)
  100. {
  101. return acpi_bus_register_driver(&pvpanic_driver);
  102. }
  103. static void pvpanic_unregister_acpi_driver(void)
  104. {
  105. acpi_bus_unregister_driver(&pvpanic_driver);
  106. }
  107. #else
  108. static int pvpanic_register_acpi_driver(void)
  109. {
  110. return -ENODEV;
  111. }
  112. static void pvpanic_unregister_acpi_driver(void) {}
  113. #endif
  114. static int pvpanic_mmio_probe(struct platform_device *pdev)
  115. {
  116. base = devm_platform_ioremap_resource(pdev, 0);
  117. if (IS_ERR(base))
  118. return PTR_ERR(base);
  119. atomic_notifier_chain_register(&panic_notifier_list,
  120. &pvpanic_panic_nb);
  121. return 0;
  122. }
  123. static int pvpanic_mmio_remove(struct platform_device *pdev)
  124. {
  125. atomic_notifier_chain_unregister(&panic_notifier_list,
  126. &pvpanic_panic_nb);
  127. return 0;
  128. }
  129. static const struct of_device_id pvpanic_mmio_match[] = {
  130. { .compatible = "qemu,pvpanic-mmio", },
  131. {}
  132. };
  133. MODULE_DEVICE_TABLE(of, pvpanic_mmio_match);
  134. static struct platform_driver pvpanic_mmio_driver = {
  135. .driver = {
  136. .name = "pvpanic-mmio",
  137. .of_match_table = pvpanic_mmio_match,
  138. },
  139. .probe = pvpanic_mmio_probe,
  140. .remove = pvpanic_mmio_remove,
  141. };
  142. static int __init pvpanic_mmio_init(void)
  143. {
  144. if (acpi_disabled)
  145. return platform_driver_register(&pvpanic_mmio_driver);
  146. else
  147. return pvpanic_register_acpi_driver();
  148. }
  149. static void __exit pvpanic_mmio_exit(void)
  150. {
  151. if (acpi_disabled)
  152. platform_driver_unregister(&pvpanic_mmio_driver);
  153. else
  154. pvpanic_unregister_acpi_driver();
  155. }
  156. module_init(pvpanic_mmio_init);
  157. module_exit(pvpanic_mmio_exit);