blob: 8e507ff25b73ea4a0656d65982ba34bf9728dce6 [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));
Seemanta Dutta159af7f2013-05-28 11:09:33 -0700430 if (!ret) {
431 pr_err("[%s]: Error ready timed out\n", subsys->desc->name);
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800432 return -ETIMEDOUT;
Seemanta Dutta159af7f2013-05-28 11:09:33 -0700433 }
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800434
435 return 0;
436}
437
Stephen Boyd0daae152012-05-01 17:36:15 -0700438static void subsystem_shutdown(struct subsys_device *dev, void *data)
439{
440 const char *name = dev->desc->name;
441
442 pr_info("[%p]: Shutting down %s\n", current, name);
443 if (dev->desc->shutdown(dev->desc) < 0)
444 panic("subsys-restart: [%p]: Failed to shutdown %s!",
445 current, name);
Stephen Boyd6b567182012-09-18 11:31:02 -0700446 subsys_set_state(dev, SUBSYS_OFFLINE);
Stephen Boyd0daae152012-05-01 17:36:15 -0700447}
448
449static void subsystem_ramdump(struct subsys_device *dev, void *data)
450{
451 const char *name = dev->desc->name;
452
453 if (dev->desc->ramdump)
454 if (dev->desc->ramdump(enable_ramdumps, dev->desc) < 0)
455 pr_warn("%s[%p]: Ramdump failed.\n", name, current);
Stephen Boyd4057ec02012-12-12 17:38:38 -0800456 dev->do_ramdump_on_put = false;
Stephen Boyd0daae152012-05-01 17:36:15 -0700457}
458
459static void subsystem_powerup(struct subsys_device *dev, void *data)
460{
461 const char *name = dev->desc->name;
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800462 int ret;
Stephen Boyd0daae152012-05-01 17:36:15 -0700463
464 pr_info("[%p]: Powering up %s\n", current, name);
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800465 init_completion(&dev->err_ready);
Stephen Boyd0daae152012-05-01 17:36:15 -0700466 if (dev->desc->powerup(dev->desc) < 0)
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800467 panic("[%p]: Powerup error: %s!", current, name);
468
469 ret = wait_for_err_ready(dev);
470 if (ret)
471 panic("[%p]: Timed out waiting for error ready: %s!",
472 current, name);
Stephen Boyd6b567182012-09-18 11:31:02 -0700473 subsys_set_state(dev, SUBSYS_ONLINE);
Stephen Boyd0daae152012-05-01 17:36:15 -0700474}
475
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700476static int __find_subsys(struct device *dev, void *data)
477{
478 struct subsys_device *subsys = to_subsys(dev);
479 return !strcmp(subsys->desc->name, data);
480}
481
482static struct subsys_device *find_subsys(const char *str)
483{
484 struct device *dev;
485
486 if (!str)
487 return NULL;
488
489 dev = bus_find_device(&subsys_bus_type, NULL, (void *)str,
490 __find_subsys);
491 return dev ? to_subsys(dev) : NULL;
492}
493
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700494static int subsys_start(struct subsys_device *subsys)
495{
496 int ret;
497
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800498 init_completion(&subsys->err_ready);
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700499 ret = subsys->desc->start(subsys->desc);
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800500 if (ret)
501 return ret;
502
Seemanta Duttaf9458c92013-05-08 19:53:29 -0700503 if (subsys->desc->is_not_loadable)
Seemanta Duttad7591612013-05-03 17:44:00 -0700504 return 0;
505
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800506 ret = wait_for_err_ready(subsys);
507 if (ret)
508 /* pil-boot succeeded but we need to shutdown
509 * the device because error ready timed out.
510 */
511 subsys->desc->stop(subsys->desc);
512 else
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700513 subsys_set_state(subsys, SUBSYS_ONLINE);
514
515 return ret;
516}
517
518static void subsys_stop(struct subsys_device *subsys)
519{
520 subsys->desc->stop(subsys->desc);
521 subsys_set_state(subsys, SUBSYS_OFFLINE);
522}
523
524static struct subsys_tracking *subsys_get_track(struct subsys_device *subsys)
525{
526 struct subsys_soc_restart_order *order = subsys->restart_order;
527
Stephen Boydfcb52a32012-12-03 12:35:14 -0800528 if (order)
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700529 return &order->track;
530 else
531 return &subsys->track;
532}
533
534/**
535 * subsytem_get() - Boot a subsystem
536 * @name: pointer to a string containing the name of the subsystem to boot
537 *
538 * This function returns a pointer if it succeeds. If an error occurs an
539 * ERR_PTR is returned.
540 *
541 * If this feature is disable, the value %NULL will be returned.
542 */
543void *subsystem_get(const char *name)
544{
545 struct subsys_device *subsys;
546 struct subsys_device *subsys_d;
547 int ret;
548 void *retval;
549 struct subsys_tracking *track;
550
551 if (!name)
552 return NULL;
553
554 subsys = retval = find_subsys(name);
555 if (!subsys)
556 return ERR_PTR(-ENODEV);
557 if (!try_module_get(subsys->owner)) {
558 retval = ERR_PTR(-ENODEV);
559 goto err_module;
560 }
561
562 subsys_d = subsystem_get(subsys->desc->depends_on);
563 if (IS_ERR(subsys_d)) {
564 retval = subsys_d;
565 goto err_depends;
566 }
567
568 track = subsys_get_track(subsys);
569 mutex_lock(&track->lock);
570 if (!subsys->count) {
571 ret = subsys_start(subsys);
572 if (ret) {
573 retval = ERR_PTR(ret);
574 goto err_start;
575 }
576 }
577 subsys->count++;
578 mutex_unlock(&track->lock);
579 return retval;
580err_start:
581 mutex_unlock(&track->lock);
582 subsystem_put(subsys_d);
583err_depends:
584 module_put(subsys->owner);
585err_module:
586 put_device(&subsys->dev);
587 return retval;
588}
589EXPORT_SYMBOL(subsystem_get);
590
591/**
592 * subsystem_put() - Shutdown a subsystem
593 * @peripheral_handle: pointer from a previous call to subsystem_get()
594 *
595 * This doesn't imply that a subsystem is shutdown until all callers of
596 * subsystem_get() have called subsystem_put().
597 */
598void subsystem_put(void *subsystem)
599{
600 struct subsys_device *subsys_d, *subsys = subsystem;
601 struct subsys_tracking *track;
602
603 if (IS_ERR_OR_NULL(subsys))
604 return;
605
606 track = subsys_get_track(subsys);
607 mutex_lock(&track->lock);
608 if (WARN(!subsys->count, "%s: %s: Reference count mismatch\n",
609 subsys->desc->name, __func__))
610 goto err_out;
Stephen Boyd4057ec02012-12-12 17:38:38 -0800611 if (!--subsys->count) {
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700612 subsys_stop(subsys);
Stephen Boyd4057ec02012-12-12 17:38:38 -0800613 if (subsys->do_ramdump_on_put)
614 subsystem_ramdump(subsys, NULL);
615 }
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700616 mutex_unlock(&track->lock);
617
618 subsys_d = find_subsys(subsys->desc->depends_on);
619 if (subsys_d) {
620 subsystem_put(subsys_d);
621 put_device(&subsys_d->dev);
622 }
623 module_put(subsys->owner);
624 put_device(&subsys->dev);
625 return;
626err_out:
627 mutex_unlock(&track->lock);
628}
629EXPORT_SYMBOL(subsystem_put);
630
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700631static void subsystem_restart_wq_func(struct work_struct *work)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700632{
Stephen Boyd497ef402012-06-21 12:54:40 -0700633 struct subsys_device *dev = container_of(work,
634 struct subsys_device, work);
Stephen Boyd0daae152012-05-01 17:36:15 -0700635 struct subsys_device **list;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700636 struct subsys_desc *desc = dev->desc;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700637 struct subsys_soc_restart_order *order = dev->restart_order;
638 struct subsys_tracking *track;
Stephen Boyd0daae152012-05-01 17:36:15 -0700639 unsigned count;
Stephen Boyd497ef402012-06-21 12:54:40 -0700640 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700641
Stephen Boydc68ee642012-05-01 17:02:45 -0700642 /*
643 * It's OK to not take the registration lock at this point.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700644 * This is because the subsystem list inside the relevant
645 * restart order is not being traversed.
646 */
Stephen Boydfcb52a32012-12-03 12:35:14 -0800647 if (order) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700648 list = order->subsys_ptrs;
649 count = order->count;
650 track = &order->track;
651 } else {
Stephen Boyd0daae152012-05-01 17:36:15 -0700652 list = &dev;
653 count = 1;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700654 track = &dev->track;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700655 }
656
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700657 mutex_lock(&track->lock);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700658 do_epoch_check(dev);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700659
Stephen Boydc68ee642012-05-01 17:02:45 -0700660 /*
661 * It's necessary to take the registration lock because the subsystem
662 * list in the SoC restart order will be traversed and it shouldn't be
663 * changed until _this_ restart sequence completes.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700664 */
665 mutex_lock(&soc_order_reg_lock);
666
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700667 pr_debug("[%p]: Starting restart sequence for %s\n", current,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700668 desc->name);
Seemanta Duttabac64552012-12-14 15:47:27 -0800669 notify_each_subsys_device(list, count, SUBSYS_BEFORE_SHUTDOWN, NULL);
Stephen Boyd0daae152012-05-01 17:36:15 -0700670 for_each_subsys_device(list, count, NULL, subsystem_shutdown);
Seemanta Duttabac64552012-12-14 15:47:27 -0800671 notify_each_subsys_device(list, count, SUBSYS_AFTER_SHUTDOWN, NULL);
672
673 notify_each_subsys_device(list, count, SUBSYS_RAMDUMP_NOTIFICATION,
674 &enable_ramdumps);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700675
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700676 spin_lock_irqsave(&track->s_lock, flags);
677 track->p_state = SUBSYS_RESTARTING;
678 spin_unlock_irqrestore(&track->s_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700679
680 /* Collect ram dumps for all subsystems in order here */
Stephen Boyd0daae152012-05-01 17:36:15 -0700681 for_each_subsys_device(list, count, NULL, subsystem_ramdump);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700682
Seemanta Duttabac64552012-12-14 15:47:27 -0800683 notify_each_subsys_device(list, count, SUBSYS_BEFORE_POWERUP, NULL);
Stephen Boyd0daae152012-05-01 17:36:15 -0700684 for_each_subsys_device(list, count, NULL, subsystem_powerup);
Seemanta Duttabac64552012-12-14 15:47:27 -0800685 notify_each_subsys_device(list, count, SUBSYS_AFTER_POWERUP, NULL);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700686
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700687 pr_info("[%p]: Restart sequence for %s completed.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700688 current, desc->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700689
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700690 mutex_unlock(&soc_order_reg_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700691 mutex_unlock(&track->lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700692
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700693 spin_lock_irqsave(&track->s_lock, flags);
694 track->p_state = SUBSYS_NORMAL;
Stephen Boyd6b567182012-09-18 11:31:02 -0700695 wake_unlock(&dev->wake_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700696 spin_unlock_irqrestore(&track->s_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700697}
698
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700699static void __subsystem_restart_dev(struct subsys_device *dev)
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700700{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700701 struct subsys_desc *desc = dev->desc;
Stephen Boyd6b567182012-09-18 11:31:02 -0700702 const char *name = dev->desc->name;
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700703 struct subsys_tracking *track;
Stephen Boyd497ef402012-06-21 12:54:40 -0700704 unsigned long flags;
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700705
Stephen Boydfcb52a32012-12-03 12:35:14 -0800706 pr_debug("Restarting %s [level=%s]!\n", desc->name,
707 restart_levels[dev->restart_level]);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700708
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700709 track = subsys_get_track(dev);
Stephen Boyd6b567182012-09-18 11:31:02 -0700710 /*
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700711 * Allow drivers to call subsystem_restart{_dev}() as many times as
712 * they want up until the point where the subsystem is shutdown.
Stephen Boyd6b567182012-09-18 11:31:02 -0700713 */
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700714 spin_lock_irqsave(&track->s_lock, flags);
715 if (track->p_state != SUBSYS_CRASHED) {
716 if (dev->track.state == SUBSYS_ONLINE &&
717 track->p_state != SUBSYS_RESTARTING) {
718 track->p_state = SUBSYS_CRASHED;
Stephen Boyd6b567182012-09-18 11:31:02 -0700719 wake_lock(&dev->wake_lock);
720 queue_work(ssr_wq, &dev->work);
721 } else {
722 panic("Subsystem %s crashed during SSR!", name);
723 }
Stephen Boyd77c22742012-07-24 18:46:45 -0700724 }
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700725 spin_unlock_irqrestore(&track->s_lock, flags);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700726}
727
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700728int subsystem_restart_dev(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700729{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700730 const char *name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700731
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700732 if (!get_device(&dev->dev))
733 return -ENODEV;
734
735 if (!try_module_get(dev->owner)) {
736 put_device(&dev->dev);
737 return -ENODEV;
738 }
739
740 name = dev->desc->name;
Vikram Mulukutla3d58c2f2012-10-29 18:43:02 -0700741
742 /*
743 * If a system reboot/shutdown is underway, ignore subsystem errors.
744 * However, print a message so that we know that a subsystem behaved
745 * unexpectedly here.
746 */
747 if (system_state == SYSTEM_RESTART
748 || system_state == SYSTEM_POWER_OFF) {
749 pr_err("%s crashed during a system poweroff/shutdown.\n", name);
750 return -EBUSY;
751 }
752
Stephen Boydfcb52a32012-12-03 12:35:14 -0800753 pr_info("Restart sequence requested for %s, restart_level = %s.\n",
754 name, restart_levels[dev->restart_level]);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700755
Stephen Boydfcb52a32012-12-03 12:35:14 -0800756 switch (dev->restart_level) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700757
758 case RESET_SUBSYS_COUPLED:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700759 __subsystem_restart_dev(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700760 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700761 case RESET_SOC:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700762 panic("subsys-restart: Resetting the SoC - %s crashed.", name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700763 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700764 default:
765 panic("subsys-restart: Unknown restart level!\n");
Stephen Boydc68ee642012-05-01 17:02:45 -0700766 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700767 }
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700768 module_put(dev->owner);
769 put_device(&dev->dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700770
771 return 0;
772}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700773EXPORT_SYMBOL(subsystem_restart_dev);
774
775int subsystem_restart(const char *name)
776{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700777 int ret;
778 struct subsys_device *dev = find_subsys(name);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700779
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700780 if (!dev)
781 return -ENODEV;
782
783 ret = subsystem_restart_dev(dev);
784 put_device(&dev->dev);
785 return ret;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700786}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700787EXPORT_SYMBOL(subsystem_restart);
788
Stephen Boyd4057ec02012-12-12 17:38:38 -0800789int subsystem_crashed(const char *name)
790{
791 struct subsys_device *dev = find_subsys(name);
792 struct subsys_tracking *track;
793
794 if (!dev)
795 return -ENODEV;
796
797 if (!get_device(&dev->dev))
798 return -ENODEV;
799
800 track = subsys_get_track(dev);
801
802 mutex_lock(&track->lock);
803 dev->do_ramdump_on_put = true;
804 /*
805 * TODO: Make this work with multiple consumers where one is calling
806 * subsystem_restart() and another is calling this function. To do
807 * so would require updating private state, etc.
808 */
809 mutex_unlock(&track->lock);
810
811 put_device(&dev->dev);
812 return 0;
813}
814EXPORT_SYMBOL(subsystem_crashed);
815
Stephen Boydeff41a62012-07-06 13:19:02 -0700816#ifdef CONFIG_DEBUG_FS
817static ssize_t subsys_debugfs_read(struct file *filp, char __user *ubuf,
818 size_t cnt, loff_t *ppos)
819{
820 int r;
821 char buf[40];
822 struct subsys_device *subsys = filp->private_data;
823
824 r = snprintf(buf, sizeof(buf), "%d\n", subsys->count);
825 return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
826}
827
828static ssize_t subsys_debugfs_write(struct file *filp,
829 const char __user *ubuf, size_t cnt, loff_t *ppos)
830{
831 struct subsys_device *subsys = filp->private_data;
832 char buf[10];
833 char *cmp;
834
835 cnt = min(cnt, sizeof(buf) - 1);
836 if (copy_from_user(&buf, ubuf, cnt))
837 return -EFAULT;
838 buf[cnt] = '\0';
839 cmp = strstrip(buf);
840
841 if (!strcmp(cmp, "restart")) {
842 if (subsystem_restart_dev(subsys))
843 return -EIO;
Stephen Boyd2e5c80d2012-06-25 19:33:43 -0700844 } else if (!strcmp(cmp, "get")) {
845 if (subsystem_get(subsys->desc->name))
846 return -EIO;
847 } else if (!strcmp(cmp, "put")) {
848 subsystem_put(subsys);
Stephen Boydeff41a62012-07-06 13:19:02 -0700849 } else {
850 return -EINVAL;
851 }
852
853 return cnt;
854}
855
856static const struct file_operations subsys_debugfs_fops = {
857 .open = simple_open,
858 .read = subsys_debugfs_read,
859 .write = subsys_debugfs_write,
860};
861
862static struct dentry *subsys_base_dir;
863
864static int __init subsys_debugfs_init(void)
865{
866 subsys_base_dir = debugfs_create_dir("msm_subsys", NULL);
867 return !subsys_base_dir ? -ENOMEM : 0;
868}
869
870static void subsys_debugfs_exit(void)
871{
872 debugfs_remove_recursive(subsys_base_dir);
873}
874
875static int subsys_debugfs_add(struct subsys_device *subsys)
876{
877 if (!subsys_base_dir)
878 return -ENOMEM;
879
880 subsys->dentry = debugfs_create_file(subsys->desc->name,
881 S_IRUGO | S_IWUSR, subsys_base_dir,
882 subsys, &subsys_debugfs_fops);
883 return !subsys->dentry ? -ENOMEM : 0;
884}
885
886static void subsys_debugfs_remove(struct subsys_device *subsys)
887{
888 debugfs_remove(subsys->dentry);
889}
890#else
891static int __init subsys_debugfs_init(void) { return 0; };
892static void subsys_debugfs_exit(void) { }
893static int subsys_debugfs_add(struct subsys_device *subsys) { return 0; }
894static void subsys_debugfs_remove(struct subsys_device *subsys) { }
895#endif
896
Seemanta Duttaf055ac72013-01-10 18:08:04 -0800897static int subsys_device_open(struct inode *inode, struct file *file)
898{
899 void *retval;
900 struct subsys_device *subsys_dev = container_of(file->private_data,
901 struct subsys_device, misc_dev);
902
903 if (!file->private_data)
904 return -EINVAL;
905
906 retval = subsystem_get(subsys_dev->desc->name);
907 if (IS_ERR(retval))
908 return PTR_ERR(retval);
909
910 return 0;
911}
912
913static int subsys_device_close(struct inode *inode, struct file *file)
914{
915 struct subsys_device *subsys_dev = container_of(file->private_data,
916 struct subsys_device, misc_dev);
917
918 if (!file->private_data)
919 return -EINVAL;
920
921 subsystem_put(subsys_dev);
922
923 return 0;
924}
925
926static const struct file_operations subsys_device_fops = {
927 .owner = THIS_MODULE,
928 .open = subsys_device_open,
929 .release = subsys_device_close,
930};
931
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700932static void subsys_device_release(struct device *dev)
933{
934 struct subsys_device *subsys = to_subsys(dev);
935
936 wake_lock_destroy(&subsys->wake_lock);
Stephen Boyd5bd20e42012-10-03 12:28:43 -0700937 mutex_destroy(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700938 ida_simple_remove(&subsys_ida, subsys->id);
939 kfree(subsys);
940}
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800941static irqreturn_t subsys_err_ready_intr_handler(int irq, void *subsys)
942{
943 struct subsys_device *subsys_dev = subsys;
944 pr_info("Error ready interrupt occured for %s\n",
945 subsys_dev->desc->name);
Seemanta Duttaa52bda62013-05-10 17:17:56 -0700946
947 if (subsys_dev->desc->is_not_loadable)
948 return IRQ_HANDLED;
949
Seemanta Duttad32f92a2013-01-25 14:22:15 -0800950 complete(&subsys_dev->err_ready);
951 return IRQ_HANDLED;
952}
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700953
Seemanta Duttaf055ac72013-01-10 18:08:04 -0800954static int subsys_misc_device_add(struct subsys_device *subsys_dev)
955{
956 int ret;
957 memset(subsys_dev->miscdevice_name, 0,
958 ARRAY_SIZE(subsys_dev->miscdevice_name));
959 snprintf(subsys_dev->miscdevice_name,
960 ARRAY_SIZE(subsys_dev->miscdevice_name), "subsys_%s",
961 subsys_dev->desc->name);
962
963 subsys_dev->misc_dev.minor = MISC_DYNAMIC_MINOR;
964 subsys_dev->misc_dev.name = subsys_dev->miscdevice_name;
965 subsys_dev->misc_dev.fops = &subsys_device_fops;
966 subsys_dev->misc_dev.parent = &subsys_dev->dev;
967
968 ret = misc_register(&subsys_dev->misc_dev);
969 if (ret) {
970 pr_err("%s: misc_register() failed for %s (%d)", __func__,
971 subsys_dev->miscdevice_name, ret);
972 }
973 return ret;
974}
975
976static void subsys_misc_device_remove(struct subsys_device *subsys_dev)
977{
978 misc_deregister(&subsys_dev->misc_dev);
979}
980
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700981struct subsys_device *subsys_register(struct subsys_desc *desc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700982{
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700983 struct subsys_device *subsys;
984 int ret;
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700985
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700986 subsys = kzalloc(sizeof(*subsys), GFP_KERNEL);
987 if (!subsys)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700988 return ERR_PTR(-ENOMEM);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700989
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700990 subsys->desc = desc;
991 subsys->owner = desc->owner;
992 subsys->dev.parent = desc->dev;
993 subsys->dev.bus = &subsys_bus_type;
994 subsys->dev.release = subsys_device_release;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700995
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700996 subsys->notify = subsys_notif_add_subsys(desc->name);
997 subsys->restart_order = update_restart_order(subsys);
Stephen Boyd497ef402012-06-21 12:54:40 -0700998
Stephen Boyd4ec9a942012-06-21 19:10:48 -0700999 snprintf(subsys->wlname, sizeof(subsys->wlname), "ssr(%s)", desc->name);
1000 wake_lock_init(&subsys->wake_lock, WAKE_LOCK_SUSPEND, subsys->wlname);
1001 INIT_WORK(&subsys->work, subsystem_restart_wq_func);
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001002 spin_lock_init(&subsys->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001003
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001004 subsys->id = ida_simple_get(&subsys_ida, 0, 0, GFP_KERNEL);
1005 if (subsys->id < 0) {
1006 ret = subsys->id;
1007 goto err_ida;
1008 }
1009 dev_set_name(&subsys->dev, "subsys%d", subsys->id);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001010
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001011 mutex_init(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001012
Stephen Boydeff41a62012-07-06 13:19:02 -07001013 ret = subsys_debugfs_add(subsys);
1014 if (ret)
1015 goto err_debugfs;
1016
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001017 ret = device_register(&subsys->dev);
1018 if (ret) {
Seemanta Duttaf055ac72013-01-10 18:08:04 -08001019 device_unregister(&subsys->dev);
1020 goto err_register;
1021 }
1022
1023 ret = subsys_misc_device_add(subsys);
1024 if (ret) {
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001025 put_device(&subsys->dev);
1026 goto err_register;
1027 }
Stephen Boydeff41a62012-07-06 13:19:02 -07001028
Seemanta Duttad32f92a2013-01-25 14:22:15 -08001029 if (subsys->desc->err_ready_irq) {
1030 ret = devm_request_irq(&subsys->dev,
1031 subsys->desc->err_ready_irq,
1032 subsys_err_ready_intr_handler,
1033 IRQF_TRIGGER_RISING,
1034 "error_ready_interrupt", subsys);
1035 if (ret < 0) {
1036 dev_err(&subsys->dev,
1037 "[%s]: Unable to register err ready handler\n",
1038 subsys->desc->name);
1039 goto err_misc_device;
1040 }
1041 }
1042
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001043 return subsys;
1044
Seemanta Duttad32f92a2013-01-25 14:22:15 -08001045err_misc_device:
1046 subsys_misc_device_remove(subsys);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001047err_register:
Stephen Boydeff41a62012-07-06 13:19:02 -07001048 subsys_debugfs_remove(subsys);
1049err_debugfs:
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001050 mutex_destroy(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001051 ida_simple_remove(&subsys_ida, subsys->id);
1052err_ida:
1053 wake_lock_destroy(&subsys->wake_lock);
1054 kfree(subsys);
1055 return ERR_PTR(ret);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001056}
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001057EXPORT_SYMBOL(subsys_register);
1058
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001059void subsys_unregister(struct subsys_device *subsys)
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001060{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001061 if (IS_ERR_OR_NULL(subsys))
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001062 return;
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001063
1064 if (get_device(&subsys->dev)) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001065 mutex_lock(&subsys->track.lock);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001066 WARN_ON(subsys->count);
1067 device_unregister(&subsys->dev);
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001068 mutex_unlock(&subsys->track.lock);
Stephen Boydeff41a62012-07-06 13:19:02 -07001069 subsys_debugfs_remove(subsys);
Seemanta Duttaf055ac72013-01-10 18:08:04 -08001070 subsys_misc_device_remove(subsys);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001071 put_device(&subsys->dev);
1072 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -07001073}
1074EXPORT_SYMBOL(subsys_unregister);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001075
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001076static int subsys_panic(struct device *dev, void *data)
1077{
1078 struct subsys_device *subsys = to_subsys(dev);
1079
1080 if (subsys->desc->crash_shutdown)
1081 subsys->desc->crash_shutdown(subsys->desc);
1082 return 0;
1083}
1084
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001085static int ssr_panic_handler(struct notifier_block *this,
1086 unsigned long event, void *ptr)
1087{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001088 bus_for_each_dev(&subsys_bus_type, NULL, NULL, subsys_panic);
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001089 return NOTIFY_DONE;
1090}
1091
1092static struct notifier_block panic_nb = {
1093 .notifier_call = ssr_panic_handler,
1094};
1095
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001096static int __init ssr_init_soc_restart_orders(void)
1097{
1098 int i;
1099
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -07001100 atomic_notifier_chain_register(&panic_notifier_list,
1101 &panic_nb);
1102
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001103 if (cpu_is_msm8x60()) {
1104 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001105 mutex_init(&orders_8x60_all[i]->track.lock);
1106 spin_lock_init(&orders_8x60_all[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001107 }
1108
1109 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001110 mutex_init(&orders_8x60_modems[i]->track.lock);
1111 spin_lock_init(&orders_8x60_modems[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001112 }
1113
1114 restart_orders = orders_8x60_all;
1115 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
1116 }
1117
Vikram Mulukutlae4ae75e2012-07-27 15:14:10 -07001118 if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) {
1119 restart_orders = restart_orders_8960_sglte;
1120 n_restart_orders = ARRAY_SIZE(restart_orders_8960_sglte);
1121 }
1122
1123 for (i = 0; i < n_restart_orders; i++) {
Stephen Boyd5bd20e42012-10-03 12:28:43 -07001124 mutex_init(&restart_orders[i]->track.lock);
1125 spin_lock_init(&restart_orders[i]->track.s_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001126 }
1127
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001128 return 0;
1129}
1130
1131static int __init subsys_restart_init(void)
1132{
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001133 int ret;
Vikram Mulukutla69177e12012-03-21 20:51:43 -07001134
Vikram Mulukutla8d4c3f02012-09-10 19:25:32 -07001135 ssr_wq = alloc_workqueue("ssr_wq", WQ_CPU_INTENSIVE, 0);
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001136 BUG_ON(!ssr_wq);
1137
1138 ret = bus_register(&subsys_bus_type);
1139 if (ret)
1140 goto err_bus;
Stephen Boydeff41a62012-07-06 13:19:02 -07001141 ret = subsys_debugfs_init();
1142 if (ret)
1143 goto err_debugfs;
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001144 ret = ssr_init_soc_restart_orders();
1145 if (ret)
1146 goto err_soc;
1147 return 0;
Stephen Boydeff41a62012-07-06 13:19:02 -07001148
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001149err_soc:
Stephen Boydeff41a62012-07-06 13:19:02 -07001150 subsys_debugfs_exit();
1151err_debugfs:
Stephen Boyd4ec9a942012-06-21 19:10:48 -07001152 bus_unregister(&subsys_bus_type);
1153err_bus:
1154 destroy_workqueue(ssr_wq);
1155 return ret;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001156}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001157arch_initcall(subsys_restart_init);
1158
1159MODULE_DESCRIPTION("Subsystem Restart Driver");
1160MODULE_LICENSE("GPL v2");