blob: 29481d3dc06b641204c541e94ea2926cfb1cc1b8 [file] [log] [blame]
Seemanta Duttabac64552012-12-14 15:47:27 -08001/* Copyright (c) 2011-2013, 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>
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>
Seemanta Duttaf055ac72013-01-10 18:08:04 -080033#include <linux/miscdevice.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070034
35#include <asm/current.h>
36
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
Stephen Boyd5bd20e42012-10-03 12:28:43 -070043/**
44 * enum p_subsys_state - state of a subsystem (private)
45 * @SUBSYS_NORMAL: subsystem is operating normally
46 * @SUBSYS_CRASHED: subsystem has crashed and hasn't been shutdown
47 * @SUBSYS_RESTARTING: subsystem has been shutdown and is now restarting
48 *
49 * The 'private' side of the subsytem state used to determine where in the
50 * restart process the subsystem is.
51 */
52enum p_subsys_state {
53 SUBSYS_NORMAL,
54 SUBSYS_CRASHED,
55 SUBSYS_RESTARTING,
56};
57
58/**
59 * enum subsys_state - state of a subsystem (public)
60 * @SUBSYS_OFFLINE: subsystem is offline
61 * @SUBSYS_ONLINE: subsystem is online
62 *
63 * The 'public' side of the subsytem state, exposed to userspace.
64 */
65enum subsys_state {
66 SUBSYS_OFFLINE,
67 SUBSYS_ONLINE,
68};
69
70static const char * const subsys_states[] = {
71 [SUBSYS_OFFLINE] = "OFFLINE",
72 [SUBSYS_ONLINE] = "ONLINE",
73};
74
75/**
76 * struct subsys_tracking - track state of a subsystem or restart order
77 * @p_state: private state of subsystem/order
78 * @state: public state of subsystem/order
79 * @s_lock: protects p_state
80 * @lock: protects subsystem/order callbacks and state
81 *
82 * Tracks the state of a subsystem or a set of subsystems (restart order).
83 * Doing this avoids the need to grab each subsystem's lock and update
84 * each subsystems state when restarting an order.
85 */
86struct subsys_tracking {
87 enum p_subsys_state p_state;
88 spinlock_t s_lock;
89 enum subsys_state state;
90 struct mutex lock;
91};
92
93/**
94 * struct subsys_soc_restart_order - subsystem restart order
95 * @subsystem_list: names of subsystems in this restart order
96 * @count: number of subsystems in order
97 * @track: state tracking and locking
98 * @subsys_ptrs: pointers to subsystems in this restart order
99 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700100struct subsys_soc_restart_order {
101 const char * const *subsystem_list;
102 int count;
103
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700104 struct subsys_tracking track;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700105 struct subsys_device *subsys_ptrs[];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700106};
107
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700108struct restart_log {
109 struct timeval time;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700110 struct subsys_device *dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700111 struct list_head list;
112};
113
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700114/**
115 * struct subsys_device - subsystem device
116 * @desc: subsystem descriptor
117 * @wake_lock: prevents suspend during subsystem_restart()
118 * @wlname: name of @wake_lock
119 * @work: context for subsystem_restart_wq_func() for this device
120 * @track: state tracking and locking
121 * @notify: subsys notify handle
122 * @dev: device
123 * @owner: module that provides @desc
124 * @count: reference count of subsystem_get()/subsystem_put()
125 * @id: ida
126 * @restart_order: order of other devices this devices restarts with
127 * @dentry: debugfs directory for this device
Stephen Boyd4057ec02012-12-12 17:38:38 -0800128 * @do_ramdump_on_put: ramdump on subsystem_put() if true
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700129 */
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700130struct subsys_device {
131 struct subsys_desc *desc;
Stephen Boyd497ef402012-06-21 12:54:40 -0700132 struct wake_lock wake_lock;
133 char wlname[64];
134 struct work_struct work;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700135
136 struct subsys_tracking track;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700137
138 void *notify;
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700139 struct device dev;
140 struct module *owner;
141 int count;
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700142 int id;
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700143 struct subsys_soc_restart_order *restart_order;
Stephen Boydeff41a62012-07-06 13:19:02 -0700144#ifdef CONFIG_DEBUG_FS
145 struct dentry *dentry;
146#endif
Stephen Boyd4057ec02012-12-12 17:38:38 -0800147 bool do_ramdump_on_put;
Seemanta Duttaf055ac72013-01-10 18:08:04 -0800148 struct miscdevice misc_dev;
149 char miscdevice_name[32];
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700150};
151
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700152static struct subsys_device *to_subsys(struct device *d)
153{
154 return container_of(d, struct subsys_device, dev);
155}
156
157static ssize_t name_show(struct device *dev, struct device_attribute *attr,
158 char *buf)
159{
160 return snprintf(buf, PAGE_SIZE, "%s\n", to_subsys(dev)->desc->name);
161}
162
163static ssize_t state_show(struct device *dev, struct device_attribute *attr,
164 char *buf)
165{
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700166 enum subsys_state state = to_subsys(dev)->track.state;
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700167 return snprintf(buf, PAGE_SIZE, "%s\n", subsys_states[state]);
168}
169
Stephen Boyd6b567182012-09-18 11:31:02 -0700170static void subsys_set_state(struct subsys_device *subsys,
171 enum subsys_state state)
172{
173 unsigned long flags;
174
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700175 spin_lock_irqsave(&subsys->track.s_lock, flags);
176 if (subsys->track.state != state) {
177 subsys->track.state = state;
178 spin_unlock_irqrestore(&subsys->track.s_lock, flags);
Stephen Boyd6b567182012-09-18 11:31:02 -0700179 sysfs_notify(&subsys->dev.kobj, NULL, "state");
180 return;
181 }
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700182 spin_unlock_irqrestore(&subsys->track.s_lock, flags);
Stephen Boyd6b567182012-09-18 11:31:02 -0700183}
184
Stephen Boyd43b380a2012-09-21 17:34:24 -0700185/**
186 * subsytem_default_online() - Mark a subsystem as online by default
187 * @dev: subsystem to mark as online
188 *
189 * Marks a subsystem as "online" without increasing the reference count
190 * on the subsystem. This is typically used by subsystems that are already
191 * online when the kernel boots up.
192 */
193void subsys_default_online(struct subsys_device *dev)
194{
195 subsys_set_state(dev, SUBSYS_ONLINE);
196}
197EXPORT_SYMBOL(subsys_default_online);
198
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700199static struct device_attribute subsys_attrs[] = {
200 __ATTR_RO(name),
201 __ATTR_RO(state),
202 __ATTR_NULL,
203};
204
205static struct bus_type subsys_bus_type = {
206 .name = "msm_subsys",
207 .dev_attrs = subsys_attrs,
208};
209
210static DEFINE_IDA(subsys_ida);
211
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700212static int enable_ramdumps;
Stephen Boydc68ee642012-05-01 17:02:45 -0700213module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR);
214
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700215struct workqueue_struct *ssr_wq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700216
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700217static LIST_HEAD(restart_log_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700218static DEFINE_MUTEX(soc_order_reg_lock);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700219static DEFINE_MUTEX(restart_log_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700220
221/* SOC specific restart orders go here */
222
223#define DEFINE_SINGLE_RESTART_ORDER(name, order) \
224 static struct subsys_soc_restart_order __##name = { \
225 .subsystem_list = order, \
226 .count = ARRAY_SIZE(order), \
227 .subsys_ptrs = {[ARRAY_SIZE(order)] = NULL} \
228 }; \
229 static struct subsys_soc_restart_order *name[] = { \
230 &__##name, \
231 }
232
233/* MSM 8x60 restart ordering info */
234static const char * const _order_8x60_all[] = {
Stephen Boyd77db8bb2012-06-27 15:15:16 -0700235 "external_modem", "modem", "adsp"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700236};
237DEFINE_SINGLE_RESTART_ORDER(orders_8x60_all, _order_8x60_all);
238
239static const char * const _order_8x60_modems[] = {"external_modem", "modem"};
240DEFINE_SINGLE_RESTART_ORDER(orders_8x60_modems, _order_8x60_modems);
241
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700242/*SGLTE restart ordering info*/
243static const char * const order_8960_sglte[] = {"external_modem",
244 "modem"};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700245
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700246static struct subsys_soc_restart_order restart_orders_8960_fusion_sglte = {
247 .subsystem_list = order_8960_sglte,
248 .count = ARRAY_SIZE(order_8960_sglte),
249 .subsys_ptrs = {[ARRAY_SIZE(order_8960_sglte)] = NULL}
250 };
251
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700252static struct subsys_soc_restart_order *restart_orders_8960_sglte[] = {
253 &restart_orders_8960_fusion_sglte,
254 };
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700255
256/* These will be assigned to one of the sets above after
257 * runtime SoC identification.
258 */
259static struct subsys_soc_restart_order **restart_orders;
260static int n_restart_orders;
261
Stephen Boydc68ee642012-05-01 17:02:45 -0700262static int restart_level = RESET_SOC;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700263
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700264int get_restart_level()
265{
266 return restart_level;
267}
268EXPORT_SYMBOL(get_restart_level);
269
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700270static int restart_level_set(const char *val, struct kernel_param *kp)
271{
272 int ret;
273 int old_val = restart_level;
274
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800275 if (cpu_is_msm9615()) {
276 pr_err("Only Phase 1 subsystem restart is supported\n");
277 return -EINVAL;
278 }
279
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700280 ret = param_set_int(val, kp);
281 if (ret)
282 return ret;
283
284 switch (restart_level) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700285 case RESET_SUBSYS_INDEPENDENT:
Vikram Mulukutlae4ae75e2012-07-27 15:14:10 -0700286 if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) {
287 pr_info("Phase 3 is currently unsupported. Using phase 2 instead.\n");
288 restart_level = RESET_SUBSYS_COUPLED;
289 }
290 case RESET_SUBSYS_COUPLED:
291 case RESET_SOC:
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700292 pr_info("Phase %d behavior activated.\n", restart_level);
Stephen Boydc68ee642012-05-01 17:02:45 -0700293 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700294 default:
295 restart_level = old_val;
296 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700297 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700298 return 0;
299}
300
301module_param_call(restart_level, restart_level_set, param_get_int,
302 &restart_level, 0644);
303
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700304static struct subsys_soc_restart_order *
305update_restart_order(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700306{
307 int i, j;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700308 struct subsys_soc_restart_order *order;
309 const char *name = dev->desc->name;
310 int len = SUBSYS_NAME_MAX_LENGTH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700311
312 mutex_lock(&soc_order_reg_lock);
313 for (j = 0; j < n_restart_orders; j++) {
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700314 order = restart_orders[j];
315 for (i = 0; i < order->count; i++) {
316 if (!strncmp(order->subsystem_list[i], name, len)) {
317 order->subsys_ptrs[i] = dev;
318 goto found;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700319 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700320 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700321 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700322 order = NULL;
323found:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700324 mutex_unlock(&soc_order_reg_lock);
325
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700326 return order;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700327}
328
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700329static int max_restarts;
330module_param(max_restarts, int, 0644);
331
332static long max_history_time = 3600;
333module_param(max_history_time, long, 0644);
334
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700335static void do_epoch_check(struct subsys_device *dev)
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700336{
337 int n = 0;
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600338 struct timeval *time_first = NULL, *curr_time;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700339 struct restart_log *r_log, *temp;
340 static int max_restarts_check;
341 static long max_history_time_check;
342
343 mutex_lock(&restart_log_mutex);
344
345 max_restarts_check = max_restarts;
346 max_history_time_check = max_history_time;
347
348 /* Check if epoch checking is enabled */
349 if (!max_restarts_check)
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700350 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700351
352 r_log = kmalloc(sizeof(struct restart_log), GFP_KERNEL);
Matt Wagantalleecca342011-11-10 20:12:05 -0800353 if (!r_log)
354 goto out;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700355 r_log->dev = dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700356 do_gettimeofday(&r_log->time);
357 curr_time = &r_log->time;
358 INIT_LIST_HEAD(&r_log->list);
359
360 list_add_tail(&r_log->list, &restart_log_list);
361
362 list_for_each_entry_safe(r_log, temp, &restart_log_list, list) {
363
364 if ((curr_time->tv_sec - r_log->time.tv_sec) >
365 max_history_time_check) {
366
367 pr_debug("Deleted node with restart_time = %ld\n",
368 r_log->time.tv_sec);
369 list_del(&r_log->list);
370 kfree(r_log);
371 continue;
372 }
373 if (!n) {
374 time_first = &r_log->time;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700375 pr_debug("Time_first: %ld\n", time_first->tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700376 }
377 n++;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700378 pr_debug("Restart_time: %ld\n", r_log->time.tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700379 }
380
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600381 if (time_first && n >= max_restarts_check) {
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700382 if ((curr_time->tv_sec - time_first->tv_sec) <
383 max_history_time_check)
384 panic("Subsystems have crashed %d times in less than "
385 "%ld seconds!", max_restarts_check,
386 max_history_time_check);
387 }
388
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700389out:
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700390 mutex_unlock(&restart_log_mutex);
391}
392
Stephen Boyd0daae152012-05-01 17:36:15 -0700393static void for_each_subsys_device(struct subsys_device **list, unsigned count,
394 void *data, void (*fn)(struct subsys_device *, void *))
395{
396 while (count--) {
397 struct subsys_device *dev = *list++;
398 if (!dev)
399 continue;
400 fn(dev, data);
401 }
402}
403
Seemanta Duttabac64552012-12-14 15:47:27 -0800404static void notify_each_subsys_device(struct subsys_device **list,
405 unsigned count,
406 enum subsys_notif_type notif, void *data)
Stephen Boyd0daae152012-05-01 17:36:15 -0700407{
Seemanta Duttabac64552012-12-14 15:47:27 -0800408 while (count--) {
409 enum subsys_notif_type type = (enum subsys_notif_type)type;
410 struct subsys_device *dev = *list++;
411 if (!dev)
412 continue;
413 subsys_notif_queue_notification(dev->notify, notif, data);
414 }
Stephen Boyd0daae152012-05-01 17:36:15 -0700415}
416
417static void subsystem_shutdown(struct subsys_device *dev, void *data)
418{
419 const char *name = dev->desc->name;
420
421 pr_info("[%p]: Shutting down %s\n", current, name);
422 if (dev->desc->shutdown(dev->desc) < 0)
423 panic("subsys-restart: [%p]: Failed to shutdown %s!",
424 current, name);
Stephen Boyd6b567182012-09-18 11:31:02 -0700425 subsys_set_state(dev, SUBSYS_OFFLINE);
Stephen Boyd0daae152012-05-01 17:36:15 -0700426}
427
428static void subsystem_ramdump(struct subsys_device *dev, void *data)
429{
430 const char *name = dev->desc->name;
431
432 if (dev->desc->ramdump)
433 if (dev->desc->ramdump(enable_ramdumps, dev->desc) < 0)
434 pr_warn("%s[%p]: Ramdump failed.\n", name, current);
Stephen Boyd4057ec02012-12-12 17:38:38 -0800435 dev->do_ramdump_on_put = false;
Stephen Boyd0daae152012-05-01 17:36:15 -0700436}
437
438static void subsystem_powerup(struct subsys_device *dev, void *data)
439{
440 const char *name = dev->desc->name;
441
442 pr_info("[%p]: Powering up %s\n", current, name);
443 if (dev->desc->powerup(dev->desc) < 0)
444 panic("[%p]: Failed to powerup %s!", current, name);
Stephen Boyd6b567182012-09-18 11:31:02 -0700445 subsys_set_state(dev, SUBSYS_ONLINE);
Stephen Boyd0daae152012-05-01 17:36:15 -0700446}
447
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700448static int __find_subsys(struct device *dev, void *data)
449{
450 struct subsys_device *subsys = to_subsys(dev);
451 return !strcmp(subsys->desc->name, data);
452}
453
454static struct subsys_device *find_subsys(const char *str)
455{
456 struct device *dev;
457
458 if (!str)
459 return NULL;
460
461 dev = bus_find_device(&subsys_bus_type, NULL, (void *)str,
462 __find_subsys);
463 return dev ? to_subsys(dev) : NULL;
464}
465
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700466static int subsys_start(struct subsys_device *subsys)
467{
468 int ret;
469
470 ret = subsys->desc->start(subsys->desc);
471 if (!ret)
472 subsys_set_state(subsys, SUBSYS_ONLINE);
473
474 return ret;
475}
476
477static void subsys_stop(struct subsys_device *subsys)
478{
479 subsys->desc->stop(subsys->desc);
480 subsys_set_state(subsys, SUBSYS_OFFLINE);
481}
482
483static struct subsys_tracking *subsys_get_track(struct subsys_device *subsys)
484{
485 struct subsys_soc_restart_order *order = subsys->restart_order;
486
487 if (restart_level != RESET_SUBSYS_INDEPENDENT && order)
488 return &order->track;
489 else
490 return &subsys->track;
491}
492
493/**
494 * subsytem_get() - Boot a subsystem
495 * @name: pointer to a string containing the name of the subsystem to boot
496 *
497 * This function returns a pointer if it succeeds. If an error occurs an
498 * ERR_PTR is returned.
499 *
500 * If this feature is disable, the value %NULL will be returned.
501 */
502void *subsystem_get(const char *name)
503{
504 struct subsys_device *subsys;
505 struct subsys_device *subsys_d;
506 int ret;
507 void *retval;
508 struct subsys_tracking *track;
509
510 if (!name)
511 return NULL;
512
513 subsys = retval = find_subsys(name);
514 if (!subsys)
515 return ERR_PTR(-ENODEV);
516 if (!try_module_get(subsys->owner)) {
517 retval = ERR_PTR(-ENODEV);
518 goto err_module;
519 }
520
521 subsys_d = subsystem_get(subsys->desc->depends_on);
522 if (IS_ERR(subsys_d)) {
523 retval = subsys_d;
524 goto err_depends;
525 }
526
527 track = subsys_get_track(subsys);
528 mutex_lock(&track->lock);
529 if (!subsys->count) {
530 ret = subsys_start(subsys);
531 if (ret) {
532 retval = ERR_PTR(ret);
533 goto err_start;
534 }
535 }
536 subsys->count++;
537 mutex_unlock(&track->lock);
538 return retval;
539err_start:
540 mutex_unlock(&track->lock);
541 subsystem_put(subsys_d);
542err_depends:
543 module_put(subsys->owner);
544err_module:
545 put_device(&subsys->dev);
546 return retval;
547}
548EXPORT_SYMBOL(subsystem_get);
549
550/**
551 * subsystem_put() - Shutdown a subsystem
552 * @peripheral_handle: pointer from a previous call to subsystem_get()
553 *
554 * This doesn't imply that a subsystem is shutdown until all callers of
555 * subsystem_get() have called subsystem_put().
556 */
557void subsystem_put(void *subsystem)
558{
559 struct subsys_device *subsys_d, *subsys = subsystem;
560 struct subsys_tracking *track;
561
562 if (IS_ERR_OR_NULL(subsys))
563 return;
564
565 track = subsys_get_track(subsys);
566 mutex_lock(&track->lock);
567 if (WARN(!subsys->count, "%s: %s: Reference count mismatch\n",
568 subsys->desc->name, __func__))
569 goto err_out;
Stephen Boyd4057ec02012-12-12 17:38:38 -0800570 if (!--subsys->count) {
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700571 subsys_stop(subsys);
Stephen Boyd4057ec02012-12-12 17:38:38 -0800572 if (subsys->do_ramdump_on_put)
573 subsystem_ramdump(subsys, NULL);
574 }
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700575 mutex_unlock(&track->lock);
576
577 subsys_d = find_subsys(subsys->desc->depends_on);
578 if (subsys_d) {
579 subsystem_put(subsys_d);
580 put_device(&subsys_d->dev);
581 }
582 module_put(subsys->owner);
583 put_device(&subsys->dev);
584 return;
585err_out:
586 mutex_unlock(&track->lock);
587}
588EXPORT_SYMBOL(subsystem_put);
589
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700590static void subsystem_restart_wq_func(struct work_struct *work)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700591{
Stephen Boyd497ef402012-06-21 12:54:40 -0700592 struct subsys_device *dev = container_of(work,
593 struct subsys_device, work);
Stephen Boyd0daae152012-05-01 17:36:15 -0700594 struct subsys_device **list;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700595 struct subsys_desc *desc = dev->desc;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700596 struct subsys_soc_restart_order *order = dev->restart_order;
597 struct subsys_tracking *track;
Stephen Boyd0daae152012-05-01 17:36:15 -0700598 unsigned count;
Stephen Boyd497ef402012-06-21 12:54:40 -0700599 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700600
Stephen Boydc68ee642012-05-01 17:02:45 -0700601 /*
602 * It's OK to not take the registration lock at this point.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700603 * This is because the subsystem list inside the relevant
604 * restart order is not being traversed.
605 */
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700606 if (restart_level != RESET_SUBSYS_INDEPENDENT && order) {
607 list = order->subsys_ptrs;
608 count = order->count;
609 track = &order->track;
610 } else {
Stephen Boyd0daae152012-05-01 17:36:15 -0700611 list = &dev;
612 count = 1;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700613 track = &dev->track;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700614 }
615
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700616 mutex_lock(&track->lock);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700617 do_epoch_check(dev);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700618
Stephen Boydc68ee642012-05-01 17:02:45 -0700619 /*
620 * It's necessary to take the registration lock because the subsystem
621 * list in the SoC restart order will be traversed and it shouldn't be
622 * changed until _this_ restart sequence completes.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700623 */
624 mutex_lock(&soc_order_reg_lock);
625
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700626 pr_debug("[%p]: Starting restart sequence for %s\n", current,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700627 desc->name);
Seemanta Duttabac64552012-12-14 15:47:27 -0800628 notify_each_subsys_device(list, count, SUBSYS_BEFORE_SHUTDOWN, NULL);
Stephen Boyd0daae152012-05-01 17:36:15 -0700629 for_each_subsys_device(list, count, NULL, subsystem_shutdown);
Seemanta Duttabac64552012-12-14 15:47:27 -0800630 notify_each_subsys_device(list, count, SUBSYS_AFTER_SHUTDOWN, NULL);
631
632 notify_each_subsys_device(list, count, SUBSYS_RAMDUMP_NOTIFICATION,
633 &enable_ramdumps);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700634
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700635 spin_lock_irqsave(&track->s_lock, flags);
636 track->p_state = SUBSYS_RESTARTING;
637 spin_unlock_irqrestore(&track->s_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700638
639 /* Collect ram dumps for all subsystems in order here */
Stephen Boyd0daae152012-05-01 17:36:15 -0700640 for_each_subsys_device(list, count, NULL, subsystem_ramdump);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700641
Seemanta Duttabac64552012-12-14 15:47:27 -0800642 notify_each_subsys_device(list, count, SUBSYS_BEFORE_POWERUP, NULL);
Stephen Boyd0daae152012-05-01 17:36:15 -0700643 for_each_subsys_device(list, count, NULL, subsystem_powerup);
Seemanta Duttabac64552012-12-14 15:47:27 -0800644 notify_each_subsys_device(list, count, SUBSYS_AFTER_POWERUP, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700645
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700646 pr_info("[%p]: Restart sequence for %s completed.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700647 current, desc->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700648
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700649 mutex_unlock(&soc_order_reg_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700650 mutex_unlock(&track->lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700651
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700652 spin_lock_irqsave(&track->s_lock, flags);
653 track->p_state = SUBSYS_NORMAL;
Stephen Boyd6b567182012-09-18 11:31:02 -0700654 wake_unlock(&dev->wake_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700655 spin_unlock_irqrestore(&track->s_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700656}
657
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700658static void __subsystem_restart_dev(struct subsys_device *dev)
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700659{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700660 struct subsys_desc *desc = dev->desc;
Stephen Boyd6b567182012-09-18 11:31:02 -0700661 const char *name = dev->desc->name;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700662 struct subsys_tracking *track;
Stephen Boyd497ef402012-06-21 12:54:40 -0700663 unsigned long flags;
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700664
Stephen Boyd77c22742012-07-24 18:46:45 -0700665 pr_debug("Restarting %s [level=%d]!\n", desc->name, restart_level);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700666
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700667 track = subsys_get_track(dev);
Stephen Boyd6b567182012-09-18 11:31:02 -0700668 /*
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700669 * Allow drivers to call subsystem_restart{_dev}() as many times as
670 * they want up until the point where the subsystem is shutdown.
Stephen Boyd6b567182012-09-18 11:31:02 -0700671 */
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700672 spin_lock_irqsave(&track->s_lock, flags);
673 if (track->p_state != SUBSYS_CRASHED) {
674 if (dev->track.state == SUBSYS_ONLINE &&
675 track->p_state != SUBSYS_RESTARTING) {
676 track->p_state = SUBSYS_CRASHED;
Stephen Boyd6b567182012-09-18 11:31:02 -0700677 wake_lock(&dev->wake_lock);
678 queue_work(ssr_wq, &dev->work);
679 } else {
680 panic("Subsystem %s crashed during SSR!", name);
681 }
Stephen Boyd77c22742012-07-24 18:46:45 -0700682 }
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700683 spin_unlock_irqrestore(&track->s_lock, flags);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700684}
685
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700686int subsystem_restart_dev(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700687{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700688 const char *name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700689
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700690 if (!get_device(&dev->dev))
691 return -ENODEV;
692
693 if (!try_module_get(dev->owner)) {
694 put_device(&dev->dev);
695 return -ENODEV;
696 }
697
698 name = dev->desc->name;
Vikram Mulukutla3d58c2f2012-10-29 18:43:02 -0700699
700 /*
701 * If a system reboot/shutdown is underway, ignore subsystem errors.
702 * However, print a message so that we know that a subsystem behaved
703 * unexpectedly here.
704 */
705 if (system_state == SYSTEM_RESTART
706 || system_state == SYSTEM_POWER_OFF) {
707 pr_err("%s crashed during a system poweroff/shutdown.\n", name);
708 return -EBUSY;
709 }
710
Vikram Mulukutla3b24c8c2011-12-02 15:12:53 -0800711 pr_info("Restart sequence requested for %s, restart_level = %d.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700712 name, restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700713
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700714 switch (restart_level) {
715
716 case RESET_SUBSYS_COUPLED:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700717 case RESET_SUBSYS_INDEPENDENT:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700718 __subsystem_restart_dev(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700719 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700720 case RESET_SOC:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700721 panic("subsys-restart: Resetting the SoC - %s crashed.", name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700722 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700723 default:
724 panic("subsys-restart: Unknown restart level!\n");
Stephen Boydc68ee642012-05-01 17:02:45 -0700725 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700726 }
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700727 module_put(dev->owner);
728 put_device(&dev->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700729
730 return 0;
731}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700732EXPORT_SYMBOL(subsystem_restart_dev);
733
734int subsystem_restart(const char *name)
735{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700736 int ret;
737 struct subsys_device *dev = find_subsys(name);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700738
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700739 if (!dev)
740 return -ENODEV;
741
742 ret = subsystem_restart_dev(dev);
743 put_device(&dev->dev);
744 return ret;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700745}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700746EXPORT_SYMBOL(subsystem_restart);
747
Stephen Boyd4057ec02012-12-12 17:38:38 -0800748int subsystem_crashed(const char *name)
749{
750 struct subsys_device *dev = find_subsys(name);
751 struct subsys_tracking *track;
752
753 if (!dev)
754 return -ENODEV;
755
756 if (!get_device(&dev->dev))
757 return -ENODEV;
758
759 track = subsys_get_track(dev);
760
761 mutex_lock(&track->lock);
762 dev->do_ramdump_on_put = true;
763 /*
764 * TODO: Make this work with multiple consumers where one is calling
765 * subsystem_restart() and another is calling this function. To do
766 * so would require updating private state, etc.
767 */
768 mutex_unlock(&track->lock);
769
770 put_device(&dev->dev);
771 return 0;
772}
773EXPORT_SYMBOL(subsystem_crashed);
774
Stephen Boydeff41a62012-07-06 13:19:02 -0700775#ifdef CONFIG_DEBUG_FS
776static ssize_t subsys_debugfs_read(struct file *filp, char __user *ubuf,
777 size_t cnt, loff_t *ppos)
778{
779 int r;
780 char buf[40];
781 struct subsys_device *subsys = filp->private_data;
782
783 r = snprintf(buf, sizeof(buf), "%d\n", subsys->count);
784 return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
785}
786
787static ssize_t subsys_debugfs_write(struct file *filp,
788 const char __user *ubuf, size_t cnt, loff_t *ppos)
789{
790 struct subsys_device *subsys = filp->private_data;
791 char buf[10];
792 char *cmp;
793
794 cnt = min(cnt, sizeof(buf) - 1);
795 if (copy_from_user(&buf, ubuf, cnt))
796 return -EFAULT;
797 buf[cnt] = '\0';
798 cmp = strstrip(buf);
799
800 if (!strcmp(cmp, "restart")) {
801 if (subsystem_restart_dev(subsys))
802 return -EIO;
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700803 } else if (!strcmp(cmp, "get")) {
804 if (subsystem_get(subsys->desc->name))
805 return -EIO;
806 } else if (!strcmp(cmp, "put")) {
807 subsystem_put(subsys);
Stephen Boydeff41a62012-07-06 13:19:02 -0700808 } else {
809 return -EINVAL;
810 }
811
812 return cnt;
813}
814
815static const struct file_operations subsys_debugfs_fops = {
816 .open = simple_open,
817 .read = subsys_debugfs_read,
818 .write = subsys_debugfs_write,
819};
820
821static struct dentry *subsys_base_dir;
822
823static int __init subsys_debugfs_init(void)
824{
825 subsys_base_dir = debugfs_create_dir("msm_subsys", NULL);
826 return !subsys_base_dir ? -ENOMEM : 0;
827}
828
829static void subsys_debugfs_exit(void)
830{
831 debugfs_remove_recursive(subsys_base_dir);
832}
833
834static int subsys_debugfs_add(struct subsys_device *subsys)
835{
836 if (!subsys_base_dir)
837 return -ENOMEM;
838
839 subsys->dentry = debugfs_create_file(subsys->desc->name,
840 S_IRUGO | S_IWUSR, subsys_base_dir,
841 subsys, &subsys_debugfs_fops);
842 return !subsys->dentry ? -ENOMEM : 0;
843}
844
845static void subsys_debugfs_remove(struct subsys_device *subsys)
846{
847 debugfs_remove(subsys->dentry);
848}
849#else
850static int __init subsys_debugfs_init(void) { return 0; };
851static void subsys_debugfs_exit(void) { }
852static int subsys_debugfs_add(struct subsys_device *subsys) { return 0; }
853static void subsys_debugfs_remove(struct subsys_device *subsys) { }
854#endif
855
Seemanta Duttaf055ac72013-01-10 18:08:04 -0800856static int subsys_device_open(struct inode *inode, struct file *file)
857{
858 void *retval;
859 struct subsys_device *subsys_dev = container_of(file->private_data,
860 struct subsys_device, misc_dev);
861
862 if (!file->private_data)
863 return -EINVAL;
864
865 retval = subsystem_get(subsys_dev->desc->name);
866 if (IS_ERR(retval))
867 return PTR_ERR(retval);
868
869 return 0;
870}
871
872static int subsys_device_close(struct inode *inode, struct file *file)
873{
874 struct subsys_device *subsys_dev = container_of(file->private_data,
875 struct subsys_device, misc_dev);
876
877 if (!file->private_data)
878 return -EINVAL;
879
880 subsystem_put(subsys_dev);
881
882 return 0;
883}
884
885static const struct file_operations subsys_device_fops = {
886 .owner = THIS_MODULE,
887 .open = subsys_device_open,
888 .release = subsys_device_close,
889};
890
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700891static void subsys_device_release(struct device *dev)
892{
893 struct subsys_device *subsys = to_subsys(dev);
894
895 wake_lock_destroy(&subsys->wake_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700896 mutex_destroy(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700897 ida_simple_remove(&subsys_ida, subsys->id);
898 kfree(subsys);
899}
900
Seemanta Duttaf055ac72013-01-10 18:08:04 -0800901static int subsys_misc_device_add(struct subsys_device *subsys_dev)
902{
903 int ret;
904 memset(subsys_dev->miscdevice_name, 0,
905 ARRAY_SIZE(subsys_dev->miscdevice_name));
906 snprintf(subsys_dev->miscdevice_name,
907 ARRAY_SIZE(subsys_dev->miscdevice_name), "subsys_%s",
908 subsys_dev->desc->name);
909
910 subsys_dev->misc_dev.minor = MISC_DYNAMIC_MINOR;
911 subsys_dev->misc_dev.name = subsys_dev->miscdevice_name;
912 subsys_dev->misc_dev.fops = &subsys_device_fops;
913 subsys_dev->misc_dev.parent = &subsys_dev->dev;
914
915 ret = misc_register(&subsys_dev->misc_dev);
916 if (ret) {
917 pr_err("%s: misc_register() failed for %s (%d)", __func__,
918 subsys_dev->miscdevice_name, ret);
919 }
920 return ret;
921}
922
923static void subsys_misc_device_remove(struct subsys_device *subsys_dev)
924{
925 misc_deregister(&subsys_dev->misc_dev);
926}
927
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700928struct subsys_device *subsys_register(struct subsys_desc *desc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700929{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700930 struct subsys_device *subsys;
931 int ret;
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700932
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700933 subsys = kzalloc(sizeof(*subsys), GFP_KERNEL);
934 if (!subsys)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700935 return ERR_PTR(-ENOMEM);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700936
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700937 subsys->desc = desc;
938 subsys->owner = desc->owner;
939 subsys->dev.parent = desc->dev;
940 subsys->dev.bus = &subsys_bus_type;
941 subsys->dev.release = subsys_device_release;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700942
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700943 subsys->notify = subsys_notif_add_subsys(desc->name);
944 subsys->restart_order = update_restart_order(subsys);
Stephen Boyd497ef402012-06-21 12:54:40 -0700945
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700946 snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name);
947 wake_lock_init(&subsys->wake_lock, WAKE_LOCK_SUSPEND, subsys->wlname);
948 INIT_WORK(&subsys->work, subsystem_restart_wq_func);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700949 spin_lock_init(&subsys->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700950
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700951 subsys->id = ida_simple_get(&subsys_ida, 0, 0, GFP_KERNEL);
952 if (subsys->id < 0) {
953 ret = subsys->id;
954 goto err_ida;
955 }
956 dev_set_name(&subsys->dev, "subsys%d", subsys->id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700957
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700958 mutex_init(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700959
Stephen Boydeff41a62012-07-06 13:19:02 -0700960 ret = subsys_debugfs_add(subsys);
961 if (ret)
962 goto err_debugfs;
963
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700964 ret = device_register(&subsys->dev);
965 if (ret) {
Seemanta Duttaf055ac72013-01-10 18:08:04 -0800966 device_unregister(&subsys->dev);
967 goto err_register;
968 }
969
970 ret = subsys_misc_device_add(subsys);
971 if (ret) {
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700972 put_device(&subsys->dev);
973 goto err_register;
974 }
Stephen Boydeff41a62012-07-06 13:19:02 -0700975
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700976 return subsys;
977
978err_register:
Stephen Boydeff41a62012-07-06 13:19:02 -0700979 subsys_debugfs_remove(subsys);
980err_debugfs:
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700981 mutex_destroy(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700982 ida_simple_remove(&subsys_ida, subsys->id);
983err_ida:
984 wake_lock_destroy(&subsys->wake_lock);
985 kfree(subsys);
986 return ERR_PTR(ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700987}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700988EXPORT_SYMBOL(subsys_register);
989
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700990void subsys_unregister(struct subsys_device *subsys)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700991{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700992 if (IS_ERR_OR_NULL(subsys))
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700993 return;
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700994
995 if (get_device(&subsys->dev)) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700996 mutex_lock(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700997 WARN_ON(subsys->count);
998 device_unregister(&subsys->dev);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700999 mutex_unlock(&subsys->track.lock);
Stephen Boydeff41a62012-07-06 13:19:02 -07001000 subsys_debugfs_remove(subsys);
Seemanta Duttaf055ac72013-01-10 18:08:04 -08001001 subsys_misc_device_remove(subsys);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001002 put_device(&subsys->dev);
1003 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001004}
1005EXPORT_SYMBOL(subsys_unregister);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001006
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001007static int subsys_panic(struct device *dev, void *data)
1008{
1009 struct subsys_device *subsys = to_subsys(dev);
1010
1011 if (subsys->desc->crash_shutdown)
1012 subsys->desc->crash_shutdown(subsys->desc);
1013 return 0;
1014}
1015
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001016static int ssr_panic_handler(struct notifier_block *this,
1017 unsigned long event, void *ptr)
1018{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001019 bus_for_each_dev(&subsys_bus_type, NULL, NULL, subsys_panic);
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001020 return NOTIFY_DONE;
1021}
1022
1023static struct notifier_block panic_nb = {
1024 .notifier_call = ssr_panic_handler,
1025};
1026
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001027static int __init ssr_init_soc_restart_orders(void)
1028{
1029 int i;
1030
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001031 atomic_notifier_chain_register(&panic_notifier_list,
1032 &panic_nb);
1033
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001034 if (cpu_is_msm8x60()) {
1035 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001036 mutex_init(&orders_8x60_all[i]->track.lock);
1037 spin_lock_init(&orders_8x60_all[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001038 }
1039
1040 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001041 mutex_init(&orders_8x60_modems[i]->track.lock);
1042 spin_lock_init(&orders_8x60_modems[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001043 }
1044
1045 restart_orders = orders_8x60_all;
1046 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
1047 }
1048
Vikram Mulukutlae4ae75e2012-07-27 15:14:10 -07001049 if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) {
1050 restart_orders = restart_orders_8960_sglte;
1051 n_restart_orders = ARRAY_SIZE(restart_orders_8960_sglte);
1052 }
1053
1054 for (i = 0; i < n_restart_orders; i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001055 mutex_init(&restart_orders[i]->track.lock);
1056 spin_lock_init(&restart_orders[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001057 }
1058
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001059 return 0;
1060}
1061
1062static int __init subsys_restart_init(void)
1063{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001064 int ret;
Vikram Mulukutla69177e12012-03-21 20:51:43 -07001065
Vikram Mulukutla8d4c3f02012-09-10 19:25:32 -07001066 ssr_wq = alloc_workqueue("ssr_wq", WQ_CPU_INTENSIVE, 0);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001067 BUG_ON(!ssr_wq);
1068
1069 ret = bus_register(&subsys_bus_type);
1070 if (ret)
1071 goto err_bus;
Stephen Boydeff41a62012-07-06 13:19:02 -07001072 ret = subsys_debugfs_init();
1073 if (ret)
1074 goto err_debugfs;
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001075 ret = ssr_init_soc_restart_orders();
1076 if (ret)
1077 goto err_soc;
1078 return 0;
Stephen Boydeff41a62012-07-06 13:19:02 -07001079
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001080err_soc:
Stephen Boydeff41a62012-07-06 13:19:02 -07001081 subsys_debugfs_exit();
1082err_debugfs:
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001083 bus_unregister(&subsys_bus_type);
1084err_bus:
1085 destroy_workqueue(ssr_wq);
1086 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001087}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001088arch_initcall(subsys_restart_init);
1089
1090MODULE_DESCRIPTION("Subsystem Restart Driver");
1091MODULE_LICENSE("GPL v2");