blob: 01e09859b9ebfda96e35c873b5964198af0edf87 [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>
Seemanta Dutta076d00e2013-06-17 17:58:18 -070035#include <linux/of_gpio.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070036
37#include <asm/current.h>
38
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070039#include <mach/socinfo.h>
40#include <mach/subsystem_notif.h>
41#include <mach/subsystem_restart.h>
42
43#include "smd_private.h"
44
Seemanta Dutta6222f392013-05-10 18:17:38 -070045static int enable_debug;
46module_param(enable_debug, int, S_IRUGO | S_IWUSR);
47
Stephen Boyd5bd20e42012-10-03 12:28:43 -070048/**
49 * enum p_subsys_state - state of a subsystem (private)
50 * @SUBSYS_NORMAL: subsystem is operating normally
51 * @SUBSYS_CRASHED: subsystem has crashed and hasn't been shutdown
52 * @SUBSYS_RESTARTING: subsystem has been shutdown and is now restarting
53 *
54 * The 'private' side of the subsytem state used to determine where in the
55 * restart process the subsystem is.
56 */
57enum p_subsys_state {
58 SUBSYS_NORMAL,
59 SUBSYS_CRASHED,
60 SUBSYS_RESTARTING,
61};
62
63/**
64 * enum subsys_state - state of a subsystem (public)
65 * @SUBSYS_OFFLINE: subsystem is offline
66 * @SUBSYS_ONLINE: subsystem is online
67 *
68 * The 'public' side of the subsytem state, exposed to userspace.
69 */
70enum subsys_state {
71 SUBSYS_OFFLINE,
72 SUBSYS_ONLINE,
73};
74
75static const char * const subsys_states[] = {
76 [SUBSYS_OFFLINE] = "OFFLINE",
77 [SUBSYS_ONLINE] = "ONLINE",
78};
79
Stephen Boydfcb52a32012-12-03 12:35:14 -080080static const char * const restart_levels[] = {
81 [RESET_SOC] = "SYSTEM",
82 [RESET_SUBSYS_COUPLED] = "RELATED",
83};
84
Stephen Boyd5bd20e42012-10-03 12:28:43 -070085/**
86 * struct subsys_tracking - track state of a subsystem or restart order
87 * @p_state: private state of subsystem/order
88 * @state: public state of subsystem/order
89 * @s_lock: protects p_state
90 * @lock: protects subsystem/order callbacks and state
91 *
92 * Tracks the state of a subsystem or a set of subsystems (restart order).
93 * Doing this avoids the need to grab each subsystem's lock and update
94 * each subsystems state when restarting an order.
95 */
96struct subsys_tracking {
97 enum p_subsys_state p_state;
98 spinlock_t s_lock;
99 enum subsys_state state;
100 struct mutex lock;
101};
102
103/**
104 * struct subsys_soc_restart_order - subsystem restart order
105 * @subsystem_list: names of subsystems in this restart order
106 * @count: number of subsystems in order
107 * @track: state tracking and locking
108 * @subsys_ptrs: pointers to subsystems in this restart order
109 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700110struct subsys_soc_restart_order {
111 const char * const *subsystem_list;
112 int count;
113
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700114 struct subsys_tracking track;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700115 struct subsys_device *subsys_ptrs[];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700116};
117
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700118struct restart_log {
119 struct timeval time;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700120 struct subsys_device *dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700121 struct list_head list;
122};
123
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700124/**
125 * struct subsys_device - subsystem device
126 * @desc: subsystem descriptor
127 * @wake_lock: prevents suspend during subsystem_restart()
128 * @wlname: name of @wake_lock
129 * @work: context for subsystem_restart_wq_func() for this device
130 * @track: state tracking and locking
131 * @notify: subsys notify handle
132 * @dev: device
133 * @owner: module that provides @desc
134 * @count: reference count of subsystem_get()/subsystem_put()
135 * @id: ida
Stephen Boydfcb52a32012-12-03 12:35:14 -0800136 * @restart_level: restart level (0 - panic, 1 - related, 2 - independent, etc.)
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700137 * @restart_order: order of other devices this devices restarts with
138 * @dentry: debugfs directory for this device
Stephen Boyd4057ec02012-12-12 17:38:38 -0800139 * @do_ramdump_on_put: ramdump on subsystem_put() if true
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800140 * @err_ready: completion variable to record error ready from subsystem
Seemanta Dutta0adbbf02013-03-12 17:26:17 -0700141 * @crashed: indicates if subsystem has crashed
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700142 */
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700143struct subsys_device {
144 struct subsys_desc *desc;
Stephen Boyd497ef402012-06-21 12:54:40 -0700145 struct wake_lock wake_lock;
146 char wlname[64];
147 struct work_struct work;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700148
149 struct subsys_tracking track;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700150
151 void *notify;
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700152 struct device dev;
153 struct module *owner;
154 int count;
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700155 int id;
Stephen Boydfcb52a32012-12-03 12:35:14 -0800156 int restart_level;
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700157 struct subsys_soc_restart_order *restart_order;
Stephen Boydeff41a62012-07-06 13:19:02 -0700158#ifdef CONFIG_DEBUG_FS
159 struct dentry *dentry;
160#endif
Stephen Boyd4057ec02012-12-12 17:38:38 -0800161 bool do_ramdump_on_put;
Seemanta Duttaf055ac72013-01-10 18:08:04 -0800162 struct miscdevice misc_dev;
163 char miscdevice_name[32];
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800164 struct completion err_ready;
Seemanta Dutta0adbbf02013-03-12 17:26:17 -0700165 bool crashed;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700166};
167
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700168static struct subsys_device *to_subsys(struct device *d)
169{
170 return container_of(d, struct subsys_device, dev);
171}
172
173static ssize_t name_show(struct device *dev, struct device_attribute *attr,
174 char *buf)
175{
176 return snprintf(buf, PAGE_SIZE, "%s\n", to_subsys(dev)->desc->name);
177}
178
179static ssize_t state_show(struct device *dev, struct device_attribute *attr,
180 char *buf)
181{
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700182 enum subsys_state state = to_subsys(dev)->track.state;
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700183 return snprintf(buf, PAGE_SIZE, "%s\n", subsys_states[state]);
184}
185
Stephen Boydfcb52a32012-12-03 12:35:14 -0800186static ssize_t
187restart_level_show(struct device *dev, struct device_attribute *attr, char *buf)
188{
189 int level = to_subsys(dev)->restart_level;
190 return snprintf(buf, PAGE_SIZE, "%s\n", restart_levels[level]);
191}
192
193static ssize_t restart_level_store(struct device *dev,
194 struct device_attribute *attr, const char *buf, size_t count)
195{
196 struct subsys_device *subsys = to_subsys(dev);
197 int i;
198 const char *p;
199
200 p = memchr(buf, '\n', count);
201 if (p)
202 count = p - buf;
203
204 for (i = 0; i < ARRAY_SIZE(restart_levels); i++)
205 if (!strncasecmp(buf, restart_levels[i], count)) {
206 subsys->restart_level = i;
207 return count;
208 }
209 return -EPERM;
210}
211
212int subsys_get_restart_level(struct subsys_device *dev)
213{
214 return dev->restart_level;
215}
216EXPORT_SYMBOL(subsys_get_restart_level);
217
Stephen Boyd6b567182012-09-18 11:31:02 -0700218static void subsys_set_state(struct subsys_device *subsys,
219 enum subsys_state state)
220{
221 unsigned long flags;
222
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700223 spin_lock_irqsave(&subsys->track.s_lock, flags);
224 if (subsys->track.state != state) {
225 subsys->track.state = state;
226 spin_unlock_irqrestore(&subsys->track.s_lock, flags);
Stephen Boyd6b567182012-09-18 11:31:02 -0700227 sysfs_notify(&subsys->dev.kobj, NULL, "state");
228 return;
229 }
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700230 spin_unlock_irqrestore(&subsys->track.s_lock, flags);
Stephen Boyd6b567182012-09-18 11:31:02 -0700231}
232
Stephen Boyd43b380a2012-09-21 17:34:24 -0700233/**
234 * subsytem_default_online() - Mark a subsystem as online by default
235 * @dev: subsystem to mark as online
236 *
237 * Marks a subsystem as "online" without increasing the reference count
238 * on the subsystem. This is typically used by subsystems that are already
239 * online when the kernel boots up.
240 */
241void subsys_default_online(struct subsys_device *dev)
242{
243 subsys_set_state(dev, SUBSYS_ONLINE);
244}
245EXPORT_SYMBOL(subsys_default_online);
246
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700247static struct device_attribute subsys_attrs[] = {
248 __ATTR_RO(name),
249 __ATTR_RO(state),
Stephen Boydfcb52a32012-12-03 12:35:14 -0800250 __ATTR(restart_level, 0644, restart_level_show, restart_level_store),
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700251 __ATTR_NULL,
252};
253
254static struct bus_type subsys_bus_type = {
255 .name = "msm_subsys",
256 .dev_attrs = subsys_attrs,
257};
258
259static DEFINE_IDA(subsys_ida);
260
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700261static int enable_ramdumps;
Stephen Boydc68ee642012-05-01 17:02:45 -0700262module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR);
263
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700264struct workqueue_struct *ssr_wq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700265
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700266static LIST_HEAD(restart_log_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700267static DEFINE_MUTEX(soc_order_reg_lock);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700268static DEFINE_MUTEX(restart_log_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700269
270/* SOC specific restart orders go here */
271
272#define DEFINE_SINGLE_RESTART_ORDER(name, order) \
273 static struct subsys_soc_restart_order __##name = { \
274 .subsystem_list = order, \
275 .count = ARRAY_SIZE(order), \
276 .subsys_ptrs = {[ARRAY_SIZE(order)] = NULL} \
277 }; \
278 static struct subsys_soc_restart_order *name[] = { \
279 &__##name, \
280 }
281
282/* MSM 8x60 restart ordering info */
283static const char * const _order_8x60_all[] = {
Stephen Boyd77db8bb2012-06-27 15:15:16 -0700284 "external_modem", "modem", "adsp"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700285};
286DEFINE_SINGLE_RESTART_ORDER(orders_8x60_all, _order_8x60_all);
287
288static const char * const _order_8x60_modems[] = {"external_modem", "modem"};
289DEFINE_SINGLE_RESTART_ORDER(orders_8x60_modems, _order_8x60_modems);
290
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700291/*SGLTE restart ordering info*/
292static const char * const order_8960_sglte[] = {"external_modem",
293 "modem"};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700294
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700295static struct subsys_soc_restart_order restart_orders_8960_fusion_sglte = {
296 .subsystem_list = order_8960_sglte,
297 .count = ARRAY_SIZE(order_8960_sglte),
298 .subsys_ptrs = {[ARRAY_SIZE(order_8960_sglte)] = NULL}
299 };
300
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700301static struct subsys_soc_restart_order *restart_orders_8960_sglte[] = {
302 &restart_orders_8960_fusion_sglte,
303 };
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700304
305/* These will be assigned to one of the sets above after
306 * runtime SoC identification.
307 */
308static struct subsys_soc_restart_order **restart_orders;
309static int n_restart_orders;
310
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700311static struct subsys_soc_restart_order *
312update_restart_order(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700313{
314 int i, j;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700315 struct subsys_soc_restart_order *order;
316 const char *name = dev->desc->name;
317 int len = SUBSYS_NAME_MAX_LENGTH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700318
319 mutex_lock(&soc_order_reg_lock);
320 for (j = 0; j < n_restart_orders; j++) {
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700321 order = restart_orders[j];
322 for (i = 0; i < order->count; i++) {
323 if (!strncmp(order->subsystem_list[i], name, len)) {
324 order->subsys_ptrs[i] = dev;
325 goto found;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700326 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700327 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700328 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700329 order = NULL;
330found:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700331 mutex_unlock(&soc_order_reg_lock);
332
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700333 return order;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700334}
335
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700336static int max_restarts;
337module_param(max_restarts, int, 0644);
338
339static long max_history_time = 3600;
340module_param(max_history_time, long, 0644);
341
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700342static void do_epoch_check(struct subsys_device *dev)
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700343{
344 int n = 0;
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600345 struct timeval *time_first = NULL, *curr_time;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700346 struct restart_log *r_log, *temp;
347 static int max_restarts_check;
348 static long max_history_time_check;
349
350 mutex_lock(&restart_log_mutex);
351
352 max_restarts_check = max_restarts;
353 max_history_time_check = max_history_time;
354
355 /* Check if epoch checking is enabled */
356 if (!max_restarts_check)
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700357 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700358
359 r_log = kmalloc(sizeof(struct restart_log), GFP_KERNEL);
Matt Wagantalleecca342011-11-10 20:12:05 -0800360 if (!r_log)
361 goto out;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700362 r_log->dev = dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700363 do_gettimeofday(&r_log->time);
364 curr_time = &r_log->time;
365 INIT_LIST_HEAD(&r_log->list);
366
367 list_add_tail(&r_log->list, &restart_log_list);
368
369 list_for_each_entry_safe(r_log, temp, &restart_log_list, list) {
370
371 if ((curr_time->tv_sec - r_log->time.tv_sec) >
372 max_history_time_check) {
373
374 pr_debug("Deleted node with restart_time = %ld\n",
375 r_log->time.tv_sec);
376 list_del(&r_log->list);
377 kfree(r_log);
378 continue;
379 }
380 if (!n) {
381 time_first = &r_log->time;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700382 pr_debug("Time_first: %ld\n", time_first->tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700383 }
384 n++;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700385 pr_debug("Restart_time: %ld\n", r_log->time.tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700386 }
387
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600388 if (time_first && n >= max_restarts_check) {
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700389 if ((curr_time->tv_sec - time_first->tv_sec) <
390 max_history_time_check)
391 panic("Subsystems have crashed %d times in less than "
392 "%ld seconds!", max_restarts_check,
393 max_history_time_check);
394 }
395
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700396out:
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700397 mutex_unlock(&restart_log_mutex);
398}
399
Stephen Boyd0daae152012-05-01 17:36:15 -0700400static void for_each_subsys_device(struct subsys_device **list, unsigned count,
401 void *data, void (*fn)(struct subsys_device *, void *))
402{
403 while (count--) {
404 struct subsys_device *dev = *list++;
405 if (!dev)
406 continue;
407 fn(dev, data);
408 }
409}
410
Seemanta Duttabac64552012-12-14 15:47:27 -0800411static void notify_each_subsys_device(struct subsys_device **list,
412 unsigned count,
413 enum subsys_notif_type notif, void *data)
Stephen Boyd0daae152012-05-01 17:36:15 -0700414{
Seemanta Duttabac64552012-12-14 15:47:27 -0800415 while (count--) {
416 enum subsys_notif_type type = (enum subsys_notif_type)type;
417 struct subsys_device *dev = *list++;
418 if (!dev)
419 continue;
420 subsys_notif_queue_notification(dev->notify, notif, data);
421 }
Stephen Boyd0daae152012-05-01 17:36:15 -0700422}
423
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800424static int wait_for_err_ready(struct subsys_device *subsys)
425{
426 int ret;
427
Seemanta Dutta6222f392013-05-10 18:17:38 -0700428 if (!subsys->desc->err_ready_irq || enable_debug == 1)
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800429 return 0;
430
431 ret = wait_for_completion_timeout(&subsys->err_ready,
432 msecs_to_jiffies(10000));
Seemanta Dutta159af7f2013-05-28 11:09:33 -0700433 if (!ret) {
434 pr_err("[%s]: Error ready timed out\n", subsys->desc->name);
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800435 return -ETIMEDOUT;
Seemanta Dutta159af7f2013-05-28 11:09:33 -0700436 }
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800437
438 return 0;
439}
440
Stephen Boyd0daae152012-05-01 17:36:15 -0700441static void subsystem_shutdown(struct subsys_device *dev, void *data)
442{
443 const char *name = dev->desc->name;
444
445 pr_info("[%p]: Shutting down %s\n", current, name);
446 if (dev->desc->shutdown(dev->desc) < 0)
447 panic("subsys-restart: [%p]: Failed to shutdown %s!",
448 current, name);
Stephen Boyd6b567182012-09-18 11:31:02 -0700449 subsys_set_state(dev, SUBSYS_OFFLINE);
Stephen Boyd0daae152012-05-01 17:36:15 -0700450}
451
452static void subsystem_ramdump(struct subsys_device *dev, void *data)
453{
454 const char *name = dev->desc->name;
455
456 if (dev->desc->ramdump)
457 if (dev->desc->ramdump(enable_ramdumps, dev->desc) < 0)
458 pr_warn("%s[%p]: Ramdump failed.\n", name, current);
Stephen Boyd4057ec02012-12-12 17:38:38 -0800459 dev->do_ramdump_on_put = false;
Stephen Boyd0daae152012-05-01 17:36:15 -0700460}
461
462static void subsystem_powerup(struct subsys_device *dev, void *data)
463{
464 const char *name = dev->desc->name;
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800465 int ret;
Stephen Boyd0daae152012-05-01 17:36:15 -0700466
467 pr_info("[%p]: Powering up %s\n", current, name);
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800468 init_completion(&dev->err_ready);
Stephen Boyd0daae152012-05-01 17:36:15 -0700469 if (dev->desc->powerup(dev->desc) < 0)
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800470 panic("[%p]: Powerup error: %s!", current, name);
471
472 ret = wait_for_err_ready(dev);
473 if (ret)
474 panic("[%p]: Timed out waiting for error ready: %s!",
475 current, name);
Stephen Boyd6b567182012-09-18 11:31:02 -0700476 subsys_set_state(dev, SUBSYS_ONLINE);
Stephen Boyd0daae152012-05-01 17:36:15 -0700477}
478
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700479static int __find_subsys(struct device *dev, void *data)
480{
481 struct subsys_device *subsys = to_subsys(dev);
482 return !strcmp(subsys->desc->name, data);
483}
484
485static struct subsys_device *find_subsys(const char *str)
486{
487 struct device *dev;
488
489 if (!str)
490 return NULL;
491
492 dev = bus_find_device(&subsys_bus_type, NULL, (void *)str,
493 __find_subsys);
494 return dev ? to_subsys(dev) : NULL;
495}
496
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700497static int subsys_start(struct subsys_device *subsys)
498{
499 int ret;
500
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800501 init_completion(&subsys->err_ready);
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700502 ret = subsys->desc->start(subsys->desc);
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800503 if (ret)
504 return ret;
505
Seemanta Duttaef377982013-05-09 16:22:45 -0700506 if (subsys->desc->is_not_loadable) {
507 subsys_set_state(subsys, SUBSYS_ONLINE);
Seemanta Duttad7591612013-05-03 17:44:00 -0700508 return 0;
Seemanta Duttaef377982013-05-09 16:22:45 -0700509 }
Seemanta Duttad7591612013-05-03 17:44:00 -0700510
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800511 ret = wait_for_err_ready(subsys);
512 if (ret)
513 /* pil-boot succeeded but we need to shutdown
514 * the device because error ready timed out.
515 */
516 subsys->desc->stop(subsys->desc);
517 else
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700518 subsys_set_state(subsys, SUBSYS_ONLINE);
519
520 return ret;
521}
522
523static void subsys_stop(struct subsys_device *subsys)
524{
525 subsys->desc->stop(subsys->desc);
526 subsys_set_state(subsys, SUBSYS_OFFLINE);
527}
528
529static struct subsys_tracking *subsys_get_track(struct subsys_device *subsys)
530{
531 struct subsys_soc_restart_order *order = subsys->restart_order;
532
Stephen Boydfcb52a32012-12-03 12:35:14 -0800533 if (order)
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700534 return &order->track;
535 else
536 return &subsys->track;
537}
538
539/**
540 * subsytem_get() - Boot a subsystem
541 * @name: pointer to a string containing the name of the subsystem to boot
542 *
543 * This function returns a pointer if it succeeds. If an error occurs an
544 * ERR_PTR is returned.
545 *
546 * If this feature is disable, the value %NULL will be returned.
547 */
548void *subsystem_get(const char *name)
549{
550 struct subsys_device *subsys;
551 struct subsys_device *subsys_d;
552 int ret;
553 void *retval;
554 struct subsys_tracking *track;
555
556 if (!name)
557 return NULL;
558
559 subsys = retval = find_subsys(name);
560 if (!subsys)
561 return ERR_PTR(-ENODEV);
562 if (!try_module_get(subsys->owner)) {
563 retval = ERR_PTR(-ENODEV);
564 goto err_module;
565 }
566
567 subsys_d = subsystem_get(subsys->desc->depends_on);
568 if (IS_ERR(subsys_d)) {
569 retval = subsys_d;
570 goto err_depends;
571 }
572
573 track = subsys_get_track(subsys);
574 mutex_lock(&track->lock);
575 if (!subsys->count) {
576 ret = subsys_start(subsys);
577 if (ret) {
578 retval = ERR_PTR(ret);
579 goto err_start;
580 }
581 }
582 subsys->count++;
583 mutex_unlock(&track->lock);
584 return retval;
585err_start:
586 mutex_unlock(&track->lock);
587 subsystem_put(subsys_d);
588err_depends:
589 module_put(subsys->owner);
590err_module:
591 put_device(&subsys->dev);
592 return retval;
593}
594EXPORT_SYMBOL(subsystem_get);
595
596/**
597 * subsystem_put() - Shutdown a subsystem
598 * @peripheral_handle: pointer from a previous call to subsystem_get()
599 *
600 * This doesn't imply that a subsystem is shutdown until all callers of
601 * subsystem_get() have called subsystem_put().
602 */
603void subsystem_put(void *subsystem)
604{
605 struct subsys_device *subsys_d, *subsys = subsystem;
606 struct subsys_tracking *track;
607
608 if (IS_ERR_OR_NULL(subsys))
609 return;
610
611 track = subsys_get_track(subsys);
612 mutex_lock(&track->lock);
613 if (WARN(!subsys->count, "%s: %s: Reference count mismatch\n",
614 subsys->desc->name, __func__))
615 goto err_out;
Stephen Boyd4057ec02012-12-12 17:38:38 -0800616 if (!--subsys->count) {
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700617 subsys_stop(subsys);
Stephen Boyd4057ec02012-12-12 17:38:38 -0800618 if (subsys->do_ramdump_on_put)
619 subsystem_ramdump(subsys, NULL);
620 }
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700621 mutex_unlock(&track->lock);
622
623 subsys_d = find_subsys(subsys->desc->depends_on);
624 if (subsys_d) {
625 subsystem_put(subsys_d);
626 put_device(&subsys_d->dev);
627 }
628 module_put(subsys->owner);
629 put_device(&subsys->dev);
630 return;
631err_out:
632 mutex_unlock(&track->lock);
633}
634EXPORT_SYMBOL(subsystem_put);
635
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700636static void subsystem_restart_wq_func(struct work_struct *work)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700637{
Stephen Boyd497ef402012-06-21 12:54:40 -0700638 struct subsys_device *dev = container_of(work,
639 struct subsys_device, work);
Stephen Boyd0daae152012-05-01 17:36:15 -0700640 struct subsys_device **list;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700641 struct subsys_desc *desc = dev->desc;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700642 struct subsys_soc_restart_order *order = dev->restart_order;
643 struct subsys_tracking *track;
Stephen Boyd0daae152012-05-01 17:36:15 -0700644 unsigned count;
Stephen Boyd497ef402012-06-21 12:54:40 -0700645 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700646
Stephen Boydc68ee642012-05-01 17:02:45 -0700647 /*
648 * It's OK to not take the registration lock at this point.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700649 * This is because the subsystem list inside the relevant
650 * restart order is not being traversed.
651 */
Stephen Boydfcb52a32012-12-03 12:35:14 -0800652 if (order) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700653 list = order->subsys_ptrs;
654 count = order->count;
655 track = &order->track;
656 } else {
Stephen Boyd0daae152012-05-01 17:36:15 -0700657 list = &dev;
658 count = 1;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700659 track = &dev->track;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700660 }
661
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700662 mutex_lock(&track->lock);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700663 do_epoch_check(dev);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700664
Stephen Boydc68ee642012-05-01 17:02:45 -0700665 /*
666 * It's necessary to take the registration lock because the subsystem
667 * list in the SoC restart order will be traversed and it shouldn't be
668 * changed until _this_ restart sequence completes.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700669 */
670 mutex_lock(&soc_order_reg_lock);
671
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700672 pr_debug("[%p]: Starting restart sequence for %s\n", current,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700673 desc->name);
Seemanta Duttabac64552012-12-14 15:47:27 -0800674 notify_each_subsys_device(list, count, SUBSYS_BEFORE_SHUTDOWN, NULL);
Stephen Boyd0daae152012-05-01 17:36:15 -0700675 for_each_subsys_device(list, count, NULL, subsystem_shutdown);
Seemanta Duttabac64552012-12-14 15:47:27 -0800676 notify_each_subsys_device(list, count, SUBSYS_AFTER_SHUTDOWN, NULL);
677
678 notify_each_subsys_device(list, count, SUBSYS_RAMDUMP_NOTIFICATION,
679 &enable_ramdumps);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700680
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700681 spin_lock_irqsave(&track->s_lock, flags);
682 track->p_state = SUBSYS_RESTARTING;
683 spin_unlock_irqrestore(&track->s_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700684
685 /* Collect ram dumps for all subsystems in order here */
Stephen Boyd0daae152012-05-01 17:36:15 -0700686 for_each_subsys_device(list, count, NULL, subsystem_ramdump);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700687
Seemanta Duttabac64552012-12-14 15:47:27 -0800688 notify_each_subsys_device(list, count, SUBSYS_BEFORE_POWERUP, NULL);
Stephen Boyd0daae152012-05-01 17:36:15 -0700689 for_each_subsys_device(list, count, NULL, subsystem_powerup);
Seemanta Duttabac64552012-12-14 15:47:27 -0800690 notify_each_subsys_device(list, count, SUBSYS_AFTER_POWERUP, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700691
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700692 pr_info("[%p]: Restart sequence for %s completed.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700693 current, desc->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700694
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700695 mutex_unlock(&soc_order_reg_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700696 mutex_unlock(&track->lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700697
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700698 spin_lock_irqsave(&track->s_lock, flags);
699 track->p_state = SUBSYS_NORMAL;
Stephen Boyd6b567182012-09-18 11:31:02 -0700700 wake_unlock(&dev->wake_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700701 spin_unlock_irqrestore(&track->s_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700702}
703
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700704static void __subsystem_restart_dev(struct subsys_device *dev)
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700705{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700706 struct subsys_desc *desc = dev->desc;
Stephen Boyd6b567182012-09-18 11:31:02 -0700707 const char *name = dev->desc->name;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700708 struct subsys_tracking *track;
Stephen Boyd497ef402012-06-21 12:54:40 -0700709 unsigned long flags;
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700710
Stephen Boydfcb52a32012-12-03 12:35:14 -0800711 pr_debug("Restarting %s [level=%s]!\n", desc->name,
712 restart_levels[dev->restart_level]);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700713
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700714 track = subsys_get_track(dev);
Stephen Boyd6b567182012-09-18 11:31:02 -0700715 /*
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700716 * Allow drivers to call subsystem_restart{_dev}() as many times as
717 * they want up until the point where the subsystem is shutdown.
Stephen Boyd6b567182012-09-18 11:31:02 -0700718 */
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700719 spin_lock_irqsave(&track->s_lock, flags);
720 if (track->p_state != SUBSYS_CRASHED) {
721 if (dev->track.state == SUBSYS_ONLINE &&
722 track->p_state != SUBSYS_RESTARTING) {
723 track->p_state = SUBSYS_CRASHED;
Stephen Boyd6b567182012-09-18 11:31:02 -0700724 wake_lock(&dev->wake_lock);
725 queue_work(ssr_wq, &dev->work);
726 } else {
727 panic("Subsystem %s crashed during SSR!", name);
728 }
Stephen Boyd77c22742012-07-24 18:46:45 -0700729 }
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700730 spin_unlock_irqrestore(&track->s_lock, flags);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700731}
732
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700733int subsystem_restart_dev(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700734{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700735 const char *name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700736
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700737 if (!get_device(&dev->dev))
738 return -ENODEV;
739
740 if (!try_module_get(dev->owner)) {
741 put_device(&dev->dev);
742 return -ENODEV;
743 }
744
745 name = dev->desc->name;
Vikram Mulukutla3d58c2f2012-10-29 18:43:02 -0700746
747 /*
748 * If a system reboot/shutdown is underway, ignore subsystem errors.
749 * However, print a message so that we know that a subsystem behaved
750 * unexpectedly here.
751 */
752 if (system_state == SYSTEM_RESTART
753 || system_state == SYSTEM_POWER_OFF) {
754 pr_err("%s crashed during a system poweroff/shutdown.\n", name);
755 return -EBUSY;
756 }
757
Stephen Boydfcb52a32012-12-03 12:35:14 -0800758 pr_info("Restart sequence requested for %s, restart_level = %s.\n",
759 name, restart_levels[dev->restart_level]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700760
Stephen Boydfcb52a32012-12-03 12:35:14 -0800761 switch (dev->restart_level) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700762
763 case RESET_SUBSYS_COUPLED:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700764 __subsystem_restart_dev(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700765 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700766 case RESET_SOC:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700767 panic("subsys-restart: Resetting the SoC - %s crashed.", name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700768 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700769 default:
770 panic("subsys-restart: Unknown restart level!\n");
Stephen Boydc68ee642012-05-01 17:02:45 -0700771 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700772 }
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700773 module_put(dev->owner);
774 put_device(&dev->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700775
776 return 0;
777}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700778EXPORT_SYMBOL(subsystem_restart_dev);
779
780int subsystem_restart(const char *name)
781{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700782 int ret;
783 struct subsys_device *dev = find_subsys(name);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700784
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700785 if (!dev)
786 return -ENODEV;
787
788 ret = subsystem_restart_dev(dev);
789 put_device(&dev->dev);
790 return ret;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700791}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700792EXPORT_SYMBOL(subsystem_restart);
793
Stephen Boyd4057ec02012-12-12 17:38:38 -0800794int subsystem_crashed(const char *name)
795{
796 struct subsys_device *dev = find_subsys(name);
797 struct subsys_tracking *track;
798
799 if (!dev)
800 return -ENODEV;
801
802 if (!get_device(&dev->dev))
803 return -ENODEV;
804
805 track = subsys_get_track(dev);
806
807 mutex_lock(&track->lock);
808 dev->do_ramdump_on_put = true;
809 /*
810 * TODO: Make this work with multiple consumers where one is calling
811 * subsystem_restart() and another is calling this function. To do
812 * so would require updating private state, etc.
813 */
814 mutex_unlock(&track->lock);
815
816 put_device(&dev->dev);
817 return 0;
818}
819EXPORT_SYMBOL(subsystem_crashed);
820
Seemanta Dutta0adbbf02013-03-12 17:26:17 -0700821void subsys_set_crash_status(struct subsys_device *dev, bool crashed)
822{
823 dev->crashed = true;
824}
825
826bool subsys_get_crash_status(struct subsys_device *dev)
827{
828 return dev->crashed;
829}
Stephen Boydeff41a62012-07-06 13:19:02 -0700830#ifdef CONFIG_DEBUG_FS
831static ssize_t subsys_debugfs_read(struct file *filp, char __user *ubuf,
832 size_t cnt, loff_t *ppos)
833{
834 int r;
835 char buf[40];
836 struct subsys_device *subsys = filp->private_data;
837
838 r = snprintf(buf, sizeof(buf), "%d\n", subsys->count);
839 return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
840}
841
842static ssize_t subsys_debugfs_write(struct file *filp,
843 const char __user *ubuf, size_t cnt, loff_t *ppos)
844{
845 struct subsys_device *subsys = filp->private_data;
846 char buf[10];
847 char *cmp;
848
849 cnt = min(cnt, sizeof(buf) - 1);
850 if (copy_from_user(&buf, ubuf, cnt))
851 return -EFAULT;
852 buf[cnt] = '\0';
853 cmp = strstrip(buf);
854
855 if (!strcmp(cmp, "restart")) {
856 if (subsystem_restart_dev(subsys))
857 return -EIO;
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700858 } else if (!strcmp(cmp, "get")) {
859 if (subsystem_get(subsys->desc->name))
860 return -EIO;
861 } else if (!strcmp(cmp, "put")) {
862 subsystem_put(subsys);
Stephen Boydeff41a62012-07-06 13:19:02 -0700863 } else {
864 return -EINVAL;
865 }
866
867 return cnt;
868}
869
870static const struct file_operations subsys_debugfs_fops = {
871 .open = simple_open,
872 .read = subsys_debugfs_read,
873 .write = subsys_debugfs_write,
874};
875
876static struct dentry *subsys_base_dir;
877
878static int __init subsys_debugfs_init(void)
879{
880 subsys_base_dir = debugfs_create_dir("msm_subsys", NULL);
881 return !subsys_base_dir ? -ENOMEM : 0;
882}
883
884static void subsys_debugfs_exit(void)
885{
886 debugfs_remove_recursive(subsys_base_dir);
887}
888
889static int subsys_debugfs_add(struct subsys_device *subsys)
890{
891 if (!subsys_base_dir)
892 return -ENOMEM;
893
894 subsys->dentry = debugfs_create_file(subsys->desc->name,
895 S_IRUGO | S_IWUSR, subsys_base_dir,
896 subsys, &subsys_debugfs_fops);
897 return !subsys->dentry ? -ENOMEM : 0;
898}
899
900static void subsys_debugfs_remove(struct subsys_device *subsys)
901{
902 debugfs_remove(subsys->dentry);
903}
904#else
905static int __init subsys_debugfs_init(void) { return 0; };
906static void subsys_debugfs_exit(void) { }
907static int subsys_debugfs_add(struct subsys_device *subsys) { return 0; }
908static void subsys_debugfs_remove(struct subsys_device *subsys) { }
909#endif
910
Seemanta Duttaf055ac72013-01-10 18:08:04 -0800911static int subsys_device_open(struct inode *inode, struct file *file)
912{
913 void *retval;
914 struct subsys_device *subsys_dev = container_of(file->private_data,
915 struct subsys_device, misc_dev);
916
917 if (!file->private_data)
918 return -EINVAL;
919
920 retval = subsystem_get(subsys_dev->desc->name);
921 if (IS_ERR(retval))
922 return PTR_ERR(retval);
923
924 return 0;
925}
926
927static int subsys_device_close(struct inode *inode, struct file *file)
928{
929 struct subsys_device *subsys_dev = container_of(file->private_data,
930 struct subsys_device, misc_dev);
931
932 if (!file->private_data)
933 return -EINVAL;
934
935 subsystem_put(subsys_dev);
936
937 return 0;
938}
939
940static const struct file_operations subsys_device_fops = {
941 .owner = THIS_MODULE,
942 .open = subsys_device_open,
943 .release = subsys_device_close,
944};
945
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700946static void subsys_device_release(struct device *dev)
947{
948 struct subsys_device *subsys = to_subsys(dev);
949
950 wake_lock_destroy(&subsys->wake_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700951 mutex_destroy(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700952 ida_simple_remove(&subsys_ida, subsys->id);
953 kfree(subsys);
954}
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800955static irqreturn_t subsys_err_ready_intr_handler(int irq, void *subsys)
956{
957 struct subsys_device *subsys_dev = subsys;
Seemanta Duttaf516a082013-06-06 18:56:52 -0700958 dev_info(subsys_dev->desc->dev,
959 "Subsystem error monitoring/handling services are up\n");
Seemanta Duttaa52bda62013-05-10 17:17:56 -0700960
961 if (subsys_dev->desc->is_not_loadable)
962 return IRQ_HANDLED;
963
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800964 complete(&subsys_dev->err_ready);
965 return IRQ_HANDLED;
966}
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700967
Seemanta Duttaf055ac72013-01-10 18:08:04 -0800968static int subsys_misc_device_add(struct subsys_device *subsys_dev)
969{
970 int ret;
971 memset(subsys_dev->miscdevice_name, 0,
972 ARRAY_SIZE(subsys_dev->miscdevice_name));
973 snprintf(subsys_dev->miscdevice_name,
974 ARRAY_SIZE(subsys_dev->miscdevice_name), "subsys_%s",
975 subsys_dev->desc->name);
976
977 subsys_dev->misc_dev.minor = MISC_DYNAMIC_MINOR;
978 subsys_dev->misc_dev.name = subsys_dev->miscdevice_name;
979 subsys_dev->misc_dev.fops = &subsys_device_fops;
980 subsys_dev->misc_dev.parent = &subsys_dev->dev;
981
982 ret = misc_register(&subsys_dev->misc_dev);
983 if (ret) {
984 pr_err("%s: misc_register() failed for %s (%d)", __func__,
985 subsys_dev->miscdevice_name, ret);
986 }
987 return ret;
988}
989
990static void subsys_misc_device_remove(struct subsys_device *subsys_dev)
991{
992 misc_deregister(&subsys_dev->misc_dev);
993}
994
Seemanta Dutta076d00e2013-06-17 17:58:18 -0700995static int __get_gpio(struct subsys_desc *desc, const char *prop,
996 int *gpio)
997{
998 struct device_node *dnode = desc->dev->of_node;
999 int ret = -ENOENT;
1000
1001 if (of_find_property(dnode, prop, NULL)) {
1002 *gpio = of_get_named_gpio(dnode, prop, 0);
1003 ret = *gpio < 0 ? *gpio : 0;
1004 }
1005
1006 return ret;
1007}
1008
1009static int __get_irq(struct subsys_desc *desc, const char *prop,
1010 unsigned int *irq)
1011{
1012 int ret, gpio, irql;
1013
1014 ret = __get_gpio(desc, prop, &gpio);
1015 if (ret)
1016 return ret;
1017
1018 irql = gpio_to_irq(gpio);
1019
1020 if (irql == -ENOENT)
1021 irql = -ENXIO;
1022
1023 if (irql < 0) {
1024 pr_err("[%s]: Error getting IRQ \"%s\"\n", desc->name,
1025 prop);
1026 return irql;
1027 } else {
1028 *irq = irql;
1029 }
1030
1031 return 0;
1032}
1033
1034static int subsys_parse_devicetree(struct subsys_desc *desc)
1035{
1036 int ret;
1037 struct platform_device *pdev = container_of(desc->dev,
1038 struct platform_device, dev);
1039
1040 ret = __get_irq(desc, "qcom,gpio-err-fatal", &desc->err_fatal_irq);
1041 if (ret && ret != -ENOENT)
1042 return ret;
1043
1044 ret = __get_irq(desc, "qcom,gpio-err-ready", &desc->err_ready_irq);
1045 if (ret && ret != -ENOENT)
1046 return ret;
1047
1048 ret = __get_irq(desc, "qcom,gpio-stop-ack", &desc->stop_ack_irq);
1049 if (ret && ret != -ENOENT)
1050 return ret;
1051
1052 ret = __get_gpio(desc, "qcom,gpio-force-stop", &desc->force_stop_gpio);
1053 if (ret && ret != -ENOENT)
1054 return ret;
1055
1056 desc->wdog_bite_irq = platform_get_irq(pdev, 0);
1057 if (desc->wdog_bite_irq < 0)
1058 return desc->wdog_bite_irq;
1059
1060 return 0;
1061}
1062
1063static int subsys_setup_irqs(struct subsys_device *subsys)
1064{
1065 struct subsys_desc *desc = subsys->desc;
1066 int ret;
1067
1068 if (desc->err_fatal_irq && desc->err_fatal_handler) {
1069 ret = devm_request_irq(desc->dev, desc->err_fatal_irq,
1070 desc->err_fatal_handler,
1071 IRQF_TRIGGER_RISING, desc->name, desc);
1072 if (ret < 0) {
1073 dev_err(desc->dev, "[%s]: Unable to register error fatal IRQ handler!: %d\n",
1074 desc->name, ret);
1075 return ret;
1076 }
1077 }
1078
1079 if (desc->stop_ack_irq && desc->stop_ack_handler) {
1080 ret = devm_request_irq(desc->dev, desc->stop_ack_irq,
1081 desc->stop_ack_handler,
1082 IRQF_TRIGGER_RISING, desc->name, desc);
1083 if (ret < 0) {
1084 dev_err(desc->dev, "[%s]: Unable to register stop ack handler!: %d\n",
1085 desc->name, ret);
1086 return ret;
1087 }
1088 }
1089
1090 if (desc->wdog_bite_irq && desc->wdog_bite_handler) {
1091 ret = devm_request_irq(desc->dev, desc->wdog_bite_irq,
1092 desc->wdog_bite_handler,
1093 IRQF_TRIGGER_RISING, desc->name, desc);
1094 if (ret < 0) {
1095 dev_err(desc->dev, "[%s]: Unable to register wdog bite handler!: %d\n",
1096 desc->name, ret);
1097 return ret;
1098 }
1099 }
1100
1101 if (desc->err_ready_irq) {
1102 ret = devm_request_irq(desc->dev,
1103 desc->err_ready_irq,
1104 subsys_err_ready_intr_handler,
1105 IRQF_TRIGGER_RISING,
1106 "error_ready_interrupt", subsys);
1107 if (ret < 0) {
1108 dev_err(desc->dev,
1109 "[%s]: Unable to register err ready handler\n",
1110 desc->name);
1111 return ret;
1112 }
1113 }
1114
1115 return 0;
1116}
1117
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001118struct subsys_device *subsys_register(struct subsys_desc *desc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001119{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001120 struct subsys_device *subsys;
1121 int ret;
Vikram Mulukutla705e6892012-06-08 17:57:31 -07001122
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001123 subsys = kzalloc(sizeof(*subsys), GFP_KERNEL);
1124 if (!subsys)
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001125 return ERR_PTR(-ENOMEM);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001126
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001127 subsys->desc = desc;
1128 subsys->owner = desc->owner;
1129 subsys->dev.parent = desc->dev;
1130 subsys->dev.bus = &subsys_bus_type;
1131 subsys->dev.release = subsys_device_release;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001132
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001133 subsys->notify = subsys_notif_add_subsys(desc->name);
1134 subsys->restart_order = update_restart_order(subsys);
Seemanta Dutta076d00e2013-06-17 17:58:18 -07001135 ret = subsys_parse_devicetree(desc);
1136 if (ret)
1137 goto err_dtree;
Stephen Boyd497ef402012-06-21 12:54:40 -07001138
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001139 snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name);
1140 wake_lock_init(&subsys->wake_lock, WAKE_LOCK_SUSPEND, subsys->wlname);
1141 INIT_WORK(&subsys->work, subsystem_restart_wq_func);
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001142 spin_lock_init(&subsys->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001143
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001144 subsys->id = ida_simple_get(&subsys_ida, 0, 0, GFP_KERNEL);
1145 if (subsys->id < 0) {
1146 ret = subsys->id;
1147 goto err_ida;
1148 }
1149 dev_set_name(&subsys->dev, "subsys%d", subsys->id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001150
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001151 mutex_init(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001152
Stephen Boydeff41a62012-07-06 13:19:02 -07001153 ret = subsys_debugfs_add(subsys);
1154 if (ret)
1155 goto err_debugfs;
1156
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001157 ret = device_register(&subsys->dev);
1158 if (ret) {
Seemanta Duttaf055ac72013-01-10 18:08:04 -08001159 device_unregister(&subsys->dev);
1160 goto err_register;
1161 }
1162
1163 ret = subsys_misc_device_add(subsys);
1164 if (ret) {
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001165 put_device(&subsys->dev);
1166 goto err_register;
1167 }
Stephen Boydeff41a62012-07-06 13:19:02 -07001168
Seemanta Dutta076d00e2013-06-17 17:58:18 -07001169 ret = subsys_setup_irqs(subsys);
1170 if (ret < 0)
1171 goto err_misc_device;
Seemanta Duttad32f92a2013-01-25 14:22:15 -08001172
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001173 return subsys;
1174
Seemanta Duttad32f92a2013-01-25 14:22:15 -08001175err_misc_device:
1176 subsys_misc_device_remove(subsys);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001177err_register:
Stephen Boydeff41a62012-07-06 13:19:02 -07001178 subsys_debugfs_remove(subsys);
1179err_debugfs:
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001180 mutex_destroy(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001181 ida_simple_remove(&subsys_ida, subsys->id);
1182err_ida:
1183 wake_lock_destroy(&subsys->wake_lock);
Seemanta Dutta076d00e2013-06-17 17:58:18 -07001184err_dtree:
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001185 kfree(subsys);
1186 return ERR_PTR(ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001187}
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001188EXPORT_SYMBOL(subsys_register);
1189
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001190void subsys_unregister(struct subsys_device *subsys)
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001191{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001192 if (IS_ERR_OR_NULL(subsys))
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001193 return;
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001194
1195 if (get_device(&subsys->dev)) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001196 mutex_lock(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001197 WARN_ON(subsys->count);
1198 device_unregister(&subsys->dev);
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001199 mutex_unlock(&subsys->track.lock);
Stephen Boydeff41a62012-07-06 13:19:02 -07001200 subsys_debugfs_remove(subsys);
Seemanta Duttaf055ac72013-01-10 18:08:04 -08001201 subsys_misc_device_remove(subsys);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001202 put_device(&subsys->dev);
1203 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001204}
1205EXPORT_SYMBOL(subsys_unregister);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001206
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001207static int subsys_panic(struct device *dev, void *data)
1208{
1209 struct subsys_device *subsys = to_subsys(dev);
1210
1211 if (subsys->desc->crash_shutdown)
1212 subsys->desc->crash_shutdown(subsys->desc);
1213 return 0;
1214}
1215
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001216static int ssr_panic_handler(struct notifier_block *this,
1217 unsigned long event, void *ptr)
1218{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001219 bus_for_each_dev(&subsys_bus_type, NULL, NULL, subsys_panic);
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001220 return NOTIFY_DONE;
1221}
1222
1223static struct notifier_block panic_nb = {
1224 .notifier_call = ssr_panic_handler,
1225};
1226
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001227static int __init ssr_init_soc_restart_orders(void)
1228{
1229 int i;
1230
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001231 atomic_notifier_chain_register(&panic_notifier_list,
1232 &panic_nb);
1233
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001234 if (cpu_is_msm8x60()) {
1235 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001236 mutex_init(&orders_8x60_all[i]->track.lock);
1237 spin_lock_init(&orders_8x60_all[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001238 }
1239
1240 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001241 mutex_init(&orders_8x60_modems[i]->track.lock);
1242 spin_lock_init(&orders_8x60_modems[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001243 }
1244
1245 restart_orders = orders_8x60_all;
1246 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
1247 }
1248
Vikram Mulukutlae4ae75e2012-07-27 15:14:10 -07001249 if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) {
1250 restart_orders = restart_orders_8960_sglte;
1251 n_restart_orders = ARRAY_SIZE(restart_orders_8960_sglte);
1252 }
1253
1254 for (i = 0; i < n_restart_orders; i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001255 mutex_init(&restart_orders[i]->track.lock);
1256 spin_lock_init(&restart_orders[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001257 }
1258
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001259 return 0;
1260}
1261
1262static int __init subsys_restart_init(void)
1263{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001264 int ret;
Vikram Mulukutla69177e12012-03-21 20:51:43 -07001265
Vikram Mulukutla8d4c3f02012-09-10 19:25:32 -07001266 ssr_wq = alloc_workqueue("ssr_wq", WQ_CPU_INTENSIVE, 0);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001267 BUG_ON(!ssr_wq);
1268
1269 ret = bus_register(&subsys_bus_type);
1270 if (ret)
1271 goto err_bus;
Stephen Boydeff41a62012-07-06 13:19:02 -07001272 ret = subsys_debugfs_init();
1273 if (ret)
1274 goto err_debugfs;
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001275 ret = ssr_init_soc_restart_orders();
1276 if (ret)
1277 goto err_soc;
1278 return 0;
Stephen Boydeff41a62012-07-06 13:19:02 -07001279
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001280err_soc:
Stephen Boydeff41a62012-07-06 13:19:02 -07001281 subsys_debugfs_exit();
1282err_debugfs:
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001283 bus_unregister(&subsys_bus_type);
1284err_bus:
1285 destroy_workqueue(ssr_wq);
1286 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001287}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001288arch_initcall(subsys_restart_init);
1289
1290MODULE_DESCRIPTION("Subsystem Restart Driver");
1291MODULE_LICENSE("GPL v2");