blob: 884586d7402ae5503c76bc5394d0c738de035676 [file] [log] [blame]
Rohit Vaswanid0fb4182012-03-19 18:07:59 -07001/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002 *
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
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -070013#define pr_fmt(fmt) "subsys-restart: %s(): " fmt, __func__
14
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070015#include <linux/kernel.h>
16#include <linux/module.h>
17#include <linux/uaccess.h>
18#include <linux/module.h>
19#include <linux/fs.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070020#include <linux/delay.h>
21#include <linux/list.h>
22#include <linux/io.h>
23#include <linux/kthread.h>
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070024#include <linux/time.h>
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -080025#include <linux/wakelock.h>
26#include <linux/suspend.h>
Stephen Boyd0ebf7212012-04-30 20:42:35 -070027#include <linux/mutex.h>
Stephen Boydc68ee642012-05-01 17:02:45 -070028#include <linux/slab.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070029
30#include <asm/current.h>
31
32#include <mach/peripheral-loader.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033#include <mach/socinfo.h>
34#include <mach/subsystem_notif.h>
35#include <mach/subsystem_restart.h>
36
37#include "smd_private.h"
38
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070039struct subsys_soc_restart_order {
40 const char * const *subsystem_list;
41 int count;
42
43 struct mutex shutdown_lock;
44 struct mutex powerup_lock;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070045 struct subsys_device *subsys_ptrs[];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046};
47
Vikram Mulukutla69177e12012-03-21 20:51:43 -070048struct restart_wq_data {
Stephen Boyd0ebf7212012-04-30 20:42:35 -070049 struct subsys_device *dev;
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -080050 struct wake_lock ssr_wake_lock;
Vikram Mulukutla922a4f12012-04-13 14:36:50 -070051 char wlname[64];
52 int use_restart_order;
Vikram Mulukutla69177e12012-03-21 20:51:43 -070053 struct work_struct work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070054};
55
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070056struct restart_log {
57 struct timeval time;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070058 struct subsys_device *dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070059 struct list_head list;
60};
61
Stephen Boyd0ebf7212012-04-30 20:42:35 -070062struct subsys_device {
63 struct subsys_desc *desc;
64 struct list_head list;
65
66 void *notify;
67
68 struct mutex shutdown_lock;
69 struct mutex powerup_lock;
70
71 void *restart_order;
72};
73
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070074static int enable_ramdumps;
Stephen Boydc68ee642012-05-01 17:02:45 -070075module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR);
76
Vikram Mulukutla69177e12012-03-21 20:51:43 -070077struct workqueue_struct *ssr_wq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070078
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070079static LIST_HEAD(restart_log_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080static LIST_HEAD(subsystem_list);
Stephen Boyd0ebf7212012-04-30 20:42:35 -070081static DEFINE_MUTEX(subsystem_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070082static DEFINE_MUTEX(soc_order_reg_lock);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070083static DEFINE_MUTEX(restart_log_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070084
85/* SOC specific restart orders go here */
86
87#define DEFINE_SINGLE_RESTART_ORDER(name, order) \
88 static struct subsys_soc_restart_order __##name = { \
89 .subsystem_list = order, \
90 .count = ARRAY_SIZE(order), \
91 .subsys_ptrs = {[ARRAY_SIZE(order)] = NULL} \
92 }; \
93 static struct subsys_soc_restart_order *name[] = { \
94 &__##name, \
95 }
96
97/* MSM 8x60 restart ordering info */
98static const char * const _order_8x60_all[] = {
99 "external_modem", "modem", "lpass"
100};
101DEFINE_SINGLE_RESTART_ORDER(orders_8x60_all, _order_8x60_all);
102
103static const char * const _order_8x60_modems[] = {"external_modem", "modem"};
104DEFINE_SINGLE_RESTART_ORDER(orders_8x60_modems, _order_8x60_modems);
105
106/* MSM 8960 restart ordering info */
107static const char * const order_8960[] = {"modem", "lpass"};
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700108/*SGLTE restart ordering info*/
109static const char * const order_8960_sglte[] = {"external_modem",
110 "modem"};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111
112static struct subsys_soc_restart_order restart_orders_8960_one = {
113 .subsystem_list = order_8960,
114 .count = ARRAY_SIZE(order_8960),
115 .subsys_ptrs = {[ARRAY_SIZE(order_8960)] = NULL}
116 };
117
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700118static struct subsys_soc_restart_order restart_orders_8960_fusion_sglte = {
119 .subsystem_list = order_8960_sglte,
120 .count = ARRAY_SIZE(order_8960_sglte),
121 .subsys_ptrs = {[ARRAY_SIZE(order_8960_sglte)] = NULL}
122 };
123
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700124static struct subsys_soc_restart_order *restart_orders_8960[] = {
125 &restart_orders_8960_one,
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700126 };
127
128static struct subsys_soc_restart_order *restart_orders_8960_sglte[] = {
129 &restart_orders_8960_fusion_sglte,
130 };
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700131
132/* These will be assigned to one of the sets above after
133 * runtime SoC identification.
134 */
135static struct subsys_soc_restart_order **restart_orders;
136static int n_restart_orders;
137
Stephen Boydc68ee642012-05-01 17:02:45 -0700138static int restart_level = RESET_SOC;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700139
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140int get_restart_level()
141{
142 return restart_level;
143}
144EXPORT_SYMBOL(get_restart_level);
145
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146static int restart_level_set(const char *val, struct kernel_param *kp)
147{
148 int ret;
149 int old_val = restart_level;
150
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800151 if (cpu_is_msm9615()) {
152 pr_err("Only Phase 1 subsystem restart is supported\n");
153 return -EINVAL;
154 }
155
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700156 ret = param_set_int(val, kp);
157 if (ret)
158 return ret;
159
160 switch (restart_level) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700161 case RESET_SOC:
162 case RESET_SUBSYS_COUPLED:
163 case RESET_SUBSYS_INDEPENDENT:
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700164 pr_info("Phase %d behavior activated.\n", restart_level);
Stephen Boydc68ee642012-05-01 17:02:45 -0700165 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700166 default:
167 restart_level = old_val;
168 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700169 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700170 return 0;
171}
172
173module_param_call(restart_level, restart_level_set, param_get_int,
174 &restart_level, 0644);
175
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700176static struct subsys_soc_restart_order *
177update_restart_order(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700178{
179 int i, j;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700180 struct subsys_soc_restart_order *order;
181 const char *name = dev->desc->name;
182 int len = SUBSYS_NAME_MAX_LENGTH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700183
184 mutex_lock(&soc_order_reg_lock);
185 for (j = 0; j < n_restart_orders; j++) {
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700186 order = restart_orders[j];
187 for (i = 0; i < order->count; i++) {
188 if (!strncmp(order->subsystem_list[i], name, len)) {
189 order->subsys_ptrs[i] = dev;
190 goto found;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700191 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700192 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700193 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700194 order = NULL;
195found:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700196 mutex_unlock(&soc_order_reg_lock);
197
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700198 return order;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700199}
200
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700201static void _send_notification_to_order(struct subsys_device **restart_list,
202 int count, enum subsys_notif_type type)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700203{
204 int i;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700205 struct subsys_device **dev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700206
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700207 for (i = 0, dev = restart_list; i < count; i++, dev++)
208 if (*dev)
209 subsys_notif_queue_notification((*dev)->notify, type);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700210}
211
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700212static int max_restarts;
213module_param(max_restarts, int, 0644);
214
215static long max_history_time = 3600;
216module_param(max_history_time, long, 0644);
217
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700218static void do_epoch_check(struct subsys_device *dev)
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700219{
220 int n = 0;
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600221 struct timeval *time_first = NULL, *curr_time;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700222 struct restart_log *r_log, *temp;
223 static int max_restarts_check;
224 static long max_history_time_check;
225
226 mutex_lock(&restart_log_mutex);
227
228 max_restarts_check = max_restarts;
229 max_history_time_check = max_history_time;
230
231 /* Check if epoch checking is enabled */
232 if (!max_restarts_check)
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700233 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700234
235 r_log = kmalloc(sizeof(struct restart_log), GFP_KERNEL);
Matt Wagantalleecca342011-11-10 20:12:05 -0800236 if (!r_log)
237 goto out;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700238 r_log->dev = dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700239 do_gettimeofday(&r_log->time);
240 curr_time = &r_log->time;
241 INIT_LIST_HEAD(&r_log->list);
242
243 list_add_tail(&r_log->list, &restart_log_list);
244
245 list_for_each_entry_safe(r_log, temp, &restart_log_list, list) {
246
247 if ((curr_time->tv_sec - r_log->time.tv_sec) >
248 max_history_time_check) {
249
250 pr_debug("Deleted node with restart_time = %ld\n",
251 r_log->time.tv_sec);
252 list_del(&r_log->list);
253 kfree(r_log);
254 continue;
255 }
256 if (!n) {
257 time_first = &r_log->time;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700258 pr_debug("Time_first: %ld\n", time_first->tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700259 }
260 n++;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700261 pr_debug("Restart_time: %ld\n", r_log->time.tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700262 }
263
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600264 if (time_first && n >= max_restarts_check) {
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700265 if ((curr_time->tv_sec - time_first->tv_sec) <
266 max_history_time_check)
267 panic("Subsystems have crashed %d times in less than "
268 "%ld seconds!", max_restarts_check,
269 max_history_time_check);
270 }
271
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700272out:
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700273 mutex_unlock(&restart_log_mutex);
274}
275
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700276static void subsystem_restart_wq_func(struct work_struct *work)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700277{
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700278 struct restart_wq_data *r_work = container_of(work,
279 struct restart_wq_data, work);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700280 struct subsys_device **restart_list;
281 struct subsys_device *dev = r_work->dev;
282 struct subsys_desc *desc = dev->desc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700283 struct subsys_soc_restart_order *soc_restart_order = NULL;
284
285 struct mutex *powerup_lock;
286 struct mutex *shutdown_lock;
287
288 int i;
289 int restart_list_count = 0;
290
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700291 if (r_work->use_restart_order)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700292 soc_restart_order = dev->restart_order;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293
Stephen Boydc68ee642012-05-01 17:02:45 -0700294 /*
295 * It's OK to not take the registration lock at this point.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700296 * This is because the subsystem list inside the relevant
297 * restart order is not being traversed.
298 */
299 if (!soc_restart_order) {
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700300 restart_list = &dev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301 restart_list_count = 1;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700302 powerup_lock = &dev->powerup_lock;
303 shutdown_lock = &dev->shutdown_lock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700304 } else {
305 restart_list = soc_restart_order->subsys_ptrs;
306 restart_list_count = soc_restart_order->count;
307 powerup_lock = &soc_restart_order->powerup_lock;
308 shutdown_lock = &soc_restart_order->shutdown_lock;
309 }
310
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700311 pr_debug("[%p]: Attempting to get shutdown lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700312
Stephen Boydc68ee642012-05-01 17:02:45 -0700313 /*
314 * Try to acquire shutdown_lock. If this fails, these subsystems are
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700315 * already being restarted - return.
316 */
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800317 if (!mutex_trylock(shutdown_lock))
318 goto out;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700319
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700320 pr_debug("[%p]: Attempting to get powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700321
Stephen Boydc68ee642012-05-01 17:02:45 -0700322 /*
323 * Now that we've acquired the shutdown lock, either we're the first to
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700324 * restart these subsystems or some other thread is doing the powerup
325 * sequence for these subsystems. In the latter case, panic and bail
326 * out, since a subsystem died in its powerup sequence.
327 */
328 if (!mutex_trylock(powerup_lock))
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700329 panic("%s[%p]: Subsystem died during powerup!",
330 __func__, current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700331
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700332 do_epoch_check(dev);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700333
Stephen Boydc68ee642012-05-01 17:02:45 -0700334 /*
335 * It's necessary to take the registration lock because the subsystem
336 * list in the SoC restart order will be traversed and it shouldn't be
337 * changed until _this_ restart sequence completes.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700338 */
339 mutex_lock(&soc_order_reg_lock);
340
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700341 pr_debug("[%p]: Starting restart sequence for %s\n", current,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700342 desc->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700343
344 _send_notification_to_order(restart_list,
345 restart_list_count,
346 SUBSYS_BEFORE_SHUTDOWN);
347
348 for (i = 0; i < restart_list_count; i++) {
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700349 const struct subsys_device *dev = restart_list[i];
350 const char *name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700351
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700352 if (!dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700353 continue;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700354 name = dev->desc->name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700355
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700356 pr_info("[%p]: Shutting down %s\n", current, name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700357
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700358 if (dev->desc->shutdown(dev->desc) < 0)
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700359 panic("subsys-restart: %s[%p]: Failed to shutdown %s!",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700360 __func__, current, name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700361 }
362
363 _send_notification_to_order(restart_list, restart_list_count,
364 SUBSYS_AFTER_SHUTDOWN);
365
Stephen Boydc68ee642012-05-01 17:02:45 -0700366 /*
367 * Now that we've finished shutting down these subsystems, release the
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700368 * shutdown lock. If a subsystem restart request comes in for a
369 * subsystem in _this_ restart order after the unlock below, and
370 * before the powerup lock is released, panic and bail out.
371 */
372 mutex_unlock(shutdown_lock);
373
374 /* Collect ram dumps for all subsystems in order here */
375 for (i = 0; i < restart_list_count; i++) {
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700376 const struct subsys_device *dev = restart_list[i];
377 const char *name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700378
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700379 if (!dev)
380 continue;
381 name = dev->desc->name;
382
383 if (dev->desc->ramdump)
384 if (dev->desc->ramdump(enable_ramdumps, dev->desc) < 0)
385 pr_warn("%s[%p]: Ramdump failed.\n", name,
386 current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700387 }
388
389 _send_notification_to_order(restart_list,
390 restart_list_count,
391 SUBSYS_BEFORE_POWERUP);
392
393 for (i = restart_list_count - 1; i >= 0; i--) {
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700394 const struct subsys_device *dev = restart_list[i];
395 const char *name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700396
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700397 if (!dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700398 continue;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700399 name = dev->desc->name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700400
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700401 pr_info("[%p]: Powering up %s\n", current, name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700402
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700403 if (dev->desc->powerup(dev->desc) < 0)
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700404 panic("%s[%p]: Failed to powerup %s!", __func__,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700405 current, name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406 }
407
408 _send_notification_to_order(restart_list,
409 restart_list_count,
410 SUBSYS_AFTER_POWERUP);
411
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700412 pr_info("[%p]: Restart sequence for %s completed.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700413 current, desc->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700414
415 mutex_unlock(powerup_lock);
416
417 mutex_unlock(&soc_order_reg_lock);
418
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700419 pr_debug("[%p]: Released powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700420
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800421out:
422 wake_unlock(&r_work->ssr_wake_lock);
423 wake_lock_destroy(&r_work->ssr_wake_lock);
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700424 kfree(r_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700425}
426
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700427static void __subsystem_restart_dev(struct subsys_device *dev)
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700428{
429 struct restart_wq_data *data = NULL;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700430 struct subsys_desc *desc = dev->desc;
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700431
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700432 pr_debug("Restarting %s [level=%d]!\n", desc->name, restart_level);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700433
434 data = kzalloc(sizeof(struct restart_wq_data), GFP_ATOMIC);
435 if (!data)
436 panic("%s: Unable to allocate memory to restart %s.",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700437 __func__, desc->name);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700438
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700439 data->dev = dev;
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700440
441 if (restart_level != RESET_SUBSYS_INDEPENDENT)
442 data->use_restart_order = 1;
443
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700444 snprintf(data->wlname, sizeof(data->wlname), "ssr(%s)", desc->name);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700445 wake_lock_init(&data->ssr_wake_lock, WAKE_LOCK_SUSPEND, data->wlname);
446 wake_lock(&data->ssr_wake_lock);
447
448 INIT_WORK(&data->work, subsystem_restart_wq_func);
Stephen Boydc68ee642012-05-01 17:02:45 -0700449 queue_work(ssr_wq, &data->work);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700450}
451
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700452int subsystem_restart_dev(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700453{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700454 const char *name = dev->desc->name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700455
Vikram Mulukutla3b24c8c2011-12-02 15:12:53 -0800456 pr_info("Restart sequence requested for %s, restart_level = %d.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700457 name, restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700458
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700459 switch (restart_level) {
460
461 case RESET_SUBSYS_COUPLED:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700462 case RESET_SUBSYS_INDEPENDENT:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700463 __subsystem_restart_dev(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700464 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700465 case RESET_SOC:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700466 panic("subsys-restart: Resetting the SoC - %s crashed.", name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700467 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700468 default:
469 panic("subsys-restart: Unknown restart level!\n");
Stephen Boydc68ee642012-05-01 17:02:45 -0700470 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700471 }
472
473 return 0;
474}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700475EXPORT_SYMBOL(subsystem_restart_dev);
476
477int subsystem_restart(const char *name)
478{
479 struct subsys_device *dev;
480
481 mutex_lock(&subsystem_list_lock);
482 list_for_each_entry(dev, &subsystem_list, list)
483 if (!strncmp(dev->desc->name, name, SUBSYS_NAME_MAX_LENGTH))
484 goto found;
485 dev = NULL;
486found:
487 mutex_unlock(&subsystem_list_lock);
488 if (dev)
489 return subsystem_restart_dev(dev);
490 return -ENODEV;
491}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700492EXPORT_SYMBOL(subsystem_restart);
493
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700494struct subsys_device *subsys_register(struct subsys_desc *desc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700495{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700496 struct subsys_device *dev;
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700497
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700498 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
499 if (!dev)
500 return ERR_PTR(-ENOMEM);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700501
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700502 dev->desc = desc;
503 dev->notify = subsys_notif_add_subsys(desc->name);
504 dev->restart_order = update_restart_order(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700505
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700506 mutex_init(&dev->shutdown_lock);
507 mutex_init(&dev->powerup_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700508
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700509 mutex_lock(&subsystem_list_lock);
510 list_add(&dev->list, &subsystem_list);
511 mutex_unlock(&subsystem_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700512
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700513 return dev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700514}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700515EXPORT_SYMBOL(subsys_register);
516
517void subsys_unregister(struct subsys_device *dev)
518{
519 if (IS_ERR_OR_NULL(dev))
520 return;
521 mutex_lock(&subsystem_list_lock);
522 list_del(&dev->list);
523 mutex_unlock(&subsystem_list_lock);
524 kfree(dev);
525}
526EXPORT_SYMBOL(subsys_unregister);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700527
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700528static int ssr_panic_handler(struct notifier_block *this,
529 unsigned long event, void *ptr)
530{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700531 struct subsys_device *dev;
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700532
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700533 list_for_each_entry(dev, &subsystem_list, list)
534 if (dev->desc->crash_shutdown)
535 dev->desc->crash_shutdown(dev->desc);
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700536 return NOTIFY_DONE;
537}
538
539static struct notifier_block panic_nb = {
540 .notifier_call = ssr_panic_handler,
541};
542
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700543static int __init ssr_init_soc_restart_orders(void)
544{
545 int i;
546
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700547 atomic_notifier_chain_register(&panic_notifier_list,
548 &panic_nb);
549
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700550 if (cpu_is_msm8x60()) {
551 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
552 mutex_init(&orders_8x60_all[i]->powerup_lock);
553 mutex_init(&orders_8x60_all[i]->shutdown_lock);
554 }
555
556 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
557 mutex_init(&orders_8x60_modems[i]->powerup_lock);
558 mutex_init(&orders_8x60_modems[i]->shutdown_lock);
559 }
560
561 restart_orders = orders_8x60_all;
562 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
563 }
564
Stepan Moskovchenko0df9bb22012-07-06 18:19:15 -0700565 if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
Stepan Moskovchenko9c749262012-07-09 19:30:44 -0700566 cpu_is_msm9615() || cpu_is_apq8064() || cpu_is_msm8627() ||
567 cpu_is_msm8960ab()) {
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700568 if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) {
569 restart_orders = restart_orders_8960_sglte;
570 n_restart_orders =
571 ARRAY_SIZE(restart_orders_8960_sglte);
572 } else {
573 restart_orders = restart_orders_8960;
574 n_restart_orders = ARRAY_SIZE(restart_orders_8960);
575 }
576 for (i = 0; i < n_restart_orders; i++) {
577 mutex_init(&restart_orders[i]->powerup_lock);
578 mutex_init(&restart_orders[i]->shutdown_lock);
579 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700580 }
581
582 if (restart_orders == NULL || n_restart_orders < 1) {
583 WARN_ON(1);
584 return -EINVAL;
585 }
586
587 return 0;
588}
589
590static int __init subsys_restart_init(void)
591{
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700592 ssr_wq = alloc_workqueue("ssr_wq", 0, 0);
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700593 if (!ssr_wq)
594 panic("Couldn't allocate workqueue for subsystem restart.\n");
595
Stephen Boydc68ee642012-05-01 17:02:45 -0700596 return ssr_init_soc_restart_orders();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700597}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700598arch_initcall(subsys_restart_init);
599
600MODULE_DESCRIPTION("Subsystem Restart Driver");
601MODULE_LICENSE("GPL v2");