blob: bae1ab04ad87daf4d289221bb26ce067514186de [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>
Stephen Boyd4ec9a942012-06-21 19:10:48 -070030#include <linux/device.h>
31#include <linux/idr.h>
Stephen Boydeff41a62012-07-06 13:19:02 -070032#include <linux/debugfs.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033
34#include <asm/current.h>
35
36#include <mach/peripheral-loader.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070037#include <mach/socinfo.h>
38#include <mach/subsystem_notif.h>
39#include <mach/subsystem_restart.h>
40
41#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;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070049 struct subsys_device *subsys_ptrs[];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070050};
51
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070052struct restart_log {
53 struct timeval time;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070054 struct subsys_device *dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070055 struct list_head list;
56};
57
Stephen Boyd4ec9a942012-06-21 19:10:48 -070058enum subsys_state {
59 SUBSYS_OFFLINE,
60 SUBSYS_ONLINE,
Stephen Boyd6b567182012-09-18 11:31:02 -070061 SUBSYS_CRASHED,
Stephen Boyd4ec9a942012-06-21 19:10:48 -070062};
63
64static const char * const subsys_states[] = {
65 [SUBSYS_OFFLINE] = "OFFLINE",
66 [SUBSYS_ONLINE] = "ONLINE",
Stephen Boyd6b567182012-09-18 11:31:02 -070067 [SUBSYS_CRASHED] = "CRASHED",
Stephen Boyd4ec9a942012-06-21 19:10:48 -070068};
69
Stephen Boyd0ebf7212012-04-30 20:42:35 -070070struct subsys_device {
71 struct subsys_desc *desc;
Stephen Boyd497ef402012-06-21 12:54:40 -070072 struct wake_lock wake_lock;
73 char wlname[64];
74 struct work_struct work;
75 spinlock_t restart_lock;
Stephen Boyd6b567182012-09-18 11:31:02 -070076 bool restarting;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070077
78 void *notify;
Stephen Boyd4ec9a942012-06-21 19:10:48 -070079 struct device dev;
80 struct module *owner;
81 int count;
82 enum subsys_state state;
83 int id;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070084
85 struct mutex shutdown_lock;
86 struct mutex powerup_lock;
87
88 void *restart_order;
Stephen Boydeff41a62012-07-06 13:19:02 -070089#ifdef CONFIG_DEBUG_FS
90 struct dentry *dentry;
91#endif
Stephen Boyd0ebf7212012-04-30 20:42:35 -070092};
93
Stephen Boyd4ec9a942012-06-21 19:10:48 -070094static struct subsys_device *to_subsys(struct device *d)
95{
96 return container_of(d, struct subsys_device, dev);
97}
98
99static ssize_t name_show(struct device *dev, struct device_attribute *attr,
100 char *buf)
101{
102 return snprintf(buf, PAGE_SIZE, "%s\n", to_subsys(dev)->desc->name);
103}
104
105static ssize_t state_show(struct device *dev, struct device_attribute *attr,
106 char *buf)
107{
108 enum subsys_state state = to_subsys(dev)->state;
109 return snprintf(buf, PAGE_SIZE, "%s\n", subsys_states[state]);
110}
111
Stephen Boyd6b567182012-09-18 11:31:02 -0700112static void subsys_set_state(struct subsys_device *subsys,
113 enum subsys_state state)
114{
115 unsigned long flags;
116
117 spin_lock_irqsave(&subsys->restart_lock, flags);
118 if (subsys->state != state) {
119 subsys->state = state;
120 spin_unlock_irqrestore(&subsys->restart_lock, flags);
121 sysfs_notify(&subsys->dev.kobj, NULL, "state");
122 return;
123 }
124 spin_unlock_irqrestore(&subsys->restart_lock, flags);
125}
126
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700127static struct device_attribute subsys_attrs[] = {
128 __ATTR_RO(name),
129 __ATTR_RO(state),
130 __ATTR_NULL,
131};
132
133static struct bus_type subsys_bus_type = {
134 .name = "msm_subsys",
135 .dev_attrs = subsys_attrs,
136};
137
138static DEFINE_IDA(subsys_ida);
139
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700140static int enable_ramdumps;
Stephen Boydc68ee642012-05-01 17:02:45 -0700141module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR);
142
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700143struct workqueue_struct *ssr_wq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700144
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700145static LIST_HEAD(restart_log_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146static DEFINE_MUTEX(soc_order_reg_lock);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700147static DEFINE_MUTEX(restart_log_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700148
149/* SOC specific restart orders go here */
150
151#define DEFINE_SINGLE_RESTART_ORDER(name, order) \
152 static struct subsys_soc_restart_order __##name = { \
153 .subsystem_list = order, \
154 .count = ARRAY_SIZE(order), \
155 .subsys_ptrs = {[ARRAY_SIZE(order)] = NULL} \
156 }; \
157 static struct subsys_soc_restart_order *name[] = { \
158 &__##name, \
159 }
160
161/* MSM 8x60 restart ordering info */
162static const char * const _order_8x60_all[] = {
163 "external_modem", "modem", "lpass"
164};
165DEFINE_SINGLE_RESTART_ORDER(orders_8x60_all, _order_8x60_all);
166
167static const char * const _order_8x60_modems[] = {"external_modem", "modem"};
168DEFINE_SINGLE_RESTART_ORDER(orders_8x60_modems, _order_8x60_modems);
169
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700170/*SGLTE restart ordering info*/
171static const char * const order_8960_sglte[] = {"external_modem",
172 "modem"};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700173
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700174static struct subsys_soc_restart_order restart_orders_8960_fusion_sglte = {
175 .subsystem_list = order_8960_sglte,
176 .count = ARRAY_SIZE(order_8960_sglte),
177 .subsys_ptrs = {[ARRAY_SIZE(order_8960_sglte)] = NULL}
178 };
179
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700180static struct subsys_soc_restart_order *restart_orders_8960_sglte[] = {
181 &restart_orders_8960_fusion_sglte,
182 };
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700183
184/* These will be assigned to one of the sets above after
185 * runtime SoC identification.
186 */
187static struct subsys_soc_restart_order **restart_orders;
188static int n_restart_orders;
189
Stephen Boydc68ee642012-05-01 17:02:45 -0700190static int restart_level = RESET_SOC;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700191
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700192int get_restart_level()
193{
194 return restart_level;
195}
196EXPORT_SYMBOL(get_restart_level);
197
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700198static int restart_level_set(const char *val, struct kernel_param *kp)
199{
200 int ret;
201 int old_val = restart_level;
202
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800203 if (cpu_is_msm9615()) {
204 pr_err("Only Phase 1 subsystem restart is supported\n");
205 return -EINVAL;
206 }
207
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700208 ret = param_set_int(val, kp);
209 if (ret)
210 return ret;
211
212 switch (restart_level) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700213 case RESET_SUBSYS_INDEPENDENT:
Vikram Mulukutlae4ae75e2012-07-27 15:14:10 -0700214 if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) {
215 pr_info("Phase 3 is currently unsupported. Using phase 2 instead.\n");
216 restart_level = RESET_SUBSYS_COUPLED;
217 }
218 case RESET_SUBSYS_COUPLED:
219 case RESET_SOC:
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700220 pr_info("Phase %d behavior activated.\n", restart_level);
Stephen Boydc68ee642012-05-01 17:02:45 -0700221 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700222 default:
223 restart_level = old_val;
224 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700225 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226 return 0;
227}
228
229module_param_call(restart_level, restart_level_set, param_get_int,
230 &restart_level, 0644);
231
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700232static struct subsys_soc_restart_order *
233update_restart_order(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700234{
235 int i, j;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700236 struct subsys_soc_restart_order *order;
237 const char *name = dev->desc->name;
238 int len = SUBSYS_NAME_MAX_LENGTH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700239
240 mutex_lock(&soc_order_reg_lock);
241 for (j = 0; j < n_restart_orders; j++) {
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700242 order = restart_orders[j];
243 for (i = 0; i < order->count; i++) {
244 if (!strncmp(order->subsystem_list[i], name, len)) {
245 order->subsys_ptrs[i] = dev;
246 goto found;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700247 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700248 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700249 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700250 order = NULL;
251found:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700252 mutex_unlock(&soc_order_reg_lock);
253
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700254 return order;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700255}
256
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700257static int max_restarts;
258module_param(max_restarts, int, 0644);
259
260static long max_history_time = 3600;
261module_param(max_history_time, long, 0644);
262
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700263static void do_epoch_check(struct subsys_device *dev)
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700264{
265 int n = 0;
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600266 struct timeval *time_first = NULL, *curr_time;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700267 struct restart_log *r_log, *temp;
268 static int max_restarts_check;
269 static long max_history_time_check;
270
271 mutex_lock(&restart_log_mutex);
272
273 max_restarts_check = max_restarts;
274 max_history_time_check = max_history_time;
275
276 /* Check if epoch checking is enabled */
277 if (!max_restarts_check)
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700278 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700279
280 r_log = kmalloc(sizeof(struct restart_log), GFP_KERNEL);
Matt Wagantalleecca342011-11-10 20:12:05 -0800281 if (!r_log)
282 goto out;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700283 r_log->dev = dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700284 do_gettimeofday(&r_log->time);
285 curr_time = &r_log->time;
286 INIT_LIST_HEAD(&r_log->list);
287
288 list_add_tail(&r_log->list, &restart_log_list);
289
290 list_for_each_entry_safe(r_log, temp, &restart_log_list, list) {
291
292 if ((curr_time->tv_sec - r_log->time.tv_sec) >
293 max_history_time_check) {
294
295 pr_debug("Deleted node with restart_time = %ld\n",
296 r_log->time.tv_sec);
297 list_del(&r_log->list);
298 kfree(r_log);
299 continue;
300 }
301 if (!n) {
302 time_first = &r_log->time;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700303 pr_debug("Time_first: %ld\n", time_first->tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700304 }
305 n++;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700306 pr_debug("Restart_time: %ld\n", r_log->time.tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700307 }
308
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600309 if (time_first && n >= max_restarts_check) {
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700310 if ((curr_time->tv_sec - time_first->tv_sec) <
311 max_history_time_check)
312 panic("Subsystems have crashed %d times in less than "
313 "%ld seconds!", max_restarts_check,
314 max_history_time_check);
315 }
316
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700317out:
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700318 mutex_unlock(&restart_log_mutex);
319}
320
Stephen Boyd0daae152012-05-01 17:36:15 -0700321static void for_each_subsys_device(struct subsys_device **list, unsigned count,
322 void *data, void (*fn)(struct subsys_device *, void *))
323{
324 while (count--) {
325 struct subsys_device *dev = *list++;
326 if (!dev)
327 continue;
328 fn(dev, data);
329 }
330}
331
332static void __send_notification_to_order(struct subsys_device *dev, void *data)
333{
334 enum subsys_notif_type type = (enum subsys_notif_type)data;
335
336 subsys_notif_queue_notification(dev->notify, type);
337}
338
339static void send_notification_to_order(struct subsys_device **l, unsigned n,
340 enum subsys_notif_type t)
341{
342 for_each_subsys_device(l, n, (void *)t, __send_notification_to_order);
343}
344
345static void subsystem_shutdown(struct subsys_device *dev, void *data)
346{
347 const char *name = dev->desc->name;
348
349 pr_info("[%p]: Shutting down %s\n", current, name);
350 if (dev->desc->shutdown(dev->desc) < 0)
351 panic("subsys-restart: [%p]: Failed to shutdown %s!",
352 current, name);
Stephen Boyd6b567182012-09-18 11:31:02 -0700353 subsys_set_state(dev, SUBSYS_OFFLINE);
Stephen Boyd0daae152012-05-01 17:36:15 -0700354}
355
356static void subsystem_ramdump(struct subsys_device *dev, void *data)
357{
358 const char *name = dev->desc->name;
359
360 if (dev->desc->ramdump)
361 if (dev->desc->ramdump(enable_ramdumps, dev->desc) < 0)
362 pr_warn("%s[%p]: Ramdump failed.\n", name, current);
363}
364
365static void subsystem_powerup(struct subsys_device *dev, void *data)
366{
367 const char *name = dev->desc->name;
368
369 pr_info("[%p]: Powering up %s\n", current, name);
370 if (dev->desc->powerup(dev->desc) < 0)
371 panic("[%p]: Failed to powerup %s!", current, name);
Stephen Boyd6b567182012-09-18 11:31:02 -0700372 subsys_set_state(dev, SUBSYS_ONLINE);
Stephen Boyd0daae152012-05-01 17:36:15 -0700373}
374
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700375static int __find_subsys(struct device *dev, void *data)
376{
377 struct subsys_device *subsys = to_subsys(dev);
378 return !strcmp(subsys->desc->name, data);
379}
380
381static struct subsys_device *find_subsys(const char *str)
382{
383 struct device *dev;
384
385 if (!str)
386 return NULL;
387
388 dev = bus_find_device(&subsys_bus_type, NULL, (void *)str,
389 __find_subsys);
390 return dev ? to_subsys(dev) : NULL;
391}
392
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700393static void subsystem_restart_wq_func(struct work_struct *work)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700394{
Stephen Boyd497ef402012-06-21 12:54:40 -0700395 struct subsys_device *dev = container_of(work,
396 struct subsys_device, work);
Stephen Boyd0daae152012-05-01 17:36:15 -0700397 struct subsys_device **list;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700398 struct subsys_desc *desc = dev->desc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700399 struct subsys_soc_restart_order *soc_restart_order = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700400 struct mutex *powerup_lock;
401 struct mutex *shutdown_lock;
Stephen Boyd0daae152012-05-01 17:36:15 -0700402 unsigned count;
Stephen Boyd497ef402012-06-21 12:54:40 -0700403 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700404
Stephen Boyda82ee082012-06-21 12:35:49 -0700405 if (restart_level != RESET_SUBSYS_INDEPENDENT)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700406 soc_restart_order = dev->restart_order;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700407
Stephen Boydc68ee642012-05-01 17:02:45 -0700408 /*
409 * It's OK to not take the registration lock at this point.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700410 * This is because the subsystem list inside the relevant
411 * restart order is not being traversed.
412 */
413 if (!soc_restart_order) {
Stephen Boyd0daae152012-05-01 17:36:15 -0700414 list = &dev;
415 count = 1;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700416 powerup_lock = &dev->powerup_lock;
417 shutdown_lock = &dev->shutdown_lock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700418 } else {
Stephen Boyd0daae152012-05-01 17:36:15 -0700419 list = soc_restart_order->subsys_ptrs;
420 count = soc_restart_order->count;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700421 powerup_lock = &soc_restart_order->powerup_lock;
422 shutdown_lock = &soc_restart_order->shutdown_lock;
423 }
424
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700425 pr_debug("[%p]: Attempting to get shutdown lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700426
Stephen Boydc68ee642012-05-01 17:02:45 -0700427 /*
428 * Try to acquire shutdown_lock. If this fails, these subsystems are
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700429 * already being restarted - return.
430 */
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800431 if (!mutex_trylock(shutdown_lock))
432 goto out;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700433
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700434 pr_debug("[%p]: Attempting to get powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700435
Stephen Boydc68ee642012-05-01 17:02:45 -0700436 /*
437 * Now that we've acquired the shutdown lock, either we're the first to
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700438 * restart these subsystems or some other thread is doing the powerup
439 * sequence for these subsystems. In the latter case, panic and bail
Stephen Boyd6b567182012-09-18 11:31:02 -0700440 * out, since a subsystem died in its powerup sequence. This catches
441 * the case where a subsystem in a restart order isn't the one
442 * who initiated the original restart but has crashed while the restart
443 * order is being rebooted.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700444 */
445 if (!mutex_trylock(powerup_lock))
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700446 panic("%s[%p]: Subsystem died during powerup!",
447 __func__, current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700448
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700449 do_epoch_check(dev);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700450
Stephen Boydc68ee642012-05-01 17:02:45 -0700451 /*
452 * It's necessary to take the registration lock because the subsystem
453 * list in the SoC restart order will be traversed and it shouldn't be
454 * changed until _this_ restart sequence completes.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700455 */
456 mutex_lock(&soc_order_reg_lock);
457
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700458 pr_debug("[%p]: Starting restart sequence for %s\n", current,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700459 desc->name);
Stephen Boyd0daae152012-05-01 17:36:15 -0700460 send_notification_to_order(list, count, SUBSYS_BEFORE_SHUTDOWN);
461 for_each_subsys_device(list, count, NULL, subsystem_shutdown);
462 send_notification_to_order(list, count, SUBSYS_AFTER_SHUTDOWN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700463
Stephen Boydc68ee642012-05-01 17:02:45 -0700464 /*
465 * Now that we've finished shutting down these subsystems, release the
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700466 * shutdown lock. If a subsystem restart request comes in for a
467 * subsystem in _this_ restart order after the unlock below, and
468 * before the powerup lock is released, panic and bail out.
469 */
470 mutex_unlock(shutdown_lock);
471
472 /* Collect ram dumps for all subsystems in order here */
Stephen Boyd0daae152012-05-01 17:36:15 -0700473 for_each_subsys_device(list, count, NULL, subsystem_ramdump);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700474
Stephen Boyd0daae152012-05-01 17:36:15 -0700475 send_notification_to_order(list, count, SUBSYS_BEFORE_POWERUP);
476 for_each_subsys_device(list, count, NULL, subsystem_powerup);
477 send_notification_to_order(list, count, SUBSYS_AFTER_POWERUP);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700478
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700479 pr_info("[%p]: Restart sequence for %s completed.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700480 current, desc->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700481
482 mutex_unlock(powerup_lock);
483
484 mutex_unlock(&soc_order_reg_lock);
485
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700486 pr_debug("[%p]: Released powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700487
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800488out:
Stephen Boyd497ef402012-06-21 12:54:40 -0700489 spin_lock_irqsave(&dev->restart_lock, flags);
Stephen Boyd6b567182012-09-18 11:31:02 -0700490 dev->restarting = false;
491 wake_unlock(&dev->wake_lock);
Stephen Boyd497ef402012-06-21 12:54:40 -0700492 spin_unlock_irqrestore(&dev->restart_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700493}
494
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700495static void __subsystem_restart_dev(struct subsys_device *dev)
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700496{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700497 struct subsys_desc *desc = dev->desc;
Stephen Boyd6b567182012-09-18 11:31:02 -0700498 const char *name = dev->desc->name;
Stephen Boyd497ef402012-06-21 12:54:40 -0700499 unsigned long flags;
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700500
Stephen Boyd77c22742012-07-24 18:46:45 -0700501 pr_debug("Restarting %s [level=%d]!\n", desc->name, restart_level);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700502
Stephen Boyd6b567182012-09-18 11:31:02 -0700503 /*
504 * We want to allow drivers to call subsystem_restart{_dev}() as many
505 * times as they want up until the point where the subsystem is
506 * shutdown.
507 */
Stephen Boyd77c22742012-07-24 18:46:45 -0700508 spin_lock_irqsave(&dev->restart_lock, flags);
Stephen Boyd6b567182012-09-18 11:31:02 -0700509 if (dev->state != SUBSYS_CRASHED) {
510 if (dev->state == SUBSYS_ONLINE && !dev->restarting) {
511 dev->restarting = true;
512 dev->state = SUBSYS_CRASHED;
513 wake_lock(&dev->wake_lock);
514 queue_work(ssr_wq, &dev->work);
515 } else {
516 panic("Subsystem %s crashed during SSR!", name);
517 }
Stephen Boyd77c22742012-07-24 18:46:45 -0700518 }
Stephen Boyd6b567182012-09-18 11:31:02 -0700519 spin_unlock_irqrestore(&dev->restart_lock, flags);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700520}
521
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700522int subsystem_restart_dev(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700523{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700524 const char *name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700525
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700526 if (!get_device(&dev->dev))
527 return -ENODEV;
528
529 if (!try_module_get(dev->owner)) {
530 put_device(&dev->dev);
531 return -ENODEV;
532 }
533
534 name = dev->desc->name;
Vikram Mulukutla3b24c8c2011-12-02 15:12:53 -0800535 pr_info("Restart sequence requested for %s, restart_level = %d.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700536 name, restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700537
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700538 switch (restart_level) {
539
540 case RESET_SUBSYS_COUPLED:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700541 case RESET_SUBSYS_INDEPENDENT:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700542 __subsystem_restart_dev(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700543 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700544 case RESET_SOC:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700545 panic("subsys-restart: Resetting the SoC - %s crashed.", name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700546 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700547 default:
548 panic("subsys-restart: Unknown restart level!\n");
Stephen Boydc68ee642012-05-01 17:02:45 -0700549 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700550 }
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700551 module_put(dev->owner);
552 put_device(&dev->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700553
554 return 0;
555}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700556EXPORT_SYMBOL(subsystem_restart_dev);
557
558int subsystem_restart(const char *name)
559{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700560 int ret;
561 struct subsys_device *dev = find_subsys(name);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700562
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700563 if (!dev)
564 return -ENODEV;
565
566 ret = subsystem_restart_dev(dev);
567 put_device(&dev->dev);
568 return ret;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700569}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700570EXPORT_SYMBOL(subsystem_restart);
571
Stephen Boydeff41a62012-07-06 13:19:02 -0700572#ifdef CONFIG_DEBUG_FS
573static ssize_t subsys_debugfs_read(struct file *filp, char __user *ubuf,
574 size_t cnt, loff_t *ppos)
575{
576 int r;
577 char buf[40];
578 struct subsys_device *subsys = filp->private_data;
579
580 r = snprintf(buf, sizeof(buf), "%d\n", subsys->count);
581 return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
582}
583
584static ssize_t subsys_debugfs_write(struct file *filp,
585 const char __user *ubuf, size_t cnt, loff_t *ppos)
586{
587 struct subsys_device *subsys = filp->private_data;
588 char buf[10];
589 char *cmp;
590
591 cnt = min(cnt, sizeof(buf) - 1);
592 if (copy_from_user(&buf, ubuf, cnt))
593 return -EFAULT;
594 buf[cnt] = '\0';
595 cmp = strstrip(buf);
596
597 if (!strcmp(cmp, "restart")) {
598 if (subsystem_restart_dev(subsys))
599 return -EIO;
600 } else {
601 return -EINVAL;
602 }
603
604 return cnt;
605}
606
607static const struct file_operations subsys_debugfs_fops = {
608 .open = simple_open,
609 .read = subsys_debugfs_read,
610 .write = subsys_debugfs_write,
611};
612
613static struct dentry *subsys_base_dir;
614
615static int __init subsys_debugfs_init(void)
616{
617 subsys_base_dir = debugfs_create_dir("msm_subsys", NULL);
618 return !subsys_base_dir ? -ENOMEM : 0;
619}
620
621static void subsys_debugfs_exit(void)
622{
623 debugfs_remove_recursive(subsys_base_dir);
624}
625
626static int subsys_debugfs_add(struct subsys_device *subsys)
627{
628 if (!subsys_base_dir)
629 return -ENOMEM;
630
631 subsys->dentry = debugfs_create_file(subsys->desc->name,
632 S_IRUGO | S_IWUSR, subsys_base_dir,
633 subsys, &subsys_debugfs_fops);
634 return !subsys->dentry ? -ENOMEM : 0;
635}
636
637static void subsys_debugfs_remove(struct subsys_device *subsys)
638{
639 debugfs_remove(subsys->dentry);
640}
641#else
642static int __init subsys_debugfs_init(void) { return 0; };
643static void subsys_debugfs_exit(void) { }
644static int subsys_debugfs_add(struct subsys_device *subsys) { return 0; }
645static void subsys_debugfs_remove(struct subsys_device *subsys) { }
646#endif
647
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700648static void subsys_device_release(struct device *dev)
649{
650 struct subsys_device *subsys = to_subsys(dev);
651
652 wake_lock_destroy(&subsys->wake_lock);
653 mutex_destroy(&subsys->shutdown_lock);
654 mutex_destroy(&subsys->powerup_lock);
655 ida_simple_remove(&subsys_ida, subsys->id);
656 kfree(subsys);
657}
658
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700659struct subsys_device *subsys_register(struct subsys_desc *desc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700660{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700661 struct subsys_device *subsys;
662 int ret;
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700663
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700664 subsys = kzalloc(sizeof(*subsys), GFP_KERNEL);
665 if (!subsys)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700666 return ERR_PTR(-ENOMEM);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700667
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700668 subsys->desc = desc;
669 subsys->owner = desc->owner;
670 subsys->dev.parent = desc->dev;
671 subsys->dev.bus = &subsys_bus_type;
672 subsys->dev.release = subsys_device_release;
Stephen Boyd6b567182012-09-18 11:31:02 -0700673 subsys->state = SUBSYS_ONLINE; /* Until proper refcounting appears */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700674
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700675 subsys->notify = subsys_notif_add_subsys(desc->name);
676 subsys->restart_order = update_restart_order(subsys);
Stephen Boyd497ef402012-06-21 12:54:40 -0700677
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700678 snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name);
679 wake_lock_init(&subsys->wake_lock, WAKE_LOCK_SUSPEND, subsys->wlname);
680 INIT_WORK(&subsys->work, subsystem_restart_wq_func);
681 spin_lock_init(&subsys->restart_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700682
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700683 subsys->id = ida_simple_get(&subsys_ida, 0, 0, GFP_KERNEL);
684 if (subsys->id < 0) {
685 ret = subsys->id;
686 goto err_ida;
687 }
688 dev_set_name(&subsys->dev, "subsys%d", subsys->id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700689
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700690 mutex_init(&subsys->shutdown_lock);
691 mutex_init(&subsys->powerup_lock);
692
Stephen Boydeff41a62012-07-06 13:19:02 -0700693 ret = subsys_debugfs_add(subsys);
694 if (ret)
695 goto err_debugfs;
696
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700697 ret = device_register(&subsys->dev);
698 if (ret) {
699 put_device(&subsys->dev);
700 goto err_register;
701 }
Stephen Boydeff41a62012-07-06 13:19:02 -0700702
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700703 return subsys;
704
705err_register:
Stephen Boydeff41a62012-07-06 13:19:02 -0700706 subsys_debugfs_remove(subsys);
707err_debugfs:
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700708 mutex_destroy(&subsys->shutdown_lock);
709 mutex_destroy(&subsys->powerup_lock);
710 ida_simple_remove(&subsys_ida, subsys->id);
711err_ida:
712 wake_lock_destroy(&subsys->wake_lock);
713 kfree(subsys);
714 return ERR_PTR(ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700715}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700716EXPORT_SYMBOL(subsys_register);
717
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700718void subsys_unregister(struct subsys_device *subsys)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700719{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700720 if (IS_ERR_OR_NULL(subsys))
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700721 return;
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700722
723 if (get_device(&subsys->dev)) {
724 mutex_lock(&subsys->powerup_lock);
725 WARN_ON(subsys->count);
726 device_unregister(&subsys->dev);
727 mutex_unlock(&subsys->powerup_lock);
Stephen Boydeff41a62012-07-06 13:19:02 -0700728 subsys_debugfs_remove(subsys);
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700729 put_device(&subsys->dev);
730 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700731}
732EXPORT_SYMBOL(subsys_unregister);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700733
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700734static int subsys_panic(struct device *dev, void *data)
735{
736 struct subsys_device *subsys = to_subsys(dev);
737
738 if (subsys->desc->crash_shutdown)
739 subsys->desc->crash_shutdown(subsys->desc);
740 return 0;
741}
742
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700743static int ssr_panic_handler(struct notifier_block *this,
744 unsigned long event, void *ptr)
745{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700746 bus_for_each_dev(&subsys_bus_type, NULL, NULL, subsys_panic);
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700747 return NOTIFY_DONE;
748}
749
750static struct notifier_block panic_nb = {
751 .notifier_call = ssr_panic_handler,
752};
753
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700754static int __init ssr_init_soc_restart_orders(void)
755{
756 int i;
757
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700758 atomic_notifier_chain_register(&panic_notifier_list,
759 &panic_nb);
760
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700761 if (cpu_is_msm8x60()) {
762 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
763 mutex_init(&orders_8x60_all[i]->powerup_lock);
764 mutex_init(&orders_8x60_all[i]->shutdown_lock);
765 }
766
767 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
768 mutex_init(&orders_8x60_modems[i]->powerup_lock);
769 mutex_init(&orders_8x60_modems[i]->shutdown_lock);
770 }
771
772 restart_orders = orders_8x60_all;
773 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
774 }
775
Vikram Mulukutlae4ae75e2012-07-27 15:14:10 -0700776 if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) {
777 restart_orders = restart_orders_8960_sglte;
778 n_restart_orders = ARRAY_SIZE(restart_orders_8960_sglte);
779 }
780
781 for (i = 0; i < n_restart_orders; i++) {
782 mutex_init(&restart_orders[i]->powerup_lock);
783 mutex_init(&restart_orders[i]->shutdown_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700784 }
785
786 if (restart_orders == NULL || n_restart_orders < 1) {
787 WARN_ON(1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700788 }
789
790 return 0;
791}
792
793static int __init subsys_restart_init(void)
794{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700795 int ret;
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700796
Vikram Mulukutla8d4c3f02012-09-10 19:25:32 -0700797 ssr_wq = alloc_workqueue("ssr_wq", WQ_CPU_INTENSIVE, 0);
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700798 BUG_ON(!ssr_wq);
799
800 ret = bus_register(&subsys_bus_type);
801 if (ret)
802 goto err_bus;
Stephen Boydeff41a62012-07-06 13:19:02 -0700803 ret = subsys_debugfs_init();
804 if (ret)
805 goto err_debugfs;
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700806 ret = ssr_init_soc_restart_orders();
807 if (ret)
808 goto err_soc;
809 return 0;
Stephen Boydeff41a62012-07-06 13:19:02 -0700810
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700811err_soc:
Stephen Boydeff41a62012-07-06 13:19:02 -0700812 subsys_debugfs_exit();
813err_debugfs:
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700814 bus_unregister(&subsys_bus_type);
815err_bus:
816 destroy_workqueue(ssr_wq);
817 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700818}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700819arch_initcall(subsys_restart_init);
820
821MODULE_DESCRIPTION("Subsystem Restart Driver");
822MODULE_LICENSE("GPL v2");