blob: 142ae943aa3cb7dd3c38ce9a75ee821d90393ca4 [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>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070032
33#include <asm/current.h>
34
35#include <mach/peripheral-loader.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070036#include <mach/socinfo.h>
37#include <mach/subsystem_notif.h>
38#include <mach/subsystem_restart.h>
39
40#include "smd_private.h"
41
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070042struct subsys_soc_restart_order {
43 const char * const *subsystem_list;
44 int count;
45
46 struct mutex shutdown_lock;
47 struct mutex powerup_lock;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070048 struct subsys_device *subsys_ptrs[];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049};
50
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070051struct restart_log {
52 struct timeval time;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070053 struct subsys_device *dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070054 struct list_head list;
55};
56
Stephen Boyd4ec9a942012-06-21 19:10:48 -070057enum subsys_state {
58 SUBSYS_OFFLINE,
59 SUBSYS_ONLINE,
60};
61
62static const char * const subsys_states[] = {
63 [SUBSYS_OFFLINE] = "OFFLINE",
64 [SUBSYS_ONLINE] = "ONLINE",
65};
66
Stephen Boyd0ebf7212012-04-30 20:42:35 -070067struct subsys_device {
68 struct subsys_desc *desc;
Stephen Boyd497ef402012-06-21 12:54:40 -070069 struct wake_lock wake_lock;
70 char wlname[64];
71 struct work_struct work;
72 spinlock_t restart_lock;
Stephen Boyd77c22742012-07-24 18:46:45 -070073 int restart_count;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070074
75 void *notify;
Stephen Boyd4ec9a942012-06-21 19:10:48 -070076 struct device dev;
77 struct module *owner;
78 int count;
79 enum subsys_state state;
80 int id;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070081
82 struct mutex shutdown_lock;
83 struct mutex powerup_lock;
84
85 void *restart_order;
86};
87
Stephen Boyd4ec9a942012-06-21 19:10:48 -070088static struct subsys_device *to_subsys(struct device *d)
89{
90 return container_of(d, struct subsys_device, dev);
91}
92
93static ssize_t name_show(struct device *dev, struct device_attribute *attr,
94 char *buf)
95{
96 return snprintf(buf, PAGE_SIZE, "%s\n", to_subsys(dev)->desc->name);
97}
98
99static ssize_t state_show(struct device *dev, struct device_attribute *attr,
100 char *buf)
101{
102 enum subsys_state state = to_subsys(dev)->state;
103 return snprintf(buf, PAGE_SIZE, "%s\n", subsys_states[state]);
104}
105
106static struct device_attribute subsys_attrs[] = {
107 __ATTR_RO(name),
108 __ATTR_RO(state),
109 __ATTR_NULL,
110};
111
112static struct bus_type subsys_bus_type = {
113 .name = "msm_subsys",
114 .dev_attrs = subsys_attrs,
115};
116
117static DEFINE_IDA(subsys_ida);
118
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700119static int enable_ramdumps;
Stephen Boydc68ee642012-05-01 17:02:45 -0700120module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR);
121
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700122struct workqueue_struct *ssr_wq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700123
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700124static LIST_HEAD(restart_log_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700125static DEFINE_MUTEX(soc_order_reg_lock);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700126static DEFINE_MUTEX(restart_log_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700127
128/* SOC specific restart orders go here */
129
130#define DEFINE_SINGLE_RESTART_ORDER(name, order) \
131 static struct subsys_soc_restart_order __##name = { \
132 .subsystem_list = order, \
133 .count = ARRAY_SIZE(order), \
134 .subsys_ptrs = {[ARRAY_SIZE(order)] = NULL} \
135 }; \
136 static struct subsys_soc_restart_order *name[] = { \
137 &__##name, \
138 }
139
140/* MSM 8x60 restart ordering info */
141static const char * const _order_8x60_all[] = {
142 "external_modem", "modem", "lpass"
143};
144DEFINE_SINGLE_RESTART_ORDER(orders_8x60_all, _order_8x60_all);
145
146static const char * const _order_8x60_modems[] = {"external_modem", "modem"};
147DEFINE_SINGLE_RESTART_ORDER(orders_8x60_modems, _order_8x60_modems);
148
149/* MSM 8960 restart ordering info */
150static const char * const order_8960[] = {"modem", "lpass"};
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700151/*SGLTE restart ordering info*/
152static const char * const order_8960_sglte[] = {"external_modem",
153 "modem"};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700154
155static struct subsys_soc_restart_order restart_orders_8960_one = {
156 .subsystem_list = order_8960,
157 .count = ARRAY_SIZE(order_8960),
158 .subsys_ptrs = {[ARRAY_SIZE(order_8960)] = NULL}
159 };
160
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700161static struct subsys_soc_restart_order restart_orders_8960_fusion_sglte = {
162 .subsystem_list = order_8960_sglte,
163 .count = ARRAY_SIZE(order_8960_sglte),
164 .subsys_ptrs = {[ARRAY_SIZE(order_8960_sglte)] = NULL}
165 };
166
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700167static struct subsys_soc_restart_order *restart_orders_8960[] = {
168 &restart_orders_8960_one,
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700169 };
170
171static struct subsys_soc_restart_order *restart_orders_8960_sglte[] = {
172 &restart_orders_8960_fusion_sglte,
173 };
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700174
175/* These will be assigned to one of the sets above after
176 * runtime SoC identification.
177 */
178static struct subsys_soc_restart_order **restart_orders;
179static int n_restart_orders;
180
Stephen Boydc68ee642012-05-01 17:02:45 -0700181static int restart_level = RESET_SOC;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700182
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700183int get_restart_level()
184{
185 return restart_level;
186}
187EXPORT_SYMBOL(get_restart_level);
188
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189static int restart_level_set(const char *val, struct kernel_param *kp)
190{
191 int ret;
192 int old_val = restart_level;
193
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800194 if (cpu_is_msm9615()) {
195 pr_err("Only Phase 1 subsystem restart is supported\n");
196 return -EINVAL;
197 }
198
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700199 ret = param_set_int(val, kp);
200 if (ret)
201 return ret;
202
203 switch (restart_level) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700204 case RESET_SOC:
205 case RESET_SUBSYS_COUPLED:
206 case RESET_SUBSYS_INDEPENDENT:
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700207 pr_info("Phase %d behavior activated.\n", restart_level);
Stephen Boydc68ee642012-05-01 17:02:45 -0700208 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700209 default:
210 restart_level = old_val;
211 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700212 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700213 return 0;
214}
215
216module_param_call(restart_level, restart_level_set, param_get_int,
217 &restart_level, 0644);
218
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700219static struct subsys_soc_restart_order *
220update_restart_order(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700221{
222 int i, j;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700223 struct subsys_soc_restart_order *order;
224 const char *name = dev->desc->name;
225 int len = SUBSYS_NAME_MAX_LENGTH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700226
227 mutex_lock(&soc_order_reg_lock);
228 for (j = 0; j < n_restart_orders; j++) {
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700229 order = restart_orders[j];
230 for (i = 0; i < order->count; i++) {
231 if (!strncmp(order->subsystem_list[i], name, len)) {
232 order->subsys_ptrs[i] = dev;
233 goto found;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700234 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700235 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700236 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700237 order = NULL;
238found:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700239 mutex_unlock(&soc_order_reg_lock);
240
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700241 return order;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700242}
243
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700244static int max_restarts;
245module_param(max_restarts, int, 0644);
246
247static long max_history_time = 3600;
248module_param(max_history_time, long, 0644);
249
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700250static void do_epoch_check(struct subsys_device *dev)
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700251{
252 int n = 0;
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600253 struct timeval *time_first = NULL, *curr_time;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700254 struct restart_log *r_log, *temp;
255 static int max_restarts_check;
256 static long max_history_time_check;
257
258 mutex_lock(&restart_log_mutex);
259
260 max_restarts_check = max_restarts;
261 max_history_time_check = max_history_time;
262
263 /* Check if epoch checking is enabled */
264 if (!max_restarts_check)
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700265 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700266
267 r_log = kmalloc(sizeof(struct restart_log), GFP_KERNEL);
Matt Wagantalleecca342011-11-10 20:12:05 -0800268 if (!r_log)
269 goto out;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700270 r_log->dev = dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700271 do_gettimeofday(&r_log->time);
272 curr_time = &r_log->time;
273 INIT_LIST_HEAD(&r_log->list);
274
275 list_add_tail(&r_log->list, &restart_log_list);
276
277 list_for_each_entry_safe(r_log, temp, &restart_log_list, list) {
278
279 if ((curr_time->tv_sec - r_log->time.tv_sec) >
280 max_history_time_check) {
281
282 pr_debug("Deleted node with restart_time = %ld\n",
283 r_log->time.tv_sec);
284 list_del(&r_log->list);
285 kfree(r_log);
286 continue;
287 }
288 if (!n) {
289 time_first = &r_log->time;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700290 pr_debug("Time_first: %ld\n", time_first->tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700291 }
292 n++;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700293 pr_debug("Restart_time: %ld\n", r_log->time.tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700294 }
295
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600296 if (time_first && n >= max_restarts_check) {
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700297 if ((curr_time->tv_sec - time_first->tv_sec) <
298 max_history_time_check)
299 panic("Subsystems have crashed %d times in less than "
300 "%ld seconds!", max_restarts_check,
301 max_history_time_check);
302 }
303
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700304out:
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700305 mutex_unlock(&restart_log_mutex);
306}
307
Stephen Boyd0daae152012-05-01 17:36:15 -0700308static void for_each_subsys_device(struct subsys_device **list, unsigned count,
309 void *data, void (*fn)(struct subsys_device *, void *))
310{
311 while (count--) {
312 struct subsys_device *dev = *list++;
313 if (!dev)
314 continue;
315 fn(dev, data);
316 }
317}
318
319static void __send_notification_to_order(struct subsys_device *dev, void *data)
320{
321 enum subsys_notif_type type = (enum subsys_notif_type)data;
322
323 subsys_notif_queue_notification(dev->notify, type);
324}
325
326static void send_notification_to_order(struct subsys_device **l, unsigned n,
327 enum subsys_notif_type t)
328{
329 for_each_subsys_device(l, n, (void *)t, __send_notification_to_order);
330}
331
332static void subsystem_shutdown(struct subsys_device *dev, void *data)
333{
334 const char *name = dev->desc->name;
335
336 pr_info("[%p]: Shutting down %s\n", current, name);
337 if (dev->desc->shutdown(dev->desc) < 0)
338 panic("subsys-restart: [%p]: Failed to shutdown %s!",
339 current, name);
340}
341
342static void subsystem_ramdump(struct subsys_device *dev, void *data)
343{
344 const char *name = dev->desc->name;
345
346 if (dev->desc->ramdump)
347 if (dev->desc->ramdump(enable_ramdumps, dev->desc) < 0)
348 pr_warn("%s[%p]: Ramdump failed.\n", name, current);
349}
350
351static void subsystem_powerup(struct subsys_device *dev, void *data)
352{
353 const char *name = dev->desc->name;
354
355 pr_info("[%p]: Powering up %s\n", current, name);
356 if (dev->desc->powerup(dev->desc) < 0)
357 panic("[%p]: Failed to powerup %s!", current, name);
358}
359
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700360static int __find_subsys(struct device *dev, void *data)
361{
362 struct subsys_device *subsys = to_subsys(dev);
363 return !strcmp(subsys->desc->name, data);
364}
365
366static struct subsys_device *find_subsys(const char *str)
367{
368 struct device *dev;
369
370 if (!str)
371 return NULL;
372
373 dev = bus_find_device(&subsys_bus_type, NULL, (void *)str,
374 __find_subsys);
375 return dev ? to_subsys(dev) : NULL;
376}
377
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700378static void subsystem_restart_wq_func(struct work_struct *work)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700379{
Stephen Boyd497ef402012-06-21 12:54:40 -0700380 struct subsys_device *dev = container_of(work,
381 struct subsys_device, work);
Stephen Boyd0daae152012-05-01 17:36:15 -0700382 struct subsys_device **list;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700383 struct subsys_desc *desc = dev->desc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700384 struct subsys_soc_restart_order *soc_restart_order = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700385 struct mutex *powerup_lock;
386 struct mutex *shutdown_lock;
Stephen Boyd0daae152012-05-01 17:36:15 -0700387 unsigned count;
Stephen Boyd497ef402012-06-21 12:54:40 -0700388 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700389
Stephen Boyda82ee082012-06-21 12:35:49 -0700390 if (restart_level != RESET_SUBSYS_INDEPENDENT)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700391 soc_restart_order = dev->restart_order;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700392
Stephen Boydc68ee642012-05-01 17:02:45 -0700393 /*
394 * It's OK to not take the registration lock at this point.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700395 * This is because the subsystem list inside the relevant
396 * restart order is not being traversed.
397 */
398 if (!soc_restart_order) {
Stephen Boyd0daae152012-05-01 17:36:15 -0700399 list = &dev;
400 count = 1;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700401 powerup_lock = &dev->powerup_lock;
402 shutdown_lock = &dev->shutdown_lock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700403 } else {
Stephen Boyd0daae152012-05-01 17:36:15 -0700404 list = soc_restart_order->subsys_ptrs;
405 count = soc_restart_order->count;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406 powerup_lock = &soc_restart_order->powerup_lock;
407 shutdown_lock = &soc_restart_order->shutdown_lock;
408 }
409
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700410 pr_debug("[%p]: Attempting to get shutdown lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700411
Stephen Boydc68ee642012-05-01 17:02:45 -0700412 /*
413 * Try to acquire shutdown_lock. If this fails, these subsystems are
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700414 * already being restarted - return.
415 */
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800416 if (!mutex_trylock(shutdown_lock))
417 goto out;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700418
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700419 pr_debug("[%p]: Attempting to get powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700420
Stephen Boydc68ee642012-05-01 17:02:45 -0700421 /*
422 * Now that we've acquired the shutdown lock, either we're the first to
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700423 * restart these subsystems or some other thread is doing the powerup
424 * sequence for these subsystems. In the latter case, panic and bail
425 * out, since a subsystem died in its powerup sequence.
426 */
427 if (!mutex_trylock(powerup_lock))
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700428 panic("%s[%p]: Subsystem died during powerup!",
429 __func__, current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700430
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700431 do_epoch_check(dev);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700432
Stephen Boydc68ee642012-05-01 17:02:45 -0700433 /*
434 * It's necessary to take the registration lock because the subsystem
435 * list in the SoC restart order will be traversed and it shouldn't be
436 * changed until _this_ restart sequence completes.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700437 */
438 mutex_lock(&soc_order_reg_lock);
439
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700440 pr_debug("[%p]: Starting restart sequence for %s\n", current,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700441 desc->name);
Stephen Boyd0daae152012-05-01 17:36:15 -0700442 send_notification_to_order(list, count, SUBSYS_BEFORE_SHUTDOWN);
443 for_each_subsys_device(list, count, NULL, subsystem_shutdown);
444 send_notification_to_order(list, count, SUBSYS_AFTER_SHUTDOWN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700445
Stephen Boydc68ee642012-05-01 17:02:45 -0700446 /*
447 * Now that we've finished shutting down these subsystems, release the
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700448 * shutdown lock. If a subsystem restart request comes in for a
449 * subsystem in _this_ restart order after the unlock below, and
450 * before the powerup lock is released, panic and bail out.
451 */
452 mutex_unlock(shutdown_lock);
453
454 /* Collect ram dumps for all subsystems in order here */
Stephen Boyd0daae152012-05-01 17:36:15 -0700455 for_each_subsys_device(list, count, NULL, subsystem_ramdump);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700456
Stephen Boyd0daae152012-05-01 17:36:15 -0700457 send_notification_to_order(list, count, SUBSYS_BEFORE_POWERUP);
458 for_each_subsys_device(list, count, NULL, subsystem_powerup);
459 send_notification_to_order(list, count, SUBSYS_AFTER_POWERUP);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700460
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700461 pr_info("[%p]: Restart sequence for %s completed.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700462 current, desc->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700463
464 mutex_unlock(powerup_lock);
465
466 mutex_unlock(&soc_order_reg_lock);
467
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700468 pr_debug("[%p]: Released powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700469
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800470out:
Stephen Boyd497ef402012-06-21 12:54:40 -0700471 spin_lock_irqsave(&dev->restart_lock, flags);
Stephen Boyd77c22742012-07-24 18:46:45 -0700472 dev->restart_count--;
473 if (!dev->restart_count)
474 wake_unlock(&dev->wake_lock);
Stephen Boyd497ef402012-06-21 12:54:40 -0700475 spin_unlock_irqrestore(&dev->restart_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700476}
477
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700478static void __subsystem_restart_dev(struct subsys_device *dev)
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700479{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700480 struct subsys_desc *desc = dev->desc;
Stephen Boyd497ef402012-06-21 12:54:40 -0700481 unsigned long flags;
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700482
Stephen Boyd77c22742012-07-24 18:46:45 -0700483 pr_debug("Restarting %s [level=%d]!\n", desc->name, restart_level);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700484
Stephen Boyd77c22742012-07-24 18:46:45 -0700485 spin_lock_irqsave(&dev->restart_lock, flags);
486 if (!dev->restart_count)
Stephen Boyd497ef402012-06-21 12:54:40 -0700487 wake_lock(&dev->wake_lock);
Stephen Boyd77c22742012-07-24 18:46:45 -0700488 dev->restart_count++;
Stephen Boyd497ef402012-06-21 12:54:40 -0700489 spin_unlock_irqrestore(&dev->restart_lock, flags);
Stephen Boyd77c22742012-07-24 18:46:45 -0700490
491 if (!queue_work(ssr_wq, &dev->work)) {
492 spin_lock_irqsave(&dev->restart_lock, flags);
493 dev->restart_count--;
494 if (!dev->restart_count)
495 wake_unlock(&dev->wake_lock);
496 spin_unlock_irqrestore(&dev->restart_lock, flags);
497 }
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700498}
499
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700500int subsystem_restart_dev(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700501{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700502 const char *name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700503
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700504 if (!get_device(&dev->dev))
505 return -ENODEV;
506
507 if (!try_module_get(dev->owner)) {
508 put_device(&dev->dev);
509 return -ENODEV;
510 }
511
512 name = dev->desc->name;
Vikram Mulukutla3b24c8c2011-12-02 15:12:53 -0800513 pr_info("Restart sequence requested for %s, restart_level = %d.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700514 name, restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700515
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700516 switch (restart_level) {
517
518 case RESET_SUBSYS_COUPLED:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700519 case RESET_SUBSYS_INDEPENDENT:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700520 __subsystem_restart_dev(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700521 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700522 case RESET_SOC:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700523 panic("subsys-restart: Resetting the SoC - %s crashed.", name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700524 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700525 default:
526 panic("subsys-restart: Unknown restart level!\n");
Stephen Boydc68ee642012-05-01 17:02:45 -0700527 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700528 }
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700529 module_put(dev->owner);
530 put_device(&dev->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700531
532 return 0;
533}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700534EXPORT_SYMBOL(subsystem_restart_dev);
535
536int subsystem_restart(const char *name)
537{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700538 int ret;
539 struct subsys_device *dev = find_subsys(name);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700540
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700541 if (!dev)
542 return -ENODEV;
543
544 ret = subsystem_restart_dev(dev);
545 put_device(&dev->dev);
546 return ret;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700547}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700548EXPORT_SYMBOL(subsystem_restart);
549
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700550static void subsys_device_release(struct device *dev)
551{
552 struct subsys_device *subsys = to_subsys(dev);
553
554 wake_lock_destroy(&subsys->wake_lock);
555 mutex_destroy(&subsys->shutdown_lock);
556 mutex_destroy(&subsys->powerup_lock);
557 ida_simple_remove(&subsys_ida, subsys->id);
558 kfree(subsys);
559}
560
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700561struct subsys_device *subsys_register(struct subsys_desc *desc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700562{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700563 struct subsys_device *subsys;
564 int ret;
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700565
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700566 subsys = kzalloc(sizeof(*subsys), GFP_KERNEL);
567 if (!subsys)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700568 return ERR_PTR(-ENOMEM);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700569
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700570 subsys->desc = desc;
571 subsys->owner = desc->owner;
572 subsys->dev.parent = desc->dev;
573 subsys->dev.bus = &subsys_bus_type;
574 subsys->dev.release = subsys_device_release;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700575
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700576 subsys->notify = subsys_notif_add_subsys(desc->name);
577 subsys->restart_order = update_restart_order(subsys);
Stephen Boyd497ef402012-06-21 12:54:40 -0700578
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700579 snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name);
580 wake_lock_init(&subsys->wake_lock, WAKE_LOCK_SUSPEND, subsys->wlname);
581 INIT_WORK(&subsys->work, subsystem_restart_wq_func);
582 spin_lock_init(&subsys->restart_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700583
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700584 subsys->id = ida_simple_get(&subsys_ida, 0, 0, GFP_KERNEL);
585 if (subsys->id < 0) {
586 ret = subsys->id;
587 goto err_ida;
588 }
589 dev_set_name(&subsys->dev, "subsys%d", subsys->id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700590
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700591 mutex_init(&subsys->shutdown_lock);
592 mutex_init(&subsys->powerup_lock);
593
594 ret = device_register(&subsys->dev);
595 if (ret) {
596 put_device(&subsys->dev);
597 goto err_register;
598 }
599 return subsys;
600
601err_register:
602 mutex_destroy(&subsys->shutdown_lock);
603 mutex_destroy(&subsys->powerup_lock);
604 ida_simple_remove(&subsys_ida, subsys->id);
605err_ida:
606 wake_lock_destroy(&subsys->wake_lock);
607 kfree(subsys);
608 return ERR_PTR(ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700609}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700610EXPORT_SYMBOL(subsys_register);
611
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700612void subsys_unregister(struct subsys_device *subsys)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700613{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700614 if (IS_ERR_OR_NULL(subsys))
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700615 return;
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700616
617 if (get_device(&subsys->dev)) {
618 mutex_lock(&subsys->powerup_lock);
619 WARN_ON(subsys->count);
620 device_unregister(&subsys->dev);
621 mutex_unlock(&subsys->powerup_lock);
622 put_device(&subsys->dev);
623 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700624}
625EXPORT_SYMBOL(subsys_unregister);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700626
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700627static int subsys_panic(struct device *dev, void *data)
628{
629 struct subsys_device *subsys = to_subsys(dev);
630
631 if (subsys->desc->crash_shutdown)
632 subsys->desc->crash_shutdown(subsys->desc);
633 return 0;
634}
635
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700636static int ssr_panic_handler(struct notifier_block *this,
637 unsigned long event, void *ptr)
638{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700639 bus_for_each_dev(&subsys_bus_type, NULL, NULL, subsys_panic);
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700640 return NOTIFY_DONE;
641}
642
643static struct notifier_block panic_nb = {
644 .notifier_call = ssr_panic_handler,
645};
646
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700647static int __init ssr_init_soc_restart_orders(void)
648{
649 int i;
650
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700651 atomic_notifier_chain_register(&panic_notifier_list,
652 &panic_nb);
653
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700654 if (cpu_is_msm8x60()) {
655 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
656 mutex_init(&orders_8x60_all[i]->powerup_lock);
657 mutex_init(&orders_8x60_all[i]->shutdown_lock);
658 }
659
660 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
661 mutex_init(&orders_8x60_modems[i]->powerup_lock);
662 mutex_init(&orders_8x60_modems[i]->shutdown_lock);
663 }
664
665 restart_orders = orders_8x60_all;
666 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
667 }
668
Stepan Moskovchenko0df9bb22012-07-06 18:19:15 -0700669 if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
Stepan Moskovchenko9c749262012-07-09 19:30:44 -0700670 cpu_is_msm9615() || cpu_is_apq8064() || cpu_is_msm8627() ||
671 cpu_is_msm8960ab()) {
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700672 if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) {
673 restart_orders = restart_orders_8960_sglte;
674 n_restart_orders =
675 ARRAY_SIZE(restart_orders_8960_sglte);
676 } else {
677 restart_orders = restart_orders_8960;
678 n_restart_orders = ARRAY_SIZE(restart_orders_8960);
679 }
680 for (i = 0; i < n_restart_orders; i++) {
681 mutex_init(&restart_orders[i]->powerup_lock);
682 mutex_init(&restart_orders[i]->shutdown_lock);
683 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700684 }
685
686 if (restart_orders == NULL || n_restart_orders < 1) {
687 WARN_ON(1);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700688 }
689
690 return 0;
691}
692
693static int __init subsys_restart_init(void)
694{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700695 int ret;
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700696
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700697 ssr_wq = alloc_workqueue("ssr_wq", 0, 0);
698 BUG_ON(!ssr_wq);
699
700 ret = bus_register(&subsys_bus_type);
701 if (ret)
702 goto err_bus;
703 ret = ssr_init_soc_restart_orders();
704 if (ret)
705 goto err_soc;
706 return 0;
707err_soc:
708 bus_unregister(&subsys_bus_type);
709err_bus:
710 destroy_workqueue(ssr_wq);
711 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700712}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700713arch_initcall(subsys_restart_init);
714
715MODULE_DESCRIPTION("Subsystem Restart Driver");
716MODULE_LICENSE("GPL v2");