blob: ff699e2ba2fa9dfc27ca65f631fce950b236c325 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2011, Code Aurora Forum. 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
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>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070026
27#include <asm/current.h>
28
29#include <mach/peripheral-loader.h>
30#include <mach/scm.h>
31#include <mach/socinfo.h>
32#include <mach/subsystem_notif.h>
33#include <mach/subsystem_restart.h>
34
35#include "smd_private.h"
36
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037struct subsys_soc_restart_order {
38 const char * const *subsystem_list;
39 int count;
40
41 struct mutex shutdown_lock;
42 struct mutex powerup_lock;
43 struct subsys_data *subsys_ptrs[];
44};
45
46struct restart_thread_data {
47 struct subsys_data *subsys;
48 int coupled;
49};
50
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070051struct restart_log {
52 struct timeval time;
53 struct subsys_data *subsys;
54 struct list_head list;
55};
56
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070057static int restart_level;
58static int enable_ramdumps;
59
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070060static LIST_HEAD(restart_log_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070061static LIST_HEAD(subsystem_list);
62static DEFINE_MUTEX(subsystem_list_lock);
63static DEFINE_MUTEX(soc_order_reg_lock);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070064static DEFINE_MUTEX(restart_log_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065
66/* SOC specific restart orders go here */
67
68#define DEFINE_SINGLE_RESTART_ORDER(name, order) \
69 static struct subsys_soc_restart_order __##name = { \
70 .subsystem_list = order, \
71 .count = ARRAY_SIZE(order), \
72 .subsys_ptrs = {[ARRAY_SIZE(order)] = NULL} \
73 }; \
74 static struct subsys_soc_restart_order *name[] = { \
75 &__##name, \
76 }
77
78/* MSM 8x60 restart ordering info */
79static const char * const _order_8x60_all[] = {
80 "external_modem", "modem", "lpass"
81};
82DEFINE_SINGLE_RESTART_ORDER(orders_8x60_all, _order_8x60_all);
83
84static const char * const _order_8x60_modems[] = {"external_modem", "modem"};
85DEFINE_SINGLE_RESTART_ORDER(orders_8x60_modems, _order_8x60_modems);
86
87/* MSM 8960 restart ordering info */
88static const char * const order_8960[] = {"modem", "lpass"};
89
90static struct subsys_soc_restart_order restart_orders_8960_one = {
91 .subsystem_list = order_8960,
92 .count = ARRAY_SIZE(order_8960),
93 .subsys_ptrs = {[ARRAY_SIZE(order_8960)] = NULL}
94 };
95
96static struct subsys_soc_restart_order *restart_orders_8960[] = {
97 &restart_orders_8960_one,
98};
99
100/* These will be assigned to one of the sets above after
101 * runtime SoC identification.
102 */
103static struct subsys_soc_restart_order **restart_orders;
104static int n_restart_orders;
105
106module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR);
107
108static struct subsys_soc_restart_order *_update_restart_order(
109 struct subsys_data *subsys);
110
111int get_restart_level()
112{
113 return restart_level;
114}
115EXPORT_SYMBOL(get_restart_level);
116
117static void restart_level_changed(void)
118{
119 struct subsys_data *subsys;
120
121 if (cpu_is_msm8x60() && restart_level == RESET_SUBSYS_COUPLED) {
122 restart_orders = orders_8x60_all;
123 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
124 }
125
126 if (cpu_is_msm8x60() && restart_level == RESET_SUBSYS_MIXED) {
127 restart_orders = orders_8x60_modems;
128 n_restart_orders = ARRAY_SIZE(orders_8x60_modems);
129 }
130
131 mutex_lock(&subsystem_list_lock);
132 list_for_each_entry(subsys, &subsystem_list, list)
133 subsys->restart_order = _update_restart_order(subsys);
134 mutex_unlock(&subsystem_list_lock);
135}
136
137static int restart_level_set(const char *val, struct kernel_param *kp)
138{
139 int ret;
140 int old_val = restart_level;
141
142 ret = param_set_int(val, kp);
143 if (ret)
144 return ret;
145
146 switch (restart_level) {
147
148 case RESET_SOC:
149 case RESET_SUBSYS_COUPLED:
150 case RESET_SUBSYS_INDEPENDENT:
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700151 pr_info("Phase %d behavior activated.\n", restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700152 break;
153
154 case RESET_SUBSYS_MIXED:
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700155 pr_info("Phase 2+ behavior activated.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700156 break;
157
158 default:
159 restart_level = old_val;
160 return -EINVAL;
161 break;
162
163 }
164
165 if (restart_level != old_val)
166 restart_level_changed();
167
168 return 0;
169}
170
171module_param_call(restart_level, restart_level_set, param_get_int,
172 &restart_level, 0644);
173
174static struct subsys_data *_find_subsystem(const char *subsys_name)
175{
176 struct subsys_data *subsys;
177
178 mutex_lock(&subsystem_list_lock);
179 list_for_each_entry(subsys, &subsystem_list, list)
180 if (!strncmp(subsys->name, subsys_name,
181 SUBSYS_NAME_MAX_LENGTH)) {
182 mutex_unlock(&subsystem_list_lock);
183 return subsys;
184 }
185 mutex_unlock(&subsystem_list_lock);
186
187 return NULL;
188}
189
190static struct subsys_soc_restart_order *_update_restart_order(
191 struct subsys_data *subsys)
192{
193 int i, j;
194
195 if (!subsys)
196 return NULL;
197
198 if (!subsys->name)
199 return NULL;
200
201 mutex_lock(&soc_order_reg_lock);
202 for (j = 0; j < n_restart_orders; j++) {
203 for (i = 0; i < restart_orders[j]->count; i++)
204 if (!strncmp(restart_orders[j]->subsystem_list[i],
205 subsys->name, SUBSYS_NAME_MAX_LENGTH)) {
206
207 restart_orders[j]->subsys_ptrs[i] =
208 subsys;
209 mutex_unlock(&soc_order_reg_lock);
210 return restart_orders[j];
211 }
212 }
213
214 mutex_unlock(&soc_order_reg_lock);
215
216 return NULL;
217}
218
219static void _send_notification_to_order(struct subsys_data
220 **restart_list, int count,
221 enum subsys_notif_type notif_type)
222{
223 int i;
224
225 for (i = 0; i < count; i++)
226 if (restart_list[i])
227 subsys_notif_queue_notification(
228 restart_list[i]->notif_handle, notif_type);
229}
230
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700231static int max_restarts;
232module_param(max_restarts, int, 0644);
233
234static long max_history_time = 3600;
235module_param(max_history_time, long, 0644);
236
237static void do_epoch_check(struct subsys_data *subsys)
238{
239 int n = 0;
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600240 struct timeval *time_first = NULL, *curr_time;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700241 struct restart_log *r_log, *temp;
242 static int max_restarts_check;
243 static long max_history_time_check;
244
245 mutex_lock(&restart_log_mutex);
246
247 max_restarts_check = max_restarts;
248 max_history_time_check = max_history_time;
249
250 /* Check if epoch checking is enabled */
251 if (!max_restarts_check)
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700252 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700253
254 r_log = kmalloc(sizeof(struct restart_log), GFP_KERNEL);
255 r_log->subsys = subsys;
256 do_gettimeofday(&r_log->time);
257 curr_time = &r_log->time;
258 INIT_LIST_HEAD(&r_log->list);
259
260 list_add_tail(&r_log->list, &restart_log_list);
261
262 list_for_each_entry_safe(r_log, temp, &restart_log_list, list) {
263
264 if ((curr_time->tv_sec - r_log->time.tv_sec) >
265 max_history_time_check) {
266
267 pr_debug("Deleted node with restart_time = %ld\n",
268 r_log->time.tv_sec);
269 list_del(&r_log->list);
270 kfree(r_log);
271 continue;
272 }
273 if (!n) {
274 time_first = &r_log->time;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700275 pr_debug("Time_first: %ld\n", time_first->tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700276 }
277 n++;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700278 pr_debug("Restart_time: %ld\n", r_log->time.tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700279 }
280
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600281 if (time_first && n >= max_restarts_check) {
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700282 if ((curr_time->tv_sec - time_first->tv_sec) <
283 max_history_time_check)
284 panic("Subsystems have crashed %d times in less than "
285 "%ld seconds!", max_restarts_check,
286 max_history_time_check);
287 }
288
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700289out:
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700290 mutex_unlock(&restart_log_mutex);
291}
292
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293static int subsystem_restart_thread(void *data)
294{
295 struct restart_thread_data *r_work = data;
296 struct subsys_data **restart_list;
297 struct subsys_data *subsys = r_work->subsys;
298 struct subsys_soc_restart_order *soc_restart_order = NULL;
299
300 struct mutex *powerup_lock;
301 struct mutex *shutdown_lock;
302
303 int i;
304 int restart_list_count = 0;
305
306 if (r_work->coupled)
307 soc_restart_order = subsys->restart_order;
308
309 /* It's OK to not take the registration lock at this point.
310 * This is because the subsystem list inside the relevant
311 * restart order is not being traversed.
312 */
313 if (!soc_restart_order) {
314 restart_list = subsys->single_restart_list;
315 restart_list_count = 1;
316 powerup_lock = &subsys->powerup_lock;
317 shutdown_lock = &subsys->shutdown_lock;
318 } else {
319 restart_list = soc_restart_order->subsys_ptrs;
320 restart_list_count = soc_restart_order->count;
321 powerup_lock = &soc_restart_order->powerup_lock;
322 shutdown_lock = &soc_restart_order->shutdown_lock;
323 }
324
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700325 pr_debug("[%p]: Attempting to get shutdown lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700326
327 /* Try to acquire shutdown_lock. If this fails, these subsystems are
328 * already being restarted - return.
329 */
330 if (!mutex_trylock(shutdown_lock)) {
331 kfree(data);
332 do_exit(0);
333 }
334
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700335 pr_debug("[%p]: Attempting to get powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700336
337 /* Now that we've acquired the shutdown lock, either we're the first to
338 * restart these subsystems or some other thread is doing the powerup
339 * sequence for these subsystems. In the latter case, panic and bail
340 * out, since a subsystem died in its powerup sequence.
341 */
342 if (!mutex_trylock(powerup_lock))
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700343 panic("%s[%p]: Subsystem died during powerup!",
344 __func__, current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700345
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700346 do_epoch_check(subsys);
347
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700348 /* Now it is necessary to take the registration lock. This is because
349 * the subsystem list in the SoC restart order will be traversed
350 * and it shouldn't be changed until _this_ restart sequence completes.
351 */
352 mutex_lock(&soc_order_reg_lock);
353
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700354 pr_debug("[%p]: Starting restart sequence for %s\n", current,
355 r_work->subsys->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700356
357 _send_notification_to_order(restart_list,
358 restart_list_count,
359 SUBSYS_BEFORE_SHUTDOWN);
360
361 for (i = 0; i < restart_list_count; i++) {
362
363 if (!restart_list[i])
364 continue;
365
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700366 pr_info("[%p]: Shutting down %s\n", current,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700367 restart_list[i]->name);
368
369 if (restart_list[i]->shutdown(subsys) < 0)
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700370 panic("subsys-restart: %s[%p]: Failed to shutdown %s!",
371 __func__, current, restart_list[i]->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700372 }
373
374 _send_notification_to_order(restart_list, restart_list_count,
375 SUBSYS_AFTER_SHUTDOWN);
376
377 /* Now that we've finished shutting down these subsystems, release the
378 * shutdown lock. If a subsystem restart request comes in for a
379 * subsystem in _this_ restart order after the unlock below, and
380 * before the powerup lock is released, panic and bail out.
381 */
382 mutex_unlock(shutdown_lock);
383
384 /* Collect ram dumps for all subsystems in order here */
385 for (i = 0; i < restart_list_count; i++) {
386 if (!restart_list[i])
387 continue;
388
389 if (restart_list[i]->ramdump)
390 if (restart_list[i]->ramdump(enable_ramdumps,
391 subsys) < 0)
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700392 pr_warn("%s[%p]: Ramdump failed.\n",
393 restart_list[i]->name, current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394 }
395
396 _send_notification_to_order(restart_list,
397 restart_list_count,
398 SUBSYS_BEFORE_POWERUP);
399
400 for (i = restart_list_count - 1; i >= 0; i--) {
401
402 if (!restart_list[i])
403 continue;
404
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700405 pr_info("[%p]: Powering up %s\n", current,
406 restart_list[i]->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407
408 if (restart_list[i]->powerup(subsys) < 0)
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700409 panic("%s[%p]: Failed to powerup %s!", __func__,
410 current, restart_list[i]->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700411 }
412
413 _send_notification_to_order(restart_list,
414 restart_list_count,
415 SUBSYS_AFTER_POWERUP);
416
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700417 pr_info("[%p]: Restart sequence for %s completed.\n",
418 current, r_work->subsys->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700419
420 mutex_unlock(powerup_lock);
421
422 mutex_unlock(&soc_order_reg_lock);
423
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700424 pr_debug("[%p]: Released powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700425
426 kfree(data);
427 do_exit(0);
428}
429
430int subsystem_restart(const char *subsys_name)
431{
432 struct subsys_data *subsys;
433 struct task_struct *tsk;
434 struct restart_thread_data *data = NULL;
435
436 if (!subsys_name) {
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700437 pr_err("Invalid subsystem name.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700438 return -EINVAL;
439 }
440
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700441 pr_info("Restart sequence requested for %s\n",
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700442 subsys_name);
443
444 /* List of subsystems is protected by a lock. New subsystems can
445 * still come in.
446 */
447 subsys = _find_subsystem(subsys_name);
448
449 if (!subsys) {
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700450 pr_warn("Unregistered subsystem %s!\n", subsys_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700451 return -EINVAL;
452 }
453
454 if (restart_level != RESET_SOC) {
455 data = kzalloc(sizeof(struct restart_thread_data), GFP_KERNEL);
456 if (!data) {
457 restart_level = RESET_SOC;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700458 pr_warn("Failed to alloc restart data. Resetting.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700459 } else {
460 if (restart_level == RESET_SUBSYS_COUPLED ||
461 restart_level == RESET_SUBSYS_MIXED)
462 data->coupled = 1;
463 else
464 data->coupled = 0;
465
466 data->subsys = subsys;
467 }
468 }
469
470 switch (restart_level) {
471
472 case RESET_SUBSYS_COUPLED:
473 case RESET_SUBSYS_MIXED:
474 case RESET_SUBSYS_INDEPENDENT:
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700475 pr_debug("Restarting %s [level=%d]!\n", subsys_name,
476 restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700477
478 /* Let the kthread handle the actual restarting. Using a
479 * workqueue will not work since all restart requests are
480 * serialized and it prevents the short circuiting of
481 * restart requests for subsystems already in a restart
482 * sequence.
483 */
484 tsk = kthread_run(subsystem_restart_thread, data,
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700485 "subsystem_restart_thread");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700486 if (IS_ERR(tsk))
487 panic("%s: Unable to create thread to restart %s",
488 __func__, subsys->name);
489
490 break;
491
492 case RESET_SOC:
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700493 panic("subsys-restart: Resetting the SoC");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700494 break;
495
496 default:
497 panic("subsys-restart: Unknown restart level!\n");
498 break;
499
500 }
501
502 return 0;
503}
504EXPORT_SYMBOL(subsystem_restart);
505
506int ssr_register_subsystem(struct subsys_data *subsys)
507{
508 if (!subsys)
509 goto err;
510
511 if (!subsys->name)
512 goto err;
513
514 if (!subsys->powerup || !subsys->shutdown)
515 goto err;
516
517 subsys->notif_handle = subsys_notif_add_subsys(subsys->name);
518 subsys->restart_order = _update_restart_order(subsys);
519 subsys->single_restart_list[0] = subsys;
520
521 mutex_init(&subsys->shutdown_lock);
522 mutex_init(&subsys->powerup_lock);
523
524 mutex_lock(&subsystem_list_lock);
525 list_add(&subsys->list, &subsystem_list);
526 mutex_unlock(&subsystem_list_lock);
527
528 return 0;
529
530err:
531 return -EINVAL;
532}
533EXPORT_SYMBOL(ssr_register_subsystem);
534
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700535static int ssr_panic_handler(struct notifier_block *this,
536 unsigned long event, void *ptr)
537{
538 struct subsys_data *subsys;
539
540 list_for_each_entry(subsys, &subsystem_list, list)
541 if (subsys->crash_shutdown)
542 subsys->crash_shutdown(subsys);
543 return NOTIFY_DONE;
544}
545
546static struct notifier_block panic_nb = {
547 .notifier_call = ssr_panic_handler,
548};
549
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700550static int __init ssr_init_soc_restart_orders(void)
551{
552 int i;
553
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700554 atomic_notifier_chain_register(&panic_notifier_list,
555 &panic_nb);
556
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700557 if (cpu_is_msm8x60()) {
558 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
559 mutex_init(&orders_8x60_all[i]->powerup_lock);
560 mutex_init(&orders_8x60_all[i]->shutdown_lock);
561 }
562
563 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
564 mutex_init(&orders_8x60_modems[i]->powerup_lock);
565 mutex_init(&orders_8x60_modems[i]->shutdown_lock);
566 }
567
568 restart_orders = orders_8x60_all;
569 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
570 }
571
Stepan Moskovchenkodd4d2fb2011-10-25 14:46:15 -0700572 if (cpu_is_msm8960() || cpu_is_msm8930()) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700573 restart_orders = restart_orders_8960;
574 n_restart_orders = ARRAY_SIZE(restart_orders_8960);
575 }
576
577 if (restart_orders == NULL || n_restart_orders < 1) {
578 WARN_ON(1);
579 return -EINVAL;
580 }
581
582 return 0;
583}
584
585static int __init subsys_restart_init(void)
586{
587 int ret = 0;
588
589 restart_level = RESET_SOC;
590
591 ret = ssr_init_soc_restart_orders();
592
593 return ret;
594}
595
596arch_initcall(subsys_restart_init);
597
598MODULE_DESCRIPTION("Subsystem Restart Driver");
599MODULE_LICENSE("GPL v2");