blob: 0008629fb9f8d911de7d8d4d0da9877d56241548 [file] [log] [blame]
Duy Truonge833aca2013-02-12 13:35:08 -08001/* Copyright (c) 2011-2012, The Linux Foundation. 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
38#include "smd_private.h"
39
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070040struct subsys_soc_restart_order {
41 const char * const *subsystem_list;
42 int count;
43
44 struct mutex shutdown_lock;
45 struct mutex powerup_lock;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070046 struct subsys_device *subsys_ptrs[];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070047};
48
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070049struct restart_log {
50 struct timeval time;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070051 struct subsys_device *dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070052 struct list_head list;
53};
54
Stephen Boyd8f57c0f2012-09-18 11:31:02 -070055enum subsys_state {
56 SUBSYS_OFFLINE,
57 SUBSYS_ONLINE,
58 SUBSYS_CRASHED,
59};
60
61static const char * const subsys_states[] = {
62 [SUBSYS_OFFLINE] = "OFFLINE",
63 [SUBSYS_ONLINE] = "ONLINE",
64 [SUBSYS_CRASHED] = "CRASHED",
65};
66
Stephen Boyd0ebf7212012-04-30 20:42:35 -070067struct subsys_device {
68 struct subsys_desc *desc;
69 struct list_head list;
Stephen Boyd497ef402012-06-21 12:54:40 -070070 struct wake_lock wake_lock;
71 char wlname[64];
72 struct work_struct work;
73 spinlock_t restart_lock;
Stephen Boyd8f57c0f2012-09-18 11:31:02 -070074 bool restarting;
75 enum subsys_state state;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070076
77 void *notify;
78
79 struct mutex shutdown_lock;
80 struct mutex powerup_lock;
81
82 void *restart_order;
83};
84
Jaeseong GIMcbea9362012-10-10 15:42:17 +090085static int enable_ramdumps;
Stephen Boydc68ee642012-05-01 17:02:45 -070086module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR);
87
Vikram Mulukutla69177e12012-03-21 20:51:43 -070088struct workqueue_struct *ssr_wq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070089
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070090static LIST_HEAD(restart_log_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070091static LIST_HEAD(subsystem_list);
Stephen Boyd0ebf7212012-04-30 20:42:35 -070092static DEFINE_MUTEX(subsystem_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070093static DEFINE_MUTEX(soc_order_reg_lock);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070094static DEFINE_MUTEX(restart_log_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070095
96/* SOC specific restart orders go here */
97
98#define DEFINE_SINGLE_RESTART_ORDER(name, order) \
99 static struct subsys_soc_restart_order __##name = { \
100 .subsystem_list = order, \
101 .count = ARRAY_SIZE(order), \
102 .subsys_ptrs = {[ARRAY_SIZE(order)] = NULL} \
103 }; \
104 static struct subsys_soc_restart_order *name[] = { \
105 &__##name, \
106 }
107
108/* MSM 8x60 restart ordering info */
109static const char * const _order_8x60_all[] = {
110 "external_modem", "modem", "lpass"
111};
112DEFINE_SINGLE_RESTART_ORDER(orders_8x60_all, _order_8x60_all);
113
114static const char * const _order_8x60_modems[] = {"external_modem", "modem"};
115DEFINE_SINGLE_RESTART_ORDER(orders_8x60_modems, _order_8x60_modems);
116
Flemmard3394eca2013-04-15 00:20:20 -0700117#ifdef CONFIG_MACH_HTC
118/* MSM 8960 restart ordering info */
119static const char * const order_8960[] = {"modem", "lpass"};
120
121static struct subsys_soc_restart_order restart_orders_8960_one = {
122 .subsystem_list = order_8960,
123 .count = ARRAY_SIZE(order_8960),
124 .subsys_ptrs = {[ARRAY_SIZE(order_8960)] = NULL}
125 };
126
127static struct subsys_soc_restart_order *restart_orders_8960[] = {
128 &restart_orders_8960_one,
129 };
130#else
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700131/*SGLTE restart ordering info*/
132static const char * const order_8960_sglte[] = {"external_modem",
133 "modem"};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700134
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700135static struct subsys_soc_restart_order restart_orders_8960_fusion_sglte = {
136 .subsystem_list = order_8960_sglte,
137 .count = ARRAY_SIZE(order_8960_sglte),
138 .subsys_ptrs = {[ARRAY_SIZE(order_8960_sglte)] = NULL}
139 };
140
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700141static struct subsys_soc_restart_order *restart_orders_8960_sglte[] = {
142 &restart_orders_8960_fusion_sglte,
143 };
Flemmard3394eca2013-04-15 00:20:20 -0700144#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700145
Joel Kinge4f98902013-03-25 13:12:27 -0700146/* SGLTE2 restart ordering info*/
147static const char * const order_8064_sglte2[] = {"external_modem",
148 "external_modem_mdm"};
149
150static struct subsys_soc_restart_order restart_orders_8064_fusion_sglte2 = {
151 .subsystem_list = order_8064_sglte2,
152 .count = ARRAY_SIZE(order_8064_sglte2),
153 .subsys_ptrs = {[ARRAY_SIZE(order_8064_sglte2)] = NULL}
154 };
155
156static struct subsys_soc_restart_order *restart_orders_8064_sglte2[] = {
157 &restart_orders_8064_fusion_sglte2,
158 };
159
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160/* These will be assigned to one of the sets above after
161 * runtime SoC identification.
162 */
163static struct subsys_soc_restart_order **restart_orders;
164static int n_restart_orders;
165
Brian Muramatsu0f0df2f2012-08-01 23:30:40 -0700166static int restart_level = RESET_SUBSYS_INDEPENDENT;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700167
168int get_restart_level()
169{
170 return restart_level;
171}
172EXPORT_SYMBOL(get_restart_level);
173
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700174static int restart_level_set(const char *val, struct kernel_param *kp)
175{
176 int ret;
177 int old_val = restart_level;
Joel King4e204292013-04-16 12:48:51 -0700178 int subtype;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700179
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800180 if (cpu_is_msm9615()) {
181 pr_err("Only Phase 1 subsystem restart is supported\n");
182 return -EINVAL;
183 }
184
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700185 ret = param_set_int(val, kp);
186 if (ret)
187 return ret;
188
189 switch (restart_level) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700190 case RESET_SUBSYS_INDEPENDENT:
Joel King4e204292013-04-16 12:48:51 -0700191 subtype = socinfo_get_platform_subtype();
192 if ((subtype == PLATFORM_SUBTYPE_SGLTE) ||
193 (subtype == PLATFORM_SUBTYPE_SGLTE2)) {
Vikram Mulukutlaf68456d2012-07-27 15:14:10 -0700194 pr_info("Phase 3 is currently unsupported. Using phase 2 instead.\n");
195 restart_level = RESET_SUBSYS_COUPLED;
196 }
197 case RESET_SUBSYS_COUPLED:
198 case RESET_SOC:
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700199 pr_info("Phase %d behavior activated.\n", restart_level);
Stephen Boydc68ee642012-05-01 17:02:45 -0700200 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700201 default:
202 restart_level = old_val;
203 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700204 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700205 return 0;
206}
207
208module_param_call(restart_level, restart_level_set, param_get_int,
209 &restart_level, 0644);
210
Stephen Boyd8f57c0f2012-09-18 11:31:02 -0700211static void subsys_set_state(struct subsys_device *subsys,
212 enum subsys_state state)
213{
214 unsigned long flags;
215
216 spin_lock_irqsave(&subsys->restart_lock, flags);
217 if (subsys->state != state) {
218 subsys->state = state;
219 spin_unlock_irqrestore(&subsys->restart_lock, flags);
220 return;
221 }
222 spin_unlock_irqrestore(&subsys->restart_lock, flags);
223}
224
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700225static struct subsys_soc_restart_order *
226update_restart_order(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700227{
228 int i, j;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700229 struct subsys_soc_restart_order *order;
230 const char *name = dev->desc->name;
231 int len = SUBSYS_NAME_MAX_LENGTH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700232
233 mutex_lock(&soc_order_reg_lock);
234 for (j = 0; j < n_restart_orders; j++) {
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700235 order = restart_orders[j];
236 for (i = 0; i < order->count; i++) {
237 if (!strncmp(order->subsystem_list[i], name, len)) {
238 order->subsys_ptrs[i] = dev;
239 goto found;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700240 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700241 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700242 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700243 order = NULL;
244found:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700245 mutex_unlock(&soc_order_reg_lock);
246
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700247 return order;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700248}
249
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700250static int max_restarts;
251module_param(max_restarts, int, 0644);
252
253static long max_history_time = 3600;
254module_param(max_history_time, long, 0644);
255
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700256static void do_epoch_check(struct subsys_device *dev)
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700257{
258 int n = 0;
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600259 struct timeval *time_first = NULL, *curr_time;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700260 struct restart_log *r_log, *temp;
261 static int max_restarts_check;
262 static long max_history_time_check;
263
264 mutex_lock(&restart_log_mutex);
265
266 max_restarts_check = max_restarts;
267 max_history_time_check = max_history_time;
268
269 /* Check if epoch checking is enabled */
270 if (!max_restarts_check)
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700271 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700272
273 r_log = kmalloc(sizeof(struct restart_log), GFP_KERNEL);
Matt Wagantalleecca342011-11-10 20:12:05 -0800274 if (!r_log)
275 goto out;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700276 r_log->dev = dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700277 do_gettimeofday(&r_log->time);
278 curr_time = &r_log->time;
279 INIT_LIST_HEAD(&r_log->list);
280
281 list_add_tail(&r_log->list, &restart_log_list);
282
283 list_for_each_entry_safe(r_log, temp, &restart_log_list, list) {
284
285 if ((curr_time->tv_sec - r_log->time.tv_sec) >
286 max_history_time_check) {
287
288 pr_debug("Deleted node with restart_time = %ld\n",
289 r_log->time.tv_sec);
290 list_del(&r_log->list);
291 kfree(r_log);
292 continue;
293 }
294 if (!n) {
295 time_first = &r_log->time;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700296 pr_debug("Time_first: %ld\n", time_first->tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700297 }
298 n++;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700299 pr_debug("Restart_time: %ld\n", r_log->time.tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700300 }
301
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600302 if (time_first && n >= max_restarts_check) {
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700303 if ((curr_time->tv_sec - time_first->tv_sec) <
Jaeseong GIMb8499412012-06-26 10:36:57 -0700304 max_history_time_check) {
Vikram Mulukutla0ad34f62013-01-22 11:01:13 -0800305 panic("Subsystems have crashed %d times in less than "\
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700306 "%ld seconds!", max_restarts_check,
307 max_history_time_check);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700308 }
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700309 }
310
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700311out:
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700312 mutex_unlock(&restart_log_mutex);
313}
314
Stephen Boyd0daae152012-05-01 17:36:15 -0700315static void for_each_subsys_device(struct subsys_device **list, unsigned count,
316 void *data, void (*fn)(struct subsys_device *, void *))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700317{
Stephen Boyd0daae152012-05-01 17:36:15 -0700318 while (count--) {
319 struct subsys_device *dev = *list++;
320 if (!dev)
321 continue;
322 fn(dev, data);
323 }
324}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700325
Stephen Boyd0daae152012-05-01 17:36:15 -0700326static void __send_notification_to_order(struct subsys_device *dev, void *data)
327{
328 enum subsys_notif_type type = (enum subsys_notif_type)data;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700329
Stephen Boyd0daae152012-05-01 17:36:15 -0700330 subsys_notif_queue_notification(dev->notify, type);
331}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700332
Stephen Boyd0daae152012-05-01 17:36:15 -0700333static void send_notification_to_order(struct subsys_device **l, unsigned n,
334 enum subsys_notif_type t)
335{
336 for_each_subsys_device(l, n, (void *)t, __send_notification_to_order);
337}
338
339static void subsystem_shutdown(struct subsys_device *dev, void *data)
340{
341 const char *name = dev->desc->name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700342
Stephen Boyd0daae152012-05-01 17:36:15 -0700343 pr_info("[%p]: Shutting down %s\n", current, name);
Brian Muramatsu0f0df2f2012-08-01 23:30:40 -0700344 if (dev->desc->shutdown(dev->desc) < 0) {
Vikram Mulukutla0ad34f62013-01-22 11:01:13 -0800345 panic("subsys-restart: [%p]: Failed to shutdown %s!",
Stephen Boyd0daae152012-05-01 17:36:15 -0700346 current, name);
Brian Muramatsu0f0df2f2012-08-01 23:30:40 -0700347 }
Stephen Boyd8f57c0f2012-09-18 11:31:02 -0700348 subsys_set_state(dev, SUBSYS_OFFLINE);
Stephen Boyd0daae152012-05-01 17:36:15 -0700349}
350
351static void subsystem_ramdump(struct subsys_device *dev, void *data)
352{
353 const char *name = dev->desc->name;
354
355 if (dev->desc->ramdump)
356 if (dev->desc->ramdump(enable_ramdumps, dev->desc) < 0)
357 pr_warn("%s[%p]: Ramdump failed.\n", name, current);
358}
359
360static void subsystem_powerup(struct subsys_device *dev, void *data)
361{
362 const char *name = dev->desc->name;
363
364 pr_info("[%p]: Powering up %s\n", current, name);
Brian Muramatsu0f0df2f2012-08-01 23:30:40 -0700365 if (dev->desc->powerup(dev->desc) < 0) {
Vikram Mulukutla0ad34f62013-01-22 11:01:13 -0800366 panic("[%p]: Failed to powerup %s!", current, name);
Brian Muramatsu0f0df2f2012-08-01 23:30:40 -0700367 }
Stephen Boyd8f57c0f2012-09-18 11:31:02 -0700368 subsys_set_state(dev, SUBSYS_ONLINE);
Stephen Boyd0daae152012-05-01 17:36:15 -0700369}
370
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700371static void subsystem_restart_wq_func(struct work_struct *work)
372{
Stephen Boyd497ef402012-06-21 12:54:40 -0700373 struct subsys_device *dev = container_of(work,
374 struct subsys_device, work);
Stephen Boyd0daae152012-05-01 17:36:15 -0700375 struct subsys_device **list;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700376 struct subsys_desc *desc = dev->desc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700377 struct subsys_soc_restart_order *soc_restart_order = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700378 struct mutex *powerup_lock;
379 struct mutex *shutdown_lock;
Stephen Boyd0daae152012-05-01 17:36:15 -0700380 unsigned count;
Stephen Boyd497ef402012-06-21 12:54:40 -0700381 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700382
Stephen Boyda82ee082012-06-21 12:35:49 -0700383 if (restart_level != RESET_SUBSYS_INDEPENDENT)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700384 soc_restart_order = dev->restart_order;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700385
Stephen Boydc68ee642012-05-01 17:02:45 -0700386 /*
387 * It's OK to not take the registration lock at this point.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700388 * This is because the subsystem list inside the relevant
389 * restart order is not being traversed.
390 */
391 if (!soc_restart_order) {
Stephen Boyd0daae152012-05-01 17:36:15 -0700392 list = &dev;
393 count = 1;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700394 powerup_lock = &dev->powerup_lock;
395 shutdown_lock = &dev->shutdown_lock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700396 } else {
Stephen Boyd0daae152012-05-01 17:36:15 -0700397 list = soc_restart_order->subsys_ptrs;
398 count = soc_restart_order->count;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700399 powerup_lock = &soc_restart_order->powerup_lock;
400 shutdown_lock = &soc_restart_order->shutdown_lock;
401 }
402
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700403 pr_debug("[%p]: Attempting to get shutdown lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700404
Stephen Boydc68ee642012-05-01 17:02:45 -0700405 /*
406 * Try to acquire shutdown_lock. If this fails, these subsystems are
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407 * already being restarted - return.
408 */
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800409 if (!mutex_trylock(shutdown_lock))
410 goto out;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700411
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700412 pr_debug("[%p]: Attempting to get powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700413
Stephen Boydc68ee642012-05-01 17:02:45 -0700414 /*
415 * Now that we've acquired the shutdown lock, either we're the first to
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700416 * restart these subsystems or some other thread is doing the powerup
417 * sequence for these subsystems. In the latter case, panic and bail
Stephen Boyd8f57c0f2012-09-18 11:31:02 -0700418 * out, since a subsystem died in its powerup sequence. This catches
419 * the case where a subsystem in a restart order isn't the one
420 * who initiated the original restart but has crashed while the restart
421 * order is being rebooted.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700422 */
Jaeseong GIMb8499412012-06-26 10:36:57 -0700423 if (!mutex_trylock(powerup_lock)) {
Vikram Mulukutla0ad34f62013-01-22 11:01:13 -0800424 panic("%s[%p]: Subsystem died during powerup!",
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700425 __func__, current);
Jaeseong GIMb8499412012-06-26 10:36:57 -0700426 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700427
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700428 do_epoch_check(dev);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700429
Stephen Boydc68ee642012-05-01 17:02:45 -0700430 /*
431 * It's necessary to take the registration lock because the subsystem
432 * list in the SoC restart order will be traversed and it shouldn't be
433 * changed until _this_ restart sequence completes.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700434 */
435 mutex_lock(&soc_order_reg_lock);
436
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700437 pr_debug("[%p]: Starting restart sequence for %s\n", current,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700438 desc->name);
Stephen Boyd0daae152012-05-01 17:36:15 -0700439 send_notification_to_order(list, count, SUBSYS_BEFORE_SHUTDOWN);
440 for_each_subsys_device(list, count, NULL, subsystem_shutdown);
441 send_notification_to_order(list, count, SUBSYS_AFTER_SHUTDOWN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700442
Stephen Boydc68ee642012-05-01 17:02:45 -0700443 /*
444 * Now that we've finished shutting down these subsystems, release the
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700445 * shutdown lock. If a subsystem restart request comes in for a
446 * subsystem in _this_ restart order after the unlock below, and
447 * before the powerup lock is released, panic and bail out.
448 */
449 mutex_unlock(shutdown_lock);
450
451 /* Collect ram dumps for all subsystems in order here */
Stephen Boyd0daae152012-05-01 17:36:15 -0700452 for_each_subsys_device(list, count, NULL, subsystem_ramdump);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700453
Stephen Boyd0daae152012-05-01 17:36:15 -0700454 send_notification_to_order(list, count, SUBSYS_BEFORE_POWERUP);
455 for_each_subsys_device(list, count, NULL, subsystem_powerup);
456 send_notification_to_order(list, count, SUBSYS_AFTER_POWERUP);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700457
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700458 pr_info("[%p]: Restart sequence for %s completed.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700459 current, desc->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700460
461 mutex_unlock(powerup_lock);
462
463 mutex_unlock(&soc_order_reg_lock);
464
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700465 pr_debug("[%p]: Released powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700466
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800467out:
Stephen Boyd497ef402012-06-21 12:54:40 -0700468 spin_lock_irqsave(&dev->restart_lock, flags);
Stephen Boyd8f57c0f2012-09-18 11:31:02 -0700469 dev->restarting = false;
470 wake_unlock(&dev->wake_lock);
Stephen Boyd497ef402012-06-21 12:54:40 -0700471 spin_unlock_irqrestore(&dev->restart_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700472}
473
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700474static void __subsystem_restart_dev(struct subsys_device *dev)
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700475{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700476 struct subsys_desc *desc = dev->desc;
Stephen Boyd8f57c0f2012-09-18 11:31:02 -0700477 const char *name = dev->desc->name;
Stephen Boyd497ef402012-06-21 12:54:40 -0700478 unsigned long flags;
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700479
Stephen Boyd77c22742012-07-24 18:46:45 -0700480 pr_debug("Restarting %s [level=%d]!\n", desc->name, restart_level);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700481
Stephen Boyd8f57c0f2012-09-18 11:31:02 -0700482 /*
483 * We want to allow drivers to call subsystem_restart{_dev}() as many
484 * times as they want up until the point where the subsystem is
485 * shutdown.
486 */
Stephen Boyd77c22742012-07-24 18:46:45 -0700487 spin_lock_irqsave(&dev->restart_lock, flags);
Stephen Boyd8f57c0f2012-09-18 11:31:02 -0700488 if (dev->state != SUBSYS_CRASHED) {
489 if (dev->state == SUBSYS_ONLINE && !dev->restarting) {
490 dev->restarting = true;
491 dev->state = SUBSYS_CRASHED;
492 wake_lock(&dev->wake_lock);
493 queue_work(ssr_wq, &dev->work);
494 } else {
495 panic("Subsystem %s crashed during SSR!", name);
496 }
Stephen Boyd77c22742012-07-24 18:46:45 -0700497 }
Stephen Boyd8f57c0f2012-09-18 11:31:02 -0700498 spin_unlock_irqrestore(&dev->restart_lock, flags);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700499}
500
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700501int subsystem_restart_dev(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700502{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700503 const char *name = dev->desc->name;
Vikram Mulukutla3944eff2012-10-29 18:43:02 -0700504
505 /*
506 * If a system reboot/shutdown is underway, ignore subsystem errors.
507 * However, print a message so that we know that a subsystem behaved
508 * unexpectedly here.
509 */
510 if (system_state == SYSTEM_RESTART
511 || system_state == SYSTEM_POWER_OFF) {
512 pr_err("%s crashed during a system poweroff/shutdown.\n", name);
513 return -EBUSY;
514 }
515
Vikram Mulukutla3b24c8c2011-12-02 15:12:53 -0800516 pr_info("Restart sequence requested for %s, restart_level = %d.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700517 name, restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700518
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700519 switch (restart_level) {
520
521 case RESET_SUBSYS_COUPLED:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700522 case RESET_SUBSYS_INDEPENDENT:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700523 __subsystem_restart_dev(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700524 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700525 case RESET_SOC:
Ameya Thakur97d41142012-12-05 17:04:52 -0800526 panic("subsys-restart: Resetting the SoC - %s crashed.", name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700527 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700528 default:
Ameya Thakur97d41142012-12-05 17:04:52 -0800529 panic("subsys-restart: Unknown restart level!\n");
Stephen Boydc68ee642012-05-01 17:02:45 -0700530 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700531 }
532
533 return 0;
534}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700535EXPORT_SYMBOL(subsystem_restart_dev);
536
537int subsystem_restart(const char *name)
538{
539 struct subsys_device *dev;
540
541 mutex_lock(&subsystem_list_lock);
542 list_for_each_entry(dev, &subsystem_list, list)
543 if (!strncmp(dev->desc->name, name, SUBSYS_NAME_MAX_LENGTH))
544 goto found;
545 dev = NULL;
546found:
547 mutex_unlock(&subsystem_list_lock);
548 if (dev)
549 return subsystem_restart_dev(dev);
550 return -ENODEV;
551}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700552EXPORT_SYMBOL(subsystem_restart);
553
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700554struct subsys_device *subsys_register(struct subsys_desc *desc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700555{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700556 struct subsys_device *dev;
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700557
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700558 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
559 if (!dev)
560 return ERR_PTR(-ENOMEM);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700561
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700562 dev->desc = desc;
563 dev->notify = subsys_notif_add_subsys(desc->name);
564 dev->restart_order = update_restart_order(dev);
Stephen Boyd8f57c0f2012-09-18 11:31:02 -0700565 dev->state = SUBSYS_ONLINE; /* Until proper refcounting appears */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700566
Stephen Boyd497ef402012-06-21 12:54:40 -0700567 snprintf(dev->wlname, sizeof(dev->wlname), "ssr(%s)", desc->name);
568 wake_lock_init(&dev->wake_lock, WAKE_LOCK_SUSPEND, dev->wlname);
569 INIT_WORK(&dev->work, subsystem_restart_wq_func);
570 spin_lock_init(&dev->restart_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700571
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700572 mutex_init(&dev->shutdown_lock);
573 mutex_init(&dev->powerup_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700574
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700575 mutex_lock(&subsystem_list_lock);
576 list_add(&dev->list, &subsystem_list);
577 mutex_unlock(&subsystem_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700578
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700579 return dev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700580}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700581EXPORT_SYMBOL(subsys_register);
582
583void subsys_unregister(struct subsys_device *dev)
584{
585 if (IS_ERR_OR_NULL(dev))
586 return;
587 mutex_lock(&subsystem_list_lock);
588 list_del(&dev->list);
589 mutex_unlock(&subsystem_list_lock);
Stephen Boyd497ef402012-06-21 12:54:40 -0700590 wake_lock_destroy(&dev->wake_lock);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700591 kfree(dev);
592}
593EXPORT_SYMBOL(subsys_unregister);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700594
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700595static int ssr_panic_handler(struct notifier_block *this,
596 unsigned long event, void *ptr)
597{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700598 struct subsys_device *dev;
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700599
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700600 list_for_each_entry(dev, &subsystem_list, list)
601 if (dev->desc->crash_shutdown)
602 dev->desc->crash_shutdown(dev->desc);
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700603 return NOTIFY_DONE;
604}
605
606static struct notifier_block panic_nb = {
607 .notifier_call = ssr_panic_handler,
608};
609
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700610static int __init ssr_init_soc_restart_orders(void)
611{
612 int i;
613
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700614 atomic_notifier_chain_register(&panic_notifier_list,
615 &panic_nb);
616
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700617 if (cpu_is_msm8x60()) {
618 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
619 mutex_init(&orders_8x60_all[i]->powerup_lock);
620 mutex_init(&orders_8x60_all[i]->shutdown_lock);
621 }
622
623 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
624 mutex_init(&orders_8x60_modems[i]->powerup_lock);
625 mutex_init(&orders_8x60_modems[i]->shutdown_lock);
626 }
627
628 restart_orders = orders_8x60_all;
629 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
630 }
631
Flemmard3394eca2013-04-15 00:20:20 -0700632#ifdef CONFIG_MACH_HTC
633 restart_orders = restart_orders_8960;
634 n_restart_orders = ARRAY_SIZE(restart_orders_8960);
635#else
Vikram Mulukutlaf68456d2012-07-27 15:14:10 -0700636 if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) {
637 restart_orders = restart_orders_8960_sglte;
638 n_restart_orders = ARRAY_SIZE(restart_orders_8960_sglte);
639 }
Flemmard3394eca2013-04-15 00:20:20 -0700640#endif
Vikram Mulukutlaf68456d2012-07-27 15:14:10 -0700641
Joel Kinge4f98902013-03-25 13:12:27 -0700642 if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE2) {
643 restart_orders = restart_orders_8064_sglte2;
644 n_restart_orders = ARRAY_SIZE(restart_orders_8064_sglte2);
645 }
646
Vikram Mulukutlaf68456d2012-07-27 15:14:10 -0700647 for (i = 0; i < n_restart_orders; i++) {
648 mutex_init(&restart_orders[i]->powerup_lock);
649 mutex_init(&restart_orders[i]->shutdown_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700650 }
651
652 if (restart_orders == NULL || n_restart_orders < 1) {
653 WARN_ON(1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700654 }
655
656 return 0;
657}
658
659static int __init subsys_restart_init(void)
660{
Vikram Mulukutla372e15a2013-01-22 10:51:17 -0800661 restart_level = RESET_SOC;
662
Vikram Mulukutla99f84eff2012-09-10 19:25:32 -0700663 ssr_wq = alloc_workqueue("ssr_wq", WQ_CPU_INTENSIVE, 0);
Vikram Mulukutla0ad34f62013-01-22 11:01:13 -0800664 if (!ssr_wq)
665 panic("%s: out of memory\n", __func__);
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700666
Iliyan Malcheva0899ce2012-06-29 15:28:25 -0700667 return ssr_init_soc_restart_orders();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700668}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700669arch_initcall(subsys_restart_init);
670
671MODULE_DESCRIPTION("Subsystem Restart Driver");
672MODULE_LICENSE("GPL v2");