blob: 64cfe102b195c10d76d0bba89539642a7bba689d [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>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070020#include <linux/delay.h>
21#include <linux/list.h>
22#include <linux/io.h>
23#include <linux/kthread.h>
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070024#include <linux/time.h>
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -080025#include <linux/wakelock.h>
26#include <linux/suspend.h>
Stephen Boyd0ebf7212012-04-30 20:42:35 -070027#include <linux/mutex.h>
Stephen Boydc68ee642012-05-01 17:02:45 -070028#include <linux/slab.h>
Stephen Boyd497ef402012-06-21 12:54:40 -070029#include <linux/spinlock.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030
31#include <asm/current.h>
32
33#include <mach/peripheral-loader.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070034#include <mach/socinfo.h>
35#include <mach/subsystem_notif.h>
36#include <mach/subsystem_restart.h>
37
Jaeseong GIMb8499412012-06-26 10:36:57 -070038#if defined(CONFIG_LGE_CRASH_HANDLER)
39#include <mach/restart.h>
40#include <mach/board_lge.h>
41#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070042#include "smd_private.h"
43
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070044struct subsys_soc_restart_order {
45 const char * const *subsystem_list;
46 int count;
47
48 struct mutex shutdown_lock;
49 struct mutex powerup_lock;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070050 struct subsys_device *subsys_ptrs[];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051};
52
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070053struct restart_log {
54 struct timeval time;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070055 struct subsys_device *dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070056 struct list_head list;
57};
58
Stephen Boyd0ebf7212012-04-30 20:42:35 -070059struct subsys_device {
60 struct subsys_desc *desc;
61 struct list_head list;
Stephen Boyd497ef402012-06-21 12:54:40 -070062 struct wake_lock wake_lock;
63 char wlname[64];
64 struct work_struct work;
65 spinlock_t restart_lock;
Stephen Boyd77c22742012-07-24 18:46:45 -070066 int restart_count;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070067
68 void *notify;
69
70 struct mutex shutdown_lock;
71 struct mutex powerup_lock;
72
73 void *restart_order;
74};
75
Jaeseong GIMcbea9362012-10-10 15:42:17 +090076static int enable_ramdumps;
Stephen Boydc68ee642012-05-01 17:02:45 -070077module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR);
78
Vikram Mulukutla69177e12012-03-21 20:51:43 -070079struct workqueue_struct *ssr_wq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070081static LIST_HEAD(restart_log_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070082static LIST_HEAD(subsystem_list);
Stephen Boyd0ebf7212012-04-30 20:42:35 -070083static DEFINE_MUTEX(subsystem_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070084static DEFINE_MUTEX(soc_order_reg_lock);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070085static DEFINE_MUTEX(restart_log_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070086
87/* SOC specific restart orders go here */
88
89#define DEFINE_SINGLE_RESTART_ORDER(name, order) \
90 static struct subsys_soc_restart_order __##name = { \
91 .subsystem_list = order, \
92 .count = ARRAY_SIZE(order), \
93 .subsys_ptrs = {[ARRAY_SIZE(order)] = NULL} \
94 }; \
95 static struct subsys_soc_restart_order *name[] = { \
96 &__##name, \
97 }
98
99/* MSM 8x60 restart ordering info */
100static const char * const _order_8x60_all[] = {
101 "external_modem", "modem", "lpass"
102};
103DEFINE_SINGLE_RESTART_ORDER(orders_8x60_all, _order_8x60_all);
104
105static const char * const _order_8x60_modems[] = {"external_modem", "modem"};
106DEFINE_SINGLE_RESTART_ORDER(orders_8x60_modems, _order_8x60_modems);
107
108/* MSM 8960 restart ordering info */
109static const char * const order_8960[] = {"modem", "lpass"};
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700110/*SGLTE restart ordering info*/
111static const char * const order_8960_sglte[] = {"external_modem",
112 "modem"};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700113
114static struct subsys_soc_restart_order restart_orders_8960_one = {
115 .subsystem_list = order_8960,
116 .count = ARRAY_SIZE(order_8960),
117 .subsys_ptrs = {[ARRAY_SIZE(order_8960)] = NULL}
118 };
119
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700120static struct subsys_soc_restart_order restart_orders_8960_fusion_sglte = {
121 .subsystem_list = order_8960_sglte,
122 .count = ARRAY_SIZE(order_8960_sglte),
123 .subsys_ptrs = {[ARRAY_SIZE(order_8960_sglte)] = NULL}
124 };
125
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700126static struct subsys_soc_restart_order *restart_orders_8960[] = {
127 &restart_orders_8960_one,
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700128 };
129
130static struct subsys_soc_restart_order *restart_orders_8960_sglte[] = {
131 &restart_orders_8960_fusion_sglte,
132 };
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700133
134/* These will be assigned to one of the sets above after
135 * runtime SoC identification.
136 */
137static struct subsys_soc_restart_order **restart_orders;
138static int n_restart_orders;
139
Brian Muramatsu0f0df2f2012-08-01 23:30:40 -0700140static int restart_level = RESET_SUBSYS_INDEPENDENT;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700141
142int get_restart_level()
143{
144 return restart_level;
145}
146EXPORT_SYMBOL(get_restart_level);
147
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700148static int restart_level_set(const char *val, struct kernel_param *kp)
149{
150 int ret;
151 int old_val = restart_level;
152
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800153 if (cpu_is_msm9615()) {
154 pr_err("Only Phase 1 subsystem restart is supported\n");
155 return -EINVAL;
156 }
157
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700158 ret = param_set_int(val, kp);
159 if (ret)
160 return ret;
161
162 switch (restart_level) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700163 case RESET_SOC:
164 case RESET_SUBSYS_COUPLED:
165 case RESET_SUBSYS_INDEPENDENT:
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700166 pr_info("Phase %d behavior activated.\n", restart_level);
Stephen Boydc68ee642012-05-01 17:02:45 -0700167 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700168 default:
169 restart_level = old_val;
170 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700172 return 0;
173}
174
175module_param_call(restart_level, restart_level_set, param_get_int,
176 &restart_level, 0644);
177
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700178static struct subsys_soc_restart_order *
179update_restart_order(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700180{
181 int i, j;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700182 struct subsys_soc_restart_order *order;
183 const char *name = dev->desc->name;
184 int len = SUBSYS_NAME_MAX_LENGTH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700185
186 mutex_lock(&soc_order_reg_lock);
187 for (j = 0; j < n_restart_orders; j++) {
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700188 order = restart_orders[j];
189 for (i = 0; i < order->count; i++) {
190 if (!strncmp(order->subsystem_list[i], name, len)) {
191 order->subsys_ptrs[i] = dev;
192 goto found;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700193 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700194 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700195 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700196 order = NULL;
197found:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700198 mutex_unlock(&soc_order_reg_lock);
199
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700200 return order;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700201}
202
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700203static int max_restarts;
204module_param(max_restarts, int, 0644);
205
206static long max_history_time = 3600;
207module_param(max_history_time, long, 0644);
208
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700209static void do_epoch_check(struct subsys_device *dev)
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700210{
211 int n = 0;
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600212 struct timeval *time_first = NULL, *curr_time;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700213 struct restart_log *r_log, *temp;
214 static int max_restarts_check;
215 static long max_history_time_check;
Jaeseong GIMb8499412012-06-26 10:36:57 -0700216#if defined(CONFIG_LGE_CRASH_HANDLER)
217 int ssr_magic_number = get_ssr_magic_number();
218#endif
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700219
220 mutex_lock(&restart_log_mutex);
221
222 max_restarts_check = max_restarts;
223 max_history_time_check = max_history_time;
224
225 /* Check if epoch checking is enabled */
226 if (!max_restarts_check)
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700227 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700228
229 r_log = kmalloc(sizeof(struct restart_log), GFP_KERNEL);
Matt Wagantalleecca342011-11-10 20:12:05 -0800230 if (!r_log)
231 goto out;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700232 r_log->dev = dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700233 do_gettimeofday(&r_log->time);
234 curr_time = &r_log->time;
235 INIT_LIST_HEAD(&r_log->list);
236
237 list_add_tail(&r_log->list, &restart_log_list);
238
239 list_for_each_entry_safe(r_log, temp, &restart_log_list, list) {
240
241 if ((curr_time->tv_sec - r_log->time.tv_sec) >
242 max_history_time_check) {
243
244 pr_debug("Deleted node with restart_time = %ld\n",
245 r_log->time.tv_sec);
246 list_del(&r_log->list);
247 kfree(r_log);
248 continue;
249 }
250 if (!n) {
251 time_first = &r_log->time;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700252 pr_debug("Time_first: %ld\n", time_first->tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700253 }
254 n++;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700255 pr_debug("Restart_time: %ld\n", r_log->time.tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700256 }
257
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600258 if (time_first && n >= max_restarts_check) {
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700259 if ((curr_time->tv_sec - time_first->tv_sec) <
Jaeseong GIMb8499412012-06-26 10:36:57 -0700260 max_history_time_check) {
261#if defined(CONFIG_LGE_CRASH_HANDLER)
262 msm_set_restart_mode(ssr_magic_number | SUB_UNAB_THD);
263#endif
Iliyan Malcheva0899ce2012-06-29 15:28:25 -0700264 WARN(1, "Subsystems have crashed %d times in less than "\
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700265 "%ld seconds!", max_restarts_check,
266 max_history_time_check);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700267 }
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700268 }
269
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700270out:
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700271 mutex_unlock(&restart_log_mutex);
272}
273
Stephen Boyd0daae152012-05-01 17:36:15 -0700274static void for_each_subsys_device(struct subsys_device **list, unsigned count,
275 void *data, void (*fn)(struct subsys_device *, void *))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700276{
Stephen Boyd0daae152012-05-01 17:36:15 -0700277 while (count--) {
278 struct subsys_device *dev = *list++;
279 if (!dev)
280 continue;
281 fn(dev, data);
282 }
283}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700284
Stephen Boyd0daae152012-05-01 17:36:15 -0700285static void __send_notification_to_order(struct subsys_device *dev, void *data)
286{
287 enum subsys_notif_type type = (enum subsys_notif_type)data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700288
Stephen Boyd0daae152012-05-01 17:36:15 -0700289 subsys_notif_queue_notification(dev->notify, type);
290}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291
Stephen Boyd0daae152012-05-01 17:36:15 -0700292static void send_notification_to_order(struct subsys_device **l, unsigned n,
293 enum subsys_notif_type t)
294{
295 for_each_subsys_device(l, n, (void *)t, __send_notification_to_order);
296}
297
298static void subsystem_shutdown(struct subsys_device *dev, void *data)
299{
300 const char *name = dev->desc->name;
Jaeseong GIMb8499412012-06-26 10:36:57 -0700301#if defined(CONFIG_LGE_CRASH_HANDLER)
302 int ssr_magic_number = get_ssr_magic_number();
303#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700304
Stephen Boyd0daae152012-05-01 17:36:15 -0700305 pr_info("[%p]: Shutting down %s\n", current, name);
Brian Muramatsu0f0df2f2012-08-01 23:30:40 -0700306 if (dev->desc->shutdown(dev->desc) < 0) {
307#if defined(CONFIG_LGE_CRASH_HANDLER)
308 msm_set_restart_mode(ssr_magic_number | SUB_THD_F_SD);
309#endif
310 WARN(1, "subsys-restart: [%p]: Failed to shutdown %s!",
Stephen Boyd0daae152012-05-01 17:36:15 -0700311 current, name);
Brian Muramatsu0f0df2f2012-08-01 23:30:40 -0700312 }
Stephen Boyd0daae152012-05-01 17:36:15 -0700313}
314
315static void subsystem_ramdump(struct subsys_device *dev, void *data)
316{
317 const char *name = dev->desc->name;
318
319 if (dev->desc->ramdump)
320 if (dev->desc->ramdump(enable_ramdumps, dev->desc) < 0)
321 pr_warn("%s[%p]: Ramdump failed.\n", name, current);
322}
323
324static void subsystem_powerup(struct subsys_device *dev, void *data)
325{
326 const char *name = dev->desc->name;
Brian Muramatsu0f0df2f2012-08-01 23:30:40 -0700327#if defined(CONFIG_LGE_CRASH_HANDLER)
328 int ssr_magic_number = get_ssr_magic_number();
329#endif
Stephen Boyd0daae152012-05-01 17:36:15 -0700330
331 pr_info("[%p]: Powering up %s\n", current, name);
Brian Muramatsu0f0df2f2012-08-01 23:30:40 -0700332 if (dev->desc->powerup(dev->desc) < 0) {
333#if defined(CONFIG_LGE_CRASH_HANDLER)
334 msm_set_restart_mode(ssr_magic_number | SUB_THD_F_PWR);
335#endif
336 WARN(1, "[%p]: Failed to powerup %s!", current, name);
337 }
Stephen Boyd0daae152012-05-01 17:36:15 -0700338}
339
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700340static void subsystem_restart_wq_func(struct work_struct *work)
341{
Stephen Boyd497ef402012-06-21 12:54:40 -0700342 struct subsys_device *dev = container_of(work,
343 struct subsys_device, work);
Stephen Boyd0daae152012-05-01 17:36:15 -0700344 struct subsys_device **list;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700345 struct subsys_desc *desc = dev->desc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700346 struct subsys_soc_restart_order *soc_restart_order = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700347 struct mutex *powerup_lock;
348 struct mutex *shutdown_lock;
Stephen Boyd0daae152012-05-01 17:36:15 -0700349 unsigned count;
Stephen Boyd497ef402012-06-21 12:54:40 -0700350 unsigned long flags;
Brian Muramatsu0f0df2f2012-08-01 23:30:40 -0700351#if defined(CONFIG_LGE_CRASH_HANDLER)
352 int ssr_magic_number = get_ssr_magic_number();
353#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700354
Stephen Boyda82ee082012-06-21 12:35:49 -0700355 if (restart_level != RESET_SUBSYS_INDEPENDENT)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700356 soc_restart_order = dev->restart_order;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700357
Stephen Boydc68ee642012-05-01 17:02:45 -0700358 /*
359 * It's OK to not take the registration lock at this point.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700360 * This is because the subsystem list inside the relevant
361 * restart order is not being traversed.
362 */
363 if (!soc_restart_order) {
Stephen Boyd0daae152012-05-01 17:36:15 -0700364 list = &dev;
365 count = 1;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700366 powerup_lock = &dev->powerup_lock;
367 shutdown_lock = &dev->shutdown_lock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700368 } else {
Stephen Boyd0daae152012-05-01 17:36:15 -0700369 list = soc_restart_order->subsys_ptrs;
370 count = soc_restart_order->count;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700371 powerup_lock = &soc_restart_order->powerup_lock;
372 shutdown_lock = &soc_restart_order->shutdown_lock;
373 }
374
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700375 pr_debug("[%p]: Attempting to get shutdown lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700376
Stephen Boydc68ee642012-05-01 17:02:45 -0700377 /*
378 * Try to acquire shutdown_lock. If this fails, these subsystems are
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700379 * already being restarted - return.
380 */
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800381 if (!mutex_trylock(shutdown_lock))
382 goto out;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700383
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700384 pr_debug("[%p]: Attempting to get powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700385
Stephen Boydc68ee642012-05-01 17:02:45 -0700386 /*
387 * Now that we've acquired the shutdown lock, either we're the first to
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700388 * restart these subsystems or some other thread is doing the powerup
389 * sequence for these subsystems. In the latter case, panic and bail
390 * out, since a subsystem died in its powerup sequence.
391 */
Jaeseong GIMb8499412012-06-26 10:36:57 -0700392 if (!mutex_trylock(powerup_lock)) {
393#if defined(CONFIG_LGE_CRASH_HANDLER)
394 msm_set_restart_mode(ssr_magic_number | SUB_THD_F_PWR);
395#endif
Iliyan Malcheva0899ce2012-06-29 15:28:25 -0700396 WARN(1, "%s[%p]: Subsystem died during powerup!",
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700397 __func__, current);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700398 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700399
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700400 do_epoch_check(dev);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700401
Stephen Boydc68ee642012-05-01 17:02:45 -0700402 /*
403 * It's necessary to take the registration lock because the subsystem
404 * list in the SoC restart order will be traversed and it shouldn't be
405 * changed until _this_ restart sequence completes.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406 */
407 mutex_lock(&soc_order_reg_lock);
408
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700409 pr_debug("[%p]: Starting restart sequence for %s\n", current,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700410 desc->name);
Stephen Boyd0daae152012-05-01 17:36:15 -0700411 send_notification_to_order(list, count, SUBSYS_BEFORE_SHUTDOWN);
412 for_each_subsys_device(list, count, NULL, subsystem_shutdown);
413 send_notification_to_order(list, count, SUBSYS_AFTER_SHUTDOWN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700414
Stephen Boydc68ee642012-05-01 17:02:45 -0700415 /*
416 * Now that we've finished shutting down these subsystems, release the
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700417 * shutdown lock. If a subsystem restart request comes in for a
418 * subsystem in _this_ restart order after the unlock below, and
419 * before the powerup lock is released, panic and bail out.
420 */
421 mutex_unlock(shutdown_lock);
422
423 /* Collect ram dumps for all subsystems in order here */
Stephen Boyd0daae152012-05-01 17:36:15 -0700424 for_each_subsys_device(list, count, NULL, subsystem_ramdump);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700425
Stephen Boyd0daae152012-05-01 17:36:15 -0700426 send_notification_to_order(list, count, SUBSYS_BEFORE_POWERUP);
427 for_each_subsys_device(list, count, NULL, subsystem_powerup);
428 send_notification_to_order(list, count, SUBSYS_AFTER_POWERUP);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700429
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700430 pr_info("[%p]: Restart sequence for %s completed.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700431 current, desc->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700432
433 mutex_unlock(powerup_lock);
434
435 mutex_unlock(&soc_order_reg_lock);
436
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700437 pr_debug("[%p]: Released powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700438
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800439out:
Stephen Boyd497ef402012-06-21 12:54:40 -0700440 spin_lock_irqsave(&dev->restart_lock, flags);
Stephen Boyd77c22742012-07-24 18:46:45 -0700441 dev->restart_count--;
442 if (!dev->restart_count)
443 wake_unlock(&dev->wake_lock);
Stephen Boyd497ef402012-06-21 12:54:40 -0700444 spin_unlock_irqrestore(&dev->restart_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700445}
446
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700447static void __subsystem_restart_dev(struct subsys_device *dev)
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700448{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700449 struct subsys_desc *desc = dev->desc;
Stephen Boyd497ef402012-06-21 12:54:40 -0700450 unsigned long flags;
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700451
Stephen Boyd77c22742012-07-24 18:46:45 -0700452 pr_debug("Restarting %s [level=%d]!\n", desc->name, restart_level);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700453
Stephen Boyd77c22742012-07-24 18:46:45 -0700454 spin_lock_irqsave(&dev->restart_lock, flags);
455 if (!dev->restart_count)
Stephen Boyd497ef402012-06-21 12:54:40 -0700456 wake_lock(&dev->wake_lock);
Stephen Boyd77c22742012-07-24 18:46:45 -0700457 dev->restart_count++;
Stephen Boyd497ef402012-06-21 12:54:40 -0700458 spin_unlock_irqrestore(&dev->restart_lock, flags);
Stephen Boyd77c22742012-07-24 18:46:45 -0700459
460 if (!queue_work(ssr_wq, &dev->work)) {
461 spin_lock_irqsave(&dev->restart_lock, flags);
462 dev->restart_count--;
463 if (!dev->restart_count)
464 wake_unlock(&dev->wake_lock);
465 spin_unlock_irqrestore(&dev->restart_lock, flags);
466 }
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700467}
468
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700469int subsystem_restart_dev(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700470{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700471 const char *name = dev->desc->name;
Jaeseong GIMb8499412012-06-26 10:36:57 -0700472#if defined(CONFIG_LGE_CRASH_HANDLER)
473 u32 ssr_magic_number;
474#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700475
Vikram Mulukutla3b24c8c2011-12-02 15:12:53 -0800476 pr_info("Restart sequence requested for %s, restart_level = %d.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700477 name, restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700478
Jaeseong GIMb8499412012-06-26 10:36:57 -0700479#if defined(CONFIG_LGE_CRASH_HANDLER)
Brian Muramatsu0f0df2f2012-08-01 23:30:40 -0700480 set_ssr_magic_number(name);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700481 ssr_magic_number = get_ssr_magic_number();
482#endif
483
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700484 switch (restart_level) {
485
486 case RESET_SUBSYS_COUPLED:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700487 case RESET_SUBSYS_INDEPENDENT:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700488 __subsystem_restart_dev(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700489 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700490 case RESET_SOC:
Jaeseong GIMb8499412012-06-26 10:36:57 -0700491#if defined(CONFIG_LGE_CRASH_HANDLER)
492 msm_set_restart_mode(ssr_magic_number | SUB_RESET_SOC);
493#endif
Brian Muramatsu0f0df2f2012-08-01 23:30:40 -0700494 WARN(1, "subsys-restart: Resetting the SoC - %s crashed.", name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700495 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700496 default:
Jaeseong GIMb8499412012-06-26 10:36:57 -0700497#if defined(CONFIG_LGE_CRASH_HANDLER)
498 msm_set_restart_mode(ssr_magic_number | SUB_UNKNOWN);
499#endif
Iliyan Malcheva0899ce2012-06-29 15:28:25 -0700500 pr_err("subsys-restart: Unknown restart level!\n");
Stephen Boydc68ee642012-05-01 17:02:45 -0700501 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700502 }
503
504 return 0;
505}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700506EXPORT_SYMBOL(subsystem_restart_dev);
507
508int subsystem_restart(const char *name)
509{
510 struct subsys_device *dev;
511
512 mutex_lock(&subsystem_list_lock);
513 list_for_each_entry(dev, &subsystem_list, list)
514 if (!strncmp(dev->desc->name, name, SUBSYS_NAME_MAX_LENGTH))
515 goto found;
516 dev = NULL;
517found:
518 mutex_unlock(&subsystem_list_lock);
519 if (dev)
520 return subsystem_restart_dev(dev);
521 return -ENODEV;
522}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700523EXPORT_SYMBOL(subsystem_restart);
524
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700525struct subsys_device *subsys_register(struct subsys_desc *desc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700526{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700527 struct subsys_device *dev;
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700528
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700529 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
530 if (!dev)
531 return ERR_PTR(-ENOMEM);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700532
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700533 dev->desc = desc;
534 dev->notify = subsys_notif_add_subsys(desc->name);
535 dev->restart_order = update_restart_order(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700536
Stephen Boyd497ef402012-06-21 12:54:40 -0700537 snprintf(dev->wlname, sizeof(dev->wlname), "ssr(%s)", desc->name);
538 wake_lock_init(&dev->wake_lock, WAKE_LOCK_SUSPEND, dev->wlname);
539 INIT_WORK(&dev->work, subsystem_restart_wq_func);
540 spin_lock_init(&dev->restart_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700541
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700542 mutex_init(&dev->shutdown_lock);
543 mutex_init(&dev->powerup_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700544
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700545 mutex_lock(&subsystem_list_lock);
546 list_add(&dev->list, &subsystem_list);
547 mutex_unlock(&subsystem_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700548
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700549 return dev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700550}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700551EXPORT_SYMBOL(subsys_register);
552
553void subsys_unregister(struct subsys_device *dev)
554{
555 if (IS_ERR_OR_NULL(dev))
556 return;
557 mutex_lock(&subsystem_list_lock);
558 list_del(&dev->list);
559 mutex_unlock(&subsystem_list_lock);
Stephen Boyd497ef402012-06-21 12:54:40 -0700560 wake_lock_destroy(&dev->wake_lock);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700561 kfree(dev);
562}
563EXPORT_SYMBOL(subsys_unregister);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700564
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700565static int ssr_panic_handler(struct notifier_block *this,
566 unsigned long event, void *ptr)
567{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700568 struct subsys_device *dev;
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700569
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700570 list_for_each_entry(dev, &subsystem_list, list)
571 if (dev->desc->crash_shutdown)
572 dev->desc->crash_shutdown(dev->desc);
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700573 return NOTIFY_DONE;
574}
575
576static struct notifier_block panic_nb = {
577 .notifier_call = ssr_panic_handler,
578};
579
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700580static int __init ssr_init_soc_restart_orders(void)
581{
582 int i;
583
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700584 atomic_notifier_chain_register(&panic_notifier_list,
585 &panic_nb);
586
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700587 if (cpu_is_msm8x60()) {
588 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
589 mutex_init(&orders_8x60_all[i]->powerup_lock);
590 mutex_init(&orders_8x60_all[i]->shutdown_lock);
591 }
592
593 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
594 mutex_init(&orders_8x60_modems[i]->powerup_lock);
595 mutex_init(&orders_8x60_modems[i]->shutdown_lock);
596 }
597
598 restart_orders = orders_8x60_all;
599 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
600 }
601
Stepan Moskovchenko0df9bb22012-07-06 18:19:15 -0700602 if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
Stepan Moskovchenko9c749262012-07-09 19:30:44 -0700603 cpu_is_msm9615() || cpu_is_apq8064() || cpu_is_msm8627() ||
604 cpu_is_msm8960ab()) {
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700605 if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) {
606 restart_orders = restart_orders_8960_sglte;
607 n_restart_orders =
608 ARRAY_SIZE(restart_orders_8960_sglte);
609 } else {
610 restart_orders = restart_orders_8960;
611 n_restart_orders = ARRAY_SIZE(restart_orders_8960);
612 }
613 for (i = 0; i < n_restart_orders; i++) {
614 mutex_init(&restart_orders[i]->powerup_lock);
615 mutex_init(&restart_orders[i]->shutdown_lock);
616 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700617 }
618
619 if (restart_orders == NULL || n_restart_orders < 1) {
620 WARN_ON(1);
621 return -EINVAL;
622 }
623
624 return 0;
625}
626
627static int __init subsys_restart_init(void)
628{
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700629 ssr_wq = alloc_workqueue("ssr_wq", 0, 0);
Iliyan Malcheva0899ce2012-06-29 15:28:25 -0700630 if (!ssr_wq) {
631 pr_err("%s: out of memory\n", __func__);
632 return -ENOMEM;
633 }
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700634
Iliyan Malcheva0899ce2012-06-29 15:28:25 -0700635 return ssr_init_soc_restart_orders();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700636}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700637arch_initcall(subsys_restart_init);
638
639MODULE_DESCRIPTION("Subsystem Restart Driver");
640MODULE_LICENSE("GPL v2");