blob: 42c1febf7f3642da19a6c2ddec67e33c0083e988 [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"};
Ameya Thakur2c0d2f22012-06-15 15:48:20 -070099/*SGLTE restart ordering info*/
100static const char * const order_8960_sglte[] = {"external_modem",
101 "modem"};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700102
103static struct subsys_soc_restart_order restart_orders_8960_one = {
104 .subsystem_list = order_8960,
105 .count = ARRAY_SIZE(order_8960),
106 .subsys_ptrs = {[ARRAY_SIZE(order_8960)] = NULL}
107 };
108
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700109static struct subsys_soc_restart_order restart_orders_8960_fusion_sglte = {
110 .subsystem_list = order_8960_sglte,
111 .count = ARRAY_SIZE(order_8960_sglte),
112 .subsys_ptrs = {[ARRAY_SIZE(order_8960_sglte)] = NULL}
113 };
114
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700115static struct subsys_soc_restart_order *restart_orders_8960[] = {
116 &restart_orders_8960_one,
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700117 };
118
119static struct subsys_soc_restart_order *restart_orders_8960_sglte[] = {
120 &restart_orders_8960_fusion_sglte,
121 };
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122
123/* These will be assigned to one of the sets above after
124 * runtime SoC identification.
125 */
126static struct subsys_soc_restart_order **restart_orders;
127static int n_restart_orders;
128
129module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR);
130
131static struct subsys_soc_restart_order *_update_restart_order(
132 struct subsys_data *subsys);
133
134int get_restart_level()
135{
136 return restart_level;
137}
138EXPORT_SYMBOL(get_restart_level);
139
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140static int restart_level_set(const char *val, struct kernel_param *kp)
141{
142 int ret;
143 int old_val = restart_level;
144
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800145 if (cpu_is_msm9615()) {
146 pr_err("Only Phase 1 subsystem restart is supported\n");
147 return -EINVAL;
148 }
149
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700150 ret = param_set_int(val, kp);
151 if (ret)
152 return ret;
153
154 switch (restart_level) {
155
156 case RESET_SOC:
157 case RESET_SUBSYS_COUPLED:
158 case RESET_SUBSYS_INDEPENDENT:
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700159 pr_info("Phase %d behavior activated.\n", restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160 break;
161
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700162 default:
163 restart_level = old_val;
164 return -EINVAL;
165 break;
166
167 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700168 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;
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700177 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700178
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700179 spin_lock_irqsave(&subsystem_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700180 list_for_each_entry(subsys, &subsystem_list, list)
181 if (!strncmp(subsys->name, subsys_name,
182 SUBSYS_NAME_MAX_LENGTH)) {
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700183 spin_unlock_irqrestore(&subsystem_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184 return subsys;
185 }
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700186 spin_unlock_irqrestore(&subsystem_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700187
188 return NULL;
189}
190
191static struct subsys_soc_restart_order *_update_restart_order(
192 struct subsys_data *subsys)
193{
194 int i, j;
195
196 if (!subsys)
197 return NULL;
198
199 if (!subsys->name)
200 return NULL;
201
202 mutex_lock(&soc_order_reg_lock);
203 for (j = 0; j < n_restart_orders; j++) {
204 for (i = 0; i < restart_orders[j]->count; i++)
205 if (!strncmp(restart_orders[j]->subsystem_list[i],
206 subsys->name, SUBSYS_NAME_MAX_LENGTH)) {
207
208 restart_orders[j]->subsys_ptrs[i] =
209 subsys;
210 mutex_unlock(&soc_order_reg_lock);
211 return restart_orders[j];
212 }
213 }
214
215 mutex_unlock(&soc_order_reg_lock);
216
217 return NULL;
218}
219
220static void _send_notification_to_order(struct subsys_data
221 **restart_list, int count,
222 enum subsys_notif_type notif_type)
223{
224 int i;
225
226 for (i = 0; i < count; i++)
227 if (restart_list[i])
228 subsys_notif_queue_notification(
229 restart_list[i]->notif_handle, notif_type);
230}
231
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700232static int max_restarts;
233module_param(max_restarts, int, 0644);
234
235static long max_history_time = 3600;
236module_param(max_history_time, long, 0644);
237
238static void do_epoch_check(struct subsys_data *subsys)
239{
240 int n = 0;
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600241 struct timeval *time_first = NULL, *curr_time;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700242 struct restart_log *r_log, *temp;
243 static int max_restarts_check;
244 static long max_history_time_check;
Jaeseong GIMb8499412012-06-26 10:36:57 -0700245#if defined(CONFIG_LGE_CRASH_HANDLER)
246 int ssr_magic_number = get_ssr_magic_number();
247#endif
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700248
249 mutex_lock(&restart_log_mutex);
250
251 max_restarts_check = max_restarts;
252 max_history_time_check = max_history_time;
253
254 /* Check if epoch checking is enabled */
255 if (!max_restarts_check)
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700256 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700257
258 r_log = kmalloc(sizeof(struct restart_log), GFP_KERNEL);
Matt Wagantalleecca342011-11-10 20:12:05 -0800259 if (!r_log)
260 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700261 r_log->subsys = subsys;
262 do_gettimeofday(&r_log->time);
263 curr_time = &r_log->time;
264 INIT_LIST_HEAD(&r_log->list);
265
266 list_add_tail(&r_log->list, &restart_log_list);
267
268 list_for_each_entry_safe(r_log, temp, &restart_log_list, list) {
269
270 if ((curr_time->tv_sec - r_log->time.tv_sec) >
271 max_history_time_check) {
272
273 pr_debug("Deleted node with restart_time = %ld\n",
274 r_log->time.tv_sec);
275 list_del(&r_log->list);
276 kfree(r_log);
277 continue;
278 }
279 if (!n) {
280 time_first = &r_log->time;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700281 pr_debug("Time_first: %ld\n", time_first->tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700282 }
283 n++;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700284 pr_debug("Restart_time: %ld\n", r_log->time.tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700285 }
286
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600287 if (time_first && n >= max_restarts_check) {
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700288 if ((curr_time->tv_sec - time_first->tv_sec) <
Jaeseong GIMb8499412012-06-26 10:36:57 -0700289 max_history_time_check) {
290#if defined(CONFIG_LGE_CRASH_HANDLER)
291 msm_set_restart_mode(ssr_magic_number | SUB_UNAB_THD);
292#endif
Iliyan Malcheva0899ce2012-06-29 15:28:25 -0700293 WARN(1, "Subsystems have crashed %d times in less than "\
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700294 "%ld seconds!", max_restarts_check,
295 max_history_time_check);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700296 }
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700297 }
298
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700299out:
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700300 mutex_unlock(&restart_log_mutex);
301}
302
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700303static void subsystem_restart_wq_func(struct work_struct *work)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700304{
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700305 struct restart_wq_data *r_work = container_of(work,
306 struct restart_wq_data, work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700307 struct subsys_data **restart_list;
308 struct subsys_data *subsys = r_work->subsys;
309 struct subsys_soc_restart_order *soc_restart_order = NULL;
310
311 struct mutex *powerup_lock;
312 struct mutex *shutdown_lock;
313
314 int i;
315 int restart_list_count = 0;
316
Jaeseong GIMb8499412012-06-26 10:36:57 -0700317#if defined(CONFIG_LGE_CRASH_HANDLER)
318 int ssr_magic_number = get_ssr_magic_number();
319#endif
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700320 if (r_work->use_restart_order)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700321 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 */
Jaeseong GIMb8499412012-06-26 10:36:57 -0700354 if (!mutex_trylock(powerup_lock)) {
355#if defined(CONFIG_LGE_CRASH_HANDLER)
356 msm_set_restart_mode(ssr_magic_number | SUB_THD_F_PWR);
357#endif
Iliyan Malcheva0899ce2012-06-29 15:28:25 -0700358 WARN(1, "%s[%p]: Subsystem died during powerup!",
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700359 __func__, current);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700360 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700361
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700362 do_epoch_check(subsys);
363
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700364 /* Now it is necessary to take the registration lock. This is because
365 * the subsystem list in the SoC restart order will be traversed
366 * and it shouldn't be changed until _this_ restart sequence completes.
367 */
368 mutex_lock(&soc_order_reg_lock);
369
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700370 pr_debug("[%p]: Starting restart sequence for %s\n", current,
371 r_work->subsys->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700372
373 _send_notification_to_order(restart_list,
374 restart_list_count,
375 SUBSYS_BEFORE_SHUTDOWN);
376
377 for (i = 0; i < restart_list_count; i++) {
378
379 if (!restart_list[i])
380 continue;
381
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700382 pr_info("[%p]: Shutting down %s\n", current,
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700383 restart_list[i]->name);
384
Jaeseong GIMb8499412012-06-26 10:36:57 -0700385 if (restart_list[i]->shutdown(subsys) < 0) {
386#if defined(CONFIG_LGE_CRASH_HANDLER)
387 msm_set_restart_mode(ssr_magic_number | SUB_THD_F_SD);
388#endif
Iliyan Malcheva0899ce2012-06-29 15:28:25 -0700389 WARN(1, "subsys-restart: %s[%p]: Failed to shutdown %s!",
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700390 __func__, current, restart_list[i]->name);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700391 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700392 }
393
394 _send_notification_to_order(restart_list, restart_list_count,
395 SUBSYS_AFTER_SHUTDOWN);
396
397 /* Now that we've finished shutting down these subsystems, release the
398 * shutdown lock. If a subsystem restart request comes in for a
399 * subsystem in _this_ restart order after the unlock below, and
400 * before the powerup lock is released, panic and bail out.
401 */
402 mutex_unlock(shutdown_lock);
403
404 /* Collect ram dumps for all subsystems in order here */
405 for (i = 0; i < restart_list_count; i++) {
406 if (!restart_list[i])
407 continue;
408
409 if (restart_list[i]->ramdump)
410 if (restart_list[i]->ramdump(enable_ramdumps,
411 subsys) < 0)
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700412 pr_warn("%s[%p]: Ramdump failed.\n",
413 restart_list[i]->name, current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700414 }
415
416 _send_notification_to_order(restart_list,
417 restart_list_count,
418 SUBSYS_BEFORE_POWERUP);
419
420 for (i = restart_list_count - 1; i >= 0; i--) {
421
422 if (!restart_list[i])
423 continue;
424
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700425 pr_info("[%p]: Powering up %s\n", current,
426 restart_list[i]->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700427
Jaeseong GIMb8499412012-06-26 10:36:57 -0700428 if (restart_list[i]->powerup(subsys) < 0) {
429#if defined(CONFIG_LGE_CRASH_HANDLER)
430 msm_set_restart_mode(ssr_magic_number | SUB_THD_F_PWR);
431#endif
Iliyan Malcheva0899ce2012-06-29 15:28:25 -0700432 WARN(1, "%s[%p]: Failed to powerup %s!", __func__,
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700433 current, restart_list[i]->name);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700434 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700435 }
436
437 _send_notification_to_order(restart_list,
438 restart_list_count,
439 SUBSYS_AFTER_POWERUP);
440
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700441 pr_info("[%p]: Restart sequence for %s completed.\n",
442 current, r_work->subsys->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700443
444 mutex_unlock(powerup_lock);
445
446 mutex_unlock(&soc_order_reg_lock);
447
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700448 pr_debug("[%p]: Released powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700449
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800450out:
451 wake_unlock(&r_work->ssr_wake_lock);
452 wake_lock_destroy(&r_work->ssr_wake_lock);
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700453 kfree(r_work);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700454}
455
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700456static void __subsystem_restart(struct subsys_data *subsys)
457{
458 struct restart_wq_data *data = NULL;
459 int rc;
Jaeseong GIMb8499412012-06-26 10:36:57 -0700460#if defined(CONFIG_LGE_CRASH_HANDLER)
461 int ssr_magic_number = get_ssr_magic_number();
462#endif
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700463
464 pr_debug("Restarting %s [level=%d]!\n", subsys->name,
465 restart_level);
466
467 data = kzalloc(sizeof(struct restart_wq_data), GFP_ATOMIC);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700468 if (!data) {
469#if defined(CONFIG_LGE_CRASH_HANDLER)
470 msm_set_restart_mode(ssr_magic_number | SUB_UNAB_THD);
471#endif
Iliyan Malcheva0899ce2012-06-29 15:28:25 -0700472 pr_err("%s: Unable to allocate memory to restart %s.",
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700473 __func__, subsys->name);
Iliyan Malcheva0899ce2012-06-29 15:28:25 -0700474 return;
Jaeseong GIMb8499412012-06-26 10:36:57 -0700475 }
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700476
477 data->subsys = subsys;
478
479 if (restart_level != RESET_SUBSYS_INDEPENDENT)
480 data->use_restart_order = 1;
481
482 snprintf(data->wlname, sizeof(data->wlname), "ssr(%s)", subsys->name);
483 wake_lock_init(&data->ssr_wake_lock, WAKE_LOCK_SUSPEND, data->wlname);
484 wake_lock(&data->ssr_wake_lock);
485
486 INIT_WORK(&data->work, subsystem_restart_wq_func);
487 rc = queue_work(ssr_wq, &data->work);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700488 if (rc < 0) {
489#if defined(CONFIG_LGE_CRASH_HANDLER)
490 msm_set_restart_mode(ssr_magic_number | SUB_UNAB_THD);
491#endif
Iliyan Malcheva0899ce2012-06-29 15:28:25 -0700492 pr_err("%s: Unable to schedule work to restart %s (%d).",
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700493 __func__, subsys->name, rc);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700494 }
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700495}
496
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700497int subsystem_restart(const char *subsys_name)
498{
499 struct subsys_data *subsys;
Jaeseong GIMb8499412012-06-26 10:36:57 -0700500#if defined(CONFIG_LGE_CRASH_HANDLER)
501 u32 ssr_magic_number;
502#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700503
504 if (!subsys_name) {
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700505 pr_err("Invalid subsystem name.\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700506 return -EINVAL;
507 }
508
Vikram Mulukutla3b24c8c2011-12-02 15:12:53 -0800509 pr_info("Restart sequence requested for %s, restart_level = %d.\n",
510 subsys_name, restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700511
512 /* List of subsystems is protected by a lock. New subsystems can
513 * still come in.
514 */
515 subsys = _find_subsystem(subsys_name);
516
517 if (!subsys) {
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700518 pr_warn("Unregistered subsystem %s!\n", subsys_name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700519 return -EINVAL;
520 }
521
Jaeseong GIMb8499412012-06-26 10:36:57 -0700522#if defined(CONFIG_LGE_CRASH_HANDLER)
523 set_ssr_magic_number(subsys_name);
524 ssr_magic_number = get_ssr_magic_number();
525#endif
526
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700527 switch (restart_level) {
528
529 case RESET_SUBSYS_COUPLED:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700530 case RESET_SUBSYS_INDEPENDENT:
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700531 __subsystem_restart(subsys);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700532 break;
533
534 case RESET_SOC:
Jaeseong GIMb8499412012-06-26 10:36:57 -0700535#if defined(CONFIG_LGE_CRASH_HANDLER)
536 msm_set_restart_mode(ssr_magic_number | SUB_RESET_SOC);
537#endif
Iliyan Malcheva0899ce2012-06-29 15:28:25 -0700538 WARN(1, "subsys-restart: Resetting the SoC - %s crashed.",
Vikram Mulukutla3b24c8c2011-12-02 15:12:53 -0800539 subsys->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700540 break;
541
542 default:
Jaeseong GIMb8499412012-06-26 10:36:57 -0700543#if defined(CONFIG_LGE_CRASH_HANDLER)
544 msm_set_restart_mode(ssr_magic_number | SUB_UNKNOWN);
545#endif
Iliyan Malcheva0899ce2012-06-29 15:28:25 -0700546 pr_err("subsys-restart: Unknown restart level!\n");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700547 break;
548
549 }
550
551 return 0;
552}
553EXPORT_SYMBOL(subsystem_restart);
554
555int ssr_register_subsystem(struct subsys_data *subsys)
556{
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700557 unsigned long flags;
558
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700559 if (!subsys)
560 goto err;
561
562 if (!subsys->name)
563 goto err;
564
565 if (!subsys->powerup || !subsys->shutdown)
566 goto err;
567
568 subsys->notif_handle = subsys_notif_add_subsys(subsys->name);
569 subsys->restart_order = _update_restart_order(subsys);
570 subsys->single_restart_list[0] = subsys;
571
572 mutex_init(&subsys->shutdown_lock);
573 mutex_init(&subsys->powerup_lock);
574
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700575 spin_lock_irqsave(&subsystem_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700576 list_add(&subsys->list, &subsystem_list);
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700577 spin_unlock_irqrestore(&subsystem_list_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700578
579 return 0;
580
581err:
582 return -EINVAL;
583}
584EXPORT_SYMBOL(ssr_register_subsystem);
585
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700586static int ssr_panic_handler(struct notifier_block *this,
587 unsigned long event, void *ptr)
588{
589 struct subsys_data *subsys;
590
591 list_for_each_entry(subsys, &subsystem_list, list)
592 if (subsys->crash_shutdown)
593 subsys->crash_shutdown(subsys);
594 return NOTIFY_DONE;
595}
596
597static struct notifier_block panic_nb = {
598 .notifier_call = ssr_panic_handler,
599};
600
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700601static int __init ssr_init_soc_restart_orders(void)
602{
603 int i;
604
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700605 atomic_notifier_chain_register(&panic_notifier_list,
606 &panic_nb);
607
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700608 if (cpu_is_msm8x60()) {
609 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
610 mutex_init(&orders_8x60_all[i]->powerup_lock);
611 mutex_init(&orders_8x60_all[i]->shutdown_lock);
612 }
613
614 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
615 mutex_init(&orders_8x60_modems[i]->powerup_lock);
616 mutex_init(&orders_8x60_modems[i]->shutdown_lock);
617 }
618
619 restart_orders = orders_8x60_all;
620 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
621 }
622
Stepan Moskovchenko0df9bb22012-07-06 18:19:15 -0700623 if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
Stepan Moskovchenko9c749262012-07-09 19:30:44 -0700624 cpu_is_msm9615() || cpu_is_apq8064() || cpu_is_msm8627() ||
625 cpu_is_msm8960ab()) {
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700626 if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) {
627 restart_orders = restart_orders_8960_sglte;
628 n_restart_orders =
629 ARRAY_SIZE(restart_orders_8960_sglte);
630 } else {
631 restart_orders = restart_orders_8960;
632 n_restart_orders = ARRAY_SIZE(restart_orders_8960);
633 }
634 for (i = 0; i < n_restart_orders; i++) {
635 mutex_init(&restart_orders[i]->powerup_lock);
636 mutex_init(&restart_orders[i]->shutdown_lock);
637 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700638 }
639
640 if (restart_orders == NULL || n_restart_orders < 1) {
641 WARN_ON(1);
642 return -EINVAL;
643 }
644
645 return 0;
646}
647
648static int __init subsys_restart_init(void)
649{
Jaeseong GIM04e39432012-07-18 18:09:00 -0700650 restart_level = RESET_SUBSYS_INDEPENDENT;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700651
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700652 ssr_wq = alloc_workqueue("ssr_wq", 0, 0);
653
Iliyan Malcheva0899ce2012-06-29 15:28:25 -0700654 if (!ssr_wq) {
655 pr_err("%s: out of memory\n", __func__);
656 return -ENOMEM;
657 }
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700658
Iliyan Malcheva0899ce2012-06-29 15:28:25 -0700659 return ssr_init_soc_restart_orders();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700660}
661
662arch_initcall(subsys_restart_init);
663
664MODULE_DESCRIPTION("Subsystem Restart Driver");
665MODULE_LICENSE("GPL v2");