Path: blob/master/arch/powerpc/platforms/powermac/time.c
10818 views
/*1* Support for periodic interrupts (100 per second) and for getting2* the current time from the RTC on Power Macintoshes.3*4* We use the decrementer register for our periodic interrupts.5*6* Paul Mackerras August 1996.7* Copyright (C) 1996 Paul Mackerras.8* Copyright (C) 2003-2005 Benjamin Herrenschmidt.9*10*/11#include <linux/errno.h>12#include <linux/sched.h>13#include <linux/kernel.h>14#include <linux/param.h>15#include <linux/string.h>16#include <linux/mm.h>17#include <linux/init.h>18#include <linux/time.h>19#include <linux/adb.h>20#include <linux/cuda.h>21#include <linux/pmu.h>22#include <linux/interrupt.h>23#include <linux/hardirq.h>24#include <linux/rtc.h>2526#include <asm/sections.h>27#include <asm/prom.h>28#include <asm/system.h>29#include <asm/io.h>30#include <asm/pgtable.h>31#include <asm/machdep.h>32#include <asm/time.h>33#include <asm/nvram.h>34#include <asm/smu.h>3536#undef DEBUG3738#ifdef DEBUG39#define DBG(x...) printk(x)40#else41#define DBG(x...)42#endif4344/* Apparently the RTC stores seconds since 1 Jan 1904 */45#define RTC_OFFSET 20828448004647/*48* Calibrate the decrementer frequency with the VIA timer 1.49*/50#define VIA_TIMER_FREQ_6 4700000 /* time 1 frequency * 6 */5152/* VIA registers */53#define RS 0x200 /* skip between registers */54#define T1CL (4*RS) /* Timer 1 ctr/latch (low 8 bits) */55#define T1CH (5*RS) /* Timer 1 counter (high 8 bits) */56#define T1LL (6*RS) /* Timer 1 latch (low 8 bits) */57#define T1LH (7*RS) /* Timer 1 latch (high 8 bits) */58#define ACR (11*RS) /* Auxiliary control register */59#define IFR (13*RS) /* Interrupt flag register */6061/* Bits in ACR */62#define T1MODE 0xc0 /* Timer 1 mode */63#define T1MODE_CONT 0x40 /* continuous interrupts */6465/* Bits in IFR and IER */66#define T1_INT 0x40 /* Timer 1 interrupt */6768long __init pmac_time_init(void)69{70s32 delta = 0;71#ifdef CONFIG_NVRAM72int dst;7374delta = ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x9)) << 16;75delta |= ((s32)pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xa)) << 8;76delta |= pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0xb);77if (delta & 0x00800000UL)78delta |= 0xFF000000UL;79dst = ((pmac_xpram_read(PMAC_XPRAM_MACHINE_LOC + 0x8) & 0x80) != 0);80printk("GMT Delta read from XPRAM: %d minutes, DST: %s\n", delta/60,81dst ? "on" : "off");82#endif83return delta;84}8586#if defined(CONFIG_ADB_CUDA) || defined(CONFIG_ADB_PMU)87static void to_rtc_time(unsigned long now, struct rtc_time *tm)88{89to_tm(now, tm);90tm->tm_year -= 1900;91tm->tm_mon -= 1;92}93#endif9495#if defined(CONFIG_ADB_CUDA) || defined(CONFIG_ADB_PMU) || \96defined(CONFIG_PMAC_SMU)97static unsigned long from_rtc_time(struct rtc_time *tm)98{99return mktime(tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,100tm->tm_hour, tm->tm_min, tm->tm_sec);101}102#endif103104#ifdef CONFIG_ADB_CUDA105static unsigned long cuda_get_time(void)106{107struct adb_request req;108unsigned int now;109110if (cuda_request(&req, NULL, 2, CUDA_PACKET, CUDA_GET_TIME) < 0)111return 0;112while (!req.complete)113cuda_poll();114if (req.reply_len != 7)115printk(KERN_ERR "cuda_get_time: got %d byte reply\n",116req.reply_len);117now = (req.reply[3] << 24) + (req.reply[4] << 16)118+ (req.reply[5] << 8) + req.reply[6];119return ((unsigned long)now) - RTC_OFFSET;120}121122#define cuda_get_rtc_time(tm) to_rtc_time(cuda_get_time(), (tm))123124static int cuda_set_rtc_time(struct rtc_time *tm)125{126unsigned int nowtime;127struct adb_request req;128129nowtime = from_rtc_time(tm) + RTC_OFFSET;130if (cuda_request(&req, NULL, 6, CUDA_PACKET, CUDA_SET_TIME,131nowtime >> 24, nowtime >> 16, nowtime >> 8,132nowtime) < 0)133return -ENXIO;134while (!req.complete)135cuda_poll();136if ((req.reply_len != 3) && (req.reply_len != 7))137printk(KERN_ERR "cuda_set_rtc_time: got %d byte reply\n",138req.reply_len);139return 0;140}141142#else143#define cuda_get_time() 0144#define cuda_get_rtc_time(tm)145#define cuda_set_rtc_time(tm) 0146#endif147148#ifdef CONFIG_ADB_PMU149static unsigned long pmu_get_time(void)150{151struct adb_request req;152unsigned int now;153154if (pmu_request(&req, NULL, 1, PMU_READ_RTC) < 0)155return 0;156pmu_wait_complete(&req);157if (req.reply_len != 4)158printk(KERN_ERR "pmu_get_time: got %d byte reply from PMU\n",159req.reply_len);160now = (req.reply[0] << 24) + (req.reply[1] << 16)161+ (req.reply[2] << 8) + req.reply[3];162return ((unsigned long)now) - RTC_OFFSET;163}164165#define pmu_get_rtc_time(tm) to_rtc_time(pmu_get_time(), (tm))166167static int pmu_set_rtc_time(struct rtc_time *tm)168{169unsigned int nowtime;170struct adb_request req;171172nowtime = from_rtc_time(tm) + RTC_OFFSET;173if (pmu_request(&req, NULL, 5, PMU_SET_RTC, nowtime >> 24,174nowtime >> 16, nowtime >> 8, nowtime) < 0)175return -ENXIO;176pmu_wait_complete(&req);177if (req.reply_len != 0)178printk(KERN_ERR "pmu_set_rtc_time: %d byte reply from PMU\n",179req.reply_len);180return 0;181}182183#else184#define pmu_get_time() 0185#define pmu_get_rtc_time(tm)186#define pmu_set_rtc_time(tm) 0187#endif188189#ifdef CONFIG_PMAC_SMU190static unsigned long smu_get_time(void)191{192struct rtc_time tm;193194if (smu_get_rtc_time(&tm, 1))195return 0;196return from_rtc_time(&tm);197}198199#else200#define smu_get_time() 0201#define smu_get_rtc_time(tm, spin)202#define smu_set_rtc_time(tm, spin) 0203#endif204205/* Can't be __init, it's called when suspending and resuming */206unsigned long pmac_get_boot_time(void)207{208/* Get the time from the RTC, used only at boot time */209switch (sys_ctrler) {210case SYS_CTRLER_CUDA:211return cuda_get_time();212case SYS_CTRLER_PMU:213return pmu_get_time();214case SYS_CTRLER_SMU:215return smu_get_time();216default:217return 0;218}219}220221void pmac_get_rtc_time(struct rtc_time *tm)222{223/* Get the time from the RTC, used only at boot time */224switch (sys_ctrler) {225case SYS_CTRLER_CUDA:226cuda_get_rtc_time(tm);227break;228case SYS_CTRLER_PMU:229pmu_get_rtc_time(tm);230break;231case SYS_CTRLER_SMU:232smu_get_rtc_time(tm, 1);233break;234default:235;236}237}238239int pmac_set_rtc_time(struct rtc_time *tm)240{241switch (sys_ctrler) {242case SYS_CTRLER_CUDA:243return cuda_set_rtc_time(tm);244case SYS_CTRLER_PMU:245return pmu_set_rtc_time(tm);246case SYS_CTRLER_SMU:247return smu_set_rtc_time(tm, 1);248default:249return -ENODEV;250}251}252253#ifdef CONFIG_PPC32254/*255* Calibrate the decrementer register using VIA timer 1.256* This is used both on powermacs and CHRP machines.257*/258int __init via_calibrate_decr(void)259{260struct device_node *vias;261volatile unsigned char __iomem *via;262int count = VIA_TIMER_FREQ_6 / 100;263unsigned int dstart, dend;264struct resource rsrc;265266vias = of_find_node_by_name(NULL, "via-cuda");267if (vias == NULL)268vias = of_find_node_by_name(NULL, "via-pmu");269if (vias == NULL)270vias = of_find_node_by_name(NULL, "via");271if (vias == NULL || of_address_to_resource(vias, 0, &rsrc)) {272of_node_put(vias);273return 0;274}275of_node_put(vias);276via = ioremap(rsrc.start, rsrc.end - rsrc.start + 1);277if (via == NULL) {278printk(KERN_ERR "Failed to map VIA for timer calibration !\n");279return 0;280}281282/* set timer 1 for continuous interrupts */283out_8(&via[ACR], (via[ACR] & ~T1MODE) | T1MODE_CONT);284/* set the counter to a small value */285out_8(&via[T1CH], 2);286/* set the latch to `count' */287out_8(&via[T1LL], count);288out_8(&via[T1LH], count >> 8);289/* wait until it hits 0 */290while ((in_8(&via[IFR]) & T1_INT) == 0)291;292dstart = get_dec();293/* clear the interrupt & wait until it hits 0 again */294in_8(&via[T1CL]);295while ((in_8(&via[IFR]) & T1_INT) == 0)296;297dend = get_dec();298299ppc_tb_freq = (dstart - dend) * 100 / 6;300301iounmap(via);302303return 1;304}305#endif306307/*308* Query the OF and get the decr frequency.309*/310void __init pmac_calibrate_decr(void)311{312generic_calibrate_decr();313314#ifdef CONFIG_PPC32315/* We assume MacRISC2 machines have correct device-tree316* calibration. That's better since the VIA itself seems317* to be slightly off. --BenH318*/319if (!of_machine_is_compatible("MacRISC2") &&320!of_machine_is_compatible("MacRISC3") &&321!of_machine_is_compatible("MacRISC4"))322if (via_calibrate_decr())323return;324325/* Special case: QuickSilver G4s seem to have a badly calibrated326* timebase-frequency in OF, VIA is much better on these. We should327* probably implement calibration based on the KL timer on these328* machines anyway... -BenH329*/330if (of_machine_is_compatible("PowerMac3,5"))331if (via_calibrate_decr())332return;333#endif334}335336337