power: reset: reboot-mode: treat unrecognized reboot mode as normal mode
[firefly-linux-kernel-4.4.55.git] / drivers / power / reset / reboot-mode.c
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
10 #include <linux/device.h>
11 #include <linux/init.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/of.h>
15 #include <linux/reboot.h>
16 #include "reboot-mode.h"
17
18 #define PREFIX "mode-"
19
20 struct mode_info {
21         char mode[32];
22         unsigned int magic;
23         struct list_head list;
24 };
25
26 struct reboot_mode_driver {
27         struct list_head head;
28         int (*write)(int magic);
29         struct notifier_block reboot_notifier;
30 };
31
32 static int get_reboot_mode_magic(struct reboot_mode_driver *reboot,
33                                  const char *cmd)
34 {
35         const char *normal = "normal";
36         int magic = 0;
37         struct mode_info *info;
38
39         if (!cmd || !cmd[0])
40                 cmd = normal;
41
42         list_for_each_entry(info, &reboot->head, list) {
43                 if (!strcmp(info->mode, cmd)) {
44                         magic = info->magic;
45                         break;
46                 }
47         }
48
49         return magic;
50 }
51
52 static int reboot_mode_notify(struct notifier_block *this,
53                               unsigned long mode, void *cmd)
54 {
55         struct reboot_mode_driver *reboot;
56         int magic;
57
58         reboot = container_of(this, struct reboot_mode_driver, reboot_notifier);
59         magic = get_reboot_mode_magic(reboot, cmd);
60         if (!magic)
61                 magic = get_reboot_mode_magic(reboot, NULL);
62         if (magic)
63                 reboot->write(magic);
64
65         return NOTIFY_DONE;
66 }
67
68 int reboot_mode_register(struct device *dev, int (*write)(int))
69 {
70         struct reboot_mode_driver *reboot;
71         struct mode_info *info;
72         struct property *prop;
73         size_t len = strlen(PREFIX);
74         int ret;
75
76         reboot = devm_kzalloc(dev, sizeof(*reboot), GFP_KERNEL);
77         if (!reboot)
78                 return -ENOMEM;
79
80         reboot->write = write;
81         INIT_LIST_HEAD(&reboot->head);
82         for_each_property_of_node(dev->of_node, prop) {
83                 if (len > strlen(prop->name) || strncmp(prop->name, PREFIX, len))
84                         continue;
85                 info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
86                 if (!info)
87                         return -ENOMEM;
88                 strcpy(info->mode, prop->name + len);
89                 if (of_property_read_u32(dev->of_node, prop->name, &info->magic)) {
90                         dev_err(dev, "reboot mode %s without magic number\n",
91                                 info->mode);
92                         devm_kfree(dev, info);
93                         continue;
94                 }
95                 list_add_tail(&info->list, &reboot->head);
96         }
97         reboot->reboot_notifier.notifier_call = reboot_mode_notify;
98         ret = register_reboot_notifier(&reboot->reboot_notifier);
99         if (ret)
100                 dev_err(dev, "can't register reboot notifier\n");
101
102         return ret;
103 }
104 EXPORT_SYMBOL_GPL(reboot_mode_register);
105
106 MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com");
107 MODULE_DESCRIPTION("System reboot mode driver");
108 MODULE_LICENSE("GPL v2");