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
);
193 MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
194 MODULE_DESCRIPTION("System reboot mode core library");
195 MODULE_LICENSE("GPL v2");