blob: fdde328a9f5b28653fa3c78dae0bf40a45e675eb [file] [log] [blame]
Rohit Vaswanid0fb4182012-03-19 18:07:59 -07001/* Copyright (c) 2011-2012, Code Aurora Forum. 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>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030
31#include <asm/current.h>
32
33#include <mach/peripheral-loader.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070034#include <mach/socinfo.h>
35#include <mach/subsystem_notif.h>
36#include <mach/subsystem_restart.h>
37
38#include "smd_private.h"
39
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070040struct subsys_soc_restart_order {
41 const char * const *subsystem_list;
42 int count;
43
44 struct mutex shutdown_lock;
45 struct mutex powerup_lock;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070046 struct subsys_device *subsys_ptrs[];
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070047};
48
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070049struct restart_log {
50 struct timeval time;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070051 struct subsys_device *dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070052 struct list_head list;
53};
54
Stephen Boyd0ebf7212012-04-30 20:42:35 -070055struct subsys_device {
56 struct subsys_desc *desc;
57 struct list_head list;
Stephen Boyd497ef402012-06-21 12:54:40 -070058 struct wake_lock wake_lock;
59 char wlname[64];
60 struct work_struct work;
61 spinlock_t restart_lock;
Stephen Boyd77c22742012-07-24 18:46:45 -070062 int restart_count;
Stephen Boyd0ebf7212012-04-30 20:42:35 -070063
64 void *notify;
65
66 struct mutex shutdown_lock;
67 struct mutex powerup_lock;
68
69 void *restart_order;
70};
71
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070072static int enable_ramdumps;
Stephen Boydc68ee642012-05-01 17:02:45 -070073module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR);
74
Vikram Mulukutla69177e12012-03-21 20:51:43 -070075struct workqueue_struct *ssr_wq;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070076
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070077static LIST_HEAD(restart_log_list);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070078static LIST_HEAD(subsystem_list);
Stephen Boyd0ebf7212012-04-30 20:42:35 -070079static DEFINE_MUTEX(subsystem_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070080static DEFINE_MUTEX(soc_order_reg_lock);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -070081static DEFINE_MUTEX(restart_log_mutex);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070082
83/* SOC specific restart orders go here */
84
85#define DEFINE_SINGLE_RESTART_ORDER(name, order) \
86 static struct subsys_soc_restart_order __##name = { \
87 .subsystem_list = order, \
88 .count = ARRAY_SIZE(order), \
89 .subsys_ptrs = {[ARRAY_SIZE(order)] = NULL} \
90 }; \
91 static struct subsys_soc_restart_order *name[] = { \
92 &__##name, \
93 }
94
95/* MSM 8x60 restart ordering info */
96static const char * const _order_8x60_all[] = {
97 "external_modem", "modem", "lpass"
98};
99DEFINE_SINGLE_RESTART_ORDER(orders_8x60_all, _order_8x60_all);
100
101static const char * const _order_8x60_modems[] = {"external_modem", "modem"};
102DEFINE_SINGLE_RESTART_ORDER(orders_8x60_modems, _order_8x60_modems);
103
104/* MSM 8960 restart ordering info */
105static const char * const order_8960[] = {"modem", "lpass"};
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700106/*SGLTE restart ordering info*/
107static const char * const order_8960_sglte[] = {"external_modem",
108 "modem"};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700109
110static struct subsys_soc_restart_order restart_orders_8960_one = {
111 .subsystem_list = order_8960,
112 .count = ARRAY_SIZE(order_8960),
113 .subsys_ptrs = {[ARRAY_SIZE(order_8960)] = NULL}
114 };
115
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700116static struct subsys_soc_restart_order restart_orders_8960_fusion_sglte = {
117 .subsystem_list = order_8960_sglte,
118 .count = ARRAY_SIZE(order_8960_sglte),
119 .subsys_ptrs = {[ARRAY_SIZE(order_8960_sglte)] = NULL}
120 };
121
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700122static struct subsys_soc_restart_order *restart_orders_8960[] = {
123 &restart_orders_8960_one,
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700124 };
125
126static struct subsys_soc_restart_order *restart_orders_8960_sglte[] = {
127 &restart_orders_8960_fusion_sglte,
128 };
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700129
130/* These will be assigned to one of the sets above after
131 * runtime SoC identification.
132 */
133static struct subsys_soc_restart_order **restart_orders;
134static int n_restart_orders;
135
Stephen Boydc68ee642012-05-01 17:02:45 -0700136static int restart_level = RESET_SOC;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700137
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700138int get_restart_level()
139{
140 return restart_level;
141}
142EXPORT_SYMBOL(get_restart_level);
143
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700144static int restart_level_set(const char *val, struct kernel_param *kp)
145{
146 int ret;
147 int old_val = restart_level;
148
Rohit Vaswani56dd22a2011-11-11 16:21:28 -0800149 if (cpu_is_msm9615()) {
150 pr_err("Only Phase 1 subsystem restart is supported\n");
151 return -EINVAL;
152 }
153
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700154 ret = param_set_int(val, kp);
155 if (ret)
156 return ret;
157
158 switch (restart_level) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700159 case RESET_SOC:
160 case RESET_SUBSYS_COUPLED:
161 case RESET_SUBSYS_INDEPENDENT:
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700162 pr_info("Phase %d behavior activated.\n", restart_level);
Stephen Boydc68ee642012-05-01 17:02:45 -0700163 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700164 default:
165 restart_level = old_val;
166 return -EINVAL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700167 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700168 return 0;
169}
170
171module_param_call(restart_level, restart_level_set, param_get_int,
172 &restart_level, 0644);
173
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700174static struct subsys_soc_restart_order *
175update_restart_order(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700176{
177 int i, j;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700178 struct subsys_soc_restart_order *order;
179 const char *name = dev->desc->name;
180 int len = SUBSYS_NAME_MAX_LENGTH;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700181
182 mutex_lock(&soc_order_reg_lock);
183 for (j = 0; j < n_restart_orders; j++) {
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700184 order = restart_orders[j];
185 for (i = 0; i < order->count; i++) {
186 if (!strncmp(order->subsystem_list[i], name, len)) {
187 order->subsys_ptrs[i] = dev;
188 goto found;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700190 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700191 }
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700192 order = NULL;
193found:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700194 mutex_unlock(&soc_order_reg_lock);
195
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700196 return order;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197}
198
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700199static int max_restarts;
200module_param(max_restarts, int, 0644);
201
202static long max_history_time = 3600;
203module_param(max_history_time, long, 0644);
204
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700205static void do_epoch_check(struct subsys_device *dev)
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700206{
207 int n = 0;
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600208 struct timeval *time_first = NULL, *curr_time;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700209 struct restart_log *r_log, *temp;
210 static int max_restarts_check;
211 static long max_history_time_check;
212
213 mutex_lock(&restart_log_mutex);
214
215 max_restarts_check = max_restarts;
216 max_history_time_check = max_history_time;
217
218 /* Check if epoch checking is enabled */
219 if (!max_restarts_check)
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700220 goto out;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700221
222 r_log = kmalloc(sizeof(struct restart_log), GFP_KERNEL);
Matt Wagantalleecca342011-11-10 20:12:05 -0800223 if (!r_log)
224 goto out;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700225 r_log->dev = dev;
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700226 do_gettimeofday(&r_log->time);
227 curr_time = &r_log->time;
228 INIT_LIST_HEAD(&r_log->list);
229
230 list_add_tail(&r_log->list, &restart_log_list);
231
232 list_for_each_entry_safe(r_log, temp, &restart_log_list, list) {
233
234 if ((curr_time->tv_sec - r_log->time.tv_sec) >
235 max_history_time_check) {
236
237 pr_debug("Deleted node with restart_time = %ld\n",
238 r_log->time.tv_sec);
239 list_del(&r_log->list);
240 kfree(r_log);
241 continue;
242 }
243 if (!n) {
244 time_first = &r_log->time;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700245 pr_debug("Time_first: %ld\n", time_first->tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700246 }
247 n++;
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700248 pr_debug("Restart_time: %ld\n", r_log->time.tv_sec);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700249 }
250
Jordan Crouse75a25ca2011-09-09 12:49:57 -0600251 if (time_first && n >= max_restarts_check) {
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700252 if ((curr_time->tv_sec - time_first->tv_sec) <
253 max_history_time_check)
254 panic("Subsystems have crashed %d times in less than "
255 "%ld seconds!", max_restarts_check,
256 max_history_time_check);
257 }
258
Vikram Mulukutla7a289092011-09-14 10:08:36 -0700259out:
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700260 mutex_unlock(&restart_log_mutex);
261}
262
Stephen Boyd0daae152012-05-01 17:36:15 -0700263static void for_each_subsys_device(struct subsys_device **list, unsigned count,
264 void *data, void (*fn)(struct subsys_device *, void *))
265{
266 while (count--) {
267 struct subsys_device *dev = *list++;
268 if (!dev)
269 continue;
270 fn(dev, data);
271 }
272}
273
274static void __send_notification_to_order(struct subsys_device *dev, void *data)
275{
276 enum subsys_notif_type type = (enum subsys_notif_type)data;
277
278 subsys_notif_queue_notification(dev->notify, type);
279}
280
281static void send_notification_to_order(struct subsys_device **l, unsigned n,
282 enum subsys_notif_type t)
283{
284 for_each_subsys_device(l, n, (void *)t, __send_notification_to_order);
285}
286
287static void subsystem_shutdown(struct subsys_device *dev, void *data)
288{
289 const char *name = dev->desc->name;
290
291 pr_info("[%p]: Shutting down %s\n", current, name);
292 if (dev->desc->shutdown(dev->desc) < 0)
293 panic("subsys-restart: [%p]: Failed to shutdown %s!",
294 current, name);
295}
296
297static void subsystem_ramdump(struct subsys_device *dev, void *data)
298{
299 const char *name = dev->desc->name;
300
301 if (dev->desc->ramdump)
302 if (dev->desc->ramdump(enable_ramdumps, dev->desc) < 0)
303 pr_warn("%s[%p]: Ramdump failed.\n", name, current);
304}
305
306static void subsystem_powerup(struct subsys_device *dev, void *data)
307{
308 const char *name = dev->desc->name;
309
310 pr_info("[%p]: Powering up %s\n", current, name);
311 if (dev->desc->powerup(dev->desc) < 0)
312 panic("[%p]: Failed to powerup %s!", current, name);
313}
314
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700315static void subsystem_restart_wq_func(struct work_struct *work)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700316{
Stephen Boyd497ef402012-06-21 12:54:40 -0700317 struct subsys_device *dev = container_of(work,
318 struct subsys_device, work);
Stephen Boyd0daae152012-05-01 17:36:15 -0700319 struct subsys_device **list;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700320 struct subsys_desc *desc = dev->desc;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700321 struct subsys_soc_restart_order *soc_restart_order = NULL;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700322 struct mutex *powerup_lock;
323 struct mutex *shutdown_lock;
Stephen Boyd0daae152012-05-01 17:36:15 -0700324 unsigned count;
Stephen Boyd497ef402012-06-21 12:54:40 -0700325 unsigned long flags;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700326
Stephen Boyda82ee082012-06-21 12:35:49 -0700327 if (restart_level != RESET_SUBSYS_INDEPENDENT)
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700328 soc_restart_order = dev->restart_order;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700329
Stephen Boydc68ee642012-05-01 17:02:45 -0700330 /*
331 * It's OK to not take the registration lock at this point.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700332 * This is because the subsystem list inside the relevant
333 * restart order is not being traversed.
334 */
335 if (!soc_restart_order) {
Stephen Boyd0daae152012-05-01 17:36:15 -0700336 list = &dev;
337 count = 1;
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700338 powerup_lock = &dev->powerup_lock;
339 shutdown_lock = &dev->shutdown_lock;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700340 } else {
Stephen Boyd0daae152012-05-01 17:36:15 -0700341 list = soc_restart_order->subsys_ptrs;
342 count = soc_restart_order->count;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700343 powerup_lock = &soc_restart_order->powerup_lock;
344 shutdown_lock = &soc_restart_order->shutdown_lock;
345 }
346
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700347 pr_debug("[%p]: Attempting to get shutdown lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700348
Stephen Boydc68ee642012-05-01 17:02:45 -0700349 /*
350 * Try to acquire shutdown_lock. If this fails, these subsystems are
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700351 * already being restarted - return.
352 */
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800353 if (!mutex_trylock(shutdown_lock))
354 goto out;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700355
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700356 pr_debug("[%p]: Attempting to get powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700357
Stephen Boydc68ee642012-05-01 17:02:45 -0700358 /*
359 * Now that we've acquired the shutdown lock, either we're the first to
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700360 * restart these subsystems or some other thread is doing the powerup
361 * sequence for these subsystems. In the latter case, panic and bail
362 * out, since a subsystem died in its powerup sequence.
363 */
364 if (!mutex_trylock(powerup_lock))
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700365 panic("%s[%p]: Subsystem died during powerup!",
366 __func__, current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700367
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700368 do_epoch_check(dev);
Vikram Mulukutla96aecfc2011-07-28 18:18:42 -0700369
Stephen Boydc68ee642012-05-01 17:02:45 -0700370 /*
371 * It's necessary to take the registration lock because the subsystem
372 * list in the SoC restart order will be traversed and it shouldn't be
373 * changed until _this_ restart sequence completes.
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700374 */
375 mutex_lock(&soc_order_reg_lock);
376
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700377 pr_debug("[%p]: Starting restart sequence for %s\n", current,
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700378 desc->name);
Stephen Boyd0daae152012-05-01 17:36:15 -0700379 send_notification_to_order(list, count, SUBSYS_BEFORE_SHUTDOWN);
380 for_each_subsys_device(list, count, NULL, subsystem_shutdown);
381 send_notification_to_order(list, count, SUBSYS_AFTER_SHUTDOWN);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700382
Stephen Boydc68ee642012-05-01 17:02:45 -0700383 /*
384 * Now that we've finished shutting down these subsystems, release the
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700385 * shutdown lock. If a subsystem restart request comes in for a
386 * subsystem in _this_ restart order after the unlock below, and
387 * before the powerup lock is released, panic and bail out.
388 */
389 mutex_unlock(shutdown_lock);
390
391 /* Collect ram dumps for all subsystems in order here */
Stephen Boyd0daae152012-05-01 17:36:15 -0700392 for_each_subsys_device(list, count, NULL, subsystem_ramdump);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700393
Stephen Boyd0daae152012-05-01 17:36:15 -0700394 send_notification_to_order(list, count, SUBSYS_BEFORE_POWERUP);
395 for_each_subsys_device(list, count, NULL, subsystem_powerup);
396 send_notification_to_order(list, count, SUBSYS_AFTER_POWERUP);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700397
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700398 pr_info("[%p]: Restart sequence for %s completed.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700399 current, desc->name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700400
401 mutex_unlock(powerup_lock);
402
403 mutex_unlock(&soc_order_reg_lock);
404
Vikram Mulukutlaa5dd6112011-09-14 14:12:33 -0700405 pr_debug("[%p]: Released powerup lock!\n", current);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700406
Vikram Mulukutlaf6349ae2012-02-24 12:21:15 -0800407out:
Stephen Boyd497ef402012-06-21 12:54:40 -0700408 spin_lock_irqsave(&dev->restart_lock, flags);
Stephen Boyd77c22742012-07-24 18:46:45 -0700409 dev->restart_count--;
410 if (!dev->restart_count)
411 wake_unlock(&dev->wake_lock);
Stephen Boyd497ef402012-06-21 12:54:40 -0700412 spin_unlock_irqrestore(&dev->restart_lock, flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700413}
414
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700415static void __subsystem_restart_dev(struct subsys_device *dev)
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700416{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700417 struct subsys_desc *desc = dev->desc;
Stephen Boyd497ef402012-06-21 12:54:40 -0700418 unsigned long flags;
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700419
Stephen Boyd77c22742012-07-24 18:46:45 -0700420 pr_debug("Restarting %s [level=%d]!\n", desc->name, restart_level);
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700421
Stephen Boyd77c22742012-07-24 18:46:45 -0700422 spin_lock_irqsave(&dev->restart_lock, flags);
423 if (!dev->restart_count)
Stephen Boyd497ef402012-06-21 12:54:40 -0700424 wake_lock(&dev->wake_lock);
Stephen Boyd77c22742012-07-24 18:46:45 -0700425 dev->restart_count++;
Stephen Boyd497ef402012-06-21 12:54:40 -0700426 spin_unlock_irqrestore(&dev->restart_lock, flags);
Stephen Boyd77c22742012-07-24 18:46:45 -0700427
428 if (!queue_work(ssr_wq, &dev->work)) {
429 spin_lock_irqsave(&dev->restart_lock, flags);
430 dev->restart_count--;
431 if (!dev->restart_count)
432 wake_unlock(&dev->wake_lock);
433 spin_unlock_irqrestore(&dev->restart_lock, flags);
434 }
Vikram Mulukutla922a4f12012-04-13 14:36:50 -0700435}
436
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700437int subsystem_restart_dev(struct subsys_device *dev)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700438{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700439 const char *name = dev->desc->name;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700440
Vikram Mulukutla3b24c8c2011-12-02 15:12:53 -0800441 pr_info("Restart sequence requested for %s, restart_level = %d.\n",
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700442 name, restart_level);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700443
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700444 switch (restart_level) {
445
446 case RESET_SUBSYS_COUPLED:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700447 case RESET_SUBSYS_INDEPENDENT:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700448 __subsystem_restart_dev(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700449 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700450 case RESET_SOC:
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700451 panic("subsys-restart: Resetting the SoC - %s crashed.", name);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700452 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700453 default:
454 panic("subsys-restart: Unknown restart level!\n");
Stephen Boydc68ee642012-05-01 17:02:45 -0700455 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700456 }
457
458 return 0;
459}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700460EXPORT_SYMBOL(subsystem_restart_dev);
461
462int subsystem_restart(const char *name)
463{
464 struct subsys_device *dev;
465
466 mutex_lock(&subsystem_list_lock);
467 list_for_each_entry(dev, &subsystem_list, list)
468 if (!strncmp(dev->desc->name, name, SUBSYS_NAME_MAX_LENGTH))
469 goto found;
470 dev = NULL;
471found:
472 mutex_unlock(&subsystem_list_lock);
473 if (dev)
474 return subsystem_restart_dev(dev);
475 return -ENODEV;
476}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700477EXPORT_SYMBOL(subsystem_restart);
478
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700479struct subsys_device *subsys_register(struct subsys_desc *desc)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700480{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700481 struct subsys_device *dev;
Vikram Mulukutla705e6892012-06-08 17:57:31 -0700482
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700483 dev = kzalloc(sizeof(*dev), GFP_KERNEL);
484 if (!dev)
485 return ERR_PTR(-ENOMEM);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700486
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700487 dev->desc = desc;
488 dev->notify = subsys_notif_add_subsys(desc->name);
489 dev->restart_order = update_restart_order(dev);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700490
Stephen Boyd497ef402012-06-21 12:54:40 -0700491 snprintf(dev->wlname, sizeof(dev->wlname), "ssr(%s)", desc->name);
492 wake_lock_init(&dev->wake_lock, WAKE_LOCK_SUSPEND, dev->wlname);
493 INIT_WORK(&dev->work, subsystem_restart_wq_func);
494 spin_lock_init(&dev->restart_lock);
495
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700496 mutex_init(&dev->shutdown_lock);
497 mutex_init(&dev->powerup_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700498
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700499 mutex_lock(&subsystem_list_lock);
500 list_add(&dev->list, &subsystem_list);
501 mutex_unlock(&subsystem_list_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700502
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700503 return dev;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700504}
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700505EXPORT_SYMBOL(subsys_register);
506
507void subsys_unregister(struct subsys_device *dev)
508{
509 if (IS_ERR_OR_NULL(dev))
510 return;
511 mutex_lock(&subsystem_list_lock);
512 list_del(&dev->list);
513 mutex_unlock(&subsystem_list_lock);
Stephen Boyd497ef402012-06-21 12:54:40 -0700514 wake_lock_destroy(&dev->wake_lock);
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700515 kfree(dev);
516}
517EXPORT_SYMBOL(subsys_unregister);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700518
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700519static int ssr_panic_handler(struct notifier_block *this,
520 unsigned long event, void *ptr)
521{
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700522 struct subsys_device *dev;
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700523
Stephen Boyd0ebf7212012-04-30 20:42:35 -0700524 list_for_each_entry(dev, &subsystem_list, list)
525 if (dev->desc->crash_shutdown)
526 dev->desc->crash_shutdown(dev->desc);
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700527 return NOTIFY_DONE;
528}
529
530static struct notifier_block panic_nb = {
531 .notifier_call = ssr_panic_handler,
532};
533
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700534static int __init ssr_init_soc_restart_orders(void)
535{
536 int i;
537
Vikram Mulukutla2a7ea7e2011-09-06 11:38:47 -0700538 atomic_notifier_chain_register(&panic_notifier_list,
539 &panic_nb);
540
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700541 if (cpu_is_msm8x60()) {
542 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
543 mutex_init(&orders_8x60_all[i]->powerup_lock);
544 mutex_init(&orders_8x60_all[i]->shutdown_lock);
545 }
546
547 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
548 mutex_init(&orders_8x60_modems[i]->powerup_lock);
549 mutex_init(&orders_8x60_modems[i]->shutdown_lock);
550 }
551
552 restart_orders = orders_8x60_all;
553 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
554 }
555
Stepan Moskovchenko0df9bb22012-07-06 18:19:15 -0700556 if (cpu_is_msm8960() || cpu_is_msm8930() || cpu_is_msm8930aa() ||
Stepan Moskovchenko9c749262012-07-09 19:30:44 -0700557 cpu_is_msm9615() || cpu_is_apq8064() || cpu_is_msm8627() ||
558 cpu_is_msm8960ab()) {
Ameya Thakur2c0d2f22012-06-15 15:48:20 -0700559 if (socinfo_get_platform_subtype() == PLATFORM_SUBTYPE_SGLTE) {
560 restart_orders = restart_orders_8960_sglte;
561 n_restart_orders =
562 ARRAY_SIZE(restart_orders_8960_sglte);
563 } else {
564 restart_orders = restart_orders_8960;
565 n_restart_orders = ARRAY_SIZE(restart_orders_8960);
566 }
567 for (i = 0; i < n_restart_orders; i++) {
568 mutex_init(&restart_orders[i]->powerup_lock);
569 mutex_init(&restart_orders[i]->shutdown_lock);
570 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700571 }
572
573 if (restart_orders == NULL || n_restart_orders < 1) {
574 WARN_ON(1);
575 return -EINVAL;
576 }
577
578 return 0;
579}
580
581static int __init subsys_restart_init(void)
582{
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700583 ssr_wq = alloc_workqueue("ssr_wq", 0, 0);
Vikram Mulukutla69177e12012-03-21 20:51:43 -0700584 if (!ssr_wq)
585 panic("Couldn't allocate workqueue for subsystem restart.\n");
586
Stephen Boydc68ee642012-05-01 17:02:45 -0700587 return ssr_init_soc_restart_orders();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700588}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700589arch_initcall(subsys_restart_init);
590
591MODULE_DESCRIPTION("Subsystem Restart Driver");
592MODULE_LICENSE("GPL v2");