blob: e630e317fa333ab4f541dec36e80f97b48cb69be [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;
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;
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);
Vikram Mulukutla705e6892012-06-08 17:57:31 -070068static DEFINE_SPINLOCK(subsystem_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069static 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"};
Ameya Thakur2c0d2f22012-06-15 15:48:20 -070095/*SGLTE restart ordering info*/
96static const char * const order_8960_sglte[] = {"external_modem",
97 "modem"};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070098
99static struct subsys_soc_restart_order restart_orders_8960_one = {
100 .subsystem_list = order_8960,
101 .count = ARRAY_SIZE(order_8960),
102 .subsys_ptrs = {[ARRAY_SIZE(order_8960)] = NULL}
103 };
104
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700105static struct subsys_soc_restart_order restart_orders_8960_fusion_sglte = {
106 .subsystem_list = order_8960_sglte,
107 .count = ARRAY_SIZE(order_8960_sglte),
108 .subsys_ptrs = {[ARRAY_SIZE(order_8960_sglte)] = NULL}
109 };
110
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700111static struct subsys_soc_restart_order *restart_orders_8960[] = {
112 &restart_orders_8960_one,
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700113 };
114
115static struct subsys_soc_restart_order *restart_orders_8960_sglte[] = {
116 &restart_orders_8960_fusion_sglte,
117 };
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700118
119/* These will be assigned to one of the sets above after
120 * runtime SoC identification.
121 */
122static struct subsys_soc_restart_order **restart_orders;
123static int n_restart_orders;
124
125module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR);
126
127static struct subsys_soc_restart_order *_update_restart_order(
128 struct subsys_data *subsys);
129
130int get_restart_level()
131{
132 return restart_level;
133}
134EXPORT_SYMBOL(get_restart_level);
135
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700136static int restart_level_set(const char *val, struct kernel_param *kp)
137{
138 int ret;
139 int old_val = restart_level;
140
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800141 if (cpu_is_msm9615()) {
142 pr_err("Only Phase 1 subsystem restart is supported\n");
143 return -EINVAL;
144 }
145
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146 ret = param_set_int(val, kp);
147 if (ret)
148 return ret;
149
150 switch (restart_level) {
151
152 case RESET_SOC:
153 case RESET_SUBSYS_COUPLED:
154 case RESET_SUBSYS_INDEPENDENT:
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700155 pr_info("Phase %d behavior activated.\n", restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700156 break;
157
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700158 default:
159 restart_level = old_val;
160 return -EINVAL;
161 break;
162
163 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700164 return 0;
165}
166
167module_param_call(restart_level, restart_level_set, param_get_int,
168 &restart_level, 0644);
169
170static struct subsys_data *_find_subsystem(const char *subsys_name)
171{
172 struct subsys_data *subsys;
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700173 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700174
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700175 spin_lock_irqsave(&subsystem_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700176 list_for_each_entry(subsys, &subsystem_list, list)
177 if (!strncmp(subsys->name, subsys_name,
178 SUBSYS_NAME_MAX_LENGTH)) {
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700179 spin_unlock_irqrestore(&subsystem_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700180 return subsys;
181 }
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700182 spin_unlock_irqrestore(&subsystem_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700183
184 return NULL;
185}
186
187static struct subsys_soc_restart_order *_update_restart_order(
188 struct subsys_data *subsys)
189{
190 int i, j;
191
192 if (!subsys)
193 return NULL;
194
195 if (!subsys->name)
196 return NULL;
197
198 mutex_lock(&soc_order_reg_lock);
199 for (j = 0; j < n_restart_orders; j++) {
200 for (i = 0; i < restart_orders[j]->count; i++)
201 if (!strncmp(restart_orders[j]->subsystem_list[i],
202 subsys->name, SUBSYS_NAME_MAX_LENGTH)) {
203
204 restart_orders[j]->subsys_ptrs[i] =
205 subsys;
206 mutex_unlock(&soc_order_reg_lock);
207 return restart_orders[j];
208 }
209 }
210
211 mutex_unlock(&soc_order_reg_lock);
212
213 return NULL;
214}
215
216static void _send_notification_to_order(struct subsys_data
217 **restart_list, int count,
218 enum subsys_notif_type notif_type)
219{
220 int i;
221
222 for (i = 0; i < count; i++)
223 if (restart_list[i])
224 subsys_notif_queue_notification(
225 restart_list[i]->notif_handle, notif_type);
226}
227
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700228static int max_restarts;
229module_param(max_restarts, int, 0644);
230
231static long max_history_time = 3600;
232module_param(max_history_time, long, 0644);
233
234static void do_epoch_check(struct subsys_data *subsys)
235{
236 int n = 0;
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600237 struct timeval *time_first = NULL, *curr_time;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700238 struct restart_log *r_log, *temp;
239 static int max_restarts_check;
240 static long max_history_time_check;
241
242 mutex_lock(&restart_log_mutex);
243
244 max_restarts_check = max_restarts;
245 max_history_time_check = max_history_time;
246
247 /* Check if epoch checking is enabled */
248 if (!max_restarts_check)
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700249 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700250
251 r_log = kmalloc(sizeof(struct restart_log), GFP_KERNEL);
Matt Wagantalleecca342011-11-10 20:12:05 -0800252 if (!r_log)
253 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700254 r_log->subsys = subsys;
255 do_gettimeofday(&r_log->time);
256 curr_time = &r_log->time;
257 INIT_LIST_HEAD(&r_log->list);
258
259 list_add_tail(&r_log->list, &restart_log_list);
260
261 list_for_each_entry_safe(r_log, temp, &restart_log_list, list) {
262
263 if ((curr_time->tv_sec - r_log->time.tv_sec) >
264 max_history_time_check) {
265
266 pr_debug("Deleted node with restart_time = %ld\n",
267 r_log->time.tv_sec);
268 list_del(&r_log->list);
269 kfree(r_log);
270 continue;
271 }
272 if (!n) {
273 time_first = &r_log->time;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700274 pr_debug("Time_first: %ld\n", time_first->tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700275 }
276 n++;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700277 pr_debug("Restart_time: %ld\n", r_log->time.tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700278 }
279
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600280 if (time_first && n >= max_restarts_check) {
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700281 if ((curr_time->tv_sec - time_first->tv_sec) <
282 max_history_time_check)
283 panic("Subsystems have crashed %d times in less than "
284 "%ld seconds!", max_restarts_check,
285 max_history_time_check);
286 }
287
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700288out:
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700289 mutex_unlock(&restart_log_mutex);
290}
291
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700292static void subsystem_restart_wq_func(struct work_struct *work)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700293{
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700294 struct restart_wq_data *r_work = container_of(work,
295 struct restart_wq_data, work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700296 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
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700306 if (r_work->use_restart_order)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700307 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 */
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800330 if (!mutex_trylock(shutdown_lock))
331 goto out;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700332
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700333 pr_debug("[%p]: Attempting to get powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700334
335 /* Now that we've acquired the shutdown lock, either we're the first to
336 * restart these subsystems or some other thread is doing the powerup
337 * sequence for these subsystems. In the latter case, panic and bail
338 * out, since a subsystem died in its powerup sequence.
339 */
340 if (!mutex_trylock(powerup_lock))
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700341 panic("%s[%p]: Subsystem died during powerup!",
342 __func__, current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700343
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700344 do_epoch_check(subsys);
345
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700346 /* Now it is necessary to take the registration lock. This is because
347 * the subsystem list in the SoC restart order will be traversed
348 * and it shouldn't be changed until _this_ restart sequence completes.
349 */
350 mutex_lock(&soc_order_reg_lock);
351
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700352 pr_debug("[%p]: Starting restart sequence for %s\n", current,
353 r_work->subsys->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700354
355 _send_notification_to_order(restart_list,
356 restart_list_count,
357 SUBSYS_BEFORE_SHUTDOWN);
358
359 for (i = 0; i < restart_list_count; i++) {
360
361 if (!restart_list[i])
362 continue;
363
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700364 pr_info("[%p]: Shutting down %s\n", current,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700365 restart_list[i]->name);
366
367 if (restart_list[i]->shutdown(subsys) < 0)
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700368 panic("subsys-restart: %s[%p]: Failed to shutdown %s!",
369 __func__, current, restart_list[i]->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700370 }
371
372 _send_notification_to_order(restart_list, restart_list_count,
373 SUBSYS_AFTER_SHUTDOWN);
374
375 /* Now that we've finished shutting down these subsystems, release the
376 * shutdown lock. If a subsystem restart request comes in for a
377 * subsystem in _this_ restart order after the unlock below, and
378 * before the powerup lock is released, panic and bail out.
379 */
380 mutex_unlock(shutdown_lock);
381
382 /* Collect ram dumps for all subsystems in order here */
383 for (i = 0; i < restart_list_count; i++) {
384 if (!restart_list[i])
385 continue;
386
387 if (restart_list[i]->ramdump)
388 if (restart_list[i]->ramdump(enable_ramdumps,
389 subsys) < 0)
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700390 pr_warn("%s[%p]: Ramdump failed.\n",
391 restart_list[i]->name, current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700392 }
393
394 _send_notification_to_order(restart_list,
395 restart_list_count,
396 SUBSYS_BEFORE_POWERUP);
397
398 for (i = restart_list_count - 1; i >= 0; i--) {
399
400 if (!restart_list[i])
401 continue;
402
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700403 pr_info("[%p]: Powering up %s\n", current,
404 restart_list[i]->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700405
406 if (restart_list[i]->powerup(subsys) < 0)
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700407 panic("%s[%p]: Failed to powerup %s!", __func__,
408 current, restart_list[i]->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700409 }
410
411 _send_notification_to_order(restart_list,
412 restart_list_count,
413 SUBSYS_AFTER_POWERUP);
414
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700415 pr_info("[%p]: Restart sequence for %s completed.\n",
416 current, r_work->subsys->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700417
418 mutex_unlock(powerup_lock);
419
420 mutex_unlock(&soc_order_reg_lock);
421
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700422 pr_debug("[%p]: Released powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700423
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800424out:
425 wake_unlock(&r_work->ssr_wake_lock);
426 wake_lock_destroy(&r_work->ssr_wake_lock);
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700427 kfree(r_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700428}
429
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700430static void __subsystem_restart(struct subsys_data *subsys)
431{
432 struct restart_wq_data *data = NULL;
433 int rc;
434
435 pr_debug("Restarting %s [level=%d]!\n", subsys->name,
436 restart_level);
437
438 data = kzalloc(sizeof(struct restart_wq_data), GFP_ATOMIC);
439 if (!data)
440 panic("%s: Unable to allocate memory to restart %s.",
441 __func__, subsys->name);
442
443 data->subsys = subsys;
444
445 if (restart_level != RESET_SUBSYS_INDEPENDENT)
446 data->use_restart_order = 1;
447
448 snprintf(data->wlname, sizeof(data->wlname), "ssr(%s)", subsys->name);
449 wake_lock_init(&data->ssr_wake_lock, WAKE_LOCK_SUSPEND, data->wlname);
450 wake_lock(&data->ssr_wake_lock);
451
452 INIT_WORK(&data->work, subsystem_restart_wq_func);
453 rc = queue_work(ssr_wq, &data->work);
454 if (rc < 0)
455 panic("%s: Unable to schedule work to restart %s (%d).",
456 __func__, subsys->name, rc);
457}
458
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700459int subsystem_restart(const char *subsys_name)
460{
461 struct subsys_data *subsys;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700462
463 if (!subsys_name) {
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700464 pr_err("Invalid subsystem name.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700465 return -EINVAL;
466 }
467
Vikram Mulukutla3b24c8c2011-12-02 15:12:53 -0800468 pr_info("Restart sequence requested for %s, restart_level = %d.\n",
469 subsys_name, restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700470
471 /* List of subsystems is protected by a lock. New subsystems can
472 * still come in.
473 */
474 subsys = _find_subsystem(subsys_name);
475
476 if (!subsys) {
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700477 pr_warn("Unregistered subsystem %s!\n", subsys_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700478 return -EINVAL;
479 }
480
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700481 switch (restart_level) {
482
483 case RESET_SUBSYS_COUPLED:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700484 case RESET_SUBSYS_INDEPENDENT:
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700485 __subsystem_restart(subsys);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700486 break;
487
488 case RESET_SOC:
Vikram Mulukutla3b24c8c2011-12-02 15:12:53 -0800489 panic("subsys-restart: Resetting the SoC - %s crashed.",
490 subsys->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700491 break;
492
493 default:
494 panic("subsys-restart: Unknown restart level!\n");
495 break;
496
497 }
498
499 return 0;
500}
501EXPORT_SYMBOL(subsystem_restart);
502
503int ssr_register_subsystem(struct subsys_data *subsys)
504{
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700505 unsigned long flags;
506
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700507 if (!subsys)
508 goto err;
509
510 if (!subsys->name)
511 goto err;
512
513 if (!subsys->powerup || !subsys->shutdown)
514 goto err;
515
516 subsys->notif_handle = subsys_notif_add_subsys(subsys->name);
517 subsys->restart_order = _update_restart_order(subsys);
518 subsys->single_restart_list[0] = subsys;
519
520 mutex_init(&subsys->shutdown_lock);
521 mutex_init(&subsys->powerup_lock);
522
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700523 spin_lock_irqsave(&subsystem_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700524 list_add(&subsys->list, &subsystem_list);
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700525 spin_unlock_irqrestore(&subsystem_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700526
527 return 0;
528
529err:
530 return -EINVAL;
531}
532EXPORT_SYMBOL(ssr_register_subsystem);
533
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700534static int ssr_panic_handler(struct notifier_block *this,
535 unsigned long event, void *ptr)
536{
537 struct subsys_data *subsys;
538
539 list_for_each_entry(subsys, &subsystem_list, list)
540 if (subsys->crash_shutdown)
541 subsys->crash_shutdown(subsys);
542 return NOTIFY_DONE;
543}
544
545static struct notifier_block panic_nb = {
546 .notifier_call = ssr_panic_handler,
547};
548
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700549static int __init ssr_init_soc_restart_orders(void)
550{
551 int i;
552
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700553 atomic_notifier_chain_register(&panic_notifier_list,
554 &panic_nb);
555
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700556 if (cpu_is_msm8x60()) {
557 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
558 mutex_init(&orders_8x60_all[i]->powerup_lock);
559 mutex_init(&orders_8x60_all[i]->shutdown_lock);
560 }
561
562 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
563 mutex_init(&orders_8x60_modems[i]->powerup_lock);
564 mutex_init(&orders_8x60_modems[i]->shutdown_lock);
565 }
566
567 restart_orders = orders_8x60_all;
568 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
569 }
570
Rohit Vaswanid0fb4182012-03-19 18:07:59 -0700571 if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm9615() ||
572 cpu_is_apq8064()) {
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700573 if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) {
574 restart_orders = restart_orders_8960_sglte;
575 n_restart_orders =
576 ARRAY_SIZE(restart_orders_8960_sglte);
577 } else {
578 restart_orders = restart_orders_8960;
579 n_restart_orders = ARRAY_SIZE(restart_orders_8960);
580 }
581 for (i = 0; i < n_restart_orders; i++) {
582 mutex_init(&restart_orders[i]->powerup_lock);
583 mutex_init(&restart_orders[i]->shutdown_lock);
584 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700585 }
586
587 if (restart_orders == NULL || n_restart_orders < 1) {
588 WARN_ON(1);
589 return -EINVAL;
590 }
591
592 return 0;
593}
594
595static int __init subsys_restart_init(void)
596{
597 int ret = 0;
598
599 restart_level = RESET_SOC;
600
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700601 ssr_wq = alloc_workqueue("ssr_wq", 0, 0);
602
603 if (!ssr_wq)
604 panic("Couldn't allocate workqueue for subsystem restart.\n");
605
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700606 ret = ssr_init_soc_restart_orders();
607
608 return ret;
609}
610
611arch_initcall(subsys_restart_init);
612
613MODULE_DESCRIPTION("Subsystem Restart Driver");
614MODULE_LICENSE("GPL v2");