1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
6 #include <linux/device.h>
7 #include <linux/init.h>
8 #include <linux/kernel.h>
9 #include <linux/module.h>
11 #include <linux/reboot.h>
12 #include <linux/reboot-mode.h>
14 #define PREFIX "mode-"
19 struct list_head list;
22 static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
25 const char *normal = "normal";
27 struct mode_info *info;
32 list_for_each_entry(info, &reboot->head, list) {
33 if (!strcmp(info->mode, cmd)) {
42 static int reboot_mode_notify(struct notifier_block *this,
43 unsigned long mode, void *cmd)
45 struct reboot_mode_driver *reboot;
48 reboot = container_of(this, struct reboot_mode_driver, reboot_notifier);
49 magic = get_reboot_mode_magic(reboot, cmd);
51 reboot->write(reboot, magic);
57 * reboot_mode_register - register a reboot mode driver
58 * @reboot: reboot mode driver
60 * Returns: 0 on success or a negative error code on failure.
62 int reboot_mode_register(struct reboot_mode_driver *reboot)
64 struct mode_info *info;
65 struct property *prop;
66 struct device_node *np = reboot->dev->of_node;
67 size_t len = strlen(PREFIX);
70 INIT_LIST_HEAD(&reboot->head);
72 for_each_property_of_node(np, prop) {
73 if (strncmp(prop->name, PREFIX, len))
76 info = devm_kzalloc(reboot->dev, sizeof(*info), GFP_KERNEL);
82 if (of_property_read_u32(np, prop->name, &info->magic)) {
83 dev_err(reboot->dev, "reboot mode %s without magic number\n",
85 devm_kfree(reboot->dev, info);
89 info->mode = kstrdup_const(prop->name + len, GFP_KERNEL);
93 } else if (info->mode[0] == '\0') {
94 kfree_const(info->mode);
96 dev_err(reboot->dev, "invalid mode name(%s): too short!\n",
101 list_add_tail(&info->list, &reboot->head);
104 reboot->reboot_notifier.notifier_call = reboot_mode_notify;
105 register_reboot_notifier(&reboot->reboot_notifier);
110 list_for_each_entry(info, &reboot->head, list)
111 kfree_const(info->mode);
115 EXPORT_SYMBOL_GPL(reboot_mode_register);
118 * reboot_mode_unregister - unregister a reboot mode driver
119 * @reboot: reboot mode driver
121 int reboot_mode_unregister(struct reboot_mode_driver *reboot)
123 struct mode_info *info;
125 unregister_reboot_notifier(&reboot->reboot_notifier);
127 list_for_each_entry(info, &reboot->head, list)
128 kfree_const(info->mode);
132 EXPORT_SYMBOL_GPL(reboot_mode_unregister);
134 static void devm_reboot_mode_release(struct device *dev, void *res)
136 reboot_mode_unregister(*(struct reboot_mode_driver **)res);
140 * devm_reboot_mode_register() - resource managed reboot_mode_register()
141 * @dev: device to associate this resource with
142 * @reboot: reboot mode driver
144 * Returns: 0 on success or a negative error code on failure.
146 int devm_reboot_mode_register(struct device *dev,
147 struct reboot_mode_driver *reboot)
149 struct reboot_mode_driver **dr;
152 dr = devres_alloc(devm_reboot_mode_release, sizeof(*dr), GFP_KERNEL);
156 rc = reboot_mode_register(reboot);
167 EXPORT_SYMBOL_GPL(devm_reboot_mode_register);
169 static int devm_reboot_mode_match(struct device *dev, void *res, void *data)
171 struct reboot_mode_driver **p = res;
173 if (WARN_ON(!p || !*p))
180 * devm_reboot_mode_unregister() - resource managed reboot_mode_unregister()
181 * @dev: device to associate this resource with
182 * @reboot: reboot mode driver
184 void devm_reboot_mode_unregister(struct device *dev,
185 struct reboot_mode_driver *reboot)
187 WARN_ON(devres_release(dev,
188 devm_reboot_mode_release,
189 devm_reboot_mode_match, reboot));
191 EXPORT_SYMBOL_GPL(devm_reboot_mode_unregister);
194 MODULE_DESCRIPTION("System reboot mode core library");
195 MODULE_LICENSE("GPL v2");