blob: c971896b60459e3c7c41fa49ed36d3d49f18ad6c [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>
Seemanta Duttad32f92a2013-01-25 14:22:15 -080034#include <linux/interrupt.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070035
36#include <asm/current.h>
37
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070038#include <mach/socinfo.h>
39#include <mach/subsystem_notif.h>
40#include <mach/subsystem_restart.h>
41
42#include "smd_private.h"
43
Stephen Boyd5bd20e42012-10-03 12:28:43 -070044/**
45 * enum p_subsys_state - state of a subsystem (private)
46 * @SUBSYS_NORMAL: subsystem is operating normally
47 * @SUBSYS_CRASHED: subsystem has crashed and hasn't been shutdown
48 * @SUBSYS_RESTARTING: subsystem has been shutdown and is now restarting
49 *
50 * The 'private' side of the subsytem state used to determine where in the
51 * restart process the subsystem is.
52 */
53enum p_subsys_state {
54 SUBSYS_NORMAL,
55 SUBSYS_CRASHED,
56 SUBSYS_RESTARTING,
57};
58
59/**
60 * enum subsys_state - state of a subsystem (public)
61 * @SUBSYS_OFFLINE: subsystem is offline
62 * @SUBSYS_ONLINE: subsystem is online
63 *
64 * The 'public' side of the subsytem state, exposed to userspace.
65 */
66enum subsys_state {
67 SUBSYS_OFFLINE,
68 SUBSYS_ONLINE,
69};
70
71static const char * const subsys_states[] = {
72 [SUBSYS_OFFLINE] = "OFFLINE",
73 [SUBSYS_ONLINE] = "ONLINE",
74};
75
Stephen Boydfcb52a32012-12-03 12:35:14 -080076static const char * const restart_levels[] = {
77 [RESET_SOC] = "SYSTEM",
78 [RESET_SUBSYS_COUPLED] = "RELATED",
79};
80
Stephen Boyd5bd20e42012-10-03 12:28:43 -070081/**
82 * struct subsys_tracking - track state of a subsystem or restart order
83 * @p_state: private state of subsystem/order
84 * @state: public state of subsystem/order
85 * @s_lock: protects p_state
86 * @lock: protects subsystem/order callbacks and state
87 *
88 * Tracks the state of a subsystem or a set of subsystems (restart order).
89 * Doing this avoids the need to grab each subsystem's lock and update
90 * each subsystems state when restarting an order.
91 */
92struct subsys_tracking {
93 enum p_subsys_state p_state;
94 spinlock_t s_lock;
95 enum subsys_state state;
96 struct mutex lock;
97};
98
99/**
100 * struct subsys_soc_restart_order - subsystem restart order
101 * @subsystem_list: names of subsystems in this restart order
102 * @count: number of subsystems in order
103 * @track: state tracking and locking
104 * @subsys_ptrs: pointers to subsystems in this restart order
105 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700106struct subsys_soc_restart_order {
107 const char * const *subsystem_list;
108 int count;
109
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700110 struct subsys_tracking track;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700111 struct subsys_device *subsys_ptrs[];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700112};
113
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700114struct restart_log {
115 struct timeval time;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700116 struct subsys_device *dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700117 struct list_head list;
118};
119
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700120/**
121 * struct subsys_device - subsystem device
122 * @desc: subsystem descriptor
123 * @wake_lock: prevents suspend during subsystem_restart()
124 * @wlname: name of @wake_lock
125 * @work: context for subsystem_restart_wq_func() for this device
126 * @track: state tracking and locking
127 * @notify: subsys notify handle
128 * @dev: device
129 * @owner: module that provides @desc
130 * @count: reference count of subsystem_get()/subsystem_put()
131 * @id: ida
Stephen Boydfcb52a32012-12-03 12:35:14 -0800132 * @restart_level: restart level (0 - panic, 1 - related, 2 - independent, etc.)
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700133 * @restart_order: order of other devices this devices restarts with
134 * @dentry: debugfs directory for this device
Stephen Boyd4057ec02012-12-12 17:38:38 -0800135 * @do_ramdump_on_put: ramdump on subsystem_put() if true
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800136 * @err_ready: completion variable to record error ready from subsystem
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700137 */
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700138struct subsys_device {
139 struct subsys_desc *desc;
Stephen Boyd497ef402012-06-21 12:54:40 -0700140 struct wake_lock wake_lock;
141 char wlname[64];
142 struct work_struct work;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700143
144 struct subsys_tracking track;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700145
146 void *notify;
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700147 struct device dev;
148 struct module *owner;
149 int count;
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700150 int id;
Stephen Boydfcb52a32012-12-03 12:35:14 -0800151 int restart_level;
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700152 struct subsys_soc_restart_order *restart_order;
Stephen Boydeff41a62012-07-06 13:19:02 -0700153#ifdef CONFIG_DEBUG_FS
154 struct dentry *dentry;
155#endif
Stephen Boyd4057ec02012-12-12 17:38:38 -0800156 bool do_ramdump_on_put;
Seemanta Duttaf055ac72013-01-10 18:08:04 -0800157 struct miscdevice misc_dev;
158 char miscdevice_name[32];
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800159 struct completion err_ready;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700160};
161
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700162static struct subsys_device *to_subsys(struct device *d)
163{
164 return container_of(d, struct subsys_device, dev);
165}
166
167static ssize_t name_show(struct device *dev, struct device_attribute *attr,
168 char *buf)
169{
170 return snprintf(buf, PAGE_SIZE, "%s\n", to_subsys(dev)->desc->name);
171}
172
173static ssize_t state_show(struct device *dev, struct device_attribute *attr,
174 char *buf)
175{
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700176 enum subsys_state state = to_subsys(dev)->track.state;
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700177 return snprintf(buf, PAGE_SIZE, "%s\n", subsys_states[state]);
178}
179
Stephen Boydfcb52a32012-12-03 12:35:14 -0800180static ssize_t
181restart_level_show(struct device *dev, struct device_attribute *attr, char *buf)
182{
183 int level = to_subsys(dev)->restart_level;
184 return snprintf(buf, PAGE_SIZE, "%s\n", restart_levels[level]);
185}
186
187static ssize_t restart_level_store(struct device *dev,
188 struct device_attribute *attr, const char *buf, size_t count)
189{
190 struct subsys_device *subsys = to_subsys(dev);
191 int i;
192 const char *p;
193
194 p = memchr(buf, '\n', count);
195 if (p)
196 count = p - buf;
197
198 for (i = 0; i < ARRAY_SIZE(restart_levels); i++)
199 if (!strncasecmp(buf, restart_levels[i], count)) {
200 subsys->restart_level = i;
201 return count;
202 }
203 return -EPERM;
204}
205
206int subsys_get_restart_level(struct subsys_device *dev)
207{
208 return dev->restart_level;
209}
210EXPORT_SYMBOL(subsys_get_restart_level);
211
Stephen Boyd6b567182012-09-18 11:31:02 -0700212static void subsys_set_state(struct subsys_device *subsys,
213 enum subsys_state state)
214{
215 unsigned long flags;
216
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700217 spin_lock_irqsave(&subsys->track.s_lock, flags);
218 if (subsys->track.state != state) {
219 subsys->track.state = state;
220 spin_unlock_irqrestore(&subsys->track.s_lock, flags);
Stephen Boyd6b567182012-09-18 11:31:02 -0700221 sysfs_notify(&subsys->dev.kobj, NULL, "state");
222 return;
223 }
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700224 spin_unlock_irqrestore(&subsys->track.s_lock, flags);
Stephen Boyd6b567182012-09-18 11:31:02 -0700225}
226
Stephen Boyd43b380a2012-09-21 17:34:24 -0700227/**
228 * subsytem_default_online() - Mark a subsystem as online by default
229 * @dev: subsystem to mark as online
230 *
231 * Marks a subsystem as "online" without increasing the reference count
232 * on the subsystem. This is typically used by subsystems that are already
233 * online when the kernel boots up.
234 */
235void subsys_default_online(struct subsys_device *dev)
236{
237 subsys_set_state(dev, SUBSYS_ONLINE);
238}
239EXPORT_SYMBOL(subsys_default_online);
240
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700241static struct device_attribute subsys_attrs[] = {
242 __ATTR_RO(name),
243 __ATTR_RO(state),
Stephen Boydfcb52a32012-12-03 12:35:14 -0800244 __ATTR(restart_level, 0644, restart_level_show, restart_level_store),
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700245 __ATTR_NULL,
246};
247
248static struct bus_type subsys_bus_type = {
249 .name = "msm_subsys",
250 .dev_attrs = subsys_attrs,
251};
252
253static DEFINE_IDA(subsys_ida);
254
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700255static int enable_ramdumps;
Stephen Boydc68ee642012-05-01 17:02:45 -0700256module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR);
257
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700258struct workqueue_struct *ssr_wq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700259
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700260static LIST_HEAD(restart_log_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700261static DEFINE_MUTEX(soc_order_reg_lock);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700262static DEFINE_MUTEX(restart_log_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700263
264/* SOC specific restart orders go here */
265
266#define DEFINE_SINGLE_RESTART_ORDER(name, order) \
267 static struct subsys_soc_restart_order __##name = { \
268 .subsystem_list = order, \
269 .count = ARRAY_SIZE(order), \
270 .subsys_ptrs = {[ARRAY_SIZE(order)] = NULL} \
271 }; \
272 static struct subsys_soc_restart_order *name[] = { \
273 &__##name, \
274 }
275
276/* MSM 8x60 restart ordering info */
277static const char * const _order_8x60_all[] = {
Stephen Boyd77db8bb2012-06-27 15:15:16 -0700278 "external_modem", "modem", "adsp"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700279};
280DEFINE_SINGLE_RESTART_ORDER(orders_8x60_all, _order_8x60_all);
281
282static const char * const _order_8x60_modems[] = {"external_modem", "modem"};
283DEFINE_SINGLE_RESTART_ORDER(orders_8x60_modems, _order_8x60_modems);
284
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700285/*SGLTE restart ordering info*/
286static const char * const order_8960_sglte[] = {"external_modem",
287 "modem"};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700288
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700289static struct subsys_soc_restart_order restart_orders_8960_fusion_sglte = {
290 .subsystem_list = order_8960_sglte,
291 .count = ARRAY_SIZE(order_8960_sglte),
292 .subsys_ptrs = {[ARRAY_SIZE(order_8960_sglte)] = NULL}
293 };
294
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700295static struct subsys_soc_restart_order *restart_orders_8960_sglte[] = {
296 &restart_orders_8960_fusion_sglte,
297 };
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700298
299/* These will be assigned to one of the sets above after
300 * runtime SoC identification.
301 */
302static struct subsys_soc_restart_order **restart_orders;
303static int n_restart_orders;
304
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700305static struct subsys_soc_restart_order *
306update_restart_order(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700307{
308 int i, j;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700309 struct subsys_soc_restart_order *order;
310 const char *name = dev->desc->name;
311 int len = SUBSYS_NAME_MAX_LENGTH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700312
313 mutex_lock(&soc_order_reg_lock);
314 for (j = 0; j < n_restart_orders; j++) {
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700315 order = restart_orders[j];
316 for (i = 0; i < order->count; i++) {
317 if (!strncmp(order->subsystem_list[i], name, len)) {
318 order->subsys_ptrs[i] = dev;
319 goto found;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700320 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700321 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700322 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700323 order = NULL;
324found:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700325 mutex_unlock(&soc_order_reg_lock);
326
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700327 return order;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700328}
329
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700330static int max_restarts;
331module_param(max_restarts, int, 0644);
332
333static long max_history_time = 3600;
334module_param(max_history_time, long, 0644);
335
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700336static void do_epoch_check(struct subsys_device *dev)
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700337{
338 int n = 0;
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600339 struct timeval *time_first = NULL, *curr_time;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700340 struct restart_log *r_log, *temp;
341 static int max_restarts_check;
342 static long max_history_time_check;
343
344 mutex_lock(&restart_log_mutex);
345
346 max_restarts_check = max_restarts;
347 max_history_time_check = max_history_time;
348
349 /* Check if epoch checking is enabled */
350 if (!max_restarts_check)
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700351 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700352
353 r_log = kmalloc(sizeof(struct restart_log), GFP_KERNEL);
Matt Wagantalleecca342011-11-10 20:12:05 -0800354 if (!r_log)
355 goto out;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700356 r_log->dev = dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700357 do_gettimeofday(&r_log->time);
358 curr_time = &r_log->time;
359 INIT_LIST_HEAD(&r_log->list);
360
361 list_add_tail(&r_log->list, &restart_log_list);
362
363 list_for_each_entry_safe(r_log, temp, &restart_log_list, list) {
364
365 if ((curr_time->tv_sec - r_log->time.tv_sec) >
366 max_history_time_check) {
367
368 pr_debug("Deleted node with restart_time = %ld\n",
369 r_log->time.tv_sec);
370 list_del(&r_log->list);
371 kfree(r_log);
372 continue;
373 }
374 if (!n) {
375 time_first = &r_log->time;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700376 pr_debug("Time_first: %ld\n", time_first->tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700377 }
378 n++;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700379 pr_debug("Restart_time: %ld\n", r_log->time.tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700380 }
381
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600382 if (time_first && n >= max_restarts_check) {
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700383 if ((curr_time->tv_sec - time_first->tv_sec) <
384 max_history_time_check)
385 panic("Subsystems have crashed %d times in less than "
386 "%ld seconds!", max_restarts_check,
387 max_history_time_check);
388 }
389
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700390out:
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700391 mutex_unlock(&restart_log_mutex);
392}
393
Stephen Boyd0daae152012-05-01 17:36:15 -0700394static void for_each_subsys_device(struct subsys_device **list, unsigned count,
395 void *data, void (*fn)(struct subsys_device *, void *))
396{
397 while (count--) {
398 struct subsys_device *dev = *list++;
399 if (!dev)
400 continue;
401 fn(dev, data);
402 }
403}
404
Seemanta Duttabac64552012-12-14 15:47:27 -0800405static void notify_each_subsys_device(struct subsys_device **list,
406 unsigned count,
407 enum subsys_notif_type notif, void *data)
Stephen Boyd0daae152012-05-01 17:36:15 -0700408{
Seemanta Duttabac64552012-12-14 15:47:27 -0800409 while (count--) {
410 enum subsys_notif_type type = (enum subsys_notif_type)type;
411 struct subsys_device *dev = *list++;
412 if (!dev)
413 continue;
414 subsys_notif_queue_notification(dev->notify, notif, data);
415 }
Stephen Boyd0daae152012-05-01 17:36:15 -0700416}
417
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800418static int wait_for_err_ready(struct subsys_device *subsys)
419{
420 int ret;
421
422 if (!subsys->desc->err_ready_irq)
423 return 0;
424
425 ret = wait_for_completion_timeout(&subsys->err_ready,
426 msecs_to_jiffies(10000));
427 if (!ret)
428 return -ETIMEDOUT;
429
430 return 0;
431}
432
Stephen Boyd0daae152012-05-01 17:36:15 -0700433static void subsystem_shutdown(struct subsys_device *dev, void *data)
434{
435 const char *name = dev->desc->name;
436
437 pr_info("[%p]: Shutting down %s\n", current, name);
438 if (dev->desc->shutdown(dev->desc) < 0)
439 panic("subsys-restart: [%p]: Failed to shutdown %s!",
440 current, name);
Stephen Boyd6b567182012-09-18 11:31:02 -0700441 subsys_set_state(dev, SUBSYS_OFFLINE);
Stephen Boyd0daae152012-05-01 17:36:15 -0700442}
443
444static void subsystem_ramdump(struct subsys_device *dev, void *data)
445{
446 const char *name = dev->desc->name;
447
448 if (dev->desc->ramdump)
449 if (dev->desc->ramdump(enable_ramdumps, dev->desc) < 0)
450 pr_warn("%s[%p]: Ramdump failed.\n", name, current);
Stephen Boyd4057ec02012-12-12 17:38:38 -0800451 dev->do_ramdump_on_put = false;
Stephen Boyd0daae152012-05-01 17:36:15 -0700452}
453
454static void subsystem_powerup(struct subsys_device *dev, void *data)
455{
456 const char *name = dev->desc->name;
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800457 int ret;
Stephen Boyd0daae152012-05-01 17:36:15 -0700458
459 pr_info("[%p]: Powering up %s\n", current, name);
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800460 init_completion(&dev->err_ready);
Stephen Boyd0daae152012-05-01 17:36:15 -0700461 if (dev->desc->powerup(dev->desc) < 0)
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800462 panic("[%p]: Powerup error: %s!", current, name);
463
464 ret = wait_for_err_ready(dev);
465 if (ret)
466 panic("[%p]: Timed out waiting for error ready: %s!",
467 current, name);
Stephen Boyd6b567182012-09-18 11:31:02 -0700468 subsys_set_state(dev, SUBSYS_ONLINE);
Stephen Boyd0daae152012-05-01 17:36:15 -0700469}
470
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700471static int __find_subsys(struct device *dev, void *data)
472{
473 struct subsys_device *subsys = to_subsys(dev);
474 return !strcmp(subsys->desc->name, data);
475}
476
477static struct subsys_device *find_subsys(const char *str)
478{
479 struct device *dev;
480
481 if (!str)
482 return NULL;
483
484 dev = bus_find_device(&subsys_bus_type, NULL, (void *)str,
485 __find_subsys);
486 return dev ? to_subsys(dev) : NULL;
487}
488
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700489static int subsys_start(struct subsys_device *subsys)
490{
491 int ret;
492
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800493 init_completion(&subsys->err_ready);
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700494 ret = subsys->desc->start(subsys->desc);
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800495 if (ret)
496 return ret;
497
Seemanta Duttaf9458c92013-05-08 19:53:29 -0700498 if (subsys->desc->is_not_loadable)
Seemanta Duttad7591612013-05-03 17:44:00 -0700499 return 0;
500
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800501 ret = wait_for_err_ready(subsys);
502 if (ret)
503 /* pil-boot succeeded but we need to shutdown
504 * the device because error ready timed out.
505 */
506 subsys->desc->stop(subsys->desc);
507 else
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700508 subsys_set_state(subsys, SUBSYS_ONLINE);
509
510 return ret;
511}
512
513static void subsys_stop(struct subsys_device *subsys)
514{
515 subsys->desc->stop(subsys->desc);
516 subsys_set_state(subsys, SUBSYS_OFFLINE);
517}
518
519static struct subsys_tracking *subsys_get_track(struct subsys_device *subsys)
520{
521 struct subsys_soc_restart_order *order = subsys->restart_order;
522
Stephen Boydfcb52a32012-12-03 12:35:14 -0800523 if (order)
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700524 return &order->track;
525 else
526 return &subsys->track;
527}
528
529/**
530 * subsytem_get() - Boot a subsystem
531 * @name: pointer to a string containing the name of the subsystem to boot
532 *
533 * This function returns a pointer if it succeeds. If an error occurs an
534 * ERR_PTR is returned.
535 *
536 * If this feature is disable, the value %NULL will be returned.
537 */
538void *subsystem_get(const char *name)
539{
540 struct subsys_device *subsys;
541 struct subsys_device *subsys_d;
542 int ret;
543 void *retval;
544 struct subsys_tracking *track;
545
546 if (!name)
547 return NULL;
548
549 subsys = retval = find_subsys(name);
550 if (!subsys)
551 return ERR_PTR(-ENODEV);
552 if (!try_module_get(subsys->owner)) {
553 retval = ERR_PTR(-ENODEV);
554 goto err_module;
555 }
556
557 subsys_d = subsystem_get(subsys->desc->depends_on);
558 if (IS_ERR(subsys_d)) {
559 retval = subsys_d;
560 goto err_depends;
561 }
562
563 track = subsys_get_track(subsys);
564 mutex_lock(&track->lock);
565 if (!subsys->count) {
566 ret = subsys_start(subsys);
567 if (ret) {
568 retval = ERR_PTR(ret);
569 goto err_start;
570 }
571 }
572 subsys->count++;
573 mutex_unlock(&track->lock);
574 return retval;
575err_start:
576 mutex_unlock(&track->lock);
577 subsystem_put(subsys_d);
578err_depends:
579 module_put(subsys->owner);
580err_module:
581 put_device(&subsys->dev);
582 return retval;
583}
584EXPORT_SYMBOL(subsystem_get);
585
586/**
587 * subsystem_put() - Shutdown a subsystem
588 * @peripheral_handle: pointer from a previous call to subsystem_get()
589 *
590 * This doesn't imply that a subsystem is shutdown until all callers of
591 * subsystem_get() have called subsystem_put().
592 */
593void subsystem_put(void *subsystem)
594{
595 struct subsys_device *subsys_d, *subsys = subsystem;
596 struct subsys_tracking *track;
597
598 if (IS_ERR_OR_NULL(subsys))
599 return;
600
601 track = subsys_get_track(subsys);
602 mutex_lock(&track->lock);
603 if (WARN(!subsys->count, "%s: %s: Reference count mismatch\n",
604 subsys->desc->name, __func__))
605 goto err_out;
Stephen Boyd4057ec02012-12-12 17:38:38 -0800606 if (!--subsys->count) {
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700607 subsys_stop(subsys);
Stephen Boyd4057ec02012-12-12 17:38:38 -0800608 if (subsys->do_ramdump_on_put)
609 subsystem_ramdump(subsys, NULL);
610 }
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700611 mutex_unlock(&track->lock);
612
613 subsys_d = find_subsys(subsys->desc->depends_on);
614 if (subsys_d) {
615 subsystem_put(subsys_d);
616 put_device(&subsys_d->dev);
617 }
618 module_put(subsys->owner);
619 put_device(&subsys->dev);
620 return;
621err_out:
622 mutex_unlock(&track->lock);
623}
624EXPORT_SYMBOL(subsystem_put);
625
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700626static void subsystem_restart_wq_func(struct work_struct *work)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700627{
Stephen Boyd497ef402012-06-21 12:54:40 -0700628 struct subsys_device *dev = container_of(work,
629 struct subsys_device, work);
Stephen Boyd0daae152012-05-01 17:36:15 -0700630 struct subsys_device **list;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700631 struct subsys_desc *desc = dev->desc;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700632 struct subsys_soc_restart_order *order = dev->restart_order;
633 struct subsys_tracking *track;
Stephen Boyd0daae152012-05-01 17:36:15 -0700634 unsigned count;
Stephen Boyd497ef402012-06-21 12:54:40 -0700635 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700636
Stephen Boydc68ee642012-05-01 17:02:45 -0700637 /*
638 * It's OK to not take the registration lock at this point.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700639 * This is because the subsystem list inside the relevant
640 * restart order is not being traversed.
641 */
Stephen Boydfcb52a32012-12-03 12:35:14 -0800642 if (order) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700643 list = order->subsys_ptrs;
644 count = order->count;
645 track = &order->track;
646 } else {
Stephen Boyd0daae152012-05-01 17:36:15 -0700647 list = &dev;
648 count = 1;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700649 track = &dev->track;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700650 }
651
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700652 mutex_lock(&track->lock);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700653 do_epoch_check(dev);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700654
Stephen Boydc68ee642012-05-01 17:02:45 -0700655 /*
656 * It's necessary to take the registration lock because the subsystem
657 * list in the SoC restart order will be traversed and it shouldn't be
658 * changed until _this_ restart sequence completes.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700659 */
660 mutex_lock(&soc_order_reg_lock);
661
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700662 pr_debug("[%p]: Starting restart sequence for %s\n", current,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700663 desc->name);
Seemanta Duttabac64552012-12-14 15:47:27 -0800664 notify_each_subsys_device(list, count, SUBSYS_BEFORE_SHUTDOWN, NULL);
Stephen Boyd0daae152012-05-01 17:36:15 -0700665 for_each_subsys_device(list, count, NULL, subsystem_shutdown);
Seemanta Duttabac64552012-12-14 15:47:27 -0800666 notify_each_subsys_device(list, count, SUBSYS_AFTER_SHUTDOWN, NULL);
667
668 notify_each_subsys_device(list, count, SUBSYS_RAMDUMP_NOTIFICATION,
669 &enable_ramdumps);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700670
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700671 spin_lock_irqsave(&track->s_lock, flags);
672 track->p_state = SUBSYS_RESTARTING;
673 spin_unlock_irqrestore(&track->s_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700674
675 /* Collect ram dumps for all subsystems in order here */
Stephen Boyd0daae152012-05-01 17:36:15 -0700676 for_each_subsys_device(list, count, NULL, subsystem_ramdump);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700677
Seemanta Duttabac64552012-12-14 15:47:27 -0800678 notify_each_subsys_device(list, count, SUBSYS_BEFORE_POWERUP, NULL);
Stephen Boyd0daae152012-05-01 17:36:15 -0700679 for_each_subsys_device(list, count, NULL, subsystem_powerup);
Seemanta Duttabac64552012-12-14 15:47:27 -0800680 notify_each_subsys_device(list, count, SUBSYS_AFTER_POWERUP, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700681
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700682 pr_info("[%p]: Restart sequence for %s completed.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700683 current, desc->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700684
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700685 mutex_unlock(&soc_order_reg_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700686 mutex_unlock(&track->lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700687
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700688 spin_lock_irqsave(&track->s_lock, flags);
689 track->p_state = SUBSYS_NORMAL;
Stephen Boyd6b567182012-09-18 11:31:02 -0700690 wake_unlock(&dev->wake_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700691 spin_unlock_irqrestore(&track->s_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700692}
693
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700694static void __subsystem_restart_dev(struct subsys_device *dev)
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700695{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700696 struct subsys_desc *desc = dev->desc;
Stephen Boyd6b567182012-09-18 11:31:02 -0700697 const char *name = dev->desc->name;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700698 struct subsys_tracking *track;
Stephen Boyd497ef402012-06-21 12:54:40 -0700699 unsigned long flags;
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700700
Stephen Boydfcb52a32012-12-03 12:35:14 -0800701 pr_debug("Restarting %s [level=%s]!\n", desc->name,
702 restart_levels[dev->restart_level]);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700703
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700704 track = subsys_get_track(dev);
Stephen Boyd6b567182012-09-18 11:31:02 -0700705 /*
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700706 * Allow drivers to call subsystem_restart{_dev}() as many times as
707 * they want up until the point where the subsystem is shutdown.
Stephen Boyd6b567182012-09-18 11:31:02 -0700708 */
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700709 spin_lock_irqsave(&track->s_lock, flags);
710 if (track->p_state != SUBSYS_CRASHED) {
711 if (dev->track.state == SUBSYS_ONLINE &&
712 track->p_state != SUBSYS_RESTARTING) {
713 track->p_state = SUBSYS_CRASHED;
Stephen Boyd6b567182012-09-18 11:31:02 -0700714 wake_lock(&dev->wake_lock);
715 queue_work(ssr_wq, &dev->work);
716 } else {
717 panic("Subsystem %s crashed during SSR!", name);
718 }
Stephen Boyd77c22742012-07-24 18:46:45 -0700719 }
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700720 spin_unlock_irqrestore(&track->s_lock, flags);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700721}
722
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700723int subsystem_restart_dev(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700724{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700725 const char *name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700726
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700727 if (!get_device(&dev->dev))
728 return -ENODEV;
729
730 if (!try_module_get(dev->owner)) {
731 put_device(&dev->dev);
732 return -ENODEV;
733 }
734
735 name = dev->desc->name;
Vikram Mulukutla3d58c2f2012-10-29 18:43:02 -0700736
737 /*
738 * If a system reboot/shutdown is underway, ignore subsystem errors.
739 * However, print a message so that we know that a subsystem behaved
740 * unexpectedly here.
741 */
742 if (system_state == SYSTEM_RESTART
743 || system_state == SYSTEM_POWER_OFF) {
744 pr_err("%s crashed during a system poweroff/shutdown.\n", name);
745 return -EBUSY;
746 }
747
Stephen Boydfcb52a32012-12-03 12:35:14 -0800748 pr_info("Restart sequence requested for %s, restart_level = %s.\n",
749 name, restart_levels[dev->restart_level]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700750
Stephen Boydfcb52a32012-12-03 12:35:14 -0800751 switch (dev->restart_level) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700752
753 case RESET_SUBSYS_COUPLED:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700754 __subsystem_restart_dev(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700755 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700756 case RESET_SOC:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700757 panic("subsys-restart: Resetting the SoC - %s crashed.", name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700758 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700759 default:
760 panic("subsys-restart: Unknown restart level!\n");
Stephen Boydc68ee642012-05-01 17:02:45 -0700761 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700762 }
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700763 module_put(dev->owner);
764 put_device(&dev->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700765
766 return 0;
767}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700768EXPORT_SYMBOL(subsystem_restart_dev);
769
770int subsystem_restart(const char *name)
771{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700772 int ret;
773 struct subsys_device *dev = find_subsys(name);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700774
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700775 if (!dev)
776 return -ENODEV;
777
778 ret = subsystem_restart_dev(dev);
779 put_device(&dev->dev);
780 return ret;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700781}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700782EXPORT_SYMBOL(subsystem_restart);
783
Stephen Boyd4057ec02012-12-12 17:38:38 -0800784int subsystem_crashed(const char *name)
785{
786 struct subsys_device *dev = find_subsys(name);
787 struct subsys_tracking *track;
788
789 if (!dev)
790 return -ENODEV;
791
792 if (!get_device(&dev->dev))
793 return -ENODEV;
794
795 track = subsys_get_track(dev);
796
797 mutex_lock(&track->lock);
798 dev->do_ramdump_on_put = true;
799 /*
800 * TODO: Make this work with multiple consumers where one is calling
801 * subsystem_restart() and another is calling this function. To do
802 * so would require updating private state, etc.
803 */
804 mutex_unlock(&track->lock);
805
806 put_device(&dev->dev);
807 return 0;
808}
809EXPORT_SYMBOL(subsystem_crashed);
810
Stephen Boydeff41a62012-07-06 13:19:02 -0700811#ifdef CONFIG_DEBUG_FS
812static ssize_t subsys_debugfs_read(struct file *filp, char __user *ubuf,
813 size_t cnt, loff_t *ppos)
814{
815 int r;
816 char buf[40];
817 struct subsys_device *subsys = filp->private_data;
818
819 r = snprintf(buf, sizeof(buf), "%d\n", subsys->count);
820 return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
821}
822
823static ssize_t subsys_debugfs_write(struct file *filp,
824 const char __user *ubuf, size_t cnt, loff_t *ppos)
825{
826 struct subsys_device *subsys = filp->private_data;
827 char buf[10];
828 char *cmp;
829
830 cnt = min(cnt, sizeof(buf) - 1);
831 if (copy_from_user(&buf, ubuf, cnt))
832 return -EFAULT;
833 buf[cnt] = '\0';
834 cmp = strstrip(buf);
835
836 if (!strcmp(cmp, "restart")) {
837 if (subsystem_restart_dev(subsys))
838 return -EIO;
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700839 } else if (!strcmp(cmp, "get")) {
840 if (subsystem_get(subsys->desc->name))
841 return -EIO;
842 } else if (!strcmp(cmp, "put")) {
843 subsystem_put(subsys);
Stephen Boydeff41a62012-07-06 13:19:02 -0700844 } else {
845 return -EINVAL;
846 }
847
848 return cnt;
849}
850
851static const struct file_operations subsys_debugfs_fops = {
852 .open = simple_open,
853 .read = subsys_debugfs_read,
854 .write = subsys_debugfs_write,
855};
856
857static struct dentry *subsys_base_dir;
858
859static int __init subsys_debugfs_init(void)
860{
861 subsys_base_dir = debugfs_create_dir("msm_subsys", NULL);
862 return !subsys_base_dir ? -ENOMEM : 0;
863}
864
865static void subsys_debugfs_exit(void)
866{
867 debugfs_remove_recursive(subsys_base_dir);
868}
869
870static int subsys_debugfs_add(struct subsys_device *subsys)
871{
872 if (!subsys_base_dir)
873 return -ENOMEM;
874
875 subsys->dentry = debugfs_create_file(subsys->desc->name,
876 S_IRUGO | S_IWUSR, subsys_base_dir,
877 subsys, &subsys_debugfs_fops);
878 return !subsys->dentry ? -ENOMEM : 0;
879}
880
881static void subsys_debugfs_remove(struct subsys_device *subsys)
882{
883 debugfs_remove(subsys->dentry);
884}
885#else
886static int __init subsys_debugfs_init(void) { return 0; };
887static void subsys_debugfs_exit(void) { }
888static int subsys_debugfs_add(struct subsys_device *subsys) { return 0; }
889static void subsys_debugfs_remove(struct subsys_device *subsys) { }
890#endif
891
Seemanta Duttaf055ac72013-01-10 18:08:04 -0800892static int subsys_device_open(struct inode *inode, struct file *file)
893{
894 void *retval;
895 struct subsys_device *subsys_dev = container_of(file->private_data,
896 struct subsys_device, misc_dev);
897
898 if (!file->private_data)
899 return -EINVAL;
900
901 retval = subsystem_get(subsys_dev->desc->name);
902 if (IS_ERR(retval))
903 return PTR_ERR(retval);
904
905 return 0;
906}
907
908static int subsys_device_close(struct inode *inode, struct file *file)
909{
910 struct subsys_device *subsys_dev = container_of(file->private_data,
911 struct subsys_device, misc_dev);
912
913 if (!file->private_data)
914 return -EINVAL;
915
916 subsystem_put(subsys_dev);
917
918 return 0;
919}
920
921static const struct file_operations subsys_device_fops = {
922 .owner = THIS_MODULE,
923 .open = subsys_device_open,
924 .release = subsys_device_close,
925};
926
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700927static void subsys_device_release(struct device *dev)
928{
929 struct subsys_device *subsys = to_subsys(dev);
930
931 wake_lock_destroy(&subsys->wake_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700932 mutex_destroy(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700933 ida_simple_remove(&subsys_ida, subsys->id);
934 kfree(subsys);
935}
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800936static irqreturn_t subsys_err_ready_intr_handler(int irq, void *subsys)
937{
938 struct subsys_device *subsys_dev = subsys;
939 pr_info("Error ready interrupt occured for %s\n",
940 subsys_dev->desc->name);
Seemanta Duttaa52bda62013-05-10 17:17:56 -0700941
942 if (subsys_dev->desc->is_not_loadable)
943 return IRQ_HANDLED;
944
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800945 complete(&subsys_dev->err_ready);
946 return IRQ_HANDLED;
947}
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700948
Seemanta Duttaf055ac72013-01-10 18:08:04 -0800949static int subsys_misc_device_add(struct subsys_device *subsys_dev)
950{
951 int ret;
952 memset(subsys_dev->miscdevice_name, 0,
953 ARRAY_SIZE(subsys_dev->miscdevice_name));
954 snprintf(subsys_dev->miscdevice_name,
955 ARRAY_SIZE(subsys_dev->miscdevice_name), "subsys_%s",
956 subsys_dev->desc->name);
957
958 subsys_dev->misc_dev.minor = MISC_DYNAMIC_MINOR;
959 subsys_dev->misc_dev.name = subsys_dev->miscdevice_name;
960 subsys_dev->misc_dev.fops = &subsys_device_fops;
961 subsys_dev->misc_dev.parent = &subsys_dev->dev;
962
963 ret = misc_register(&subsys_dev->misc_dev);
964 if (ret) {
965 pr_err("%s: misc_register() failed for %s (%d)", __func__,
966 subsys_dev->miscdevice_name, ret);
967 }
968 return ret;
969}
970
971static void subsys_misc_device_remove(struct subsys_device *subsys_dev)
972{
973 misc_deregister(&subsys_dev->misc_dev);
974}
975
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700976struct subsys_device *subsys_register(struct subsys_desc *desc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700977{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700978 struct subsys_device *subsys;
979 int ret;
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700980
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700981 subsys = kzalloc(sizeof(*subsys), GFP_KERNEL);
982 if (!subsys)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700983 return ERR_PTR(-ENOMEM);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700984
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700985 subsys->desc = desc;
986 subsys->owner = desc->owner;
987 subsys->dev.parent = desc->dev;
988 subsys->dev.bus = &subsys_bus_type;
989 subsys->dev.release = subsys_device_release;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700990
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700991 subsys->notify = subsys_notif_add_subsys(desc->name);
992 subsys->restart_order = update_restart_order(subsys);
Stephen Boyd497ef402012-06-21 12:54:40 -0700993
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700994 snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name);
995 wake_lock_init(&subsys->wake_lock, WAKE_LOCK_SUSPEND, subsys->wlname);
996 INIT_WORK(&subsys->work, subsystem_restart_wq_func);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700997 spin_lock_init(&subsys->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700998
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700999 subsys->id = ida_simple_get(&subsys_ida, 0, 0, GFP_KERNEL);
1000 if (subsys->id < 0) {
1001 ret = subsys->id;
1002 goto err_ida;
1003 }
1004 dev_set_name(&subsys->dev, "subsys%d", subsys->id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001005
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001006 mutex_init(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001007
Stephen Boydeff41a62012-07-06 13:19:02 -07001008 ret = subsys_debugfs_add(subsys);
1009 if (ret)
1010 goto err_debugfs;
1011
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001012 ret = device_register(&subsys->dev);
1013 if (ret) {
Seemanta Duttaf055ac72013-01-10 18:08:04 -08001014 device_unregister(&subsys->dev);
1015 goto err_register;
1016 }
1017
1018 ret = subsys_misc_device_add(subsys);
1019 if (ret) {
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001020 put_device(&subsys->dev);
1021 goto err_register;
1022 }
Stephen Boydeff41a62012-07-06 13:19:02 -07001023
Seemanta Duttad32f92a2013-01-25 14:22:15 -08001024 if (subsys->desc->err_ready_irq) {
1025 ret = devm_request_irq(&subsys->dev,
1026 subsys->desc->err_ready_irq,
1027 subsys_err_ready_intr_handler,
1028 IRQF_TRIGGER_RISING,
1029 "error_ready_interrupt", subsys);
1030 if (ret < 0) {
1031 dev_err(&subsys->dev,
1032 "[%s]: Unable to register err ready handler\n",
1033 subsys->desc->name);
1034 goto err_misc_device;
1035 }
1036 }
1037
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001038 return subsys;
1039
Seemanta Duttad32f92a2013-01-25 14:22:15 -08001040err_misc_device:
1041 subsys_misc_device_remove(subsys);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001042err_register:
Stephen Boydeff41a62012-07-06 13:19:02 -07001043 subsys_debugfs_remove(subsys);
1044err_debugfs:
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001045 mutex_destroy(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001046 ida_simple_remove(&subsys_ida, subsys->id);
1047err_ida:
1048 wake_lock_destroy(&subsys->wake_lock);
1049 kfree(subsys);
1050 return ERR_PTR(ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001051}
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001052EXPORT_SYMBOL(subsys_register);
1053
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001054void subsys_unregister(struct subsys_device *subsys)
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001055{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001056 if (IS_ERR_OR_NULL(subsys))
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001057 return;
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001058
1059 if (get_device(&subsys->dev)) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001060 mutex_lock(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001061 WARN_ON(subsys->count);
1062 device_unregister(&subsys->dev);
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001063 mutex_unlock(&subsys->track.lock);
Stephen Boydeff41a62012-07-06 13:19:02 -07001064 subsys_debugfs_remove(subsys);
Seemanta Duttaf055ac72013-01-10 18:08:04 -08001065 subsys_misc_device_remove(subsys);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001066 put_device(&subsys->dev);
1067 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001068}
1069EXPORT_SYMBOL(subsys_unregister);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001070
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001071static int subsys_panic(struct device *dev, void *data)
1072{
1073 struct subsys_device *subsys = to_subsys(dev);
1074
1075 if (subsys->desc->crash_shutdown)
1076 subsys->desc->crash_shutdown(subsys->desc);
1077 return 0;
1078}
1079
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001080static int ssr_panic_handler(struct notifier_block *this,
1081 unsigned long event, void *ptr)
1082{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001083 bus_for_each_dev(&subsys_bus_type, NULL, NULL, subsys_panic);
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001084 return NOTIFY_DONE;
1085}
1086
1087static struct notifier_block panic_nb = {
1088 .notifier_call = ssr_panic_handler,
1089};
1090
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001091static int __init ssr_init_soc_restart_orders(void)
1092{
1093 int i;
1094
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001095 atomic_notifier_chain_register(&panic_notifier_list,
1096 &panic_nb);
1097
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001098 if (cpu_is_msm8x60()) {
1099 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001100 mutex_init(&orders_8x60_all[i]->track.lock);
1101 spin_lock_init(&orders_8x60_all[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001102 }
1103
1104 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001105 mutex_init(&orders_8x60_modems[i]->track.lock);
1106 spin_lock_init(&orders_8x60_modems[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001107 }
1108
1109 restart_orders = orders_8x60_all;
1110 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
1111 }
1112
Vikram Mulukutlae4ae75e2012-07-27 15:14:10 -07001113 if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) {
1114 restart_orders = restart_orders_8960_sglte;
1115 n_restart_orders = ARRAY_SIZE(restart_orders_8960_sglte);
1116 }
1117
1118 for (i = 0; i < n_restart_orders; i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001119 mutex_init(&restart_orders[i]->track.lock);
1120 spin_lock_init(&restart_orders[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001121 }
1122
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001123 return 0;
1124}
1125
1126static int __init subsys_restart_init(void)
1127{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001128 int ret;
Vikram Mulukutla69177e12012-03-21 20:51:43 -07001129
Vikram Mulukutla8d4c3f02012-09-10 19:25:32 -07001130 ssr_wq = alloc_workqueue("ssr_wq", WQ_CPU_INTENSIVE, 0);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001131 BUG_ON(!ssr_wq);
1132
1133 ret = bus_register(&subsys_bus_type);
1134 if (ret)
1135 goto err_bus;
Stephen Boydeff41a62012-07-06 13:19:02 -07001136 ret = subsys_debugfs_init();
1137 if (ret)
1138 goto err_debugfs;
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001139 ret = ssr_init_soc_restart_orders();
1140 if (ret)
1141 goto err_soc;
1142 return 0;
Stephen Boydeff41a62012-07-06 13:19:02 -07001143
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001144err_soc:
Stephen Boydeff41a62012-07-06 13:19:02 -07001145 subsys_debugfs_exit();
1146err_debugfs:
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001147 bus_unregister(&subsys_bus_type);
1148err_bus:
1149 destroy_workqueue(ssr_wq);
1150 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001151}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001152arch_initcall(subsys_restart_init);
1153
1154MODULE_DESCRIPTION("Subsystem Restart Driver");
1155MODULE_LICENSE("GPL v2");