2 * Copyright (c) 2016, Fuzhou Rockchip Electronics Co., Ltd
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.
10 #include <linux/device.h>
11 #include <linux/init.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
15 #include <linux/reboot.h>
16 #include <linux/reboot-mode.h>
18 #define PREFIX "mode-"
23 struct list_head list
;
26 static unsigned int get_reboot_mode_magic(struct reboot_mode_driver
*reboot
,
29 const char *normal
= "normal";
31 struct mode_info
*info
;
36 list_for_each_entry(info
, &reboot
->head
, list
) {
37 if (!strcmp(info
->mode
, cmd
)) {
46 static int reboot_mode_notify(struct notifier_block
*this,
47 unsigned long mode
, void *cmd
)
49 struct reboot_mode_driver
*reboot
;
52 reboot
= container_of(this, struct reboot_mode_driver
, reboot_notifier
);
53 magic
= get_reboot_mode_magic(reboot
, cmd
);
55 reboot
->write(reboot
, magic
);
61 * reboot_mode_register - register a reboot mode driver
62 * @reboot: reboot mode driver
64 * Returns: 0 on success or a negative error code on failure.
66 int reboot_mode_register(struct reboot_mode_driver
*reboot
)
68 struct mode_info
*info
;
69 struct property
*prop
;
70 struct device_node
*np
= reboot
->dev
->of_node
;
71 size_t len
= strlen(PREFIX
);
74 INIT_LIST_HEAD(&reboot
->head
);
76 for_each_property_of_node(np
, prop
) {
77 if (strncmp(prop
->name
, PREFIX
, len
))
80 info
= devm_kzalloc(reboot
->dev
, sizeof(*info
), GFP_KERNEL
);
86 if (of_property_read_u32(np
, prop
->name
, &info
->magic
)) {
87 dev_err(reboot
->dev
, "reboot mode %s without magic number\n",
89 devm_kfree(reboot
->dev
, info
);
93 info
->mode
= kstrdup_const(prop
->name
+ len
, GFP_KERNEL
);
97 } else if (info
->mode
[0] == '\0') {
98 kfree_const(info
->mode
);
100 dev_err(reboot
->dev
, "invalid mode name(%s): too short!\n",
105 list_add_tail(&info
->list
, &reboot
->head
);
108 reboot
->reboot_notifier
.notifier_call
= reboot_mode_notify
;
109 register_reboot_notifier(&reboot
->reboot_notifier
);
114 list_for_each_entry(info
, &reboot
->head
, list
)
115 kfree_const(info
->mode
);
119 EXPORT_SYMBOL_GPL(reboot_mode_register
);
122 * reboot_mode_unregister - unregister a reboot mode driver
123 * @reboot: reboot mode driver
125 int reboot_mode_unregister(struct reboot_mode_driver
*reboot
)
127 struct mode_info
*info
;
129 unregister_reboot_notifier(&reboot
->reboot_notifier
);
131 list_for_each_entry(info
, &reboot
->head
, list
)
132 kfree_const(info
->mode
);
136 EXPORT_SYMBOL_GPL(reboot_mode_unregister
);
138 static void devm_reboot_mode_release(struct device
*dev
, void *res
)
140 reboot_mode_unregister(*(struct reboot_mode_driver
**)res
);
144 * devm_reboot_mode_register() - resource managed reboot_mode_register()
145 * @dev: device to associate this resource with
146 * @reboot: reboot mode driver
148 * Returns: 0 on success or a negative error code on failure.
150 int devm_reboot_mode_register(struct device
*dev
,
151 struct reboot_mode_driver
*reboot
)
153 struct reboot_mode_driver
**dr
;
156 dr
= devres_alloc(devm_reboot_mode_release
, sizeof(*dr
), GFP_KERNEL
);
160 rc
= reboot_mode_register(reboot
);
171 EXPORT_SYMBOL_GPL(devm_reboot_mode_register
);
173 static int devm_reboot_mode_match(struct device
*dev
, void *res
, void *data
)
175 struct reboot_mode_driver
**p
= res
;
177 if (WARN_ON(!p
|| !*p
))
184 * devm_reboot_mode_unregister() - resource managed reboot_mode_unregister()
185 * @dev: device to associate this resource with
186 * @reboot: reboot mode driver
188 void devm_reboot_mode_unregister(struct device
*dev
,
189 struct reboot_mode_driver
*reboot
)
191 WARN_ON(devres_release(dev
,
192 devm_reboot_mode_release
,
193 devm_reboot_mode_match
, reboot
));
195 EXPORT_SYMBOL_GPL(devm_reboot_mode_unregister
);
197 MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com");
198 MODULE_DESCRIPTION("System reboot mode core library");
199 MODULE_LICENSE("GPL v2");