syscon-reboot.c 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * Generic Syscon Reboot Driver
  4. *
  5. * Copyright (c) 2013, Applied Micro Circuits Corporation
  6. * Author: Feng Kan <fkan@apm.com>
  7. */
  8. #include <linux/delay.h>
  9. #include <linux/io.h>
  10. #include <linux/notifier.h>
  11. #include <linux/mfd/syscon.h>
  12. #include <linux/of.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/reboot.h>
  15. #include <linux/regmap.h>
  16. struct reboot_mode_bits {
  17. u32 offset;
  18. u32 mask;
  19. u32 value;
  20. bool valid;
  21. };
  22. struct reboot_data {
  23. struct reboot_mode_bits mode_bits[REBOOT_SOFT + 1];
  24. struct reboot_mode_bits catchall;
  25. };
  26. struct syscon_reboot_context {
  27. struct regmap *map;
  28. const struct reboot_data *rd; /* from of match data, if any */
  29. struct reboot_mode_bits catchall; /* from DT */
  30. struct notifier_block restart_handler;
  31. };
  32. static int syscon_restart_handle(struct notifier_block *this,
  33. unsigned long mode, void *cmd)
  34. {
  35. struct syscon_reboot_context *ctx =
  36. container_of(this, struct syscon_reboot_context,
  37. restart_handler);
  38. const struct reboot_mode_bits *mode_bits;
  39. if (ctx->rd) {
  40. if (mode < ARRAY_SIZE(ctx->rd->mode_bits) &&
  41. ctx->rd->mode_bits[mode].valid)
  42. mode_bits = &ctx->rd->mode_bits[mode];
  43. else
  44. mode_bits = &ctx->rd->catchall;
  45. } else {
  46. mode_bits = &ctx->catchall;
  47. }
  48. /* Issue the reboot */
  49. regmap_update_bits(ctx->map, mode_bits->offset, mode_bits->mask,
  50. mode_bits->value);
  51. mdelay(1000);
  52. pr_emerg("Unable to restart system\n");
  53. return NOTIFY_DONE;
  54. }
  55. static int syscon_reboot_probe(struct platform_device *pdev)
  56. {
  57. struct syscon_reboot_context *ctx;
  58. struct device *dev = &pdev->dev;
  59. int priority;
  60. int err;
  61. ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
  62. if (!ctx)
  63. return -ENOMEM;
  64. ctx->map = syscon_regmap_lookup_by_phandle(dev->of_node, "regmap");
  65. if (IS_ERR(ctx->map)) {
  66. ctx->map = syscon_node_to_regmap(dev->parent->of_node);
  67. if (IS_ERR(ctx->map))
  68. return PTR_ERR(ctx->map);
  69. }
  70. if (of_property_read_s32(pdev->dev.of_node, "priority", &priority))
  71. priority = 192;
  72. ctx->rd = of_device_get_match_data(dev);
  73. if (!ctx->rd) {
  74. int mask_err, value_err;
  75. if (of_property_read_u32(pdev->dev.of_node, "offset",
  76. &ctx->catchall.offset) &&
  77. of_property_read_u32(pdev->dev.of_node, "reg",
  78. &ctx->catchall.offset))
  79. return -EINVAL;
  80. value_err = of_property_read_u32(pdev->dev.of_node, "value",
  81. &ctx->catchall.value);
  82. mask_err = of_property_read_u32(pdev->dev.of_node, "mask",
  83. &ctx->catchall.mask);
  84. if (value_err && mask_err) {
  85. dev_err(dev, "unable to read 'value' and 'mask'");
  86. return -EINVAL;
  87. }
  88. if (value_err) {
  89. /* support old binding */
  90. ctx->catchall.value = ctx->catchall.mask;
  91. ctx->catchall.mask = 0xFFFFFFFF;
  92. } else if (mask_err) {
  93. /* support value without mask */
  94. ctx->catchall.mask = 0xFFFFFFFF;
  95. }
  96. }
  97. ctx->restart_handler.notifier_call = syscon_restart_handle;
  98. ctx->restart_handler.priority = priority;
  99. err = register_restart_handler(&ctx->restart_handler);
  100. if (err)
  101. dev_err(dev, "can't register restart notifier (err=%d)\n", err);
  102. return err;
  103. }
  104. static const struct reboot_data gs101_reboot_data = {
  105. .mode_bits = {
  106. [REBOOT_WARM] = {
  107. .offset = 0x3a00, /* SYSTEM_CONFIGURATION */
  108. .mask = 0x00000002, /* SWRESET_SYSTEM */
  109. .value = 0x00000002,
  110. .valid = true,
  111. },
  112. [REBOOT_SOFT] = {
  113. .offset = 0x3a00, /* SYSTEM_CONFIGURATION */
  114. .mask = 0x00000002, /* SWRESET_SYSTEM */
  115. .value = 0x00000002,
  116. .valid = true,
  117. },
  118. },
  119. .catchall = {
  120. .offset = 0x3e9c, /* PAD_CTRL_PWR_HOLD */
  121. .mask = 0x00000100,
  122. .value = 0x00000000,
  123. },
  124. };
  125. static const struct of_device_id syscon_reboot_of_match[] = {
  126. { .compatible = "google,gs101-reboot", .data = &gs101_reboot_data },
  127. { .compatible = "syscon-reboot" },
  128. {}
  129. };
  130. static struct platform_driver syscon_reboot_driver = {
  131. .probe = syscon_reboot_probe,
  132. .driver = {
  133. .name = "syscon-reboot",
  134. .of_match_table = syscon_reboot_of_match,
  135. },
  136. };
  137. builtin_platform_driver(syscon_reboot_driver);