blob: ffb8e21fb0c5d1e53c46a8f75e252d27c947d5ca [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>
20#include <linux/proc_fs.h>
21#include <linux/delay.h>
22#include <linux/list.h>
23#include <linux/io.h>
24#include <linux/kthread.h>
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070025#include <linux/time.h>
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -080026#include <linux/wakelock.h>
27#include <linux/suspend.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070028
29#include <asm/current.h>
30
31#include <mach/peripheral-loader.h>
32#include <mach/scm.h>
33#include <mach/socinfo.h>
34#include <mach/subsystem_notif.h>
35#include <mach/subsystem_restart.h>
36
Jaeseong GIMb8499412012-06-26 10:36:57 -070037#if defined(CONFIG_LGE_CRASH_HANDLER)
38#include <mach/restart.h>
39#include <mach/board_lge.h>
40#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070041#include "smd_private.h"
42
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070043struct subsys_soc_restart_order {
44 const char * const *subsystem_list;
45 int count;
46
47 struct mutex shutdown_lock;
48 struct mutex powerup_lock;
49 struct subsys_data *subsys_ptrs[];
50};
51
Vikram Mulukutla69177e12012-03-21 20:51:43 -070052struct restart_wq_data {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070053 struct subsys_data *subsys;
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -080054 struct wake_lock ssr_wake_lock;
Vikram Mulukutla922a4f12012-04-13 14:36:50 -070055 char wlname[64];
56 int use_restart_order;
Vikram Mulukutla69177e12012-03-21 20:51:43 -070057 struct work_struct work;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070058};
59
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070060struct restart_log {
61 struct timeval time;
62 struct subsys_data *subsys;
63 struct list_head list;
64};
65
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070066static int restart_level;
jihyun.lee20b1ec02012-06-29 09:23:58 -070067static int enable_ramdumps = 1;
Vikram Mulukutla69177e12012-03-21 20:51:43 -070068struct workqueue_struct *ssr_wq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070070static LIST_HEAD(restart_log_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070071static LIST_HEAD(subsystem_list);
Vikram Mulukutla705e6892012-06-08 17:57:31 -070072static DEFINE_SPINLOCK(subsystem_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070073static DEFINE_MUTEX(soc_order_reg_lock);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070074static DEFINE_MUTEX(restart_log_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070075
76/* SOC specific restart orders go here */
77
78#define DEFINE_SINGLE_RESTART_ORDER(name, order) \
79 static struct subsys_soc_restart_order __##name = { \
80 .subsystem_list = order, \
81 .count = ARRAY_SIZE(order), \
82 .subsys_ptrs = {[ARRAY_SIZE(order)] = NULL} \
83 }; \
84 static struct subsys_soc_restart_order *name[] = { \
85 &__##name, \
86 }
87
88/* MSM 8x60 restart ordering info */
89static const char * const _order_8x60_all[] = {
90 "external_modem", "modem", "lpass"
91};
92DEFINE_SINGLE_RESTART_ORDER(orders_8x60_all, _order_8x60_all);
93
94static const char * const _order_8x60_modems[] = {"external_modem", "modem"};
95DEFINE_SINGLE_RESTART_ORDER(orders_8x60_modems, _order_8x60_modems);
96
97/* MSM 8960 restart ordering info */
98static const char * const order_8960[] = {"modem", "lpass"};
99
100static struct subsys_soc_restart_order restart_orders_8960_one = {
101 .subsystem_list = order_8960,
102 .count = ARRAY_SIZE(order_8960),
103 .subsys_ptrs = {[ARRAY_SIZE(order_8960)] = NULL}
104 };
105
106static struct subsys_soc_restart_order *restart_orders_8960[] = {
107 &restart_orders_8960_one,
108};
109
110/* These will be assigned to one of the sets above after
111 * runtime SoC identification.
112 */
113static struct subsys_soc_restart_order **restart_orders;
114static int n_restart_orders;
115
116module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR);
117
118static struct subsys_soc_restart_order *_update_restart_order(
119 struct subsys_data *subsys);
120
121int get_restart_level()
122{
123 return restart_level;
124}
125EXPORT_SYMBOL(get_restart_level);
126
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127static int restart_level_set(const char *val, struct kernel_param *kp)
128{
129 int ret;
130 int old_val = restart_level;
131
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800132 if (cpu_is_msm9615()) {
133 pr_err("Only Phase 1 subsystem restart is supported\n");
134 return -EINVAL;
135 }
136
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700137 ret = param_set_int(val, kp);
138 if (ret)
139 return ret;
140
141 switch (restart_level) {
142
143 case RESET_SOC:
144 case RESET_SUBSYS_COUPLED:
145 case RESET_SUBSYS_INDEPENDENT:
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700146 pr_info("Phase %d behavior activated.\n", restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700147 break;
148
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700149 default:
150 restart_level = old_val;
151 return -EINVAL;
152 break;
153
154 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700155 return 0;
156}
157
158module_param_call(restart_level, restart_level_set, param_get_int,
159 &restart_level, 0644);
160
161static struct subsys_data *_find_subsystem(const char *subsys_name)
162{
163 struct subsys_data *subsys;
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700164 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700165
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700166 spin_lock_irqsave(&subsystem_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700167 list_for_each_entry(subsys, &subsystem_list, list)
168 if (!strncmp(subsys->name, subsys_name,
169 SUBSYS_NAME_MAX_LENGTH)) {
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700170 spin_unlock_irqrestore(&subsystem_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171 return subsys;
172 }
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700173 spin_unlock_irqrestore(&subsystem_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700174
175 return NULL;
176}
177
178static struct subsys_soc_restart_order *_update_restart_order(
179 struct subsys_data *subsys)
180{
181 int i, j;
182
183 if (!subsys)
184 return NULL;
185
186 if (!subsys->name)
187 return NULL;
188
189 mutex_lock(&soc_order_reg_lock);
190 for (j = 0; j < n_restart_orders; j++) {
191 for (i = 0; i < restart_orders[j]->count; i++)
192 if (!strncmp(restart_orders[j]->subsystem_list[i],
193 subsys->name, SUBSYS_NAME_MAX_LENGTH)) {
194
195 restart_orders[j]->subsys_ptrs[i] =
196 subsys;
197 mutex_unlock(&soc_order_reg_lock);
198 return restart_orders[j];
199 }
200 }
201
202 mutex_unlock(&soc_order_reg_lock);
203
204 return NULL;
205}
206
207static void _send_notification_to_order(struct subsys_data
208 **restart_list, int count,
209 enum subsys_notif_type notif_type)
210{
211 int i;
212
213 for (i = 0; i < count; i++)
214 if (restart_list[i])
215 subsys_notif_queue_notification(
216 restart_list[i]->notif_handle, notif_type);
217}
218
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700219static int max_restarts;
220module_param(max_restarts, int, 0644);
221
222static long max_history_time = 3600;
223module_param(max_history_time, long, 0644);
224
225static void do_epoch_check(struct subsys_data *subsys)
226{
227 int n = 0;
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600228 struct timeval *time_first = NULL, *curr_time;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700229 struct restart_log *r_log, *temp;
230 static int max_restarts_check;
231 static long max_history_time_check;
Jaeseong GIMb8499412012-06-26 10:36:57 -0700232#if defined(CONFIG_LGE_CRASH_HANDLER)
233 int ssr_magic_number = get_ssr_magic_number();
234#endif
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700235
236 mutex_lock(&restart_log_mutex);
237
238 max_restarts_check = max_restarts;
239 max_history_time_check = max_history_time;
240
241 /* Check if epoch checking is enabled */
242 if (!max_restarts_check)
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700243 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700244
245 r_log = kmalloc(sizeof(struct restart_log), GFP_KERNEL);
Matt Wagantalleecca342011-11-10 20:12:05 -0800246 if (!r_log)
247 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700248 r_log->subsys = subsys;
249 do_gettimeofday(&r_log->time);
250 curr_time = &r_log->time;
251 INIT_LIST_HEAD(&r_log->list);
252
253 list_add_tail(&r_log->list, &restart_log_list);
254
255 list_for_each_entry_safe(r_log, temp, &restart_log_list, list) {
256
257 if ((curr_time->tv_sec - r_log->time.tv_sec) >
258 max_history_time_check) {
259
260 pr_debug("Deleted node with restart_time = %ld\n",
261 r_log->time.tv_sec);
262 list_del(&r_log->list);
263 kfree(r_log);
264 continue;
265 }
266 if (!n) {
267 time_first = &r_log->time;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700268 pr_debug("Time_first: %ld\n", time_first->tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700269 }
270 n++;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700271 pr_debug("Restart_time: %ld\n", r_log->time.tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700272 }
273
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600274 if (time_first && n >= max_restarts_check) {
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700275 if ((curr_time->tv_sec - time_first->tv_sec) <
Jaeseong GIMb8499412012-06-26 10:36:57 -0700276 max_history_time_check) {
277#if defined(CONFIG_LGE_CRASH_HANDLER)
278 msm_set_restart_mode(ssr_magic_number | SUB_UNAB_THD);
279#endif
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700280 panic("Subsystems have crashed %d times in less than "
281 "%ld seconds!", max_restarts_check,
282 max_history_time_check);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700283 }
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700284 }
285
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700286out:
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700287 mutex_unlock(&restart_log_mutex);
288}
289
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700290static void subsystem_restart_wq_func(struct work_struct *work)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291{
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700292 struct restart_wq_data *r_work = container_of(work,
293 struct restart_wq_data, work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700294 struct subsys_data **restart_list;
295 struct subsys_data *subsys = r_work->subsys;
296 struct subsys_soc_restart_order *soc_restart_order = NULL;
297
298 struct mutex *powerup_lock;
299 struct mutex *shutdown_lock;
300
301 int i;
302 int restart_list_count = 0;
303
Jaeseong GIMb8499412012-06-26 10:36:57 -0700304#if defined(CONFIG_LGE_CRASH_HANDLER)
305 int ssr_magic_number = get_ssr_magic_number();
306#endif
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700307 if (r_work->use_restart_order)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700308 soc_restart_order = subsys->restart_order;
309
310 /* It's OK to not take the registration lock at this point.
311 * This is because the subsystem list inside the relevant
312 * restart order is not being traversed.
313 */
314 if (!soc_restart_order) {
315 restart_list = subsys->single_restart_list;
316 restart_list_count = 1;
317 powerup_lock = &subsys->powerup_lock;
318 shutdown_lock = &subsys->shutdown_lock;
319 } else {
320 restart_list = soc_restart_order->subsys_ptrs;
321 restart_list_count = soc_restart_order->count;
322 powerup_lock = &soc_restart_order->powerup_lock;
323 shutdown_lock = &soc_restart_order->shutdown_lock;
324 }
325
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700326 pr_debug("[%p]: Attempting to get shutdown lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700327
328 /* Try to acquire shutdown_lock. If this fails, these subsystems are
329 * already being restarted - return.
330 */
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800331 if (!mutex_trylock(shutdown_lock))
332 goto out;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700333
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700334 pr_debug("[%p]: Attempting to get powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700335
336 /* Now that we've acquired the shutdown lock, either we're the first to
337 * restart these subsystems or some other thread is doing the powerup
338 * sequence for these subsystems. In the latter case, panic and bail
339 * out, since a subsystem died in its powerup sequence.
340 */
Jaeseong GIMb8499412012-06-26 10:36:57 -0700341 if (!mutex_trylock(powerup_lock)) {
342#if defined(CONFIG_LGE_CRASH_HANDLER)
343 msm_set_restart_mode(ssr_magic_number | SUB_THD_F_PWR);
344#endif
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700345 panic("%s[%p]: Subsystem died during powerup!",
346 __func__, current);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700347 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700348
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700349 do_epoch_check(subsys);
350
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700351 /* Now it is necessary to take the registration lock. This is because
352 * the subsystem list in the SoC restart order will be traversed
353 * and it shouldn't be changed until _this_ restart sequence completes.
354 */
355 mutex_lock(&soc_order_reg_lock);
356
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700357 pr_debug("[%p]: Starting restart sequence for %s\n", current,
358 r_work->subsys->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700359
360 _send_notification_to_order(restart_list,
361 restart_list_count,
362 SUBSYS_BEFORE_SHUTDOWN);
363
364 for (i = 0; i < restart_list_count; i++) {
365
366 if (!restart_list[i])
367 continue;
368
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700369 pr_info("[%p]: Shutting down %s\n", current,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700370 restart_list[i]->name);
371
Jaeseong GIMb8499412012-06-26 10:36:57 -0700372 if (restart_list[i]->shutdown(subsys) < 0) {
373#if defined(CONFIG_LGE_CRASH_HANDLER)
374 msm_set_restart_mode(ssr_magic_number | SUB_THD_F_SD);
375#endif
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700376 panic("subsys-restart: %s[%p]: Failed to shutdown %s!",
377 __func__, current, restart_list[i]->name);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700378 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700379 }
380
381 _send_notification_to_order(restart_list, restart_list_count,
382 SUBSYS_AFTER_SHUTDOWN);
383
384 /* Now that we've finished shutting down these subsystems, release the
385 * shutdown lock. If a subsystem restart request comes in for a
386 * subsystem in _this_ restart order after the unlock below, and
387 * before the powerup lock is released, panic and bail out.
388 */
389 mutex_unlock(shutdown_lock);
390
391 /* Collect ram dumps for all subsystems in order here */
392 for (i = 0; i < restart_list_count; i++) {
393 if (!restart_list[i])
394 continue;
395
396 if (restart_list[i]->ramdump)
397 if (restart_list[i]->ramdump(enable_ramdumps,
398 subsys) < 0)
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700399 pr_warn("%s[%p]: Ramdump failed.\n",
400 restart_list[i]->name, current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700401 }
402
403 _send_notification_to_order(restart_list,
404 restart_list_count,
405 SUBSYS_BEFORE_POWERUP);
406
407 for (i = restart_list_count - 1; i >= 0; i--) {
408
409 if (!restart_list[i])
410 continue;
411
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700412 pr_info("[%p]: Powering up %s\n", current,
413 restart_list[i]->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700414
Jaeseong GIMb8499412012-06-26 10:36:57 -0700415 if (restart_list[i]->powerup(subsys) < 0) {
416#if defined(CONFIG_LGE_CRASH_HANDLER)
417 msm_set_restart_mode(ssr_magic_number | SUB_THD_F_PWR);
418#endif
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700419 panic("%s[%p]: Failed to powerup %s!", __func__,
420 current, restart_list[i]->name);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700421 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700422 }
423
424 _send_notification_to_order(restart_list,
425 restart_list_count,
426 SUBSYS_AFTER_POWERUP);
427
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700428 pr_info("[%p]: Restart sequence for %s completed.\n",
429 current, r_work->subsys->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700430
431 mutex_unlock(powerup_lock);
432
433 mutex_unlock(&soc_order_reg_lock);
434
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700435 pr_debug("[%p]: Released powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700436
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800437out:
438 wake_unlock(&r_work->ssr_wake_lock);
439 wake_lock_destroy(&r_work->ssr_wake_lock);
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700440 kfree(r_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700441}
442
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700443static void __subsystem_restart(struct subsys_data *subsys)
444{
445 struct restart_wq_data *data = NULL;
446 int rc;
Jaeseong GIMb8499412012-06-26 10:36:57 -0700447#if defined(CONFIG_LGE_CRASH_HANDLER)
448 int ssr_magic_number = get_ssr_magic_number();
449#endif
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700450
451 pr_debug("Restarting %s [level=%d]!\n", subsys->name,
452 restart_level);
453
454 data = kzalloc(sizeof(struct restart_wq_data), GFP_ATOMIC);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700455 if (!data) {
456#if defined(CONFIG_LGE_CRASH_HANDLER)
457 msm_set_restart_mode(ssr_magic_number | SUB_UNAB_THD);
458#endif
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700459 panic("%s: Unable to allocate memory to restart %s.",
460 __func__, subsys->name);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700461 }
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700462
463 data->subsys = subsys;
464
465 if (restart_level != RESET_SUBSYS_INDEPENDENT)
466 data->use_restart_order = 1;
467
468 snprintf(data->wlname, sizeof(data->wlname), "ssr(%s)", subsys->name);
469 wake_lock_init(&data->ssr_wake_lock, WAKE_LOCK_SUSPEND, data->wlname);
470 wake_lock(&data->ssr_wake_lock);
471
472 INIT_WORK(&data->work, subsystem_restart_wq_func);
473 rc = queue_work(ssr_wq, &data->work);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700474 if (rc < 0) {
475#if defined(CONFIG_LGE_CRASH_HANDLER)
476 msm_set_restart_mode(ssr_magic_number | SUB_UNAB_THD);
477#endif
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700478 panic("%s: Unable to schedule work to restart %s (%d).",
479 __func__, subsys->name, rc);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700480 }
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700481}
482
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700483int subsystem_restart(const char *subsys_name)
484{
485 struct subsys_data *subsys;
Jaeseong GIMb8499412012-06-26 10:36:57 -0700486#if defined(CONFIG_LGE_CRASH_HANDLER)
487 u32 ssr_magic_number;
488#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700489
490 if (!subsys_name) {
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700491 pr_err("Invalid subsystem name.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700492 return -EINVAL;
493 }
494
Vikram Mulukutla3b24c8c2011-12-02 15:12:53 -0800495 pr_info("Restart sequence requested for %s, restart_level = %d.\n",
496 subsys_name, restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700497
498 /* List of subsystems is protected by a lock. New subsystems can
499 * still come in.
500 */
501 subsys = _find_subsystem(subsys_name);
502
503 if (!subsys) {
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700504 pr_warn("Unregistered subsystem %s!\n", subsys_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700505 return -EINVAL;
506 }
507
Jaeseong GIMb8499412012-06-26 10:36:57 -0700508#if defined(CONFIG_LGE_CRASH_HANDLER)
509 set_ssr_magic_number(subsys_name);
510 ssr_magic_number = get_ssr_magic_number();
511#endif
512
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700513 switch (restart_level) {
514
515 case RESET_SUBSYS_COUPLED:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700516 case RESET_SUBSYS_INDEPENDENT:
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700517 __subsystem_restart(subsys);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700518 break;
519
520 case RESET_SOC:
Jaeseong GIMb8499412012-06-26 10:36:57 -0700521#if defined(CONFIG_LGE_CRASH_HANDLER)
522 msm_set_restart_mode(ssr_magic_number | SUB_RESET_SOC);
523#endif
Vikram Mulukutla3b24c8c2011-12-02 15:12:53 -0800524 panic("subsys-restart: Resetting the SoC - %s crashed.",
525 subsys->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700526 break;
527
528 default:
Jaeseong GIMb8499412012-06-26 10:36:57 -0700529#if defined(CONFIG_LGE_CRASH_HANDLER)
530 msm_set_restart_mode(ssr_magic_number | SUB_UNKNOWN);
531#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700532 panic("subsys-restart: Unknown restart level!\n");
533 break;
534
535 }
536
537 return 0;
538}
539EXPORT_SYMBOL(subsystem_restart);
540
541int ssr_register_subsystem(struct subsys_data *subsys)
542{
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700543 unsigned long flags;
544
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700545 if (!subsys)
546 goto err;
547
548 if (!subsys->name)
549 goto err;
550
551 if (!subsys->powerup || !subsys->shutdown)
552 goto err;
553
554 subsys->notif_handle = subsys_notif_add_subsys(subsys->name);
555 subsys->restart_order = _update_restart_order(subsys);
556 subsys->single_restart_list[0] = subsys;
557
558 mutex_init(&subsys->shutdown_lock);
559 mutex_init(&subsys->powerup_lock);
560
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700561 spin_lock_irqsave(&subsystem_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700562 list_add(&subsys->list, &subsystem_list);
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700563 spin_unlock_irqrestore(&subsystem_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700564
565 return 0;
566
567err:
568 return -EINVAL;
569}
570EXPORT_SYMBOL(ssr_register_subsystem);
571
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700572static int ssr_panic_handler(struct notifier_block *this,
573 unsigned long event, void *ptr)
574{
575 struct subsys_data *subsys;
576
577 list_for_each_entry(subsys, &subsystem_list, list)
578 if (subsys->crash_shutdown)
579 subsys->crash_shutdown(subsys);
580 return NOTIFY_DONE;
581}
582
583static struct notifier_block panic_nb = {
584 .notifier_call = ssr_panic_handler,
585};
586
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700587static int __init ssr_init_soc_restart_orders(void)
588{
589 int i;
590
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700591 atomic_notifier_chain_register(&panic_notifier_list,
592 &panic_nb);
593
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700594 if (cpu_is_msm8x60()) {
595 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
596 mutex_init(&orders_8x60_all[i]->powerup_lock);
597 mutex_init(&orders_8x60_all[i]->shutdown_lock);
598 }
599
600 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
601 mutex_init(&orders_8x60_modems[i]->powerup_lock);
602 mutex_init(&orders_8x60_modems[i]->shutdown_lock);
603 }
604
605 restart_orders = orders_8x60_all;
606 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
607 }
608
Rohit Vaswanid0fb4182012-03-19 18:07:59 -0700609 if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm9615() ||
610 cpu_is_apq8064()) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700611 restart_orders = restart_orders_8960;
612 n_restart_orders = ARRAY_SIZE(restart_orders_8960);
613 }
614
615 if (restart_orders == NULL || n_restart_orders < 1) {
616 WARN_ON(1);
617 return -EINVAL;
618 }
619
620 return 0;
621}
622
623static int __init subsys_restart_init(void)
624{
625 int ret = 0;
626
627 restart_level = RESET_SOC;
628
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700629 ssr_wq = alloc_workqueue("ssr_wq", 0, 0);
630
631 if (!ssr_wq)
632 panic("Couldn't allocate workqueue for subsystem restart.\n");
633
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700634 ret = ssr_init_soc_restart_orders();
635
636 return ret;
637}
638
639arch_initcall(subsys_restart_init);
640
641MODULE_DESCRIPTION("Subsystem Restart Driver");
642MODULE_LICENSE("GPL v2");