Commit 535c4879 authored by Anton Blanchard's avatar Anton Blanchard

ppc64: rtas rtc fixes from Todd Inglett

parent 7430b429
...@@ -72,6 +72,7 @@ extern void openpic_init_IRQ(void); ...@@ -72,6 +72,7 @@ extern void openpic_init_IRQ(void);
extern void find_and_init_phbs(void); extern void find_and_init_phbs(void);
extern void pSeries_get_boot_time(struct rtc_time *rtc_time);
extern void pSeries_get_rtc_time(struct rtc_time *rtc_time); extern void pSeries_get_rtc_time(struct rtc_time *rtc_time);
extern int pSeries_set_rtc_time(struct rtc_time *rtc_time); extern int pSeries_set_rtc_time(struct rtc_time *rtc_time);
void pSeries_calibrate_decr(void); void pSeries_calibrate_decr(void);
...@@ -256,7 +257,7 @@ chrp_init(unsigned long r3, unsigned long r4, unsigned long r5, ...@@ -256,7 +257,7 @@ chrp_init(unsigned long r3, unsigned long r4, unsigned long r5,
ppc_md.power_off = rtas_power_off; ppc_md.power_off = rtas_power_off;
ppc_md.halt = rtas_halt; ppc_md.halt = rtas_halt;
ppc_md.get_boot_time = pSeries_get_rtc_time; ppc_md.get_boot_time = pSeries_get_boot_time;
ppc_md.get_rtc_time = pSeries_get_rtc_time; ppc_md.get_rtc_time = pSeries_get_rtc_time;
ppc_md.set_rtc_time = pSeries_set_rtc_time; ppc_md.set_rtc_time = pSeries_set_rtc_time;
ppc_md.calibrate_decr = pSeries_calibrate_decr; ppc_md.calibrate_decr = pSeries_calibrate_decr;
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <asm/machdep.h> #include <asm/machdep.h>
#include <asm/paca.h> #include <asm/paca.h>
#include <asm/page.h> #include <asm/page.h>
#include <asm/param.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/abs_addr.h> #include <asm/abs_addr.h>
#include <asm/udbg.h> #include <asm/udbg.h>
...@@ -178,6 +179,26 @@ rtas_call(int token, int nargs, int nret, ...@@ -178,6 +179,26 @@ rtas_call(int token, int nargs, int nret,
return (ulong)((nret > 0) ? rtas_args->rets[0] : 0); return (ulong)((nret > 0) ? rtas_args->rets[0] : 0);
} }
/* Given an RTAS status code of 990n compute the hinted delay of 10^n
* (last digit) milliseconds. For now we bound at n=3 (1 sec).
*/
unsigned int
rtas_extended_busy_delay_time(int status)
{
int order = status - 9900;
unsigned int ms;
if (order < 0)
order = 0; /* RTC depends on this for -2 clock busy */
else if (order > 3)
order = 3; /* bound */
/* Use microseconds for reasonable accuracy */
for (ms = 1000; order > 0; order--)
ms = ms * 10;
return ms / (1000000/HZ); /* round down is fine */
}
#define FLASH_BLOCK_LIST_VERSION (1UL) #define FLASH_BLOCK_LIST_VERSION (1UL)
static void static void
rtas_flash_firmware(void) rtas_flash_firmware(void)
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/bcd.h> #include <linux/bcd.h>
#include <asm/hardirq.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/system.h> #include <asm/system.h>
...@@ -340,20 +341,63 @@ void iSeries_get_boot_time(struct rtc_time *tm) ...@@ -340,20 +341,63 @@ void iSeries_get_boot_time(struct rtc_time *tm)
#endif #endif
#ifdef CONFIG_PPC_PSERIES #ifdef CONFIG_PPC_PSERIES
#define MAX_RTC_WAIT 5000 /* 5 sec */
#define RTAS_CLOCK_BUSY (-2)
void pSeries_get_boot_time(struct rtc_time *rtc_tm)
{
unsigned long ret[8];
int error, wait_time;
unsigned long max_wait_tb;
max_wait_tb = __get_tb() + tb_ticks_per_usec * 1000 * MAX_RTC_WAIT;
do {
error = rtas_call(rtas_token("get-time-of-day"), 0, 8, (void *)&ret);
if (error == RTAS_CLOCK_BUSY || rtas_is_extended_busy(error)) {
wait_time = rtas_extended_busy_delay_time(error);
/* This is boot time so we spin. */
udelay(wait_time*1000);
error = RTAS_CLOCK_BUSY;
}
} while (error == RTAS_CLOCK_BUSY && (__get_tb() < max_wait_tb));
if (error != 0) {
printk(KERN_WARNING "error: reading the clock failed (%d)\n",
error);
return;
}
rtc_tm->tm_sec = ret[5];
rtc_tm->tm_min = ret[4];
rtc_tm->tm_hour = ret[3];
rtc_tm->tm_mday = ret[2];
rtc_tm->tm_mon = ret[1] - 1;
rtc_tm->tm_year = ret[0] - 1900;
}
/* NOTE: get_rtc_time will get an error if executed in interrupt context
* and if a delay is needed to read the clock. In this case we just
* silently return without updating rtc_tm.
*/
void pSeries_get_rtc_time(struct rtc_time *rtc_tm) void pSeries_get_rtc_time(struct rtc_time *rtc_tm)
{ {
unsigned long ret[8]; unsigned long ret[8];
int error; int error, wait_time;
int count; unsigned long max_wait_tb;
/* max_wait_tb = __get_tb() + tb_ticks_per_usec * 1000 * MAX_RTC_WAIT;
* error -2 is clock busy, we keep retrying a few times to see
* if it will come good -- paulus
*/
count = 0;
do { do {
error = rtas_call(rtas_token("get-time-of-day"), 0, 8, (void *)&ret); error = rtas_call(rtas_token("get-time-of-day"), 0, 8, (void *)&ret);
} while (error == -2 && ++count < 1000); if (error == RTAS_CLOCK_BUSY || rtas_is_extended_busy(error)) {
if (in_interrupt()) {
printk(KERN_WARNING "error: reading clock would delay interrupt\n");
return; /* delay not allowed */
}
wait_time = rtas_extended_busy_delay_time(error);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(wait_time);
error = RTAS_CLOCK_BUSY;
}
} while (error == RTAS_CLOCK_BUSY && (__get_tb() < max_wait_tb));
if (error != 0) { if (error != 0) {
printk(KERN_WARNING "error: reading the clock failed (%d)\n", printk(KERN_WARNING "error: reading the clock failed (%d)\n",
...@@ -371,20 +415,24 @@ void pSeries_get_rtc_time(struct rtc_time *rtc_tm) ...@@ -371,20 +415,24 @@ void pSeries_get_rtc_time(struct rtc_time *rtc_tm)
int pSeries_set_rtc_time(struct rtc_time *tm) int pSeries_set_rtc_time(struct rtc_time *tm)
{ {
int error; int error, wait_time;
int count; unsigned long max_wait_tb;
/* max_wait_tb = __get_tb() + tb_ticks_per_usec * 1000 * MAX_RTC_WAIT;
* error -2 is clock busy, we keep retrying a few times to see
* if it will come good -- paulus
*/
count = 0;
do { do {
error = rtas_call(rtas_token("set-time-of-day"), 7, 1, NULL, error = rtas_call(rtas_token("set-time-of-day"), 7, 1, NULL,
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_year + 1900, tm->tm_mon + 1,
tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_mday, tm->tm_hour, tm->tm_min,
tm->tm_sec, 0); tm->tm_sec, 0);
} while (error == -2 && ++count < 1000); if (error == RTAS_CLOCK_BUSY || rtas_is_extended_busy(error)) {
if (in_interrupt())
return 1; /* probably decrementer */
wait_time = rtas_extended_busy_delay_time(error);
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(wait_time);
error = RTAS_CLOCK_BUSY;
}
} while (error == RTAS_CLOCK_BUSY && (__get_tb() < max_wait_tb));
if (error != 0) if (error != 0)
printk(KERN_WARNING "error: setting the clock failed (%d)\n", printk(KERN_WARNING "error: setting the clock failed (%d)\n",
......
...@@ -166,6 +166,13 @@ extern void rtas_restart(char *cmd); ...@@ -166,6 +166,13 @@ extern void rtas_restart(char *cmd);
extern void rtas_power_off(void); extern void rtas_power_off(void);
extern void rtas_halt(void); extern void rtas_halt(void);
/* Given an RTAS status code of 9900..9905 compute the hinted delay */
unsigned int rtas_extended_busy_delay_time(int status);
static inline int rtas_is_extended_busy(int status)
{
return status >= 9900 && status <= 9909;
}
/* Some RTAS ops require a data buffer and that buffer must be < 4G. /* Some RTAS ops require a data buffer and that buffer must be < 4G.
* Rather than having a memory allocator, just use this buffer * Rather than having a memory allocator, just use this buffer
* (get the lock first), make the RTAS call. Copy the data instead * (get the lock first), make the RTAS call. Copy the data instead
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment