forked from Minki/linux
10f7e7c15e
pSeries and maple have almost the same code for calibrate_decr, and BPA would need yet another copy. Instead, I'm moving the code to arch/ppc64/kernel/time.c. Some of the related declarations were missing from header files, so I'm moving those as well. It makes sense to merge this with the pmac function of the same name, so we end up having just one implemetation for iSeries and one for Open Firmware based machines. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Paul Mackerras <paulus@samba.org>
196 lines
4.9 KiB
C
196 lines
4.9 KiB
C
/*
|
|
* Support for periodic interrupts (100 per second) and for getting
|
|
* the current time from the RTC on Power Macintoshes.
|
|
*
|
|
* We use the decrementer register for our periodic interrupts.
|
|
*
|
|
* Paul Mackerras August 1996.
|
|
* Copyright (C) 1996 Paul Mackerras.
|
|
* Copyright (C) 2003-2005 Benjamin Herrenschmidt.
|
|
*
|
|
*/
|
|
#include <linux/config.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/param.h>
|
|
#include <linux/string.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/init.h>
|
|
#include <linux/time.h>
|
|
#include <linux/adb.h>
|
|
#include <linux/pmu.h>
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <asm/sections.h>
|
|
#include <asm/prom.h>
|
|
#include <asm/system.h>
|
|
#include <asm/io.h>
|
|
#include <asm/pgtable.h>
|
|
#include <asm/machdep.h>
|
|
#include <asm/time.h>
|
|
#include <asm/nvram.h>
|
|
#include <asm/smu.h>
|
|
|
|
#undef DEBUG
|
|
|
|
#ifdef DEBUG
|
|
#define DBG(x...) printk(x)
|
|
#else
|
|
#define DBG(x...)
|
|
#endif
|
|
|
|
/* Apparently the RTC stores seconds since 1 Jan 1904 */
|
|
#define RTC_OFFSET 2082844800
|
|
|
|
/*
|
|
* Calibrate the decrementer frequency with the VIA timer 1.
|
|
*/
|
|
#define VIA_TIMER_FREQ_6 4700000 /* time 1 frequency * 6 */
|
|
|
|
extern struct timezone sys_tz;
|
|
extern void to_tm(int tim, struct rtc_time * tm);
|
|
|
|
void __pmac pmac_get_rtc_time(struct rtc_time *tm)
|
|
{
|
|
switch(sys_ctrler) {
|
|
#ifdef CONFIG_ADB_PMU
|
|
case SYS_CTRLER_PMU: {
|
|
/* TODO: Move that to a function in the PMU driver */
|
|
struct adb_request req;
|
|
unsigned int now;
|
|
|
|
if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0)
|
|
return;
|
|
pmu_wait_complete(&req);
|
|
if (req.reply_len != 4)
|
|
printk(KERN_ERR "pmac_get_rtc_time: PMU returned a %d"
|
|
" bytes reply\n", req.reply_len);
|
|
now = (req.reply[0] << 24) + (req.reply[1] << 16)
|
|
+ (req.reply[2] << 8) + req.reply[3];
|
|
DBG("get: %u -> %u\n", (int)now, (int)(now - RTC_OFFSET));
|
|
now -= RTC_OFFSET;
|
|
|
|
to_tm(now, tm);
|
|
tm->tm_year -= 1900;
|
|
tm->tm_mon -= 1;
|
|
|
|
DBG("-> tm_mday: %d, tm_mon: %d, tm_year: %d, %d:%02d:%02d\n",
|
|
tm->tm_mday, tm->tm_mon, tm->tm_year,
|
|
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
|
break;
|
|
}
|
|
#endif /* CONFIG_ADB_PMU */
|
|
|
|
#ifdef CONFIG_PMAC_SMU
|
|
case SYS_CTRLER_SMU:
|
|
smu_get_rtc_time(tm);
|
|
break;
|
|
#endif /* CONFIG_PMAC_SMU */
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
int __pmac pmac_set_rtc_time(struct rtc_time *tm)
|
|
{
|
|
switch(sys_ctrler) {
|
|
#ifdef CONFIG_ADB_PMU
|
|
case SYS_CTRLER_PMU: {
|
|
/* TODO: Move that to a function in the PMU driver */
|
|
struct adb_request req;
|
|
unsigned int nowtime;
|
|
|
|
DBG("set: tm_mday: %d, tm_mon: %d, tm_year: %d,"
|
|
" %d:%02d:%02d\n",
|
|
tm->tm_mday, tm->tm_mon, tm->tm_year,
|
|
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
|
|
|
nowtime = mktime(tm->tm_year + 1900, tm->tm_mon + 1,
|
|
tm->tm_mday, tm->tm_hour, tm->tm_min,
|
|
tm->tm_sec);
|
|
|
|
DBG("-> %u -> %u\n", (int)nowtime,
|
|
(int)(nowtime + RTC_OFFSET));
|
|
nowtime += RTC_OFFSET;
|
|
|
|
if (pmu_request(&req, NULL, 5, PMU_SET_RTC,
|
|
nowtime >> 24, nowtime >> 16,
|
|
nowtime >> 8, nowtime) < 0)
|
|
return -ENXIO;
|
|
pmu_wait_complete(&req);
|
|
if (req.reply_len != 0)
|
|
printk(KERN_ERR "pmac_set_rtc_time: PMU returned a %d"
|
|
" bytes reply\n", req.reply_len);
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_ADB_PMU */
|
|
|
|
#ifdef CONFIG_PMAC_SMU
|
|
case SYS_CTRLER_SMU:
|
|
return smu_set_rtc_time(tm);
|
|
#endif /* CONFIG_PMAC_SMU */
|
|
default:
|
|
return -ENODEV;
|
|
}
|
|
}
|
|
|
|
void __init pmac_get_boot_time(struct rtc_time *tm)
|
|
{
|
|
pmac_get_rtc_time(tm);
|
|
|
|
#ifdef disabled__CONFIG_NVRAM
|
|
s32 delta = 0;
|
|
int dst;
|
|
|
|
delta = ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x9)) << 16;
|
|
delta |= ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xa)) << 8;
|
|
delta |= pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xb);
|
|
if (delta & 0x00800000UL)
|
|
delta |= 0xFF000000UL;
|
|
dst = ((pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x8) & 0x80) != 0);
|
|
printk("GMT Delta read from XPRAM: %d minutes, DST: %s\n", delta/60,
|
|
dst ? "on" : "off");
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Query the OF and get the decr frequency.
|
|
* FIXME: merge this with generic_calibrate_decr
|
|
*/
|
|
void __init pmac_calibrate_decr(void)
|
|
{
|
|
struct device_node *cpu;
|
|
unsigned int freq, *fp;
|
|
struct div_result divres;
|
|
|
|
/*
|
|
* The cpu node should have a timebase-frequency property
|
|
* to tell us the rate at which the decrementer counts.
|
|
*/
|
|
cpu = find_type_devices("cpu");
|
|
if (cpu == 0)
|
|
panic("can't find cpu node in time_init");
|
|
fp = (unsigned int *) get_property(cpu, "timebase-frequency", NULL);
|
|
if (fp == 0)
|
|
panic("can't get cpu timebase frequency");
|
|
freq = *fp;
|
|
printk("time_init: decrementer frequency = %u.%.6u MHz\n",
|
|
freq/1000000, freq%1000000);
|
|
tb_ticks_per_jiffy = freq / HZ;
|
|
tb_ticks_per_sec = tb_ticks_per_jiffy * HZ;
|
|
tb_ticks_per_usec = freq / 1000000;
|
|
tb_to_us = mulhwu_scale_factor(freq, 1000000);
|
|
div128_by_32( 1024*1024, 0, tb_ticks_per_sec, &divres );
|
|
tb_to_xs = divres.result_low;
|
|
ppc_tb_freq = freq;
|
|
|
|
fp = (unsigned int *)get_property(cpu, "clock-frequency", NULL);
|
|
if (fp == 0)
|
|
panic("can't get cpu processor frequency");
|
|
ppc_proc_freq = *fp;
|
|
|
|
setup_default_decr();
|
|
}
|
|
|