9aa7b809972c144d3d34ab4f90b09bacb11985b0
[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)
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                 reboot->write(magic);
62
63         return NOTIFY_DONE;
64 }
65
66 int reboot_mode_register(struct device *dev, int (*write)(int))
67 {
68         struct reboot_mode_driver *reboot;
69         struct mode_info *info;
70         struct property *prop;
71         size_t len = strlen(PREFIX);
72         int ret;
73
74         reboot = devm_kzalloc(dev, sizeof(*reboot), GFP_KERNEL);
75         if (!reboot)
76                 return -ENOMEM;
77
78         reboot->write = write;
79         INIT_LIST_HEAD(&reboot->head);
80         for_each_property_of_node(dev->of_node, prop) {
81                 if (len > strlen(prop->name) || strncmp(prop->name, PREFIX, len))
82                         continue;
83                 info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
84                 if (!info)
85                         return -ENOMEM;
86                 strcpy(info->mode, prop->name + len);
87                 if (of_property_read_u32(dev->of_node, prop->name, &info->magic)) {
88                         dev_err(dev, "reboot mode %s without magic number\n",
89                                 info->mode);
90                         devm_kfree(dev, info);
91                         continue;
92                 }
93                 list_add_tail(&info->list, &reboot->head);
94         }
95         reboot->reboot_notifier.notifier_call = reboot_mode_notify;
96         ret = register_reboot_notifier(&reboot->reboot_notifier);
97         if (ret)
98                 dev_err(dev, "can't register reboot notifier\n");
99
100         return ret;
101 }
102 EXPORT_SYMBOL_GPL(reboot_mode_register);
103
104 MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com");
105 MODULE_DESCRIPTION("System reboot mode driver");
106 MODULE_LICENSE("GPL v2");