OSDN Git Service

treewide: Fix code issues detected using GCC 8
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / drivers / soc / qcom / subsystem_notif.c
1 /* Copyright (c) 2011, 2013, 2016 The Linux Foundation. All rights reserved.
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License version 2 and
5  * only version 2 as published by the Free Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  *
13  * Subsystem Notifier -- Provides notifications
14  * of subsys events.
15  *
16  * Use subsys_notif_register_notifier to register for notifications
17  * and subsys_notif_queue_notification to send notifications.
18  *
19  */
20
21 #include <linux/notifier.h>
22 #include <linux/init.h>
23 #include <linux/debugfs.h>
24 #include <linux/module.h>
25 #include <linux/workqueue.h>
26 #include <linux/stringify.h>
27 #include <linux/delay.h>
28 #include <linux/slab.h>
29 #include <soc/qcom/subsystem_notif.h>
30
31
32 struct subsys_notif_info {
33         char name[50];
34         struct srcu_notifier_head subsys_notif_rcvr_list;
35         struct list_head list;
36 };
37
38 static LIST_HEAD(subsystem_list);
39 static DEFINE_MUTEX(notif_lock);
40 static DEFINE_MUTEX(notif_add_lock);
41
42 #if defined(SUBSYS_RESTART_DEBUG)
43 static void subsys_notif_reg_test_notifier(const char *);
44 #endif
45
46 static struct subsys_notif_info *_notif_find_subsys(const char *subsys_name)
47 {
48         struct subsys_notif_info *subsys;
49
50         mutex_lock(&notif_lock);
51         list_for_each_entry(subsys, &subsystem_list, list)
52                 if (!strncmp(subsys->name, subsys_name,
53                                 ARRAY_SIZE(subsys->name))) {
54                         mutex_unlock(&notif_lock);
55                         return subsys;
56                 }
57         mutex_unlock(&notif_lock);
58
59         return NULL;
60 }
61
62 void *subsys_notif_register_notifier(
63                         const char *subsys_name, struct notifier_block *nb)
64 {
65         int ret;
66         struct subsys_notif_info *subsys = _notif_find_subsys(subsys_name);
67
68         if (!subsys) {
69
70                 /* Possible first time reference to this subsystem. Add it. */
71                 subsys = (struct subsys_notif_info *)
72                                 subsys_notif_add_subsys(subsys_name);
73
74                 if (!subsys)
75                         return ERR_PTR(-EINVAL);
76         }
77
78         ret = srcu_notifier_chain_register(
79                 &subsys->subsys_notif_rcvr_list, nb);
80
81         if (ret < 0)
82                 return ERR_PTR(ret);
83
84         return subsys;
85 }
86 EXPORT_SYMBOL(subsys_notif_register_notifier);
87
88 int subsys_notif_unregister_notifier(void *subsys_handle,
89                                 struct notifier_block *nb)
90 {
91         int ret;
92         struct subsys_notif_info *subsys =
93                         (struct subsys_notif_info *)subsys_handle;
94
95         if (!subsys)
96                 return -EINVAL;
97
98         ret = srcu_notifier_chain_unregister(
99                 &subsys->subsys_notif_rcvr_list, nb);
100
101         return ret;
102 }
103 EXPORT_SYMBOL(subsys_notif_unregister_notifier);
104
105 void *subsys_notif_add_subsys(const char *subsys_name)
106 {
107         struct subsys_notif_info *subsys = NULL;
108
109         if (!subsys_name)
110                 goto done;
111
112         mutex_lock(&notif_add_lock);
113
114         subsys = _notif_find_subsys(subsys_name);
115
116         if (subsys) {
117                 mutex_unlock(&notif_add_lock);
118                 goto done;
119         }
120
121         subsys = kmalloc(sizeof(struct subsys_notif_info), GFP_KERNEL);
122
123         if (!subsys) {
124                 mutex_unlock(&notif_add_lock);
125                 return ERR_PTR(-EINVAL);
126         }
127
128         strlcpy(subsys->name, subsys_name, ARRAY_SIZE(subsys->name));
129
130         srcu_init_notifier_head(&subsys->subsys_notif_rcvr_list);
131
132         INIT_LIST_HEAD(&subsys->list);
133
134         mutex_lock(&notif_lock);
135         list_add_tail(&subsys->list, &subsystem_list);
136         mutex_unlock(&notif_lock);
137
138         #if defined(SUBSYS_RESTART_DEBUG)
139         subsys_notif_reg_test_notifier(subsys->name);
140         #endif
141
142         mutex_unlock(&notif_add_lock);
143
144 done:
145         return subsys;
146 }
147 EXPORT_SYMBOL(subsys_notif_add_subsys);
148
149 int subsys_notif_queue_notification(void *subsys_handle,
150                                         enum subsys_notif_type notif_type,
151                                         void *data)
152 {
153         struct subsys_notif_info *subsys =
154                 (struct subsys_notif_info *) subsys_handle;
155
156         if (!subsys)
157                 return -EINVAL;
158
159         if (notif_type < 0 || notif_type >= SUBSYS_NOTIF_TYPE_COUNT)
160                 return -EINVAL;
161
162         return srcu_notifier_call_chain(
163                         &subsys->subsys_notif_rcvr_list, notif_type,
164                         data);
165 }
166 EXPORT_SYMBOL(subsys_notif_queue_notification);
167
168 #if defined(SUBSYS_RESTART_DEBUG)
169 static const char *notif_to_string(enum subsys_notif_type notif_type)
170 {
171         switch (notif_type) {
172
173         case    SUBSYS_BEFORE_SHUTDOWN:
174                 return __stringify(SUBSYS_BEFORE_SHUTDOWN);
175
176         case    SUBSYS_AFTER_SHUTDOWN:
177                 return __stringify(SUBSYS_AFTER_SHUTDOWN);
178
179         case    SUBSYS_BEFORE_POWERUP:
180                 return __stringify(SUBSYS_BEFORE_POWERUP);
181
182         case    SUBSYS_AFTER_POWERUP:
183                 return __stringify(SUBSYS_AFTER_POWERUP);
184
185         default:
186                 return "unknown";
187         }
188 }
189
190 static int subsys_notifier_test_call(struct notifier_block *this,
191                                   unsigned long code,
192                                   void *data)
193 {
194         switch (code) {
195
196         default:
197                 pr_warn("%s: Notification %s from subsystem %pK\n",
198                         __func__, notif_to_string(code), data);
199         break;
200
201         }
202
203         return NOTIFY_DONE;
204 }
205
206 static struct notifier_block nb = {
207         .notifier_call = subsys_notifier_test_call,
208 };
209
210 static void subsys_notif_reg_test_notifier(const char *subsys_name)
211 {
212         void *handle = subsys_notif_register_notifier(subsys_name, &nb);
213         pr_warn("%s: Registered test notifier, handle=%pK",
214                         __func__, handle);
215 }
216 #endif
217
218 MODULE_DESCRIPTION("Subsystem Restart Notifier");
219 MODULE_VERSION("1.0");
220 MODULE_LICENSE("GPL v2");