blob: bc4bd21bf9cc3b53c8fcc18b3c50a564e0de1639 [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
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;
45 struct subsys_data *subsys_ptrs[];
46};
47
Vikram Mulukutla69177e12012-03-21 20:51:43 -070048struct restart_wq_data {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049 struct subsys_data *subsys;
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -080050 struct wake_lock ssr_wake_lock;
51 char wakelockname[64];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070052 int coupled;
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;
58 struct subsys_data *subsys;
59 struct list_head list;
60};
61
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070062static int restart_level;
63static int enable_ramdumps;
Vikram Mulukutla69177e12012-03-21 20:51:43 -070064struct workqueue_struct *ssr_wq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070065
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070066static LIST_HEAD(restart_log_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070067static LIST_HEAD(subsystem_list);
68static DEFINE_MUTEX(subsystem_list_lock);
69static DEFINE_MUTEX(soc_order_reg_lock);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070070static DEFINE_MUTEX(restart_log_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070071
72/* SOC specific restart orders go here */
73
74#define DEFINE_SINGLE_RESTART_ORDER(name, order) \
75 static struct subsys_soc_restart_order __##name = { \
76 .subsystem_list = order, \
77 .count = ARRAY_SIZE(order), \
78 .subsys_ptrs = {[ARRAY_SIZE(order)] = NULL} \
79 }; \
80 static struct subsys_soc_restart_order *name[] = { \
81 &__##name, \
82 }
83
84/* MSM 8x60 restart ordering info */
85static const char * const _order_8x60_all[] = {
86 "external_modem", "modem", "lpass"
87};
88DEFINE_SINGLE_RESTART_ORDER(orders_8x60_all, _order_8x60_all);
89
90static const char * const _order_8x60_modems[] = {"external_modem", "modem"};
91DEFINE_SINGLE_RESTART_ORDER(orders_8x60_modems, _order_8x60_modems);
92
93/* MSM 8960 restart ordering info */
94static const char * const order_8960[] = {"modem", "lpass"};
95
96static struct subsys_soc_restart_order restart_orders_8960_one = {
97 .subsystem_list = order_8960,
98 .count = ARRAY_SIZE(order_8960),
99 .subsys_ptrs = {[ARRAY_SIZE(order_8960)] = NULL}
100 };
101
102static struct subsys_soc_restart_order *restart_orders_8960[] = {
103 &restart_orders_8960_one,
104};
105
106/* These will be assigned to one of the sets above after
107 * runtime SoC identification.
108 */
109static struct subsys_soc_restart_order **restart_orders;
110static int n_restart_orders;
111
112module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR);
113
114static struct subsys_soc_restart_order *_update_restart_order(
115 struct subsys_data *subsys);
116
117int get_restart_level()
118{
119 return restart_level;
120}
121EXPORT_SYMBOL(get_restart_level);
122
123static void restart_level_changed(void)
124{
125 struct subsys_data *subsys;
126
127 if (cpu_is_msm8x60() && restart_level == RESET_SUBSYS_COUPLED) {
128 restart_orders = orders_8x60_all;
129 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
130 }
131
132 if (cpu_is_msm8x60() && restart_level == RESET_SUBSYS_MIXED) {
133 restart_orders = orders_8x60_modems;
134 n_restart_orders = ARRAY_SIZE(orders_8x60_modems);
135 }
136
137 mutex_lock(&subsystem_list_lock);
138 list_for_each_entry(subsys, &subsystem_list, list)
139 subsys->restart_order = _update_restart_order(subsys);
140 mutex_unlock(&subsystem_list_lock);
141}
142
143static int restart_level_set(const char *val, struct kernel_param *kp)
144{
145 int ret;
146 int old_val = restart_level;
147
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800148 if (cpu_is_msm9615()) {
149 pr_err("Only Phase 1 subsystem restart is supported\n");
150 return -EINVAL;
151 }
152
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700153 ret = param_set_int(val, kp);
154 if (ret)
155 return ret;
156
157 switch (restart_level) {
158
159 case RESET_SOC:
160 case RESET_SUBSYS_COUPLED:
161 case RESET_SUBSYS_INDEPENDENT:
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700162 pr_info("Phase %d behavior activated.\n", restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700163 break;
164
165 case RESET_SUBSYS_MIXED:
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700166 pr_info("Phase 2+ behavior activated.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700167 break;
168
169 default:
170 restart_level = old_val;
171 return -EINVAL;
172 break;
173
174 }
175
176 if (restart_level != old_val)
177 restart_level_changed();
178
179 return 0;
180}
181
182module_param_call(restart_level, restart_level_set, param_get_int,
183 &restart_level, 0644);
184
185static struct subsys_data *_find_subsystem(const char *subsys_name)
186{
187 struct subsys_data *subsys;
188
189 mutex_lock(&subsystem_list_lock);
190 list_for_each_entry(subsys, &subsystem_list, list)
191 if (!strncmp(subsys->name, subsys_name,
192 SUBSYS_NAME_MAX_LENGTH)) {
193 mutex_unlock(&subsystem_list_lock);
194 return subsys;
195 }
196 mutex_unlock(&subsystem_list_lock);
197
198 return NULL;
199}
200
201static struct subsys_soc_restart_order *_update_restart_order(
202 struct subsys_data *subsys)
203{
204 int i, j;
205
206 if (!subsys)
207 return NULL;
208
209 if (!subsys->name)
210 return NULL;
211
212 mutex_lock(&soc_order_reg_lock);
213 for (j = 0; j < n_restart_orders; j++) {
214 for (i = 0; i < restart_orders[j]->count; i++)
215 if (!strncmp(restart_orders[j]->subsystem_list[i],
216 subsys->name, SUBSYS_NAME_MAX_LENGTH)) {
217
218 restart_orders[j]->subsys_ptrs[i] =
219 subsys;
220 mutex_unlock(&soc_order_reg_lock);
221 return restart_orders[j];
222 }
223 }
224
225 mutex_unlock(&soc_order_reg_lock);
226
227 return NULL;
228}
229
230static void _send_notification_to_order(struct subsys_data
231 **restart_list, int count,
232 enum subsys_notif_type notif_type)
233{
234 int i;
235
236 for (i = 0; i < count; i++)
237 if (restart_list[i])
238 subsys_notif_queue_notification(
239 restart_list[i]->notif_handle, notif_type);
240}
241
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700242static int max_restarts;
243module_param(max_restarts, int, 0644);
244
245static long max_history_time = 3600;
246module_param(max_history_time, long, 0644);
247
248static void do_epoch_check(struct subsys_data *subsys)
249{
250 int n = 0;
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600251 struct timeval *time_first = NULL, *curr_time;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700252 struct restart_log *r_log, *temp;
253 static int max_restarts_check;
254 static long max_history_time_check;
255
256 mutex_lock(&restart_log_mutex);
257
258 max_restarts_check = max_restarts;
259 max_history_time_check = max_history_time;
260
261 /* Check if epoch checking is enabled */
262 if (!max_restarts_check)
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700263 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700264
265 r_log = kmalloc(sizeof(struct restart_log), GFP_KERNEL);
Matt Wagantalleecca342011-11-10 20:12:05 -0800266 if (!r_log)
267 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700268 r_log->subsys = subsys;
269 do_gettimeofday(&r_log->time);
270 curr_time = &r_log->time;
271 INIT_LIST_HEAD(&r_log->list);
272
273 list_add_tail(&r_log->list, &restart_log_list);
274
275 list_for_each_entry_safe(r_log, temp, &restart_log_list, list) {
276
277 if ((curr_time->tv_sec - r_log->time.tv_sec) >
278 max_history_time_check) {
279
280 pr_debug("Deleted node with restart_time = %ld\n",
281 r_log->time.tv_sec);
282 list_del(&r_log->list);
283 kfree(r_log);
284 continue;
285 }
286 if (!n) {
287 time_first = &r_log->time;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700288 pr_debug("Time_first: %ld\n", time_first->tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700289 }
290 n++;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700291 pr_debug("Restart_time: %ld\n", r_log->time.tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700292 }
293
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600294 if (time_first && n >= max_restarts_check) {
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700295 if ((curr_time->tv_sec - time_first->tv_sec) <
296 max_history_time_check)
297 panic("Subsystems have crashed %d times in less than "
298 "%ld seconds!", max_restarts_check,
299 max_history_time_check);
300 }
301
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700302out:
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700303 mutex_unlock(&restart_log_mutex);
304}
305
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700306static void subsystem_restart_wq_func(struct work_struct *work)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700307{
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700308 struct restart_wq_data *r_work = container_of(work,
309 struct restart_wq_data, work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700310 struct subsys_data **restart_list;
311 struct subsys_data *subsys = r_work->subsys;
312 struct subsys_soc_restart_order *soc_restart_order = NULL;
313
314 struct mutex *powerup_lock;
315 struct mutex *shutdown_lock;
316
317 int i;
318 int restart_list_count = 0;
319
320 if (r_work->coupled)
321 soc_restart_order = subsys->restart_order;
322
323 /* It's OK to not take the registration lock at this point.
324 * This is because the subsystem list inside the relevant
325 * restart order is not being traversed.
326 */
327 if (!soc_restart_order) {
328 restart_list = subsys->single_restart_list;
329 restart_list_count = 1;
330 powerup_lock = &subsys->powerup_lock;
331 shutdown_lock = &subsys->shutdown_lock;
332 } else {
333 restart_list = soc_restart_order->subsys_ptrs;
334 restart_list_count = soc_restart_order->count;
335 powerup_lock = &soc_restart_order->powerup_lock;
336 shutdown_lock = &soc_restart_order->shutdown_lock;
337 }
338
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700339 pr_debug("[%p]: Attempting to get shutdown lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700340
341 /* Try to acquire shutdown_lock. If this fails, these subsystems are
342 * already being restarted - return.
343 */
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800344 if (!mutex_trylock(shutdown_lock))
345 goto out;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700346
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700347 pr_debug("[%p]: Attempting to get powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700348
349 /* Now that we've acquired the shutdown lock, either we're the first to
350 * restart these subsystems or some other thread is doing the powerup
351 * sequence for these subsystems. In the latter case, panic and bail
352 * out, since a subsystem died in its powerup sequence.
353 */
354 if (!mutex_trylock(powerup_lock))
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700355 panic("%s[%p]: Subsystem died during powerup!",
356 __func__, current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700357
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700358 do_epoch_check(subsys);
359
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700360 /* Now it is necessary to take the registration lock. This is because
361 * the subsystem list in the SoC restart order will be traversed
362 * and it shouldn't be changed until _this_ restart sequence completes.
363 */
364 mutex_lock(&soc_order_reg_lock);
365
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700366 pr_debug("[%p]: Starting restart sequence for %s\n", current,
367 r_work->subsys->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700368
369 _send_notification_to_order(restart_list,
370 restart_list_count,
371 SUBSYS_BEFORE_SHUTDOWN);
372
373 for (i = 0; i < restart_list_count; i++) {
374
375 if (!restart_list[i])
376 continue;
377
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700378 pr_info("[%p]: Shutting down %s\n", current,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700379 restart_list[i]->name);
380
381 if (restart_list[i]->shutdown(subsys) < 0)
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700382 panic("subsys-restart: %s[%p]: Failed to shutdown %s!",
383 __func__, current, restart_list[i]->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700384 }
385
386 _send_notification_to_order(restart_list, restart_list_count,
387 SUBSYS_AFTER_SHUTDOWN);
388
389 /* Now that we've finished shutting down these subsystems, release the
390 * shutdown lock. If a subsystem restart request comes in for a
391 * subsystem in _this_ restart order after the unlock below, and
392 * before the powerup lock is released, panic and bail out.
393 */
394 mutex_unlock(shutdown_lock);
395
396 /* Collect ram dumps for all subsystems in order here */
397 for (i = 0; i < restart_list_count; i++) {
398 if (!restart_list[i])
399 continue;
400
401 if (restart_list[i]->ramdump)
402 if (restart_list[i]->ramdump(enable_ramdumps,
403 subsys) < 0)
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700404 pr_warn("%s[%p]: Ramdump failed.\n",
405 restart_list[i]->name, current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406 }
407
408 _send_notification_to_order(restart_list,
409 restart_list_count,
410 SUBSYS_BEFORE_POWERUP);
411
412 for (i = restart_list_count - 1; i >= 0; i--) {
413
414 if (!restart_list[i])
415 continue;
416
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700417 pr_info("[%p]: Powering up %s\n", current,
418 restart_list[i]->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700419
420 if (restart_list[i]->powerup(subsys) < 0)
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700421 panic("%s[%p]: Failed to powerup %s!", __func__,
422 current, restart_list[i]->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700423 }
424
425 _send_notification_to_order(restart_list,
426 restart_list_count,
427 SUBSYS_AFTER_POWERUP);
428
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700429 pr_info("[%p]: Restart sequence for %s completed.\n",
430 current, r_work->subsys->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700431
432 mutex_unlock(powerup_lock);
433
434 mutex_unlock(&soc_order_reg_lock);
435
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700436 pr_debug("[%p]: Released powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700437
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800438out:
439 wake_unlock(&r_work->ssr_wake_lock);
440 wake_lock_destroy(&r_work->ssr_wake_lock);
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700441 kfree(r_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700442}
443
444int subsystem_restart(const char *subsys_name)
445{
446 struct subsys_data *subsys;
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700447 struct restart_wq_data *data = NULL;
448 int rc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700449
450 if (!subsys_name) {
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700451 pr_err("Invalid subsystem name.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700452 return -EINVAL;
453 }
454
Vikram Mulukutla3b24c8c2011-12-02 15:12:53 -0800455 pr_info("Restart sequence requested for %s, restart_level = %d.\n",
456 subsys_name, restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700457
458 /* List of subsystems is protected by a lock. New subsystems can
459 * still come in.
460 */
461 subsys = _find_subsystem(subsys_name);
462
463 if (!subsys) {
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700464 pr_warn("Unregistered subsystem %s!\n", subsys_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700465 return -EINVAL;
466 }
467
468 if (restart_level != RESET_SOC) {
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700469 data = kzalloc(sizeof(struct restart_wq_data), GFP_KERNEL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700470 if (!data) {
471 restart_level = RESET_SOC;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700472 pr_warn("Failed to alloc restart data. Resetting.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700473 } else {
474 if (restart_level == RESET_SUBSYS_COUPLED ||
475 restart_level == RESET_SUBSYS_MIXED)
476 data->coupled = 1;
477 else
478 data->coupled = 0;
479
480 data->subsys = subsys;
481 }
482 }
483
484 switch (restart_level) {
485
486 case RESET_SUBSYS_COUPLED:
487 case RESET_SUBSYS_MIXED:
488 case RESET_SUBSYS_INDEPENDENT:
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700489 pr_debug("Restarting %s [level=%d]!\n", subsys_name,
490 restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700491
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800492 snprintf(data->wakelockname, sizeof(data->wakelockname),
493 "ssr(%s)", subsys_name);
494 wake_lock_init(&data->ssr_wake_lock, WAKE_LOCK_SUSPEND,
495 data->wakelockname);
496 wake_lock(&data->ssr_wake_lock);
497
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700498 INIT_WORK(&data->work, subsystem_restart_wq_func);
499 rc = schedule_work(&data->work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700500
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700501 if (rc < 0)
502 panic("%s: Unable to schedule work to restart %s",
503 __func__, subsys->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700504 break;
505
506 case RESET_SOC:
Vikram Mulukutla3b24c8c2011-12-02 15:12:53 -0800507 panic("subsys-restart: Resetting the SoC - %s crashed.",
508 subsys->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700509 break;
510
511 default:
512 panic("subsys-restart: Unknown restart level!\n");
513 break;
514
515 }
516
517 return 0;
518}
519EXPORT_SYMBOL(subsystem_restart);
520
521int ssr_register_subsystem(struct subsys_data *subsys)
522{
523 if (!subsys)
524 goto err;
525
526 if (!subsys->name)
527 goto err;
528
529 if (!subsys->powerup || !subsys->shutdown)
530 goto err;
531
532 subsys->notif_handle = subsys_notif_add_subsys(subsys->name);
533 subsys->restart_order = _update_restart_order(subsys);
534 subsys->single_restart_list[0] = subsys;
535
536 mutex_init(&subsys->shutdown_lock);
537 mutex_init(&subsys->powerup_lock);
538
539 mutex_lock(&subsystem_list_lock);
540 list_add(&subsys->list, &subsystem_list);
541 mutex_unlock(&subsystem_list_lock);
542
543 return 0;
544
545err:
546 return -EINVAL;
547}
548EXPORT_SYMBOL(ssr_register_subsystem);
549
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700550static int ssr_panic_handler(struct notifier_block *this,
551 unsigned long event, void *ptr)
552{
553 struct subsys_data *subsys;
554
555 list_for_each_entry(subsys, &subsystem_list, list)
556 if (subsys->crash_shutdown)
557 subsys->crash_shutdown(subsys);
558 return NOTIFY_DONE;
559}
560
561static struct notifier_block panic_nb = {
562 .notifier_call = ssr_panic_handler,
563};
564
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700565static int __init ssr_init_soc_restart_orders(void)
566{
567 int i;
568
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700569 atomic_notifier_chain_register(&panic_notifier_list,
570 &panic_nb);
571
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700572 if (cpu_is_msm8x60()) {
573 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
574 mutex_init(&orders_8x60_all[i]->powerup_lock);
575 mutex_init(&orders_8x60_all[i]->shutdown_lock);
576 }
577
578 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
579 mutex_init(&orders_8x60_modems[i]->powerup_lock);
580 mutex_init(&orders_8x60_modems[i]->shutdown_lock);
581 }
582
583 restart_orders = orders_8x60_all;
584 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
585 }
586
Rohit Vaswanid0fb4182012-03-19 18:07:59 -0700587 if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm9615() ||
588 cpu_is_apq8064()) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700589 restart_orders = restart_orders_8960;
590 n_restart_orders = ARRAY_SIZE(restart_orders_8960);
591 }
592
593 if (restart_orders == NULL || n_restart_orders < 1) {
594 WARN_ON(1);
595 return -EINVAL;
596 }
597
598 return 0;
599}
600
601static int __init subsys_restart_init(void)
602{
603 int ret = 0;
604
605 restart_level = RESET_SOC;
606
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700607 ssr_wq = alloc_workqueue("ssr_wq", 0, 0);
608
609 if (!ssr_wq)
610 panic("Couldn't allocate workqueue for subsystem restart.\n");
611
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700612 ret = ssr_init_soc_restart_orders();
613
614 return ret;
615}
616
617arch_initcall(subsys_restart_init);
618
619MODULE_DESCRIPTION("Subsystem Restart Driver");
620MODULE_LICENSE("GPL v2");