blob: 002c9f8ba80411efe0a9873967f571e25ee00789 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
2 *
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
13#include <linux/kernel.h>
14#include <linux/module.h>
15#include <linux/uaccess.h>
16#include <linux/module.h>
17#include <linux/fs.h>
18#include <linux/proc_fs.h>
19#include <linux/delay.h>
20#include <linux/list.h>
21#include <linux/io.h>
22#include <linux/kthread.h>
23
24#include <asm/current.h>
25
26#include <mach/peripheral-loader.h>
27#include <mach/scm.h>
28#include <mach/socinfo.h>
29#include <mach/subsystem_notif.h>
30#include <mach/subsystem_restart.h>
31
32#include "smd_private.h"
33
34#if defined(SUBSYS_RESTART_DEBUG)
35#define dprintk(msg...) printk(msg)
36#else
37#define dprintk(msg...)
38#endif
39
40struct subsys_soc_restart_order {
41 const char * const *subsystem_list;
42 int count;
43
44 struct mutex shutdown_lock;
45 struct mutex powerup_lock;
46 struct subsys_data *subsys_ptrs[];
47};
48
49struct restart_thread_data {
50 struct subsys_data *subsys;
51 int coupled;
52};
53
54static int restart_level;
55static int enable_ramdumps;
56
57static LIST_HEAD(subsystem_list);
58static DEFINE_MUTEX(subsystem_list_lock);
59static DEFINE_MUTEX(soc_order_reg_lock);
60
61/* SOC specific restart orders go here */
62
63#define DEFINE_SINGLE_RESTART_ORDER(name, order) \
64 static struct subsys_soc_restart_order __##name = { \
65 .subsystem_list = order, \
66 .count = ARRAY_SIZE(order), \
67 .subsys_ptrs = {[ARRAY_SIZE(order)] = NULL} \
68 }; \
69 static struct subsys_soc_restart_order *name[] = { \
70 &__##name, \
71 }
72
73/* MSM 8x60 restart ordering info */
74static const char * const _order_8x60_all[] = {
75 "external_modem", "modem", "lpass"
76};
77DEFINE_SINGLE_RESTART_ORDER(orders_8x60_all, _order_8x60_all);
78
79static const char * const _order_8x60_modems[] = {"external_modem", "modem"};
80DEFINE_SINGLE_RESTART_ORDER(orders_8x60_modems, _order_8x60_modems);
81
82/* MSM 8960 restart ordering info */
83static const char * const order_8960[] = {"modem", "lpass"};
84
85static struct subsys_soc_restart_order restart_orders_8960_one = {
86 .subsystem_list = order_8960,
87 .count = ARRAY_SIZE(order_8960),
88 .subsys_ptrs = {[ARRAY_SIZE(order_8960)] = NULL}
89 };
90
91static struct subsys_soc_restart_order *restart_orders_8960[] = {
92 &restart_orders_8960_one,
93};
94
95/* These will be assigned to one of the sets above after
96 * runtime SoC identification.
97 */
98static struct subsys_soc_restart_order **restart_orders;
99static int n_restart_orders;
100
101module_param(enable_ramdumps, int, S_IRUGO | S_IWUSR);
102
103static struct subsys_soc_restart_order *_update_restart_order(
104 struct subsys_data *subsys);
105
106int get_restart_level()
107{
108 return restart_level;
109}
110EXPORT_SYMBOL(get_restart_level);
111
112static void restart_level_changed(void)
113{
114 struct subsys_data *subsys;
115
116 if (cpu_is_msm8x60() && restart_level == RESET_SUBSYS_COUPLED) {
117 restart_orders = orders_8x60_all;
118 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
119 }
120
121 if (cpu_is_msm8x60() && restart_level == RESET_SUBSYS_MIXED) {
122 restart_orders = orders_8x60_modems;
123 n_restart_orders = ARRAY_SIZE(orders_8x60_modems);
124 }
125
126 mutex_lock(&subsystem_list_lock);
127 list_for_each_entry(subsys, &subsystem_list, list)
128 subsys->restart_order = _update_restart_order(subsys);
129 mutex_unlock(&subsystem_list_lock);
130}
131
132static int restart_level_set(const char *val, struct kernel_param *kp)
133{
134 int ret;
135 int old_val = restart_level;
136
137 ret = param_set_int(val, kp);
138 if (ret)
139 return ret;
140
141 switch (restart_level) {
142
143 case RESET_SOC:
144 case RESET_SUBSYS_COUPLED:
145 case RESET_SUBSYS_INDEPENDENT:
146 pr_info("Subsystem Restart: Phase %d behavior activated.\n",
147 restart_level);
148 break;
149
150 case RESET_SUBSYS_MIXED:
151 pr_info("Subsystem Restart: Phase 2+ behavior activated.\n");
152 break;
153
154 default:
155 restart_level = old_val;
156 return -EINVAL;
157 break;
158
159 }
160
161 if (restart_level != old_val)
162 restart_level_changed();
163
164 return 0;
165}
166
167module_param_call(restart_level, restart_level_set, param_get_int,
168 &restart_level, 0644);
169
170static struct subsys_data *_find_subsystem(const char *subsys_name)
171{
172 struct subsys_data *subsys;
173
174 mutex_lock(&subsystem_list_lock);
175 list_for_each_entry(subsys, &subsystem_list, list)
176 if (!strncmp(subsys->name, subsys_name,
177 SUBSYS_NAME_MAX_LENGTH)) {
178 mutex_unlock(&subsystem_list_lock);
179 return subsys;
180 }
181 mutex_unlock(&subsystem_list_lock);
182
183 return NULL;
184}
185
186static struct subsys_soc_restart_order *_update_restart_order(
187 struct subsys_data *subsys)
188{
189 int i, j;
190
191 if (!subsys)
192 return NULL;
193
194 if (!subsys->name)
195 return NULL;
196
197 mutex_lock(&soc_order_reg_lock);
198 for (j = 0; j < n_restart_orders; j++) {
199 for (i = 0; i < restart_orders[j]->count; i++)
200 if (!strncmp(restart_orders[j]->subsystem_list[i],
201 subsys->name, SUBSYS_NAME_MAX_LENGTH)) {
202
203 restart_orders[j]->subsys_ptrs[i] =
204 subsys;
205 mutex_unlock(&soc_order_reg_lock);
206 return restart_orders[j];
207 }
208 }
209
210 mutex_unlock(&soc_order_reg_lock);
211
212 return NULL;
213}
214
215static void _send_notification_to_order(struct subsys_data
216 **restart_list, int count,
217 enum subsys_notif_type notif_type)
218{
219 int i;
220
221 for (i = 0; i < count; i++)
222 if (restart_list[i])
223 subsys_notif_queue_notification(
224 restart_list[i]->notif_handle, notif_type);
225}
226
227static int subsystem_restart_thread(void *data)
228{
229 struct restart_thread_data *r_work = data;
230 struct subsys_data **restart_list;
231 struct subsys_data *subsys = r_work->subsys;
232 struct subsys_soc_restart_order *soc_restart_order = NULL;
233
234 struct mutex *powerup_lock;
235 struct mutex *shutdown_lock;
236
237 int i;
238 int restart_list_count = 0;
239
240 if (r_work->coupled)
241 soc_restart_order = subsys->restart_order;
242
243 /* It's OK to not take the registration lock at this point.
244 * This is because the subsystem list inside the relevant
245 * restart order is not being traversed.
246 */
247 if (!soc_restart_order) {
248 restart_list = subsys->single_restart_list;
249 restart_list_count = 1;
250 powerup_lock = &subsys->powerup_lock;
251 shutdown_lock = &subsys->shutdown_lock;
252 } else {
253 restart_list = soc_restart_order->subsys_ptrs;
254 restart_list_count = soc_restart_order->count;
255 powerup_lock = &soc_restart_order->powerup_lock;
256 shutdown_lock = &soc_restart_order->shutdown_lock;
257 }
258
259 dprintk("%s[%p]: Attempting to get shutdown lock!\n", __func__,
260 current);
261
262 /* Try to acquire shutdown_lock. If this fails, these subsystems are
263 * already being restarted - return.
264 */
265 if (!mutex_trylock(shutdown_lock)) {
266 kfree(data);
267 do_exit(0);
268 }
269
270 dprintk("%s[%p]: Attempting to get powerup lock!\n", __func__,
271 current);
272
273 /* Now that we've acquired the shutdown lock, either we're the first to
274 * restart these subsystems or some other thread is doing the powerup
275 * sequence for these subsystems. In the latter case, panic and bail
276 * out, since a subsystem died in its powerup sequence.
277 */
278 if (!mutex_trylock(powerup_lock))
279 panic("%s: Subsystem died during powerup!", __func__);
280
281 /* Now it is necessary to take the registration lock. This is because
282 * the subsystem list in the SoC restart order will be traversed
283 * and it shouldn't be changed until _this_ restart sequence completes.
284 */
285 mutex_lock(&soc_order_reg_lock);
286
287 dprintk("%s: Starting restart sequence for %s\n", __func__,
288 r_work->subsys->name);
289
290 _send_notification_to_order(restart_list,
291 restart_list_count,
292 SUBSYS_BEFORE_SHUTDOWN);
293
294 for (i = 0; i < restart_list_count; i++) {
295
296 if (!restart_list[i])
297 continue;
298
299 pr_info("subsys-restart: Shutting down %s\n",
300 restart_list[i]->name);
301
302 if (restart_list[i]->shutdown(subsys) < 0)
303 panic("%s: Failed to shutdown %s!\n", __func__,
304 restart_list[i]->name);
305 }
306
307 _send_notification_to_order(restart_list, restart_list_count,
308 SUBSYS_AFTER_SHUTDOWN);
309
310 /* Now that we've finished shutting down these subsystems, release the
311 * shutdown lock. If a subsystem restart request comes in for a
312 * subsystem in _this_ restart order after the unlock below, and
313 * before the powerup lock is released, panic and bail out.
314 */
315 mutex_unlock(shutdown_lock);
316
317 /* Collect ram dumps for all subsystems in order here */
318 for (i = 0; i < restart_list_count; i++) {
319 if (!restart_list[i])
320 continue;
321
322 if (restart_list[i]->ramdump)
323 if (restart_list[i]->ramdump(enable_ramdumps,
324 subsys) < 0)
325 pr_warn("%s(%s): Ramdump failed.", __func__,
326 restart_list[i]->name);
327 }
328
329 _send_notification_to_order(restart_list,
330 restart_list_count,
331 SUBSYS_BEFORE_POWERUP);
332
333 for (i = restart_list_count - 1; i >= 0; i--) {
334
335 if (!restart_list[i])
336 continue;
337
338 pr_info("subsys-restart: Powering up %s\n",
339 restart_list[i]->name);
340
341 if (restart_list[i]->powerup(subsys) < 0)
342 panic("%s: Failed to powerup %s!", __func__,
343 restart_list[i]->name);
344 }
345
346 _send_notification_to_order(restart_list,
347 restart_list_count,
348 SUBSYS_AFTER_POWERUP);
349
350 pr_info("%s: Restart sequence for %s completed.", __func__,
351 r_work->subsys->name);
352
353 mutex_unlock(powerup_lock);
354
355 mutex_unlock(&soc_order_reg_lock);
356
357 dprintk("%s: Released powerup lock!\n", __func__);
358
359 kfree(data);
360 do_exit(0);
361}
362
363int subsystem_restart(const char *subsys_name)
364{
365 struct subsys_data *subsys;
366 struct task_struct *tsk;
367 struct restart_thread_data *data = NULL;
368
369 if (!subsys_name) {
370 pr_err("%s: Invalid subsystem name.", __func__);
371 return -EINVAL;
372 }
373
374 pr_info("Subsystem Restart: Restart sequence requested for %s\n",
375 subsys_name);
376
377 /* List of subsystems is protected by a lock. New subsystems can
378 * still come in.
379 */
380 subsys = _find_subsystem(subsys_name);
381
382 if (!subsys) {
383 pr_warn("%s: Unregistered subsystem %s!", __func__,
384 subsys_name);
385 return -EINVAL;
386 }
387
388 if (restart_level != RESET_SOC) {
389 data = kzalloc(sizeof(struct restart_thread_data), GFP_KERNEL);
390 if (!data) {
391 restart_level = RESET_SOC;
392 pr_warn("%s: Failed to alloc restart data. Resetting.",
393 __func__);
394 } else {
395 if (restart_level == RESET_SUBSYS_COUPLED ||
396 restart_level == RESET_SUBSYS_MIXED)
397 data->coupled = 1;
398 else
399 data->coupled = 0;
400
401 data->subsys = subsys;
402 }
403 }
404
405 switch (restart_level) {
406
407 case RESET_SUBSYS_COUPLED:
408 case RESET_SUBSYS_MIXED:
409 case RESET_SUBSYS_INDEPENDENT:
410 dprintk("%s: Restarting %s [level=%d]!\n", __func__,
411 subsys_name, restart_level);
412
413 /* Let the kthread handle the actual restarting. Using a
414 * workqueue will not work since all restart requests are
415 * serialized and it prevents the short circuiting of
416 * restart requests for subsystems already in a restart
417 * sequence.
418 */
419 tsk = kthread_run(subsystem_restart_thread, data,
420 "subsystem_subsystem_restart_thread");
421 if (IS_ERR(tsk))
422 panic("%s: Unable to create thread to restart %s",
423 __func__, subsys->name);
424
425 break;
426
427 case RESET_SOC:
428
429 mutex_lock(&subsystem_list_lock);
430 list_for_each_entry(subsys, &subsystem_list, list)
431 if (subsys->crash_shutdown)
432 subsys->crash_shutdown(subsys);
433 mutex_unlock(&subsystem_list_lock);
434
435 panic("Resetting the SOC");
436 break;
437
438 default:
439 panic("subsys-restart: Unknown restart level!\n");
440 break;
441
442 }
443
444 return 0;
445}
446EXPORT_SYMBOL(subsystem_restart);
447
448int ssr_register_subsystem(struct subsys_data *subsys)
449{
450 if (!subsys)
451 goto err;
452
453 if (!subsys->name)
454 goto err;
455
456 if (!subsys->powerup || !subsys->shutdown)
457 goto err;
458
459 subsys->notif_handle = subsys_notif_add_subsys(subsys->name);
460 subsys->restart_order = _update_restart_order(subsys);
461 subsys->single_restart_list[0] = subsys;
462
463 mutex_init(&subsys->shutdown_lock);
464 mutex_init(&subsys->powerup_lock);
465
466 mutex_lock(&subsystem_list_lock);
467 list_add(&subsys->list, &subsystem_list);
468 mutex_unlock(&subsystem_list_lock);
469
470 return 0;
471
472err:
473 return -EINVAL;
474}
475EXPORT_SYMBOL(ssr_register_subsystem);
476
477static int __init ssr_init_soc_restart_orders(void)
478{
479 int i;
480
481 if (cpu_is_msm8x60()) {
482 for (i = 0; i < ARRAY_SIZE(orders_8x60_all); i++) {
483 mutex_init(&orders_8x60_all[i]->powerup_lock);
484 mutex_init(&orders_8x60_all[i]->shutdown_lock);
485 }
486
487 for (i = 0; i < ARRAY_SIZE(orders_8x60_modems); i++) {
488 mutex_init(&orders_8x60_modems[i]->powerup_lock);
489 mutex_init(&orders_8x60_modems[i]->shutdown_lock);
490 }
491
492 restart_orders = orders_8x60_all;
493 n_restart_orders = ARRAY_SIZE(orders_8x60_all);
494 }
495
496 if (cpu_is_msm8960()) {
497 restart_orders = restart_orders_8960;
498 n_restart_orders = ARRAY_SIZE(restart_orders_8960);
499 }
500
501 if (restart_orders == NULL || n_restart_orders < 1) {
502 WARN_ON(1);
503 return -EINVAL;
504 }
505
506 return 0;
507}
508
509static int __init subsys_restart_init(void)
510{
511 int ret = 0;
512
513 restart_level = RESET_SOC;
514
515 ret = ssr_init_soc_restart_orders();
516
517 return ret;
518}
519
520arch_initcall(subsys_restart_init);
521
522MODULE_DESCRIPTION("Subsystem Restart Driver");
523MODULE_LICENSE("GPL v2");