blob: fb44e16af070762b68ef180ca432957a4a84884f [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
498 ret = wait_for_err_ready(subsys);
499 if (ret)
500 /* pil-boot succeeded but we need to shutdown
501 * the device because error ready timed out.
502 */
503 subsys->desc->stop(subsys->desc);
504 else
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700505 subsys_set_state(subsys, SUBSYS_ONLINE);
506
507 return ret;
508}
509
510static void subsys_stop(struct subsys_device *subsys)
511{
512 subsys->desc->stop(subsys->desc);
513 subsys_set_state(subsys, SUBSYS_OFFLINE);
514}
515
516static struct subsys_tracking *subsys_get_track(struct subsys_device *subsys)
517{
518 struct subsys_soc_restart_order *order = subsys->restart_order;
519
Stephen Boydfcb52a32012-12-03 12:35:14 -0800520 if (order)
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700521 return &order->track;
522 else
523 return &subsys->track;
524}
525
526/**
527 * subsytem_get() - Boot a subsystem
528 * @name: pointer to a string containing the name of the subsystem to boot
529 *
530 * This function returns a pointer if it succeeds. If an error occurs an
531 * ERR_PTR is returned.
532 *
533 * If this feature is disable, the value %NULL will be returned.
534 */
535void *subsystem_get(const char *name)
536{
537 struct subsys_device *subsys;
538 struct subsys_device *subsys_d;
539 int ret;
540 void *retval;
541 struct subsys_tracking *track;
542
543 if (!name)
544 return NULL;
545
546 subsys = retval = find_subsys(name);
547 if (!subsys)
548 return ERR_PTR(-ENODEV);
549 if (!try_module_get(subsys->owner)) {
550 retval = ERR_PTR(-ENODEV);
551 goto err_module;
552 }
553
554 subsys_d = subsystem_get(subsys->desc->depends_on);
555 if (IS_ERR(subsys_d)) {
556 retval = subsys_d;
557 goto err_depends;
558 }
559
560 track = subsys_get_track(subsys);
561 mutex_lock(&track->lock);
562 if (!subsys->count) {
563 ret = subsys_start(subsys);
564 if (ret) {
565 retval = ERR_PTR(ret);
566 goto err_start;
567 }
568 }
569 subsys->count++;
570 mutex_unlock(&track->lock);
571 return retval;
572err_start:
573 mutex_unlock(&track->lock);
574 subsystem_put(subsys_d);
575err_depends:
576 module_put(subsys->owner);
577err_module:
578 put_device(&subsys->dev);
579 return retval;
580}
581EXPORT_SYMBOL(subsystem_get);
582
583/**
584 * subsystem_put() - Shutdown a subsystem
585 * @peripheral_handle: pointer from a previous call to subsystem_get()
586 *
587 * This doesn't imply that a subsystem is shutdown until all callers of
588 * subsystem_get() have called subsystem_put().
589 */
590void subsystem_put(void *subsystem)
591{
592 struct subsys_device *subsys_d, *subsys = subsystem;
593 struct subsys_tracking *track;
594
595 if (IS_ERR_OR_NULL(subsys))
596 return;
597
598 track = subsys_get_track(subsys);
599 mutex_lock(&track->lock);
600 if (WARN(!subsys->count, "%s: %s: Reference count mismatch\n",
601 subsys->desc->name, __func__))
602 goto err_out;
Stephen Boyd4057ec02012-12-12 17:38:38 -0800603 if (!--subsys->count) {
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700604 subsys_stop(subsys);
Stephen Boyd4057ec02012-12-12 17:38:38 -0800605 if (subsys->do_ramdump_on_put)
606 subsystem_ramdump(subsys, NULL);
607 }
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700608 mutex_unlock(&track->lock);
609
610 subsys_d = find_subsys(subsys->desc->depends_on);
611 if (subsys_d) {
612 subsystem_put(subsys_d);
613 put_device(&subsys_d->dev);
614 }
615 module_put(subsys->owner);
616 put_device(&subsys->dev);
617 return;
618err_out:
619 mutex_unlock(&track->lock);
620}
621EXPORT_SYMBOL(subsystem_put);
622
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700623static void subsystem_restart_wq_func(struct work_struct *work)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700624{
Stephen Boyd497ef402012-06-21 12:54:40 -0700625 struct subsys_device *dev = container_of(work,
626 struct subsys_device, work);
Stephen Boyd0daae152012-05-01 17:36:15 -0700627 struct subsys_device **list;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700628 struct subsys_desc *desc = dev->desc;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700629 struct subsys_soc_restart_order *order = dev->restart_order;
630 struct subsys_tracking *track;
Stephen Boyd0daae152012-05-01 17:36:15 -0700631 unsigned count;
Stephen Boyd497ef402012-06-21 12:54:40 -0700632 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700633
Stephen Boydc68ee642012-05-01 17:02:45 -0700634 /*
635 * It's OK to not take the registration lock at this point.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700636 * This is because the subsystem list inside the relevant
637 * restart order is not being traversed.
638 */
Stephen Boydfcb52a32012-12-03 12:35:14 -0800639 if (order) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700640 list = order->subsys_ptrs;
641 count = order->count;
642 track = &order->track;
643 } else {
Stephen Boyd0daae152012-05-01 17:36:15 -0700644 list = &dev;
645 count = 1;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700646 track = &dev->track;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700647 }
648
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700649 mutex_lock(&track->lock);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700650 do_epoch_check(dev);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700651
Stephen Boydc68ee642012-05-01 17:02:45 -0700652 /*
653 * It's necessary to take the registration lock because the subsystem
654 * list in the SoC restart order will be traversed and it shouldn't be
655 * changed until _this_ restart sequence completes.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700656 */
657 mutex_lock(&soc_order_reg_lock);
658
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700659 pr_debug("[%p]: Starting restart sequence for %s\n", current,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700660 desc->name);
Seemanta Duttabac64552012-12-14 15:47:27 -0800661 notify_each_subsys_device(list, count, SUBSYS_BEFORE_SHUTDOWN, NULL);
Stephen Boyd0daae152012-05-01 17:36:15 -0700662 for_each_subsys_device(list, count, NULL, subsystem_shutdown);
Seemanta Duttabac64552012-12-14 15:47:27 -0800663 notify_each_subsys_device(list, count, SUBSYS_AFTER_SHUTDOWN, NULL);
664
665 notify_each_subsys_device(list, count, SUBSYS_RAMDUMP_NOTIFICATION,
666 &enable_ramdumps);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700667
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700668 spin_lock_irqsave(&track->s_lock, flags);
669 track->p_state = SUBSYS_RESTARTING;
670 spin_unlock_irqrestore(&track->s_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700671
672 /* Collect ram dumps for all subsystems in order here */
Stephen Boyd0daae152012-05-01 17:36:15 -0700673 for_each_subsys_device(list, count, NULL, subsystem_ramdump);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700674
Seemanta Duttabac64552012-12-14 15:47:27 -0800675 notify_each_subsys_device(list, count, SUBSYS_BEFORE_POWERUP, NULL);
Stephen Boyd0daae152012-05-01 17:36:15 -0700676 for_each_subsys_device(list, count, NULL, subsystem_powerup);
Seemanta Duttabac64552012-12-14 15:47:27 -0800677 notify_each_subsys_device(list, count, SUBSYS_AFTER_POWERUP, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700678
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700679 pr_info("[%p]: Restart sequence for %s completed.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700680 current, desc->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700681
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700682 mutex_unlock(&soc_order_reg_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700683 mutex_unlock(&track->lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700684
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700685 spin_lock_irqsave(&track->s_lock, flags);
686 track->p_state = SUBSYS_NORMAL;
Stephen Boyd6b567182012-09-18 11:31:02 -0700687 wake_unlock(&dev->wake_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700688 spin_unlock_irqrestore(&track->s_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700689}
690
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700691static void __subsystem_restart_dev(struct subsys_device *dev)
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700692{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700693 struct subsys_desc *desc = dev->desc;
Stephen Boyd6b567182012-09-18 11:31:02 -0700694 const char *name = dev->desc->name;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700695 struct subsys_tracking *track;
Stephen Boyd497ef402012-06-21 12:54:40 -0700696 unsigned long flags;
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700697
Stephen Boydfcb52a32012-12-03 12:35:14 -0800698 pr_debug("Restarting %s [level=%s]!\n", desc->name,
699 restart_levels[dev->restart_level]);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700700
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700701 track = subsys_get_track(dev);
Stephen Boyd6b567182012-09-18 11:31:02 -0700702 /*
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700703 * Allow drivers to call subsystem_restart{_dev}() as many times as
704 * they want up until the point where the subsystem is shutdown.
Stephen Boyd6b567182012-09-18 11:31:02 -0700705 */
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700706 spin_lock_irqsave(&track->s_lock, flags);
707 if (track->p_state != SUBSYS_CRASHED) {
708 if (dev->track.state == SUBSYS_ONLINE &&
709 track->p_state != SUBSYS_RESTARTING) {
710 track->p_state = SUBSYS_CRASHED;
Stephen Boyd6b567182012-09-18 11:31:02 -0700711 wake_lock(&dev->wake_lock);
712 queue_work(ssr_wq, &dev->work);
713 } else {
714 panic("Subsystem %s crashed during SSR!", name);
715 }
Stephen Boyd77c22742012-07-24 18:46:45 -0700716 }
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700717 spin_unlock_irqrestore(&track->s_lock, flags);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700718}
719
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700720int subsystem_restart_dev(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700721{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700722 const char *name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700723
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700724 if (!get_device(&dev->dev))
725 return -ENODEV;
726
727 if (!try_module_get(dev->owner)) {
728 put_device(&dev->dev);
729 return -ENODEV;
730 }
731
732 name = dev->desc->name;
Vikram Mulukutla3d58c2f2012-10-29 18:43:02 -0700733
734 /*
735 * If a system reboot/shutdown is underway, ignore subsystem errors.
736 * However, print a message so that we know that a subsystem behaved
737 * unexpectedly here.
738 */
739 if (system_state == SYSTEM_RESTART
740 || system_state == SYSTEM_POWER_OFF) {
741 pr_err("%s crashed during a system poweroff/shutdown.\n", name);
742 return -EBUSY;
743 }
744
Stephen Boydfcb52a32012-12-03 12:35:14 -0800745 pr_info("Restart sequence requested for %s, restart_level = %s.\n",
746 name, restart_levels[dev->restart_level]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700747
Stephen Boydfcb52a32012-12-03 12:35:14 -0800748 switch (dev->restart_level) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700749
750 case RESET_SUBSYS_COUPLED:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700751 __subsystem_restart_dev(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700752 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700753 case RESET_SOC:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700754 panic("subsys-restart: Resetting the SoC - %s crashed.", name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700755 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700756 default:
757 panic("subsys-restart: Unknown restart level!\n");
Stephen Boydc68ee642012-05-01 17:02:45 -0700758 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700759 }
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700760 module_put(dev->owner);
761 put_device(&dev->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700762
763 return 0;
764}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700765EXPORT_SYMBOL(subsystem_restart_dev);
766
767int subsystem_restart(const char *name)
768{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700769 int ret;
770 struct subsys_device *dev = find_subsys(name);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700771
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700772 if (!dev)
773 return -ENODEV;
774
775 ret = subsystem_restart_dev(dev);
776 put_device(&dev->dev);
777 return ret;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700778}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700779EXPORT_SYMBOL(subsystem_restart);
780
Stephen Boyd4057ec02012-12-12 17:38:38 -0800781int subsystem_crashed(const char *name)
782{
783 struct subsys_device *dev = find_subsys(name);
784 struct subsys_tracking *track;
785
786 if (!dev)
787 return -ENODEV;
788
789 if (!get_device(&dev->dev))
790 return -ENODEV;
791
792 track = subsys_get_track(dev);
793
794 mutex_lock(&track->lock);
795 dev->do_ramdump_on_put = true;
796 /*
797 * TODO: Make this work with multiple consumers where one is calling
798 * subsystem_restart() and another is calling this function. To do
799 * so would require updating private state, etc.
800 */
801 mutex_unlock(&track->lock);
802
803 put_device(&dev->dev);
804 return 0;
805}
806EXPORT_SYMBOL(subsystem_crashed);
807
Stephen Boydeff41a62012-07-06 13:19:02 -0700808#ifdef CONFIG_DEBUG_FS
809static ssize_t subsys_debugfs_read(struct file *filp, char __user *ubuf,
810 size_t cnt, loff_t *ppos)
811{
812 int r;
813 char buf[40];
814 struct subsys_device *subsys = filp->private_data;
815
816 r = snprintf(buf, sizeof(buf), "%d\n", subsys->count);
817 return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
818}
819
820static ssize_t subsys_debugfs_write(struct file *filp,
821 const char __user *ubuf, size_t cnt, loff_t *ppos)
822{
823 struct subsys_device *subsys = filp->private_data;
824 char buf[10];
825 char *cmp;
826
827 cnt = min(cnt, sizeof(buf) - 1);
828 if (copy_from_user(&buf, ubuf, cnt))
829 return -EFAULT;
830 buf[cnt] = '\0';
831 cmp = strstrip(buf);
832
833 if (!strcmp(cmp, "restart")) {
834 if (subsystem_restart_dev(subsys))
835 return -EIO;
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700836 } else if (!strcmp(cmp, "get")) {
837 if (subsystem_get(subsys->desc->name))
838 return -EIO;
839 } else if (!strcmp(cmp, "put")) {
840 subsystem_put(subsys);
Stephen Boydeff41a62012-07-06 13:19:02 -0700841 } else {
842 return -EINVAL;
843 }
844
845 return cnt;
846}
847
848static const struct file_operations subsys_debugfs_fops = {
849 .open = simple_open,
850 .read = subsys_debugfs_read,
851 .write = subsys_debugfs_write,
852};
853
854static struct dentry *subsys_base_dir;
855
856static int __init subsys_debugfs_init(void)
857{
858 subsys_base_dir = debugfs_create_dir("msm_subsys", NULL);
859 return !subsys_base_dir ? -ENOMEM : 0;
860}
861
862static void subsys_debugfs_exit(void)
863{
864 debugfs_remove_recursive(subsys_base_dir);
865}
866
867static int subsys_debugfs_add(struct subsys_device *subsys)
868{
869 if (!subsys_base_dir)
870 return -ENOMEM;
871
872 subsys->dentry = debugfs_create_file(subsys->desc->name,
873 S_IRUGO | S_IWUSR, subsys_base_dir,
874 subsys, &subsys_debugfs_fops);
875 return !subsys->dentry ? -ENOMEM : 0;
876}
877
878static void subsys_debugfs_remove(struct subsys_device *subsys)
879{
880 debugfs_remove(subsys->dentry);
881}
882#else
883static int __init subsys_debugfs_init(void) { return 0; };
884static void subsys_debugfs_exit(void) { }
885static int subsys_debugfs_add(struct subsys_device *subsys) { return 0; }
886static void subsys_debugfs_remove(struct subsys_device *subsys) { }
887#endif
888
Seemanta Duttaf055ac72013-01-10 18:08:04 -0800889static int subsys_device_open(struct inode *inode, struct file *file)
890{
891 void *retval;
892 struct subsys_device *subsys_dev = container_of(file->private_data,
893 struct subsys_device, misc_dev);
894
895 if (!file->private_data)
896 return -EINVAL;
897
898 retval = subsystem_get(subsys_dev->desc->name);
899 if (IS_ERR(retval))
900 return PTR_ERR(retval);
901
902 return 0;
903}
904
905static int subsys_device_close(struct inode *inode, struct file *file)
906{
907 struct subsys_device *subsys_dev = container_of(file->private_data,
908 struct subsys_device, misc_dev);
909
910 if (!file->private_data)
911 return -EINVAL;
912
913 subsystem_put(subsys_dev);
914
915 return 0;
916}
917
918static const struct file_operations subsys_device_fops = {
919 .owner = THIS_MODULE,
920 .open = subsys_device_open,
921 .release = subsys_device_close,
922};
923
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700924static void subsys_device_release(struct device *dev)
925{
926 struct subsys_device *subsys = to_subsys(dev);
927
928 wake_lock_destroy(&subsys->wake_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700929 mutex_destroy(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700930 ida_simple_remove(&subsys_ida, subsys->id);
931 kfree(subsys);
932}
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800933static irqreturn_t subsys_err_ready_intr_handler(int irq, void *subsys)
934{
935 struct subsys_device *subsys_dev = subsys;
936 pr_info("Error ready interrupt occured for %s\n",
937 subsys_dev->desc->name);
938 complete(&subsys_dev->err_ready);
939 return IRQ_HANDLED;
940}
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700941
Seemanta Duttaf055ac72013-01-10 18:08:04 -0800942static int subsys_misc_device_add(struct subsys_device *subsys_dev)
943{
944 int ret;
945 memset(subsys_dev->miscdevice_name, 0,
946 ARRAY_SIZE(subsys_dev->miscdevice_name));
947 snprintf(subsys_dev->miscdevice_name,
948 ARRAY_SIZE(subsys_dev->miscdevice_name), "subsys_%s",
949 subsys_dev->desc->name);
950
951 subsys_dev->misc_dev.minor = MISC_DYNAMIC_MINOR;
952 subsys_dev->misc_dev.name = subsys_dev->miscdevice_name;
953 subsys_dev->misc_dev.fops = &subsys_device_fops;
954 subsys_dev->misc_dev.parent = &subsys_dev->dev;
955
956 ret = misc_register(&subsys_dev->misc_dev);
957 if (ret) {
958 pr_err("%s: misc_register() failed for %s (%d)", __func__,
959 subsys_dev->miscdevice_name, ret);
960 }
961 return ret;
962}
963
964static void subsys_misc_device_remove(struct subsys_device *subsys_dev)
965{
966 misc_deregister(&subsys_dev->misc_dev);
967}
968
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700969struct subsys_device *subsys_register(struct subsys_desc *desc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700970{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700971 struct subsys_device *subsys;
972 int ret;
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700973
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700974 subsys = kzalloc(sizeof(*subsys), GFP_KERNEL);
975 if (!subsys)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700976 return ERR_PTR(-ENOMEM);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700977
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700978 subsys->desc = desc;
979 subsys->owner = desc->owner;
980 subsys->dev.parent = desc->dev;
981 subsys->dev.bus = &subsys_bus_type;
982 subsys->dev.release = subsys_device_release;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700983
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700984 subsys->notify = subsys_notif_add_subsys(desc->name);
985 subsys->restart_order = update_restart_order(subsys);
Stephen Boyd497ef402012-06-21 12:54:40 -0700986
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700987 snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name);
988 wake_lock_init(&subsys->wake_lock, WAKE_LOCK_SUSPEND, subsys->wlname);
989 INIT_WORK(&subsys->work, subsystem_restart_wq_func);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700990 spin_lock_init(&subsys->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700991
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700992 subsys->id = ida_simple_get(&subsys_ida, 0, 0, GFP_KERNEL);
993 if (subsys->id < 0) {
994 ret = subsys->id;
995 goto err_ida;
996 }
997 dev_set_name(&subsys->dev, "subsys%d", subsys->id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700998
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700999 mutex_init(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001000
Stephen Boydeff41a62012-07-06 13:19:02 -07001001 ret = subsys_debugfs_add(subsys);
1002 if (ret)
1003 goto err_debugfs;
1004
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001005 ret = device_register(&subsys->dev);
1006 if (ret) {
Seemanta Duttaf055ac72013-01-10 18:08:04 -08001007 device_unregister(&subsys->dev);
1008 goto err_register;
1009 }
1010
1011 ret = subsys_misc_device_add(subsys);
1012 if (ret) {
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001013 put_device(&subsys->dev);
1014 goto err_register;
1015 }
Stephen Boydeff41a62012-07-06 13:19:02 -07001016
Seemanta Duttad32f92a2013-01-25 14:22:15 -08001017 if (subsys->desc->err_ready_irq) {
1018 ret = devm_request_irq(&subsys->dev,
1019 subsys->desc->err_ready_irq,
1020 subsys_err_ready_intr_handler,
1021 IRQF_TRIGGER_RISING,
1022 "error_ready_interrupt", subsys);
1023 if (ret < 0) {
1024 dev_err(&subsys->dev,
1025 "[%s]: Unable to register err ready handler\n",
1026 subsys->desc->name);
1027 goto err_misc_device;
1028 }
1029 }
1030
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001031 return subsys;
1032
Seemanta Duttad32f92a2013-01-25 14:22:15 -08001033err_misc_device:
1034 subsys_misc_device_remove(subsys);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001035err_register:
Stephen Boydeff41a62012-07-06 13:19:02 -07001036 subsys_debugfs_remove(subsys);
1037err_debugfs:
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001038 mutex_destroy(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001039 ida_simple_remove(&subsys_ida, subsys->id);
1040err_ida:
1041 wake_lock_destroy(&subsys->wake_lock);
1042 kfree(subsys);
1043 return ERR_PTR(ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001044}
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001045EXPORT_SYMBOL(subsys_register);
1046
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001047void subsys_unregister(struct subsys_device *subsys)
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001048{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001049 if (IS_ERR_OR_NULL(subsys))
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001050 return;
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001051
1052 if (get_device(&subsys->dev)) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001053 mutex_lock(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001054 WARN_ON(subsys->count);
1055 device_unregister(&subsys->dev);
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001056 mutex_unlock(&subsys->track.lock);
Stephen Boydeff41a62012-07-06 13:19:02 -07001057 subsys_debugfs_remove(subsys);
Seemanta Duttaf055ac72013-01-10 18:08:04 -08001058 subsys_misc_device_remove(subsys);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001059 put_device(&subsys->dev);
1060 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001061}
1062EXPORT_SYMBOL(subsys_unregister);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001063
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001064static int subsys_panic(struct device *dev, void *data)
1065{
1066 struct subsys_device *subsys = to_subsys(dev);
1067
1068 if (subsys->desc->crash_shutdown)
1069 subsys->desc->crash_shutdown(subsys->desc);
1070 return 0;
1071}
1072
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001073static int ssr_panic_handler(struct notifier_block *this,
1074 unsigned long event, void *ptr)
1075{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001076 bus_for_each_dev(&subsys_bus_type, NULL, NULL, subsys_panic);
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001077 return NOTIFY_DONE;
1078}
1079
1080static struct notifier_block panic_nb = {
1081 .notifier_call = ssr_panic_handler,
1082};
1083
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001084static int __init ssr_init_soc_restart_orders(void)
1085{
1086 int i;
1087
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001088 atomic_notifier_chain_register(&panic_notifier_list,
1089 &panic_nb);
1090
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001091 if (cpu_is_msm8x60()) {
1092 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001093 mutex_init(&orders_8x60_all[i]->track.lock);
1094 spin_lock_init(&orders_8x60_all[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001095 }
1096
1097 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001098 mutex_init(&orders_8x60_modems[i]->track.lock);
1099 spin_lock_init(&orders_8x60_modems[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001100 }
1101
1102 restart_orders = orders_8x60_all;
1103 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
1104 }
1105
Vikram Mulukutlae4ae75e2012-07-27 15:14:10 -07001106 if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) {
1107 restart_orders = restart_orders_8960_sglte;
1108 n_restart_orders = ARRAY_SIZE(restart_orders_8960_sglte);
1109 }
1110
1111 for (i = 0; i < n_restart_orders; i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001112 mutex_init(&restart_orders[i]->track.lock);
1113 spin_lock_init(&restart_orders[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001114 }
1115
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001116 return 0;
1117}
1118
1119static int __init subsys_restart_init(void)
1120{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001121 int ret;
Vikram Mulukutla69177e12012-03-21 20:51:43 -07001122
Vikram Mulukutla8d4c3f02012-09-10 19:25:32 -07001123 ssr_wq = alloc_workqueue("ssr_wq", WQ_CPU_INTENSIVE, 0);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001124 BUG_ON(!ssr_wq);
1125
1126 ret = bus_register(&subsys_bus_type);
1127 if (ret)
1128 goto err_bus;
Stephen Boydeff41a62012-07-06 13:19:02 -07001129 ret = subsys_debugfs_init();
1130 if (ret)
1131 goto err_debugfs;
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001132 ret = ssr_init_soc_restart_orders();
1133 if (ret)
1134 goto err_soc;
1135 return 0;
Stephen Boydeff41a62012-07-06 13:19:02 -07001136
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001137err_soc:
Stephen Boydeff41a62012-07-06 13:19:02 -07001138 subsys_debugfs_exit();
1139err_debugfs:
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001140 bus_unregister(&subsys_bus_type);
1141err_bus:
1142 destroy_workqueue(ssr_wq);
1143 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001144}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001145arch_initcall(subsys_restart_init);
1146
1147MODULE_DESCRIPTION("Subsystem Restart Driver");
1148MODULE_LICENSE("GPL v2");