reboot-mode.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. /*
  2. * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. */
  9. #include <linux/device.h>
  10. #include <linux/init.h>
  11. #include <linux/kernel.h>
  12. #include <linux/module.h>
  13. #include <linux/of.h>
  14. #include <linux/reboot.h>
  15. #include "reboot-mode.h"
  16. #define PREFIX "mode-"
  17. struct mode_info {
  18. const char *mode;
  19. u32 magic;
  20. struct list_head list;
  21. };
  22. static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
  23. const char *cmd)
  24. {
  25. const char *normal = "normal";
  26. int magic = 0;
  27. struct mode_info *info;
  28. if (!cmd)
  29. cmd = normal;
  30. list_for_each_entry(info, &reboot->head, list) {
  31. if (!strcmp(info->mode, cmd)) {
  32. magic = info->magic;
  33. break;
  34. }
  35. }
  36. return magic;
  37. }
  38. static int reboot_mode_notify(struct notifier_block *this,
  39. unsigned long mode, void *cmd)
  40. {
  41. struct reboot_mode_driver *reboot;
  42. unsigned int magic;
  43. reboot = container_of(this, struct reboot_mode_driver, reboot_notifier);
  44. magic = get_reboot_mode_magic(reboot, cmd);
  45. if (magic)
  46. reboot->write(reboot, magic);
  47. return NOTIFY_DONE;
  48. }
  49. /**
  50. * reboot_mode_register - register a reboot mode driver
  51. * @reboot: reboot mode driver
  52. *
  53. * Returns: 0 on success or a negative error code on failure.
  54. */
  55. int reboot_mode_register(struct reboot_mode_driver *reboot)
  56. {
  57. struct mode_info *info;
  58. struct property *prop;
  59. struct device_node *np = reboot->dev->of_node;
  60. size_t len = strlen(PREFIX);
  61. int ret;
  62. INIT_LIST_HEAD(&reboot->head);
  63. for_each_property_of_node(np, prop) {
  64. if (strncmp(prop->name, PREFIX, len))
  65. continue;
  66. info = devm_kzalloc(reboot->dev, sizeof(*info), GFP_KERNEL);
  67. if (!info) {
  68. ret = -ENOMEM;
  69. goto error;
  70. }
  71. if (of_property_read_u32(np, prop->name, &info->magic)) {
  72. dev_err(reboot->dev, "reboot mode %s without magic number\n",
  73. info->mode);
  74. devm_kfree(reboot->dev, info);
  75. continue;
  76. }
  77. info->mode = kstrdup_const(prop->name + len, GFP_KERNEL);
  78. if (!info->mode) {
  79. ret = -ENOMEM;
  80. goto error;
  81. } else if (info->mode[0] == '\0') {
  82. kfree_const(info->mode);
  83. ret = -EINVAL;
  84. dev_err(reboot->dev, "invalid mode name(%s): too short!\n",
  85. prop->name);
  86. goto error;
  87. }
  88. list_add_tail(&info->list, &reboot->head);
  89. }
  90. reboot->reboot_notifier.notifier_call = reboot_mode_notify;
  91. register_reboot_notifier(&reboot->reboot_notifier);
  92. return 0;
  93. error:
  94. list_for_each_entry(info, &reboot->head, list)
  95. kfree_const(info->mode);
  96. return ret;
  97. }
  98. EXPORT_SYMBOL_GPL(reboot_mode_register);
  99. /**
  100. * reboot_mode_unregister - unregister a reboot mode driver
  101. * @reboot: reboot mode driver
  102. */
  103. int reboot_mode_unregister(struct reboot_mode_driver *reboot)
  104. {
  105. struct mode_info *info;
  106. unregister_reboot_notifier(&reboot->reboot_notifier);
  107. list_for_each_entry(info, &reboot->head, list)
  108. kfree_const(info->mode);
  109. return 0;
  110. }
  111. EXPORT_SYMBOL_GPL(reboot_mode_unregister);
  112. static void devm_reboot_mode_release(struct device *dev, void *res)
  113. {
  114. reboot_mode_unregister(*(struct reboot_mode_driver **)res);
  115. }
  116. /**
  117. * devm_reboot_mode_register() - resource managed reboot_mode_register()
  118. * @dev: device to associate this resource with
  119. * @reboot: reboot mode driver
  120. *
  121. * Returns: 0 on success or a negative error code on failure.
  122. */
  123. int devm_reboot_mode_register(struct device *dev,
  124. struct reboot_mode_driver *reboot)
  125. {
  126. struct reboot_mode_driver **dr;
  127. int rc;
  128. dr = devres_alloc(devm_reboot_mode_release, sizeof(*dr), GFP_KERNEL);
  129. if (!dr)
  130. return -ENOMEM;
  131. rc = reboot_mode_register(reboot);
  132. if (rc) {
  133. devres_free(dr);
  134. return rc;
  135. }
  136. *dr = reboot;
  137. devres_add(dev, dr);
  138. return 0;
  139. }
  140. EXPORT_SYMBOL_GPL(devm_reboot_mode_register);
  141. static int devm_reboot_mode_match(struct device *dev, void *res, void *data)
  142. {
  143. struct reboot_mode_driver **p = res;
  144. if (WARN_ON(!p || !*p))
  145. return 0;
  146. return *p == data;
  147. }
  148. /**
  149. * devm_reboot_mode_unregister() - resource managed reboot_mode_unregister()
  150. * @dev: device to associate this resource with
  151. * @reboot: reboot mode driver
  152. */
  153. void devm_reboot_mode_unregister(struct device *dev,
  154. struct reboot_mode_driver *reboot)
  155. {
  156. WARN_ON(devres_release(dev,
  157. devm_reboot_mode_release,
  158. devm_reboot_mode_match, reboot));
  159. }
  160. EXPORT_SYMBOL_GPL(devm_reboot_mode_unregister);
  161. MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com");
  162. MODULE_DESCRIPTION("System reboot mode core library");
  163. MODULE_LICENSE("GPL v2");