blob: b9566496c105a3a5560ab8b015005bd5519eaa38 [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
Seemanta Dutta6222f392013-05-10 18:17:38 -070044static int enable_debug;
45module_param(enable_debug, int, S_IRUGO | S_IWUSR);
46
Stephen Boyd5bd20e42012-10-03 12:28:43 -070047/**
48 * enum p_subsys_state - state of a subsystem (private)
49 * @SUBSYS_NORMAL: subsystem is operating normally
50 * @SUBSYS_CRASHED: subsystem has crashed and hasn't been shutdown
51 * @SUBSYS_RESTARTING: subsystem has been shutdown and is now restarting
52 *
53 * The 'private' side of the subsytem state used to determine where in the
54 * restart process the subsystem is.
55 */
56enum p_subsys_state {
57 SUBSYS_NORMAL,
58 SUBSYS_CRASHED,
59 SUBSYS_RESTARTING,
60};
61
62/**
63 * enum subsys_state - state of a subsystem (public)
64 * @SUBSYS_OFFLINE: subsystem is offline
65 * @SUBSYS_ONLINE: subsystem is online
66 *
67 * The 'public' side of the subsytem state, exposed to userspace.
68 */
69enum subsys_state {
70 SUBSYS_OFFLINE,
71 SUBSYS_ONLINE,
72};
73
74static const char * const subsys_states[] = {
75 [SUBSYS_OFFLINE] = "OFFLINE",
76 [SUBSYS_ONLINE] = "ONLINE",
77};
78
Stephen Boydfcb52a32012-12-03 12:35:14 -080079static const char * const restart_levels[] = {
80 [RESET_SOC] = "SYSTEM",
81 [RESET_SUBSYS_COUPLED] = "RELATED",
82};
83
Stephen Boyd5bd20e42012-10-03 12:28:43 -070084/**
85 * struct subsys_tracking - track state of a subsystem or restart order
86 * @p_state: private state of subsystem/order
87 * @state: public state of subsystem/order
88 * @s_lock: protects p_state
89 * @lock: protects subsystem/order callbacks and state
90 *
91 * Tracks the state of a subsystem or a set of subsystems (restart order).
92 * Doing this avoids the need to grab each subsystem's lock and update
93 * each subsystems state when restarting an order.
94 */
95struct subsys_tracking {
96 enum p_subsys_state p_state;
97 spinlock_t s_lock;
98 enum subsys_state state;
99 struct mutex lock;
100};
101
102/**
103 * struct subsys_soc_restart_order - subsystem restart order
104 * @subsystem_list: names of subsystems in this restart order
105 * @count: number of subsystems in order
106 * @track: state tracking and locking
107 * @subsys_ptrs: pointers to subsystems in this restart order
108 */
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109struct subsys_soc_restart_order {
110 const char * const *subsystem_list;
111 int count;
112
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700113 struct subsys_tracking track;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700114 struct subsys_device *subsys_ptrs[];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700115};
116
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700117struct restart_log {
118 struct timeval time;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700119 struct subsys_device *dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700120 struct list_head list;
121};
122
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700123/**
124 * struct subsys_device - subsystem device
125 * @desc: subsystem descriptor
126 * @wake_lock: prevents suspend during subsystem_restart()
127 * @wlname: name of @wake_lock
128 * @work: context for subsystem_restart_wq_func() for this device
129 * @track: state tracking and locking
130 * @notify: subsys notify handle
131 * @dev: device
132 * @owner: module that provides @desc
133 * @count: reference count of subsystem_get()/subsystem_put()
134 * @id: ida
Stephen Boydfcb52a32012-12-03 12:35:14 -0800135 * @restart_level: restart level (0 - panic, 1 - related, 2 - independent, etc.)
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700136 * @restart_order: order of other devices this devices restarts with
137 * @dentry: debugfs directory for this device
Stephen Boyd4057ec02012-12-12 17:38:38 -0800138 * @do_ramdump_on_put: ramdump on subsystem_put() if true
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800139 * @err_ready: completion variable to record error ready from subsystem
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700140 */
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700141struct subsys_device {
142 struct subsys_desc *desc;
Stephen Boyd497ef402012-06-21 12:54:40 -0700143 struct wake_lock wake_lock;
144 char wlname[64];
145 struct work_struct work;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700146
147 struct subsys_tracking track;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700148
149 void *notify;
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700150 struct device dev;
151 struct module *owner;
152 int count;
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700153 int id;
Stephen Boydfcb52a32012-12-03 12:35:14 -0800154 int restart_level;
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700155 struct subsys_soc_restart_order *restart_order;
Stephen Boydeff41a62012-07-06 13:19:02 -0700156#ifdef CONFIG_DEBUG_FS
157 struct dentry *dentry;
158#endif
Stephen Boyd4057ec02012-12-12 17:38:38 -0800159 bool do_ramdump_on_put;
Seemanta Duttaf055ac72013-01-10 18:08:04 -0800160 struct miscdevice misc_dev;
161 char miscdevice_name[32];
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800162 struct completion err_ready;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700163};
164
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700165static struct subsys_device *to_subsys(struct device *d)
166{
167 return container_of(d, struct subsys_device, dev);
168}
169
170static ssize_t name_show(struct device *dev, struct device_attribute *attr,
171 char *buf)
172{
173 return snprintf(buf, PAGE_SIZE, "%s\n", to_subsys(dev)->desc->name);
174}
175
176static ssize_t state_show(struct device *dev, struct device_attribute *attr,
177 char *buf)
178{
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700179 enum subsys_state state = to_subsys(dev)->track.state;
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700180 return snprintf(buf, PAGE_SIZE, "%s\n", subsys_states[state]);
181}
182
Stephen Boydfcb52a32012-12-03 12:35:14 -0800183static ssize_t
184restart_level_show(struct device *dev, struct device_attribute *attr, char *buf)
185{
186 int level = to_subsys(dev)->restart_level;
187 return snprintf(buf, PAGE_SIZE, "%s\n", restart_levels[level]);
188}
189
190static ssize_t restart_level_store(struct device *dev,
191 struct device_attribute *attr, const char *buf, size_t count)
192{
193 struct subsys_device *subsys = to_subsys(dev);
194 int i;
195 const char *p;
196
197 p = memchr(buf, '\n', count);
198 if (p)
199 count = p - buf;
200
201 for (i = 0; i < ARRAY_SIZE(restart_levels); i++)
202 if (!strncasecmp(buf, restart_levels[i], count)) {
203 subsys->restart_level = i;
204 return count;
205 }
206 return -EPERM;
207}
208
209int subsys_get_restart_level(struct subsys_device *dev)
210{
211 return dev->restart_level;
212}
213EXPORT_SYMBOL(subsys_get_restart_level);
214
Stephen Boyd6b567182012-09-18 11:31:02 -0700215static void subsys_set_state(struct subsys_device *subsys,
216 enum subsys_state state)
217{
218 unsigned long flags;
219
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700220 spin_lock_irqsave(&subsys->track.s_lock, flags);
221 if (subsys->track.state != state) {
222 subsys->track.state = state;
223 spin_unlock_irqrestore(&subsys->track.s_lock, flags);
Stephen Boyd6b567182012-09-18 11:31:02 -0700224 sysfs_notify(&subsys->dev.kobj, NULL, "state");
225 return;
226 }
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700227 spin_unlock_irqrestore(&subsys->track.s_lock, flags);
Stephen Boyd6b567182012-09-18 11:31:02 -0700228}
229
Stephen Boyd43b380a2012-09-21 17:34:24 -0700230/**
231 * subsytem_default_online() - Mark a subsystem as online by default
232 * @dev: subsystem to mark as online
233 *
234 * Marks a subsystem as "online" without increasing the reference count
235 * on the subsystem. This is typically used by subsystems that are already
236 * online when the kernel boots up.
237 */
238void subsys_default_online(struct subsys_device *dev)
239{
240 subsys_set_state(dev, SUBSYS_ONLINE);
241}
242EXPORT_SYMBOL(subsys_default_online);
243
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700244static struct device_attribute subsys_attrs[] = {
245 __ATTR_RO(name),
246 __ATTR_RO(state),
Stephen Boydfcb52a32012-12-03 12:35:14 -0800247 __ATTR(restart_level, 0644, restart_level_show, restart_level_store),
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700248 __ATTR_NULL,
249};
250
251static struct bus_type subsys_bus_type = {
252 .name = "msm_subsys",
253 .dev_attrs = subsys_attrs,
254};
255
256static DEFINE_IDA(subsys_ida);
257
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700258static int enable_ramdumps;
Stephen Boydc68ee642012-05-01 17:02:45 -0700259module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR);
260
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700261struct workqueue_struct *ssr_wq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700262
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700263static LIST_HEAD(restart_log_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700264static DEFINE_MUTEX(soc_order_reg_lock);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700265static DEFINE_MUTEX(restart_log_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700266
267/* SOC specific restart orders go here */
268
269#define DEFINE_SINGLE_RESTART_ORDER(name, order) \
270 static struct subsys_soc_restart_order __##name = { \
271 .subsystem_list = order, \
272 .count = ARRAY_SIZE(order), \
273 .subsys_ptrs = {[ARRAY_SIZE(order)] = NULL} \
274 }; \
275 static struct subsys_soc_restart_order *name[] = { \
276 &__##name, \
277 }
278
279/* MSM 8x60 restart ordering info */
280static const char * const _order_8x60_all[] = {
Stephen Boyd77db8bb2012-06-27 15:15:16 -0700281 "external_modem", "modem", "adsp"
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700282};
283DEFINE_SINGLE_RESTART_ORDER(orders_8x60_all, _order_8x60_all);
284
285static const char * const _order_8x60_modems[] = {"external_modem", "modem"};
286DEFINE_SINGLE_RESTART_ORDER(orders_8x60_modems, _order_8x60_modems);
287
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700288/*SGLTE restart ordering info*/
289static const char * const order_8960_sglte[] = {"external_modem",
290 "modem"};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700291
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700292static struct subsys_soc_restart_order restart_orders_8960_fusion_sglte = {
293 .subsystem_list = order_8960_sglte,
294 .count = ARRAY_SIZE(order_8960_sglte),
295 .subsys_ptrs = {[ARRAY_SIZE(order_8960_sglte)] = NULL}
296 };
297
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700298static struct subsys_soc_restart_order *restart_orders_8960_sglte[] = {
299 &restart_orders_8960_fusion_sglte,
300 };
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700301
302/* These will be assigned to one of the sets above after
303 * runtime SoC identification.
304 */
305static struct subsys_soc_restart_order **restart_orders;
306static int n_restart_orders;
307
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700308static struct subsys_soc_restart_order *
309update_restart_order(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700310{
311 int i, j;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700312 struct subsys_soc_restart_order *order;
313 const char *name = dev->desc->name;
314 int len = SUBSYS_NAME_MAX_LENGTH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700315
316 mutex_lock(&soc_order_reg_lock);
317 for (j = 0; j < n_restart_orders; j++) {
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700318 order = restart_orders[j];
319 for (i = 0; i < order->count; i++) {
320 if (!strncmp(order->subsystem_list[i], name, len)) {
321 order->subsys_ptrs[i] = dev;
322 goto found;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700323 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700324 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700325 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700326 order = NULL;
327found:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700328 mutex_unlock(&soc_order_reg_lock);
329
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700330 return order;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700331}
332
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700333static int max_restarts;
334module_param(max_restarts, int, 0644);
335
336static long max_history_time = 3600;
337module_param(max_history_time, long, 0644);
338
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700339static void do_epoch_check(struct subsys_device *dev)
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700340{
341 int n = 0;
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600342 struct timeval *time_first = NULL, *curr_time;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700343 struct restart_log *r_log, *temp;
344 static int max_restarts_check;
345 static long max_history_time_check;
346
347 mutex_lock(&restart_log_mutex);
348
349 max_restarts_check = max_restarts;
350 max_history_time_check = max_history_time;
351
352 /* Check if epoch checking is enabled */
353 if (!max_restarts_check)
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700354 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700355
356 r_log = kmalloc(sizeof(struct restart_log), GFP_KERNEL);
Matt Wagantalleecca342011-11-10 20:12:05 -0800357 if (!r_log)
358 goto out;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700359 r_log->dev = dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700360 do_gettimeofday(&r_log->time);
361 curr_time = &r_log->time;
362 INIT_LIST_HEAD(&r_log->list);
363
364 list_add_tail(&r_log->list, &restart_log_list);
365
366 list_for_each_entry_safe(r_log, temp, &restart_log_list, list) {
367
368 if ((curr_time->tv_sec - r_log->time.tv_sec) >
369 max_history_time_check) {
370
371 pr_debug("Deleted node with restart_time = %ld\n",
372 r_log->time.tv_sec);
373 list_del(&r_log->list);
374 kfree(r_log);
375 continue;
376 }
377 if (!n) {
378 time_first = &r_log->time;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700379 pr_debug("Time_first: %ld\n", time_first->tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700380 }
381 n++;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700382 pr_debug("Restart_time: %ld\n", r_log->time.tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700383 }
384
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600385 if (time_first && n >= max_restarts_check) {
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700386 if ((curr_time->tv_sec - time_first->tv_sec) <
387 max_history_time_check)
388 panic("Subsystems have crashed %d times in less than "
389 "%ld seconds!", max_restarts_check,
390 max_history_time_check);
391 }
392
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700393out:
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700394 mutex_unlock(&restart_log_mutex);
395}
396
Stephen Boyd0daae152012-05-01 17:36:15 -0700397static void for_each_subsys_device(struct subsys_device **list, unsigned count,
398 void *data, void (*fn)(struct subsys_device *, void *))
399{
400 while (count--) {
401 struct subsys_device *dev = *list++;
402 if (!dev)
403 continue;
404 fn(dev, data);
405 }
406}
407
Seemanta Duttabac64552012-12-14 15:47:27 -0800408static void notify_each_subsys_device(struct subsys_device **list,
409 unsigned count,
410 enum subsys_notif_type notif, void *data)
Stephen Boyd0daae152012-05-01 17:36:15 -0700411{
Seemanta Duttabac64552012-12-14 15:47:27 -0800412 while (count--) {
413 enum subsys_notif_type type = (enum subsys_notif_type)type;
414 struct subsys_device *dev = *list++;
415 if (!dev)
416 continue;
417 subsys_notif_queue_notification(dev->notify, notif, data);
418 }
Stephen Boyd0daae152012-05-01 17:36:15 -0700419}
420
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800421static int wait_for_err_ready(struct subsys_device *subsys)
422{
423 int ret;
424
Seemanta Dutta6222f392013-05-10 18:17:38 -0700425 if (!subsys->desc->err_ready_irq || enable_debug == 1)
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800426 return 0;
427
428 ret = wait_for_completion_timeout(&subsys->err_ready,
429 msecs_to_jiffies(10000));
430 if (!ret)
431 return -ETIMEDOUT;
432
433 return 0;
434}
435
Stephen Boyd0daae152012-05-01 17:36:15 -0700436static void subsystem_shutdown(struct subsys_device *dev, void *data)
437{
438 const char *name = dev->desc->name;
439
440 pr_info("[%p]: Shutting down %s\n", current, name);
441 if (dev->desc->shutdown(dev->desc) < 0)
442 panic("subsys-restart: [%p]: Failed to shutdown %s!",
443 current, name);
Stephen Boyd6b567182012-09-18 11:31:02 -0700444 subsys_set_state(dev, SUBSYS_OFFLINE);
Stephen Boyd0daae152012-05-01 17:36:15 -0700445}
446
447static void subsystem_ramdump(struct subsys_device *dev, void *data)
448{
449 const char *name = dev->desc->name;
450
451 if (dev->desc->ramdump)
452 if (dev->desc->ramdump(enable_ramdumps, dev->desc) < 0)
453 pr_warn("%s[%p]: Ramdump failed.\n", name, current);
Stephen Boyd4057ec02012-12-12 17:38:38 -0800454 dev->do_ramdump_on_put = false;
Stephen Boyd0daae152012-05-01 17:36:15 -0700455}
456
457static void subsystem_powerup(struct subsys_device *dev, void *data)
458{
459 const char *name = dev->desc->name;
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800460 int ret;
Stephen Boyd0daae152012-05-01 17:36:15 -0700461
462 pr_info("[%p]: Powering up %s\n", current, name);
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800463 init_completion(&dev->err_ready);
Stephen Boyd0daae152012-05-01 17:36:15 -0700464 if (dev->desc->powerup(dev->desc) < 0)
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800465 panic("[%p]: Powerup error: %s!", current, name);
466
467 ret = wait_for_err_ready(dev);
468 if (ret)
469 panic("[%p]: Timed out waiting for error ready: %s!",
470 current, name);
Stephen Boyd6b567182012-09-18 11:31:02 -0700471 subsys_set_state(dev, SUBSYS_ONLINE);
Stephen Boyd0daae152012-05-01 17:36:15 -0700472}
473
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700474static int __find_subsys(struct device *dev, void *data)
475{
476 struct subsys_device *subsys = to_subsys(dev);
477 return !strcmp(subsys->desc->name, data);
478}
479
480static struct subsys_device *find_subsys(const char *str)
481{
482 struct device *dev;
483
484 if (!str)
485 return NULL;
486
487 dev = bus_find_device(&subsys_bus_type, NULL, (void *)str,
488 __find_subsys);
489 return dev ? to_subsys(dev) : NULL;
490}
491
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700492static int subsys_start(struct subsys_device *subsys)
493{
494 int ret;
495
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800496 init_completion(&subsys->err_ready);
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700497 ret = subsys->desc->start(subsys->desc);
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800498 if (ret)
499 return ret;
500
Seemanta Duttaf9458c92013-05-08 19:53:29 -0700501 if (subsys->desc->is_not_loadable)
Seemanta Duttad7591612013-05-03 17:44:00 -0700502 return 0;
503
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800504 ret = wait_for_err_ready(subsys);
505 if (ret)
506 /* pil-boot succeeded but we need to shutdown
507 * the device because error ready timed out.
508 */
509 subsys->desc->stop(subsys->desc);
510 else
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700511 subsys_set_state(subsys, SUBSYS_ONLINE);
512
513 return ret;
514}
515
516static void subsys_stop(struct subsys_device *subsys)
517{
518 subsys->desc->stop(subsys->desc);
519 subsys_set_state(subsys, SUBSYS_OFFLINE);
520}
521
522static struct subsys_tracking *subsys_get_track(struct subsys_device *subsys)
523{
524 struct subsys_soc_restart_order *order = subsys->restart_order;
525
Stephen Boydfcb52a32012-12-03 12:35:14 -0800526 if (order)
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700527 return &order->track;
528 else
529 return &subsys->track;
530}
531
532/**
533 * subsytem_get() - Boot a subsystem
534 * @name: pointer to a string containing the name of the subsystem to boot
535 *
536 * This function returns a pointer if it succeeds. If an error occurs an
537 * ERR_PTR is returned.
538 *
539 * If this feature is disable, the value %NULL will be returned.
540 */
541void *subsystem_get(const char *name)
542{
543 struct subsys_device *subsys;
544 struct subsys_device *subsys_d;
545 int ret;
546 void *retval;
547 struct subsys_tracking *track;
548
549 if (!name)
550 return NULL;
551
552 subsys = retval = find_subsys(name);
553 if (!subsys)
554 return ERR_PTR(-ENODEV);
555 if (!try_module_get(subsys->owner)) {
556 retval = ERR_PTR(-ENODEV);
557 goto err_module;
558 }
559
560 subsys_d = subsystem_get(subsys->desc->depends_on);
561 if (IS_ERR(subsys_d)) {
562 retval = subsys_d;
563 goto err_depends;
564 }
565
566 track = subsys_get_track(subsys);
567 mutex_lock(&track->lock);
568 if (!subsys->count) {
569 ret = subsys_start(subsys);
570 if (ret) {
571 retval = ERR_PTR(ret);
572 goto err_start;
573 }
574 }
575 subsys->count++;
576 mutex_unlock(&track->lock);
577 return retval;
578err_start:
579 mutex_unlock(&track->lock);
580 subsystem_put(subsys_d);
581err_depends:
582 module_put(subsys->owner);
583err_module:
584 put_device(&subsys->dev);
585 return retval;
586}
587EXPORT_SYMBOL(subsystem_get);
588
589/**
590 * subsystem_put() - Shutdown a subsystem
591 * @peripheral_handle: pointer from a previous call to subsystem_get()
592 *
593 * This doesn't imply that a subsystem is shutdown until all callers of
594 * subsystem_get() have called subsystem_put().
595 */
596void subsystem_put(void *subsystem)
597{
598 struct subsys_device *subsys_d, *subsys = subsystem;
599 struct subsys_tracking *track;
600
601 if (IS_ERR_OR_NULL(subsys))
602 return;
603
604 track = subsys_get_track(subsys);
605 mutex_lock(&track->lock);
606 if (WARN(!subsys->count, "%s: %s: Reference count mismatch\n",
607 subsys->desc->name, __func__))
608 goto err_out;
Stephen Boyd4057ec02012-12-12 17:38:38 -0800609 if (!--subsys->count) {
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700610 subsys_stop(subsys);
Stephen Boyd4057ec02012-12-12 17:38:38 -0800611 if (subsys->do_ramdump_on_put)
612 subsystem_ramdump(subsys, NULL);
613 }
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700614 mutex_unlock(&track->lock);
615
616 subsys_d = find_subsys(subsys->desc->depends_on);
617 if (subsys_d) {
618 subsystem_put(subsys_d);
619 put_device(&subsys_d->dev);
620 }
621 module_put(subsys->owner);
622 put_device(&subsys->dev);
623 return;
624err_out:
625 mutex_unlock(&track->lock);
626}
627EXPORT_SYMBOL(subsystem_put);
628
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700629static void subsystem_restart_wq_func(struct work_struct *work)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700630{
Stephen Boyd497ef402012-06-21 12:54:40 -0700631 struct subsys_device *dev = container_of(work,
632 struct subsys_device, work);
Stephen Boyd0daae152012-05-01 17:36:15 -0700633 struct subsys_device **list;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700634 struct subsys_desc *desc = dev->desc;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700635 struct subsys_soc_restart_order *order = dev->restart_order;
636 struct subsys_tracking *track;
Stephen Boyd0daae152012-05-01 17:36:15 -0700637 unsigned count;
Stephen Boyd497ef402012-06-21 12:54:40 -0700638 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700639
Stephen Boydc68ee642012-05-01 17:02:45 -0700640 /*
641 * It's OK to not take the registration lock at this point.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700642 * This is because the subsystem list inside the relevant
643 * restart order is not being traversed.
644 */
Stephen Boydfcb52a32012-12-03 12:35:14 -0800645 if (order) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700646 list = order->subsys_ptrs;
647 count = order->count;
648 track = &order->track;
649 } else {
Stephen Boyd0daae152012-05-01 17:36:15 -0700650 list = &dev;
651 count = 1;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700652 track = &dev->track;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700653 }
654
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700655 mutex_lock(&track->lock);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700656 do_epoch_check(dev);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700657
Stephen Boydc68ee642012-05-01 17:02:45 -0700658 /*
659 * It's necessary to take the registration lock because the subsystem
660 * list in the SoC restart order will be traversed and it shouldn't be
661 * changed until _this_ restart sequence completes.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700662 */
663 mutex_lock(&soc_order_reg_lock);
664
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700665 pr_debug("[%p]: Starting restart sequence for %s\n", current,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700666 desc->name);
Seemanta Duttabac64552012-12-14 15:47:27 -0800667 notify_each_subsys_device(list, count, SUBSYS_BEFORE_SHUTDOWN, NULL);
Stephen Boyd0daae152012-05-01 17:36:15 -0700668 for_each_subsys_device(list, count, NULL, subsystem_shutdown);
Seemanta Duttabac64552012-12-14 15:47:27 -0800669 notify_each_subsys_device(list, count, SUBSYS_AFTER_SHUTDOWN, NULL);
670
671 notify_each_subsys_device(list, count, SUBSYS_RAMDUMP_NOTIFICATION,
672 &enable_ramdumps);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700673
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700674 spin_lock_irqsave(&track->s_lock, flags);
675 track->p_state = SUBSYS_RESTARTING;
676 spin_unlock_irqrestore(&track->s_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700677
678 /* Collect ram dumps for all subsystems in order here */
Stephen Boyd0daae152012-05-01 17:36:15 -0700679 for_each_subsys_device(list, count, NULL, subsystem_ramdump);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700680
Seemanta Duttabac64552012-12-14 15:47:27 -0800681 notify_each_subsys_device(list, count, SUBSYS_BEFORE_POWERUP, NULL);
Stephen Boyd0daae152012-05-01 17:36:15 -0700682 for_each_subsys_device(list, count, NULL, subsystem_powerup);
Seemanta Duttabac64552012-12-14 15:47:27 -0800683 notify_each_subsys_device(list, count, SUBSYS_AFTER_POWERUP, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700684
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700685 pr_info("[%p]: Restart sequence for %s completed.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700686 current, desc->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700687
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700688 mutex_unlock(&soc_order_reg_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700689 mutex_unlock(&track->lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700690
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700691 spin_lock_irqsave(&track->s_lock, flags);
692 track->p_state = SUBSYS_NORMAL;
Stephen Boyd6b567182012-09-18 11:31:02 -0700693 wake_unlock(&dev->wake_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700694 spin_unlock_irqrestore(&track->s_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700695}
696
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700697static void __subsystem_restart_dev(struct subsys_device *dev)
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700698{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700699 struct subsys_desc *desc = dev->desc;
Stephen Boyd6b567182012-09-18 11:31:02 -0700700 const char *name = dev->desc->name;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700701 struct subsys_tracking *track;
Stephen Boyd497ef402012-06-21 12:54:40 -0700702 unsigned long flags;
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700703
Stephen Boydfcb52a32012-12-03 12:35:14 -0800704 pr_debug("Restarting %s [level=%s]!\n", desc->name,
705 restart_levels[dev->restart_level]);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700706
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700707 track = subsys_get_track(dev);
Stephen Boyd6b567182012-09-18 11:31:02 -0700708 /*
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700709 * Allow drivers to call subsystem_restart{_dev}() as many times as
710 * they want up until the point where the subsystem is shutdown.
Stephen Boyd6b567182012-09-18 11:31:02 -0700711 */
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700712 spin_lock_irqsave(&track->s_lock, flags);
713 if (track->p_state != SUBSYS_CRASHED) {
714 if (dev->track.state == SUBSYS_ONLINE &&
715 track->p_state != SUBSYS_RESTARTING) {
716 track->p_state = SUBSYS_CRASHED;
Stephen Boyd6b567182012-09-18 11:31:02 -0700717 wake_lock(&dev->wake_lock);
718 queue_work(ssr_wq, &dev->work);
719 } else {
720 panic("Subsystem %s crashed during SSR!", name);
721 }
Stephen Boyd77c22742012-07-24 18:46:45 -0700722 }
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700723 spin_unlock_irqrestore(&track->s_lock, flags);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700724}
725
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700726int subsystem_restart_dev(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700727{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700728 const char *name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700729
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700730 if (!get_device(&dev->dev))
731 return -ENODEV;
732
733 if (!try_module_get(dev->owner)) {
734 put_device(&dev->dev);
735 return -ENODEV;
736 }
737
738 name = dev->desc->name;
Vikram Mulukutla3d58c2f2012-10-29 18:43:02 -0700739
740 /*
741 * If a system reboot/shutdown is underway, ignore subsystem errors.
742 * However, print a message so that we know that a subsystem behaved
743 * unexpectedly here.
744 */
745 if (system_state == SYSTEM_RESTART
746 || system_state == SYSTEM_POWER_OFF) {
747 pr_err("%s crashed during a system poweroff/shutdown.\n", name);
748 return -EBUSY;
749 }
750
Stephen Boydfcb52a32012-12-03 12:35:14 -0800751 pr_info("Restart sequence requested for %s, restart_level = %s.\n",
752 name, restart_levels[dev->restart_level]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700753
Stephen Boydfcb52a32012-12-03 12:35:14 -0800754 switch (dev->restart_level) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700755
756 case RESET_SUBSYS_COUPLED:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700757 __subsystem_restart_dev(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700758 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700759 case RESET_SOC:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700760 panic("subsys-restart: Resetting the SoC - %s crashed.", name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700761 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700762 default:
763 panic("subsys-restart: Unknown restart level!\n");
Stephen Boydc68ee642012-05-01 17:02:45 -0700764 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700765 }
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700766 module_put(dev->owner);
767 put_device(&dev->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700768
769 return 0;
770}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700771EXPORT_SYMBOL(subsystem_restart_dev);
772
773int subsystem_restart(const char *name)
774{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700775 int ret;
776 struct subsys_device *dev = find_subsys(name);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700777
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700778 if (!dev)
779 return -ENODEV;
780
781 ret = subsystem_restart_dev(dev);
782 put_device(&dev->dev);
783 return ret;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700784}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700785EXPORT_SYMBOL(subsystem_restart);
786
Stephen Boyd4057ec02012-12-12 17:38:38 -0800787int subsystem_crashed(const char *name)
788{
789 struct subsys_device *dev = find_subsys(name);
790 struct subsys_tracking *track;
791
792 if (!dev)
793 return -ENODEV;
794
795 if (!get_device(&dev->dev))
796 return -ENODEV;
797
798 track = subsys_get_track(dev);
799
800 mutex_lock(&track->lock);
801 dev->do_ramdump_on_put = true;
802 /*
803 * TODO: Make this work with multiple consumers where one is calling
804 * subsystem_restart() and another is calling this function. To do
805 * so would require updating private state, etc.
806 */
807 mutex_unlock(&track->lock);
808
809 put_device(&dev->dev);
810 return 0;
811}
812EXPORT_SYMBOL(subsystem_crashed);
813
Stephen Boydeff41a62012-07-06 13:19:02 -0700814#ifdef CONFIG_DEBUG_FS
815static ssize_t subsys_debugfs_read(struct file *filp, char __user *ubuf,
816 size_t cnt, loff_t *ppos)
817{
818 int r;
819 char buf[40];
820 struct subsys_device *subsys = filp->private_data;
821
822 r = snprintf(buf, sizeof(buf), "%d\n", subsys->count);
823 return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
824}
825
826static ssize_t subsys_debugfs_write(struct file *filp,
827 const char __user *ubuf, size_t cnt, loff_t *ppos)
828{
829 struct subsys_device *subsys = filp->private_data;
830 char buf[10];
831 char *cmp;
832
833 cnt = min(cnt, sizeof(buf) - 1);
834 if (copy_from_user(&buf, ubuf, cnt))
835 return -EFAULT;
836 buf[cnt] = '\0';
837 cmp = strstrip(buf);
838
839 if (!strcmp(cmp, "restart")) {
840 if (subsystem_restart_dev(subsys))
841 return -EIO;
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700842 } else if (!strcmp(cmp, "get")) {
843 if (subsystem_get(subsys->desc->name))
844 return -EIO;
845 } else if (!strcmp(cmp, "put")) {
846 subsystem_put(subsys);
Stephen Boydeff41a62012-07-06 13:19:02 -0700847 } else {
848 return -EINVAL;
849 }
850
851 return cnt;
852}
853
854static const struct file_operations subsys_debugfs_fops = {
855 .open = simple_open,
856 .read = subsys_debugfs_read,
857 .write = subsys_debugfs_write,
858};
859
860static struct dentry *subsys_base_dir;
861
862static int __init subsys_debugfs_init(void)
863{
864 subsys_base_dir = debugfs_create_dir("msm_subsys", NULL);
865 return !subsys_base_dir ? -ENOMEM : 0;
866}
867
868static void subsys_debugfs_exit(void)
869{
870 debugfs_remove_recursive(subsys_base_dir);
871}
872
873static int subsys_debugfs_add(struct subsys_device *subsys)
874{
875 if (!subsys_base_dir)
876 return -ENOMEM;
877
878 subsys->dentry = debugfs_create_file(subsys->desc->name,
879 S_IRUGO | S_IWUSR, subsys_base_dir,
880 subsys, &subsys_debugfs_fops);
881 return !subsys->dentry ? -ENOMEM : 0;
882}
883
884static void subsys_debugfs_remove(struct subsys_device *subsys)
885{
886 debugfs_remove(subsys->dentry);
887}
888#else
889static int __init subsys_debugfs_init(void) { return 0; };
890static void subsys_debugfs_exit(void) { }
891static int subsys_debugfs_add(struct subsys_device *subsys) { return 0; }
892static void subsys_debugfs_remove(struct subsys_device *subsys) { }
893#endif
894
Seemanta Duttaf055ac72013-01-10 18:08:04 -0800895static int subsys_device_open(struct inode *inode, struct file *file)
896{
897 void *retval;
898 struct subsys_device *subsys_dev = container_of(file->private_data,
899 struct subsys_device, misc_dev);
900
901 if (!file->private_data)
902 return -EINVAL;
903
904 retval = subsystem_get(subsys_dev->desc->name);
905 if (IS_ERR(retval))
906 return PTR_ERR(retval);
907
908 return 0;
909}
910
911static int subsys_device_close(struct inode *inode, struct file *file)
912{
913 struct subsys_device *subsys_dev = container_of(file->private_data,
914 struct subsys_device, misc_dev);
915
916 if (!file->private_data)
917 return -EINVAL;
918
919 subsystem_put(subsys_dev);
920
921 return 0;
922}
923
924static const struct file_operations subsys_device_fops = {
925 .owner = THIS_MODULE,
926 .open = subsys_device_open,
927 .release = subsys_device_close,
928};
929
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700930static void subsys_device_release(struct device *dev)
931{
932 struct subsys_device *subsys = to_subsys(dev);
933
934 wake_lock_destroy(&subsys->wake_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700935 mutex_destroy(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700936 ida_simple_remove(&subsys_ida, subsys->id);
937 kfree(subsys);
938}
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800939static irqreturn_t subsys_err_ready_intr_handler(int irq, void *subsys)
940{
941 struct subsys_device *subsys_dev = subsys;
942 pr_info("Error ready interrupt occured for %s\n",
943 subsys_dev->desc->name);
Seemanta Duttaa52bda62013-05-10 17:17:56 -0700944
945 if (subsys_dev->desc->is_not_loadable)
946 return IRQ_HANDLED;
947
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800948 complete(&subsys_dev->err_ready);
949 return IRQ_HANDLED;
950}
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700951
Seemanta Duttaf055ac72013-01-10 18:08:04 -0800952static int subsys_misc_device_add(struct subsys_device *subsys_dev)
953{
954 int ret;
955 memset(subsys_dev->miscdevice_name, 0,
956 ARRAY_SIZE(subsys_dev->miscdevice_name));
957 snprintf(subsys_dev->miscdevice_name,
958 ARRAY_SIZE(subsys_dev->miscdevice_name), "subsys_%s",
959 subsys_dev->desc->name);
960
961 subsys_dev->misc_dev.minor = MISC_DYNAMIC_MINOR;
962 subsys_dev->misc_dev.name = subsys_dev->miscdevice_name;
963 subsys_dev->misc_dev.fops = &subsys_device_fops;
964 subsys_dev->misc_dev.parent = &subsys_dev->dev;
965
966 ret = misc_register(&subsys_dev->misc_dev);
967 if (ret) {
968 pr_err("%s: misc_register() failed for %s (%d)", __func__,
969 subsys_dev->miscdevice_name, ret);
970 }
971 return ret;
972}
973
974static void subsys_misc_device_remove(struct subsys_device *subsys_dev)
975{
976 misc_deregister(&subsys_dev->misc_dev);
977}
978
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700979struct subsys_device *subsys_register(struct subsys_desc *desc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700980{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700981 struct subsys_device *subsys;
982 int ret;
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700983
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700984 subsys = kzalloc(sizeof(*subsys), GFP_KERNEL);
985 if (!subsys)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700986 return ERR_PTR(-ENOMEM);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700987
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700988 subsys->desc = desc;
989 subsys->owner = desc->owner;
990 subsys->dev.parent = desc->dev;
991 subsys->dev.bus = &subsys_bus_type;
992 subsys->dev.release = subsys_device_release;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700993
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700994 subsys->notify = subsys_notif_add_subsys(desc->name);
995 subsys->restart_order = update_restart_order(subsys);
Stephen Boyd497ef402012-06-21 12:54:40 -0700996
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700997 snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name);
998 wake_lock_init(&subsys->wake_lock, WAKE_LOCK_SUSPEND, subsys->wlname);
999 INIT_WORK(&subsys->work, subsystem_restart_wq_func);
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001000 spin_lock_init(&subsys->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001001
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001002 subsys->id = ida_simple_get(&subsys_ida, 0, 0, GFP_KERNEL);
1003 if (subsys->id < 0) {
1004 ret = subsys->id;
1005 goto err_ida;
1006 }
1007 dev_set_name(&subsys->dev, "subsys%d", subsys->id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001008
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001009 mutex_init(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001010
Stephen Boydeff41a62012-07-06 13:19:02 -07001011 ret = subsys_debugfs_add(subsys);
1012 if (ret)
1013 goto err_debugfs;
1014
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001015 ret = device_register(&subsys->dev);
1016 if (ret) {
Seemanta Duttaf055ac72013-01-10 18:08:04 -08001017 device_unregister(&subsys->dev);
1018 goto err_register;
1019 }
1020
1021 ret = subsys_misc_device_add(subsys);
1022 if (ret) {
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001023 put_device(&subsys->dev);
1024 goto err_register;
1025 }
Stephen Boydeff41a62012-07-06 13:19:02 -07001026
Seemanta Duttad32f92a2013-01-25 14:22:15 -08001027 if (subsys->desc->err_ready_irq) {
1028 ret = devm_request_irq(&subsys->dev,
1029 subsys->desc->err_ready_irq,
1030 subsys_err_ready_intr_handler,
1031 IRQF_TRIGGER_RISING,
1032 "error_ready_interrupt", subsys);
1033 if (ret < 0) {
1034 dev_err(&subsys->dev,
1035 "[%s]: Unable to register err ready handler\n",
1036 subsys->desc->name);
1037 goto err_misc_device;
1038 }
1039 }
1040
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001041 return subsys;
1042
Seemanta Duttad32f92a2013-01-25 14:22:15 -08001043err_misc_device:
1044 subsys_misc_device_remove(subsys);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001045err_register:
Stephen Boydeff41a62012-07-06 13:19:02 -07001046 subsys_debugfs_remove(subsys);
1047err_debugfs:
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001048 mutex_destroy(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001049 ida_simple_remove(&subsys_ida, subsys->id);
1050err_ida:
1051 wake_lock_destroy(&subsys->wake_lock);
1052 kfree(subsys);
1053 return ERR_PTR(ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001054}
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001055EXPORT_SYMBOL(subsys_register);
1056
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001057void subsys_unregister(struct subsys_device *subsys)
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001058{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001059 if (IS_ERR_OR_NULL(subsys))
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001060 return;
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001061
1062 if (get_device(&subsys->dev)) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001063 mutex_lock(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001064 WARN_ON(subsys->count);
1065 device_unregister(&subsys->dev);
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001066 mutex_unlock(&subsys->track.lock);
Stephen Boydeff41a62012-07-06 13:19:02 -07001067 subsys_debugfs_remove(subsys);
Seemanta Duttaf055ac72013-01-10 18:08:04 -08001068 subsys_misc_device_remove(subsys);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001069 put_device(&subsys->dev);
1070 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001071}
1072EXPORT_SYMBOL(subsys_unregister);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001073
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001074static int subsys_panic(struct device *dev, void *data)
1075{
1076 struct subsys_device *subsys = to_subsys(dev);
1077
1078 if (subsys->desc->crash_shutdown)
1079 subsys->desc->crash_shutdown(subsys->desc);
1080 return 0;
1081}
1082
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001083static int ssr_panic_handler(struct notifier_block *this,
1084 unsigned long event, void *ptr)
1085{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001086 bus_for_each_dev(&subsys_bus_type, NULL, NULL, subsys_panic);
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001087 return NOTIFY_DONE;
1088}
1089
1090static struct notifier_block panic_nb = {
1091 .notifier_call = ssr_panic_handler,
1092};
1093
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001094static int __init ssr_init_soc_restart_orders(void)
1095{
1096 int i;
1097
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001098 atomic_notifier_chain_register(&panic_notifier_list,
1099 &panic_nb);
1100
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001101 if (cpu_is_msm8x60()) {
1102 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001103 mutex_init(&orders_8x60_all[i]->track.lock);
1104 spin_lock_init(&orders_8x60_all[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001105 }
1106
1107 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001108 mutex_init(&orders_8x60_modems[i]->track.lock);
1109 spin_lock_init(&orders_8x60_modems[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001110 }
1111
1112 restart_orders = orders_8x60_all;
1113 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
1114 }
1115
Vikram Mulukutlae4ae75e2012-07-27 15:14:10 -07001116 if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) {
1117 restart_orders = restart_orders_8960_sglte;
1118 n_restart_orders = ARRAY_SIZE(restart_orders_8960_sglte);
1119 }
1120
1121 for (i = 0; i < n_restart_orders; i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001122 mutex_init(&restart_orders[i]->track.lock);
1123 spin_lock_init(&restart_orders[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001124 }
1125
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001126 return 0;
1127}
1128
1129static int __init subsys_restart_init(void)
1130{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001131 int ret;
Vikram Mulukutla69177e12012-03-21 20:51:43 -07001132
Vikram Mulukutla8d4c3f02012-09-10 19:25:32 -07001133 ssr_wq = alloc_workqueue("ssr_wq", WQ_CPU_INTENSIVE, 0);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001134 BUG_ON(!ssr_wq);
1135
1136 ret = bus_register(&subsys_bus_type);
1137 if (ret)
1138 goto err_bus;
Stephen Boydeff41a62012-07-06 13:19:02 -07001139 ret = subsys_debugfs_init();
1140 if (ret)
1141 goto err_debugfs;
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001142 ret = ssr_init_soc_restart_orders();
1143 if (ret)
1144 goto err_soc;
1145 return 0;
Stephen Boydeff41a62012-07-06 13:19:02 -07001146
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001147err_soc:
Stephen Boydeff41a62012-07-06 13:19:02 -07001148 subsys_debugfs_exit();
1149err_debugfs:
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001150 bus_unregister(&subsys_bus_type);
1151err_bus:
1152 destroy_workqueue(ssr_wq);
1153 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001154}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001155arch_initcall(subsys_restart_init);
1156
1157MODULE_DESCRIPTION("Subsystem Restart Driver");
1158MODULE_LICENSE("GPL v2");