1 /* Copyright (c) 2011, 2013, 2016 The Linux Foundation. All rights reserved.
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.
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.
13 * Subsystem Notifier -- Provides notifications
16 * Use subsys_notif_register_notifier to register for notifications
17 * and subsys_notif_queue_notification to send notifications.
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>
32 struct subsys_notif_info {
34 struct srcu_notifier_head subsys_notif_rcvr_list;
35 struct list_head list;
38 static LIST_HEAD(subsystem_list);
39 static DEFINE_MUTEX(notif_lock);
40 static DEFINE_MUTEX(notif_add_lock);
42 #if defined(SUBSYS_RESTART_DEBUG)
43 static void subsys_notif_reg_test_notifier(const char *);
46 static struct subsys_notif_info *_notif_find_subsys(const char *subsys_name)
48 struct subsys_notif_info *subsys;
50 mutex_lock(¬if_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(¬if_lock);
57 mutex_unlock(¬if_lock);
62 void *subsys_notif_register_notifier(
63 const char *subsys_name, struct notifier_block *nb)
66 struct subsys_notif_info *subsys = _notif_find_subsys(subsys_name);
70 /* Possible first time reference to this subsystem. Add it. */
71 subsys = (struct subsys_notif_info *)
72 subsys_notif_add_subsys(subsys_name);
75 return ERR_PTR(-EINVAL);
78 ret = srcu_notifier_chain_register(
79 &subsys->subsys_notif_rcvr_list, nb);
86 EXPORT_SYMBOL(subsys_notif_register_notifier);
88 int subsys_notif_unregister_notifier(void *subsys_handle,
89 struct notifier_block *nb)
92 struct subsys_notif_info *subsys =
93 (struct subsys_notif_info *)subsys_handle;
98 ret = srcu_notifier_chain_unregister(
99 &subsys->subsys_notif_rcvr_list, nb);
103 EXPORT_SYMBOL(subsys_notif_unregister_notifier);
105 void *subsys_notif_add_subsys(const char *subsys_name)
107 struct subsys_notif_info *subsys = NULL;
112 mutex_lock(¬if_add_lock);
114 subsys = _notif_find_subsys(subsys_name);
117 mutex_unlock(¬if_add_lock);
121 subsys = kmalloc(sizeof(struct subsys_notif_info), GFP_KERNEL);
124 mutex_unlock(¬if_add_lock);
125 return ERR_PTR(-EINVAL);
128 strlcpy(subsys->name, subsys_name, ARRAY_SIZE(subsys->name));
130 srcu_init_notifier_head(&subsys->subsys_notif_rcvr_list);
132 INIT_LIST_HEAD(&subsys->list);
134 mutex_lock(¬if_lock);
135 list_add_tail(&subsys->list, &subsystem_list);
136 mutex_unlock(¬if_lock);
138 #if defined(SUBSYS_RESTART_DEBUG)
139 subsys_notif_reg_test_notifier(subsys->name);
142 mutex_unlock(¬if_add_lock);
147 EXPORT_SYMBOL(subsys_notif_add_subsys);
149 int subsys_notif_queue_notification(void *subsys_handle,
150 enum subsys_notif_type notif_type,
153 struct subsys_notif_info *subsys =
154 (struct subsys_notif_info *) subsys_handle;
159 if (notif_type < 0 || notif_type >= SUBSYS_NOTIF_TYPE_COUNT)
162 return srcu_notifier_call_chain(
163 &subsys->subsys_notif_rcvr_list, notif_type,
166 EXPORT_SYMBOL(subsys_notif_queue_notification);
168 #if defined(SUBSYS_RESTART_DEBUG)
169 static const char *notif_to_string(enum subsys_notif_type notif_type)
171 switch (notif_type) {
173 case SUBSYS_BEFORE_SHUTDOWN:
174 return __stringify(SUBSYS_BEFORE_SHUTDOWN);
176 case SUBSYS_AFTER_SHUTDOWN:
177 return __stringify(SUBSYS_AFTER_SHUTDOWN);
179 case SUBSYS_BEFORE_POWERUP:
180 return __stringify(SUBSYS_BEFORE_POWERUP);
182 case SUBSYS_AFTER_POWERUP:
183 return __stringify(SUBSYS_AFTER_POWERUP);
190 static int subsys_notifier_test_call(struct notifier_block *this,
197 pr_warn("%s: Notification %s from subsystem %pK\n",
198 __func__, notif_to_string(code), data);
206 static struct notifier_block nb = {
207 .notifier_call = subsys_notifier_test_call,
210 static void subsys_notif_reg_test_notifier(const char *subsys_name)
212 void *handle = subsys_notif_register_notifier(subsys_name, &nb);
213 pr_warn("%s: Registered test notifier, handle=%pK",
218 MODULE_DESCRIPTION("Subsystem Restart Notifier");
219 MODULE_VERSION("1.0");
220 MODULE_LICENSE("GPL v2");