diff options
Diffstat (limited to 'arch/powerpc/kernel/rtas.c')
| -rw-r--r-- | arch/powerpc/kernel/rtas.c | 69 | 
1 files changed, 60 insertions, 9 deletions
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index ee4c7609b649..c434823b8c83 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c  | |||
| @@ -38,9 +38,10 @@ | |||
| 38 | #include <asm/syscalls.h> | 38 | #include <asm/syscalls.h> | 
| 39 | #include <asm/smp.h> | 39 | #include <asm/smp.h> | 
| 40 | #include <asm/atomic.h> | 40 | #include <asm/atomic.h> | 
| 41 | #include <asm/time.h> | ||
| 41 | 42 | ||
| 42 | struct rtas_t rtas = { | 43 | struct rtas_t rtas = { | 
| 43 | .lock = SPIN_LOCK_UNLOCKED | 44 | .lock = __RAW_SPIN_LOCK_UNLOCKED | 
| 44 | }; | 45 | }; | 
| 45 | EXPORT_SYMBOL(rtas); | 46 | EXPORT_SYMBOL(rtas); | 
| 46 | 47 | ||
| @@ -67,6 +68,28 @@ unsigned long rtas_rmo_buf; | |||
| 67 | void (*rtas_flash_term_hook)(int); | 68 | void (*rtas_flash_term_hook)(int); | 
| 68 | EXPORT_SYMBOL(rtas_flash_term_hook); | 69 | EXPORT_SYMBOL(rtas_flash_term_hook); | 
| 69 | 70 | ||
| 71 | /* RTAS use home made raw locking instead of spin_lock_irqsave | ||
| 72 | * because those can be called from within really nasty contexts | ||
| 73 | * such as having the timebase stopped which would lockup with | ||
| 74 | * normal locks and spinlock debugging enabled | ||
| 75 | */ | ||
| 76 | static unsigned long lock_rtas(void) | ||
| 77 | { | ||
| 78 | unsigned long flags; | ||
| 79 | |||
| 80 | local_irq_save(flags); | ||
| 81 | preempt_disable(); | ||
| 82 | __raw_spin_lock_flags(&rtas.lock, flags); | ||
| 83 | return flags; | ||
| 84 | } | ||
| 85 | |||
| 86 | static void unlock_rtas(unsigned long flags) | ||
| 87 | { | ||
| 88 | __raw_spin_unlock(&rtas.lock); | ||
| 89 | local_irq_restore(flags); | ||
| 90 | preempt_enable(); | ||
| 91 | } | ||
| 92 | |||
| 70 | /* | 93 | /* | 
| 71 | * call_rtas_display_status and call_rtas_display_status_delay | 94 | * call_rtas_display_status and call_rtas_display_status_delay | 
| 72 | * are designed only for very early low-level debugging, which | 95 | * are designed only for very early low-level debugging, which | 
| @@ -79,7 +102,7 @@ static void call_rtas_display_status(char c) | |||
| 79 | 102 | ||
| 80 | if (!rtas.base) | 103 | if (!rtas.base) | 
| 81 | return; | 104 | return; | 
| 82 | spin_lock_irqsave(&rtas.lock, s); | 105 | s = lock_rtas(); | 
| 83 | 106 | ||
| 84 | args->token = 10; | 107 | args->token = 10; | 
| 85 | args->nargs = 1; | 108 | args->nargs = 1; | 
| @@ -89,7 +112,7 @@ static void call_rtas_display_status(char c) | |||
| 89 | 112 | ||
| 90 | enter_rtas(__pa(args)); | 113 | enter_rtas(__pa(args)); | 
| 91 | 114 | ||
| 92 | spin_unlock_irqrestore(&rtas.lock, s); | 115 | unlock_rtas(s); | 
| 93 | } | 116 | } | 
| 94 | 117 | ||
| 95 | static void call_rtas_display_status_delay(char c) | 118 | static void call_rtas_display_status_delay(char c) | 
| @@ -411,8 +434,7 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...) | |||
| 411 | if (!rtas.entry || token == RTAS_UNKNOWN_SERVICE) | 434 | if (!rtas.entry || token == RTAS_UNKNOWN_SERVICE) | 
| 412 | return -1; | 435 | return -1; | 
| 413 | 436 | ||
| 414 | /* Gotta do something different here, use global lock for now... */ | 437 | s = lock_rtas(); | 
| 415 | spin_lock_irqsave(&rtas.lock, s); | ||
| 416 | rtas_args = &rtas.args; | 438 | rtas_args = &rtas.args; | 
| 417 | 439 | ||
| 418 | rtas_args->token = token; | 440 | rtas_args->token = token; | 
| @@ -439,8 +461,7 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...) | |||
| 439 | outputs[i] = rtas_args->rets[i+1]; | 461 | outputs[i] = rtas_args->rets[i+1]; | 
| 440 | ret = (nret > 0)? rtas_args->rets[0]: 0; | 462 | ret = (nret > 0)? rtas_args->rets[0]: 0; | 
| 441 | 463 | ||
| 442 | /* Gotta do something different here, use global lock for now... */ | 464 | unlock_rtas(s); | 
| 443 | spin_unlock_irqrestore(&rtas.lock, s); | ||
| 444 | 465 | ||
| 445 | if (buff_copy) { | 466 | if (buff_copy) { | 
| 446 | log_error(buff_copy, ERR_TYPE_RTAS_LOG, 0); | 467 | log_error(buff_copy, ERR_TYPE_RTAS_LOG, 0); | 
| @@ -837,7 +858,7 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs) | |||
| 837 | 858 | ||
| 838 | buff_copy = get_errorlog_buffer(); | 859 | buff_copy = get_errorlog_buffer(); | 
| 839 | 860 | ||
| 840 | spin_lock_irqsave(&rtas.lock, flags); | 861 | flags = lock_rtas(); | 
| 841 | 862 | ||
| 842 | rtas.args = args; | 863 | rtas.args = args; | 
| 843 | enter_rtas(__pa(&rtas.args)); | 864 | enter_rtas(__pa(&rtas.args)); | 
| @@ -848,7 +869,7 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs) | |||
| 848 | if (args.rets[0] == -1) | 869 | if (args.rets[0] == -1) | 
| 849 | errbuf = __fetch_rtas_last_error(buff_copy); | 870 | errbuf = __fetch_rtas_last_error(buff_copy); | 
| 850 | 871 | ||
| 851 | spin_unlock_irqrestore(&rtas.lock, flags); | 872 | unlock_rtas(flags); | 
| 852 | 873 | ||
| 853 | if (buff_copy) { | 874 | if (buff_copy) { | 
| 854 | if (errbuf) | 875 | if (errbuf) | 
| @@ -951,3 +972,33 @@ int __init early_init_dt_scan_rtas(unsigned long node, | |||
| 951 | /* break now */ | 972 | /* break now */ | 
| 952 | return 1; | 973 | return 1; | 
| 953 | } | 974 | } | 
| 975 | |||
| 976 | static raw_spinlock_t timebase_lock; | ||
| 977 | static u64 timebase = 0; | ||
| 978 | |||
| 979 | void __cpuinit rtas_give_timebase(void) | ||
| 980 | { | ||
| 981 | unsigned long flags; | ||
| 982 | |||
| 983 | local_irq_save(flags); | ||
| 984 | hard_irq_disable(); | ||
| 985 | __raw_spin_lock(&timebase_lock); | ||
| 986 | rtas_call(rtas_token("freeze-time-base"), 0, 1, NULL); | ||
| 987 | timebase = get_tb(); | ||
| 988 | __raw_spin_unlock(&timebase_lock); | ||
| 989 | |||
| 990 | while (timebase) | ||
| 991 | barrier(); | ||
| 992 | rtas_call(rtas_token("thaw-time-base"), 0, 1, NULL); | ||
| 993 | local_irq_restore(flags); | ||
| 994 | } | ||
| 995 | |||
| 996 | void __cpuinit rtas_take_timebase(void) | ||
| 997 | { | ||
| 998 | while (!timebase) | ||
| 999 | barrier(); | ||
| 1000 | __raw_spin_lock(&timebase_lock); | ||
| 1001 | set_tb(timebase >> 32, timebase & 0xffffffff); | ||
| 1002 | timebase = 0; | ||
| 1003 | __raw_spin_unlock(&timebase_lock); | ||
| 1004 | } | ||
