diff options
author | John Rose <johnrose@austin.ibm.com> | 2006-06-05 17:31:48 -0400 |
---|---|---|
committer | Paul Mackerras <paulus@samba.org> | 2006-06-09 07:21:06 -0400 |
commit | 507279db1819aacf4022e790b3fc8bc8cf56debf (patch) | |
tree | e5e95d50b093f0eab31e7856b9f596caa6e097af /arch/powerpc | |
parent | 4a3ecc622465dbff7404139a8ad18bf4cb99f836 (diff) |
[PATCH] powerpc: reorg RTAS delay code
This patch attempts to handle RTAS "busy" return codes in a more simple
and consistent manner. Typical callers of RTAS shouldn't have to
manage wait times and delay calls.
This patch also changes the kernel to use msleep() rather than udelay()
when a runtime delay is necessary. This will avoid CPU soft lockups
for extended delay conditions.
Signed-off-by: John Rose <johnrose@austin.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/kernel/rtas-rtc.c | 30 | ||||
-rw-r--r-- | arch/powerpc/kernel/rtas.c | 85 | ||||
-rw-r--r-- | arch/powerpc/kernel/rtas_flash.c | 25 |
3 files changed, 55 insertions, 85 deletions
diff --git a/arch/powerpc/kernel/rtas-rtc.c b/arch/powerpc/kernel/rtas-rtc.c index 34d073fb6091..77578c093dda 100644 --- a/arch/powerpc/kernel/rtas-rtc.c +++ b/arch/powerpc/kernel/rtas-rtc.c | |||
@@ -14,19 +14,20 @@ | |||
14 | unsigned long __init rtas_get_boot_time(void) | 14 | unsigned long __init rtas_get_boot_time(void) |
15 | { | 15 | { |
16 | int ret[8]; | 16 | int ret[8]; |
17 | int error, wait_time; | 17 | int error; |
18 | unsigned int wait_time; | ||
18 | u64 max_wait_tb; | 19 | u64 max_wait_tb; |
19 | 20 | ||
20 | max_wait_tb = get_tb() + tb_ticks_per_usec * 1000 * MAX_RTC_WAIT; | 21 | max_wait_tb = get_tb() + tb_ticks_per_usec * 1000 * MAX_RTC_WAIT; |
21 | do { | 22 | do { |
22 | error = rtas_call(rtas_token("get-time-of-day"), 0, 8, ret); | 23 | error = rtas_call(rtas_token("get-time-of-day"), 0, 8, ret); |
23 | if (error == RTAS_CLOCK_BUSY || rtas_is_extended_busy(error)) { | 24 | |
24 | wait_time = rtas_extended_busy_delay_time(error); | 25 | wait_time = rtas_busy_delay_time(error); |
26 | if (wait_time) { | ||
25 | /* This is boot time so we spin. */ | 27 | /* This is boot time so we spin. */ |
26 | udelay(wait_time*1000); | 28 | udelay(wait_time*1000); |
27 | error = RTAS_CLOCK_BUSY; | ||
28 | } | 29 | } |
29 | } while (error == RTAS_CLOCK_BUSY && (get_tb() < max_wait_tb)); | 30 | } while (wait_time && (get_tb() < max_wait_tb)); |
30 | 31 | ||
31 | if (error != 0 && printk_ratelimit()) { | 32 | if (error != 0 && printk_ratelimit()) { |
32 | printk(KERN_WARNING "error: reading the clock failed (%d)\n", | 33 | printk(KERN_WARNING "error: reading the clock failed (%d)\n", |
@@ -44,24 +45,25 @@ unsigned long __init rtas_get_boot_time(void) | |||
44 | void rtas_get_rtc_time(struct rtc_time *rtc_tm) | 45 | void rtas_get_rtc_time(struct rtc_time *rtc_tm) |
45 | { | 46 | { |
46 | int ret[8]; | 47 | int ret[8]; |
47 | int error, wait_time; | 48 | int error; |
49 | unsigned int wait_time; | ||
48 | u64 max_wait_tb; | 50 | u64 max_wait_tb; |
49 | 51 | ||
50 | max_wait_tb = get_tb() + tb_ticks_per_usec * 1000 * MAX_RTC_WAIT; | 52 | max_wait_tb = get_tb() + tb_ticks_per_usec * 1000 * MAX_RTC_WAIT; |
51 | do { | 53 | do { |
52 | error = rtas_call(rtas_token("get-time-of-day"), 0, 8, ret); | 54 | error = rtas_call(rtas_token("get-time-of-day"), 0, 8, ret); |
53 | if (error == RTAS_CLOCK_BUSY || rtas_is_extended_busy(error)) { | 55 | |
56 | wait_time = rtas_busy_delay_time(error); | ||
57 | if (wait_time) { | ||
54 | if (in_interrupt() && printk_ratelimit()) { | 58 | if (in_interrupt() && printk_ratelimit()) { |
55 | memset(rtc_tm, 0, sizeof(struct rtc_time)); | 59 | memset(rtc_tm, 0, sizeof(struct rtc_time)); |
56 | printk(KERN_WARNING "error: reading clock" | 60 | printk(KERN_WARNING "error: reading clock" |
57 | " would delay interrupt\n"); | 61 | " would delay interrupt\n"); |
58 | return; /* delay not allowed */ | 62 | return; /* delay not allowed */ |
59 | } | 63 | } |
60 | wait_time = rtas_extended_busy_delay_time(error); | ||
61 | msleep(wait_time); | 64 | msleep(wait_time); |
62 | error = RTAS_CLOCK_BUSY; | ||
63 | } | 65 | } |
64 | } while (error == RTAS_CLOCK_BUSY && (get_tb() < max_wait_tb)); | 66 | } while (wait_time && (get_tb() < max_wait_tb)); |
65 | 67 | ||
66 | if (error != 0 && printk_ratelimit()) { | 68 | if (error != 0 && printk_ratelimit()) { |
67 | printk(KERN_WARNING "error: reading the clock failed (%d)\n", | 69 | printk(KERN_WARNING "error: reading the clock failed (%d)\n", |
@@ -88,14 +90,14 @@ int rtas_set_rtc_time(struct rtc_time *tm) | |||
88 | tm->tm_year + 1900, tm->tm_mon + 1, | 90 | tm->tm_year + 1900, tm->tm_mon + 1, |
89 | tm->tm_mday, tm->tm_hour, tm->tm_min, | 91 | tm->tm_mday, tm->tm_hour, tm->tm_min, |
90 | tm->tm_sec, 0); | 92 | tm->tm_sec, 0); |
91 | if (error == RTAS_CLOCK_BUSY || rtas_is_extended_busy(error)) { | 93 | |
94 | wait_time = rtas_busy_delay_time(error); | ||
95 | if (wait_time) { | ||
92 | if (in_interrupt()) | 96 | if (in_interrupt()) |
93 | return 1; /* probably decrementer */ | 97 | return 1; /* probably decrementer */ |
94 | wait_time = rtas_extended_busy_delay_time(error); | ||
95 | msleep(wait_time); | 98 | msleep(wait_time); |
96 | error = RTAS_CLOCK_BUSY; | ||
97 | } | 99 | } |
98 | } while (error == RTAS_CLOCK_BUSY && (get_tb() < max_wait_tb)); | 100 | } while (wait_time && (get_tb() < max_wait_tb)); |
99 | 101 | ||
100 | if (error != 0 && printk_ratelimit()) | 102 | if (error != 0 && printk_ratelimit()) |
101 | printk(KERN_WARNING "error: setting the clock failed (%d)\n", | 103 | printk(KERN_WARNING "error: setting the clock failed (%d)\n", |
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c index 0112318213ab..13496f319855 100644 --- a/arch/powerpc/kernel/rtas.c +++ b/arch/powerpc/kernel/rtas.c | |||
@@ -370,24 +370,36 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...) | |||
370 | return ret; | 370 | return ret; |
371 | } | 371 | } |
372 | 372 | ||
373 | /* Given an RTAS status code of 990n compute the hinted delay of 10^n | 373 | /* For RTAS_BUSY (-2), delay for 1 millisecond. For an extended busy status |
374 | * (last digit) milliseconds. For now we bound at n=5 (100 sec). | 374 | * code of 990n, perform the hinted delay of 10^n (last digit) milliseconds. |
375 | */ | 375 | */ |
376 | unsigned int rtas_extended_busy_delay_time(int status) | 376 | unsigned int rtas_busy_delay_time(int status) |
377 | { | 377 | { |
378 | int order = status - 9900; | 378 | int order; |
379 | unsigned long ms; | 379 | unsigned int ms = 0; |
380 | |||
381 | if (status == RTAS_BUSY) { | ||
382 | ms = 1; | ||
383 | } else if (status >= 9900 && status <= 9905) { | ||
384 | order = status - 9900; | ||
385 | for (ms = 1; order > 0; order--) | ||
386 | ms *= 10; | ||
387 | } | ||
380 | 388 | ||
381 | if (order < 0) | 389 | return ms; |
382 | order = 0; /* RTC depends on this for -2 clock busy */ | 390 | } |
383 | else if (order > 5) | ||
384 | order = 5; /* bound */ | ||
385 | 391 | ||
386 | /* Use microseconds for reasonable accuracy */ | 392 | /* For an RTAS busy status code, perform the hinted delay. */ |
387 | for (ms = 1; order > 0; order--) | 393 | unsigned int rtas_busy_delay(int status) |
388 | ms *= 10; | 394 | { |
395 | unsigned int ms; | ||
389 | 396 | ||
390 | return ms; | 397 | might_sleep(); |
398 | ms = rtas_busy_delay_time(status); | ||
399 | if (ms) | ||
400 | msleep(ms); | ||
401 | |||
402 | return ms; | ||
391 | } | 403 | } |
392 | 404 | ||
393 | int rtas_error_rc(int rtas_rc) | 405 | int rtas_error_rc(int rtas_rc) |
@@ -438,22 +450,14 @@ int rtas_get_power_level(int powerdomain, int *level) | |||
438 | int rtas_set_power_level(int powerdomain, int level, int *setlevel) | 450 | int rtas_set_power_level(int powerdomain, int level, int *setlevel) |
439 | { | 451 | { |
440 | int token = rtas_token("set-power-level"); | 452 | int token = rtas_token("set-power-level"); |
441 | unsigned int wait_time; | ||
442 | int rc; | 453 | int rc; |
443 | 454 | ||
444 | if (token == RTAS_UNKNOWN_SERVICE) | 455 | if (token == RTAS_UNKNOWN_SERVICE) |
445 | return -ENOENT; | 456 | return -ENOENT; |
446 | 457 | ||
447 | while (1) { | 458 | do { |
448 | rc = rtas_call(token, 2, 2, setlevel, powerdomain, level); | 459 | rc = rtas_call(token, 2, 2, setlevel, powerdomain, level); |
449 | if (rc == RTAS_BUSY) | 460 | } while (rtas_busy_delay(rc)); |
450 | udelay(1); | ||
451 | else if (rtas_is_extended_busy(rc)) { | ||
452 | wait_time = rtas_extended_busy_delay_time(rc); | ||
453 | udelay(wait_time * 1000); | ||
454 | } else | ||
455 | break; | ||
456 | } | ||
457 | 461 | ||
458 | if (rc < 0) | 462 | if (rc < 0) |
459 | return rtas_error_rc(rc); | 463 | return rtas_error_rc(rc); |
@@ -463,22 +467,14 @@ int rtas_set_power_level(int powerdomain, int level, int *setlevel) | |||
463 | int rtas_get_sensor(int sensor, int index, int *state) | 467 | int rtas_get_sensor(int sensor, int index, int *state) |
464 | { | 468 | { |
465 | int token = rtas_token("get-sensor-state"); | 469 | int token = rtas_token("get-sensor-state"); |
466 | unsigned int wait_time; | ||
467 | int rc; | 470 | int rc; |
468 | 471 | ||
469 | if (token == RTAS_UNKNOWN_SERVICE) | 472 | if (token == RTAS_UNKNOWN_SERVICE) |
470 | return -ENOENT; | 473 | return -ENOENT; |
471 | 474 | ||
472 | while (1) { | 475 | do { |
473 | rc = rtas_call(token, 2, 2, state, sensor, index); | 476 | rc = rtas_call(token, 2, 2, state, sensor, index); |
474 | if (rc == RTAS_BUSY) | 477 | } while (rtas_busy_delay(rc)); |
475 | udelay(1); | ||
476 | else if (rtas_is_extended_busy(rc)) { | ||
477 | wait_time = rtas_extended_busy_delay_time(rc); | ||
478 | udelay(wait_time * 1000); | ||
479 | } else | ||
480 | break; | ||
481 | } | ||
482 | 478 | ||
483 | if (rc < 0) | 479 | if (rc < 0) |
484 | return rtas_error_rc(rc); | 480 | return rtas_error_rc(rc); |
@@ -488,23 +484,14 @@ int rtas_get_sensor(int sensor, int index, int *state) | |||
488 | int rtas_set_indicator(int indicator, int index, int new_value) | 484 | int rtas_set_indicator(int indicator, int index, int new_value) |
489 | { | 485 | { |
490 | int token = rtas_token("set-indicator"); | 486 | int token = rtas_token("set-indicator"); |
491 | unsigned int wait_time; | ||
492 | int rc; | 487 | int rc; |
493 | 488 | ||
494 | if (token == RTAS_UNKNOWN_SERVICE) | 489 | if (token == RTAS_UNKNOWN_SERVICE) |
495 | return -ENOENT; | 490 | return -ENOENT; |
496 | 491 | ||
497 | while (1) { | 492 | do { |
498 | rc = rtas_call(token, 3, 1, NULL, indicator, index, new_value); | 493 | rc = rtas_call(token, 3, 1, NULL, indicator, index, new_value); |
499 | if (rc == RTAS_BUSY) | 494 | } while (rtas_busy_delay(rc)); |
500 | udelay(1); | ||
501 | else if (rtas_is_extended_busy(rc)) { | ||
502 | wait_time = rtas_extended_busy_delay_time(rc); | ||
503 | udelay(wait_time * 1000); | ||
504 | } | ||
505 | else | ||
506 | break; | ||
507 | } | ||
508 | 495 | ||
509 | if (rc < 0) | 496 | if (rc < 0) |
510 | return rtas_error_rc(rc); | 497 | return rtas_error_rc(rc); |
@@ -555,13 +542,11 @@ void rtas_os_term(char *str) | |||
555 | do { | 542 | do { |
556 | status = rtas_call(rtas_token("ibm,os-term"), 1, 1, NULL, | 543 | status = rtas_call(rtas_token("ibm,os-term"), 1, 1, NULL, |
557 | __pa(rtas_os_term_buf)); | 544 | __pa(rtas_os_term_buf)); |
545 | } while (rtas_busy_delay(status)); | ||
558 | 546 | ||
559 | if (status == RTAS_BUSY) | 547 | if (status != 0) |
560 | udelay(1); | 548 | printk(KERN_EMERG "ibm,os-term call failed %d\n", |
561 | else if (status != 0) | ||
562 | printk(KERN_EMERG "ibm,os-term call failed %d\n", | ||
563 | status); | 549 | status); |
564 | } while (status == RTAS_BUSY); | ||
565 | } | 550 | } |
566 | 551 | ||
567 | static int ibm_suspend_me_token = RTAS_UNKNOWN_SERVICE; | 552 | static int ibm_suspend_me_token = RTAS_UNKNOWN_SERVICE; |
@@ -789,7 +774,7 @@ EXPORT_SYMBOL(rtas_token); | |||
789 | EXPORT_SYMBOL(rtas_call); | 774 | EXPORT_SYMBOL(rtas_call); |
790 | EXPORT_SYMBOL(rtas_data_buf); | 775 | EXPORT_SYMBOL(rtas_data_buf); |
791 | EXPORT_SYMBOL(rtas_data_buf_lock); | 776 | EXPORT_SYMBOL(rtas_data_buf_lock); |
792 | EXPORT_SYMBOL(rtas_extended_busy_delay_time); | 777 | EXPORT_SYMBOL(rtas_busy_delay_time); |
793 | EXPORT_SYMBOL(rtas_get_sensor); | 778 | EXPORT_SYMBOL(rtas_get_sensor); |
794 | EXPORT_SYMBOL(rtas_get_power_level); | 779 | EXPORT_SYMBOL(rtas_get_power_level); |
795 | EXPORT_SYMBOL(rtas_set_power_level); | 780 | EXPORT_SYMBOL(rtas_set_power_level); |
diff --git a/arch/powerpc/kernel/rtas_flash.c b/arch/powerpc/kernel/rtas_flash.c index aaf384c3f04a..1442b63a75da 100644 --- a/arch/powerpc/kernel/rtas_flash.c +++ b/arch/powerpc/kernel/rtas_flash.c | |||
@@ -365,20 +365,12 @@ static int rtas_excl_release(struct inode *inode, struct file *file) | |||
365 | 365 | ||
366 | static void manage_flash(struct rtas_manage_flash_t *args_buf) | 366 | static void manage_flash(struct rtas_manage_flash_t *args_buf) |
367 | { | 367 | { |
368 | unsigned int wait_time; | ||
369 | s32 rc; | 368 | s32 rc; |
370 | 369 | ||
371 | while (1) { | 370 | do { |
372 | rc = rtas_call(rtas_token("ibm,manage-flash-image"), 1, | 371 | rc = rtas_call(rtas_token("ibm,manage-flash-image"), 1, |
373 | 1, NULL, args_buf->op); | 372 | 1, NULL, args_buf->op); |
374 | if (rc == RTAS_RC_BUSY) | 373 | } while (rtas_busy_delay(rc)); |
375 | udelay(1); | ||
376 | else if (rtas_is_extended_busy(rc)) { | ||
377 | wait_time = rtas_extended_busy_delay_time(rc); | ||
378 | udelay(wait_time * 1000); | ||
379 | } else | ||
380 | break; | ||
381 | } | ||
382 | 374 | ||
383 | args_buf->status = rc; | 375 | args_buf->status = rc; |
384 | } | 376 | } |
@@ -451,27 +443,18 @@ static ssize_t manage_flash_write(struct file *file, const char __user *buf, | |||
451 | static void validate_flash(struct rtas_validate_flash_t *args_buf) | 443 | static void validate_flash(struct rtas_validate_flash_t *args_buf) |
452 | { | 444 | { |
453 | int token = rtas_token("ibm,validate-flash-image"); | 445 | int token = rtas_token("ibm,validate-flash-image"); |
454 | unsigned int wait_time; | ||
455 | int update_results; | 446 | int update_results; |
456 | s32 rc; | 447 | s32 rc; |
457 | 448 | ||
458 | rc = 0; | 449 | rc = 0; |
459 | while(1) { | 450 | do { |
460 | spin_lock(&rtas_data_buf_lock); | 451 | spin_lock(&rtas_data_buf_lock); |
461 | memcpy(rtas_data_buf, args_buf->buf, VALIDATE_BUF_SIZE); | 452 | memcpy(rtas_data_buf, args_buf->buf, VALIDATE_BUF_SIZE); |
462 | rc = rtas_call(token, 2, 2, &update_results, | 453 | rc = rtas_call(token, 2, 2, &update_results, |
463 | (u32) __pa(rtas_data_buf), args_buf->buf_size); | 454 | (u32) __pa(rtas_data_buf), args_buf->buf_size); |
464 | memcpy(args_buf->buf, rtas_data_buf, VALIDATE_BUF_SIZE); | 455 | memcpy(args_buf->buf, rtas_data_buf, VALIDATE_BUF_SIZE); |
465 | spin_unlock(&rtas_data_buf_lock); | 456 | spin_unlock(&rtas_data_buf_lock); |
466 | 457 | } while (rtas_busy_delay(rc)); | |
467 | if (rc == RTAS_RC_BUSY) | ||
468 | udelay(1); | ||
469 | else if (rtas_is_extended_busy(rc)) { | ||
470 | wait_time = rtas_extended_busy_delay_time(rc); | ||
471 | udelay(wait_time * 1000); | ||
472 | } else | ||
473 | break; | ||
474 | } | ||
475 | 458 | ||
476 | args_buf->status = rc; | 459 | args_buf->status = rc; |
477 | args_buf->update_results = update_results; | 460 | args_buf->update_results = update_results; |