diff options
Diffstat (limited to 'arch/sparc/kernel/time.c')
| -rw-r--r-- | arch/sparc/kernel/time.c | 601 |
1 files changed, 0 insertions, 601 deletions
diff --git a/arch/sparc/kernel/time.c b/arch/sparc/kernel/time.c deleted file mode 100644 index cfaf22c05bc..00000000000 --- a/arch/sparc/kernel/time.c +++ /dev/null @@ -1,601 +0,0 @@ -/* linux/arch/sparc/kernel/time.c - * - * Copyright (C) 1995 David S. Miller (davem@davemloft.net) - * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) - * - * Chris Davis (cdavis@cois.on.ca) 03/27/1998 - * Added support for the intersil on the sun4/4200 - * - * Gleb Raiko (rajko@mech.math.msu.su) 08/18/1998 - * Support for MicroSPARC-IIep, PCI CPU. - * - * This file handles the Sparc specific time handling details. - * - * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 - * "A Kernel Model for Precision Timekeeping" by Dave Mills - */ -#include <linux/errno.h> -#include <linux/module.h> -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/param.h> -#include <linux/string.h> -#include <linux/mm.h> -#include <linux/interrupt.h> -#include <linux/time.h> -#include <linux/timex.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/ioport.h> -#include <linux/profile.h> - -#include <asm/oplib.h> -#include <asm/timer.h> -#include <asm/mostek.h> -#include <asm/system.h> -#include <asm/irq.h> -#include <asm/io.h> -#include <asm/idprom.h> -#include <asm/machines.h> -#include <asm/sun4paddr.h> -#include <asm/page.h> -#include <asm/pcic.h> -#include <asm/of_device.h> -#include <asm/irq_regs.h> - -#include "irq.h" - -DEFINE_SPINLOCK(rtc_lock); -enum sparc_clock_type sp_clock_typ; -DEFINE_SPINLOCK(mostek_lock); -void __iomem *mstk48t02_regs = NULL; -static struct mostek48t08 __iomem *mstk48t08_regs = NULL; -static int set_rtc_mmss(unsigned long); -static int sbus_do_settimeofday(struct timespec *tv); - -#ifdef CONFIG_SUN4 -struct intersil *intersil_clock; -#define intersil_cmd(intersil_reg, intsil_cmd) intersil_reg->int_cmd_reg = \ - (intsil_cmd) - -#define intersil_intr(intersil_reg, intsil_cmd) intersil_reg->int_intr_reg = \ - (intsil_cmd) - -#define intersil_start(intersil_reg) intersil_cmd(intersil_reg, \ - ( INTERSIL_START | INTERSIL_32K | INTERSIL_NORMAL | INTERSIL_24H |\ - INTERSIL_INTR_ENABLE)) - -#define intersil_stop(intersil_reg) intersil_cmd(intersil_reg, \ - ( INTERSIL_STOP | INTERSIL_32K | INTERSIL_NORMAL | INTERSIL_24H |\ - INTERSIL_INTR_ENABLE)) - -#define intersil_read_intr(intersil_reg, towhere) towhere = \ - intersil_reg->int_intr_reg - -#endif - -unsigned long profile_pc(struct pt_regs *regs) -{ - extern char __copy_user_begin[], __copy_user_end[]; - extern char __atomic_begin[], __atomic_end[]; - extern char __bzero_begin[], __bzero_end[]; - - unsigned long pc = regs->pc; - - if (in_lock_functions(pc) || - (pc >= (unsigned long) __copy_user_begin && - pc < (unsigned long) __copy_user_end) || - (pc >= (unsigned long) __atomic_begin && - pc < (unsigned long) __atomic_end) || - (pc >= (unsigned long) __bzero_begin && - pc < (unsigned long) __bzero_end)) - pc = regs->u_regs[UREG_RETPC]; - return pc; -} - -EXPORT_SYMBOL(profile_pc); - -__volatile__ unsigned int *master_l10_counter; -__volatile__ unsigned int *master_l10_limit; - -/* - * timer_interrupt() needs to keep up the real-time clock, - * as well as call the "do_timer()" routine every clocktick - */ - -#define TICK_SIZE (tick_nsec / 1000) - -irqreturn_t timer_interrupt(int irq, void *dev_id) -{ - /* last time the cmos clock got updated */ - static long last_rtc_update; - -#ifndef CONFIG_SMP - profile_tick(CPU_PROFILING); -#endif - - /* Protect counter clear so that do_gettimeoffset works */ - write_seqlock(&xtime_lock); -#ifdef CONFIG_SUN4 - if((idprom->id_machtype == (SM_SUN4 | SM_4_260)) || - (idprom->id_machtype == (SM_SUN4 | SM_4_110))) { - int temp; - intersil_read_intr(intersil_clock, temp); - /* re-enable the irq */ - enable_pil_irq(10); - } -#endif - clear_clock_irq(); - - do_timer(1); - - /* Determine when to update the Mostek clock. */ - if (ntp_synced() && - xtime.tv_sec > last_rtc_update + 660 && - (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 && - (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) { - if (set_rtc_mmss(xtime.tv_sec) == 0) - last_rtc_update = xtime.tv_sec; - else - last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ - } - write_sequnlock(&xtime_lock); - -#ifndef CONFIG_SMP - update_process_times(user_mode(get_irq_regs())); -#endif - return IRQ_HANDLED; -} - -/* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */ -static void __devinit kick_start_clock(void) -{ - struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs; - unsigned char sec; - int i, count; - - prom_printf("CLOCK: Clock was stopped. Kick start "); - - spin_lock_irq(&mostek_lock); - - /* Turn on the kick start bit to start the oscillator. */ - regs->creg |= MSTK_CREG_WRITE; - regs->sec &= ~MSTK_STOP; - regs->hour |= MSTK_KICK_START; - regs->creg &= ~MSTK_CREG_WRITE; - - spin_unlock_irq(&mostek_lock); - - /* Delay to allow the clock oscillator to start. */ - sec = MSTK_REG_SEC(regs); - for (i = 0; i < 3; i++) { - while (sec == MSTK_REG_SEC(regs)) - for (count = 0; count < 100000; count++) - /* nothing */ ; - prom_printf("."); - sec = regs->sec; - } - prom_printf("\n"); - - spin_lock_irq(&mostek_lock); - - /* Turn off kick start and set a "valid" time and date. */ - regs->creg |= MSTK_CREG_WRITE; - regs->hour &= ~MSTK_KICK_START; - MSTK_SET_REG_SEC(regs,0); - MSTK_SET_REG_MIN(regs,0); - MSTK_SET_REG_HOUR(regs,0); - MSTK_SET_REG_DOW(regs,5); - MSTK_SET_REG_DOM(regs,1); - MSTK_SET_REG_MONTH(regs,8); - MSTK_SET_REG_YEAR(regs,1996 - MSTK_YEAR_ZERO); - regs->creg &= ~MSTK_CREG_WRITE; - - spin_unlock_irq(&mostek_lock); - - /* Ensure the kick start bit is off. If it isn't, turn it off. */ - while (regs->hour & MSTK_KICK_START) { - prom_printf("CLOCK: Kick start still on!\n"); - - spin_lock_irq(&mostek_lock); - regs->creg |= MSTK_CREG_WRITE; - regs->hour &= ~MSTK_KICK_START; - regs->creg &= ~MSTK_CREG_WRITE; - spin_unlock_irq(&mostek_lock); - } - - prom_printf("CLOCK: Kick start procedure successful.\n"); -} - -/* Return nonzero if the clock chip battery is low. */ -static inline int has_low_battery(void) -{ - struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs; - unsigned char data1, data2; - - spin_lock_irq(&mostek_lock); - data1 = regs->eeprom[0]; /* Read some data. */ - regs->eeprom[0] = ~data1; /* Write back the complement. */ - data2 = regs->eeprom[0]; /* Read back the complement. */ - regs->eeprom[0] = data1; /* Restore the original value. */ - spin_unlock_irq(&mostek_lock); - - return (data1 == data2); /* Was the write blocked? */ -} - -static void __devinit mostek_set_system_time(void) -{ - unsigned int year, mon, day, hour, min, sec; - struct mostek48t02 *mregs; - - mregs = (struct mostek48t02 *)mstk48t02_regs; - if(!mregs) { - prom_printf("Something wrong, clock regs not mapped yet.\n"); - prom_halt(); - } - spin_lock_irq(&mostek_lock); - mregs->creg |= MSTK_CREG_READ; - sec = MSTK_REG_SEC(mregs); - min = MSTK_REG_MIN(mregs); - hour = MSTK_REG_HOUR(mregs); - day = MSTK_REG_DOM(mregs); - mon = MSTK_REG_MONTH(mregs); - year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) ); - xtime.tv_sec = mktime(year, mon, day, hour, min, sec); - xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - set_normalized_timespec(&wall_to_monotonic, - -xtime.tv_sec, -xtime.tv_nsec); - mregs->creg &= ~MSTK_CREG_READ; - spin_unlock_irq(&mostek_lock); -} - -/* Probe for the real time clock chip on Sun4 */ -static inline void sun4_clock_probe(void) -{ -#ifdef CONFIG_SUN4 - int temp; - struct resource r; - - memset(&r, 0, sizeof(r)); - if( idprom->id_machtype == (SM_SUN4 | SM_4_330) ) { - sp_clock_typ = MSTK48T02; - r.start = sun4_clock_physaddr; - mstk48t02_regs = sbus_ioremap(&r, 0, - sizeof(struct mostek48t02), NULL); - mstk48t08_regs = NULL; /* To catch weirdness */ - intersil_clock = NULL; /* just in case */ - - /* Kick start the clock if it is completely stopped. */ - if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP) - kick_start_clock(); - } else if( idprom->id_machtype == (SM_SUN4 | SM_4_260)) { - /* intersil setup code */ - printk("Clock: INTERSIL at %8x ",sun4_clock_physaddr); - sp_clock_typ = INTERSIL; - r.start = sun4_clock_physaddr; - intersil_clock = (struct intersil *) - sbus_ioremap(&r, 0, sizeof(*intersil_clock), "intersil"); - mstk48t02_regs = 0; /* just be sure */ - mstk48t08_regs = NULL; /* ditto */ - /* initialise the clock */ - - intersil_intr(intersil_clock,INTERSIL_INT_100HZ); - - intersil_start(intersil_clock); - - intersil_read_intr(intersil_clock, temp); - while (!(temp & 0x80)) - intersil_read_intr(intersil_clock, temp); - - intersil_read_intr(intersil_clock, temp); - while (!(temp & 0x80)) - intersil_read_intr(intersil_clock, temp); - - intersil_stop(intersil_clock); - - } -#endif -} - -#ifndef CONFIG_SUN4 -static int __devinit clock_probe(struct of_device *op, const struct of_device_id *match) -{ - struct device_node *dp = op->node; - const char *model = of_get_property(dp, "model", NULL); - - if (!model) - return -ENODEV; - - if (!strcmp(model, "mk48t02")) { - sp_clock_typ = MSTK48T02; - - /* Map the clock register io area read-only */ - mstk48t02_regs = of_ioremap(&op->resource[0], 0, - sizeof(struct mostek48t02), - "mk48t02"); - mstk48t08_regs = NULL; /* To catch weirdness */ - } else if (!strcmp(model, "mk48t08")) { - sp_clock_typ = MSTK48T08; - mstk48t08_regs = of_ioremap(&op->resource[0], 0, - sizeof(struct mostek48t08), - "mk48t08"); - - mstk48t02_regs = &mstk48t08_regs->regs; - } else - return -ENODEV; - - /* Report a low battery voltage condition. */ - if (has_low_battery()) - printk(KERN_CRIT "NVRAM: Low battery voltage!\n"); - - /* Kick start the clock if it is completely stopped. */ - if (mostek_read(mstk48t02_regs + MOSTEK_SEC) & MSTK_STOP) - kick_start_clock(); - - mostek_set_system_time(); - - return 0; -} - -static struct of_device_id clock_match[] = { - { - .name = "eeprom", - }, - {}, -}; - -static struct of_platform_driver clock_driver = { - .match_table = clock_match, - .probe = clock_probe, - .driver = { - .name = "clock", - }, -}; - - -/* Probe for the mostek real time clock chip. */ -static int __init clock_init(void) -{ - return of_register_driver(&clock_driver, &of_platform_bus_type); -} - -/* Must be after subsys_initcall() so that busses are probed. Must - * be before device_initcall() because things like the RTC driver - * need to see the clock registers. - */ -fs_initcall(clock_init); -#endif /* !CONFIG_SUN4 */ - -void __init sbus_time_init(void) -{ - - BTFIXUPSET_CALL(bus_do_settimeofday, sbus_do_settimeofday, BTFIXUPCALL_NORM); - btfixup(); - - if (ARCH_SUN4) - sun4_clock_probe(); - - sparc_init_timers(timer_interrupt); - -#ifdef CONFIG_SUN4 - if(idprom->id_machtype == (SM_SUN4 | SM_4_330)) { - mostek_set_system_time(); - } else if(idprom->id_machtype == (SM_SUN4 | SM_4_260) ) { - /* initialise the intersil on sun4 */ - unsigned int year, mon, day, hour, min, sec; - int temp; - struct intersil *iregs; - - iregs=intersil_clock; - if(!iregs) { - prom_printf("Something wrong, clock regs not mapped yet.\n"); - prom_halt(); - } - - intersil_intr(intersil_clock,INTERSIL_INT_100HZ); - disable_pil_irq(10); - intersil_stop(iregs); - intersil_read_intr(intersil_clock, temp); - - temp = iregs->clk.int_csec; - - sec = iregs->clk.int_sec; - min = iregs->clk.int_min; - hour = iregs->clk.int_hour; - day = iregs->clk.int_day; - mon = iregs->clk.int_month; - year = MSTK_CVT_YEAR(iregs->clk.int_year); - - enable_pil_irq(10); - intersil_start(iregs); - - xtime.tv_sec = mktime(year, mon, day, hour, min, sec); - xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ); - set_normalized_timespec(&wall_to_monotonic, - -xtime.tv_sec, -xtime.tv_nsec); - printk("%u/%u/%u %u:%u:%u\n",day,mon,year,hour,min,sec); - } -#endif - - /* Now that OBP ticker has been silenced, it is safe to enable IRQ. */ - local_irq_enable(); -} - -void __init time_init(void) -{ -#ifdef CONFIG_PCI - extern void pci_time_init(void); - if (pcic_present()) { - pci_time_init(); - return; - } -#endif - sbus_time_init(); -} - -static inline unsigned long do_gettimeoffset(void) -{ - unsigned long val = *master_l10_counter; - unsigned long usec = (val >> 10) & 0x1fffff; - - /* Limit hit? */ - if (val & 0x80000000) - usec += 1000000 / HZ; - - return usec; -} - -/* Ok, my cute asm atomicity trick doesn't work anymore. - * There are just too many variables that need to be protected - * now (both members of xtime, et al.) - */ -void do_gettimeofday(struct timeval *tv) -{ - unsigned long flags; - unsigned long seq; - unsigned long usec, sec; - unsigned long max_ntp_tick = tick_usec - tickadj; - - do { - seq = read_seqbegin_irqsave(&xtime_lock, flags); - usec = do_gettimeoffset(); - - /* - * If time_adjust is negative then NTP is slowing the clock - * so make sure not to go into next possible interval. - * Better to lose some accuracy than have time go backwards.. - */ - if (unlikely(time_adjust < 0)) - usec = min(usec, max_ntp_tick); - - sec = xtime.tv_sec; - usec += (xtime.tv_nsec / 1000); - } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); - - while (usec >= 1000000) { - usec -= 1000000; - sec++; - } - - tv->tv_sec = sec; - tv->tv_usec = usec; -} - -EXPORT_SYMBOL(do_gettimeofday); - -int do_settimeofday(struct timespec *tv) -{ - int ret; - - write_seqlock_irq(&xtime_lock); - ret = bus_do_settimeofday(tv); - write_sequnlock_irq(&xtime_lock); - clock_was_set(); - return ret; -} - -EXPORT_SYMBOL(do_settimeofday); - -static int sbus_do_settimeofday(struct timespec *tv) -{ - time_t wtm_sec, sec = tv->tv_sec; - long wtm_nsec, nsec = tv->tv_nsec; - - if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) - return -EINVAL; - - /* - * This is revolting. We need to set "xtime" correctly. However, the - * value in this location is the value at the most recent update of - * wall time. Discover what correction gettimeofday() would have - * made, and then undo it! - */ - nsec -= 1000 * do_gettimeoffset(); - - wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); - wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); - - set_normalized_timespec(&xtime, sec, nsec); - set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); - - ntp_clear(); - return 0; -} - -/* - * BUG: This routine does not handle hour overflow properly; it just - * sets the minutes. Usually you won't notice until after reboot! - */ -static int set_rtc_mmss(unsigned long nowtime) -{ - int real_seconds, real_minutes, mostek_minutes; - struct mostek48t02 *regs = (struct mostek48t02 *)mstk48t02_regs; - unsigned long flags; -#ifdef CONFIG_SUN4 - struct intersil *iregs = intersil_clock; - int temp; -#endif - - /* Not having a register set can lead to trouble. */ - if (!regs) { -#ifdef CONFIG_SUN4 - if(!iregs) - return -1; - else { - temp = iregs->clk.int_csec; - - mostek_minutes = iregs->clk.int_min; - - real_seconds = nowtime % 60; - real_minutes = nowtime / 60; - if (((abs(real_minutes - mostek_minutes) + 15)/30) & 1) - real_minutes += 30; /* correct for half hour time zone */ - real_minutes %= 60; - - if (abs(real_minutes - mostek_minutes) < 30) { - intersil_stop(iregs); - iregs->clk.int_sec=real_seconds; - iregs->clk.int_min=real_minutes; - intersil_start(iregs); - } else { - printk(KERN_WARNING - "set_rtc_mmss: can't update from %d to %d\n", - mostek_minutes, real_minutes); - return -1; - } - - return 0; - } -#endif - } - - spin_lock_irqsave(&mostek_lock, flags); - /* Read the current RTC minutes. */ - regs->creg |= MSTK_CREG_READ; - mostek_minutes = MSTK_REG_MIN(regs); - regs->creg &= ~MSTK_CREG_READ; - - /* - * since we're only adjusting minutes and seconds, - * don't interfere with hour overflow. This avoids - * messing with unknown time zones but requires your - * RTC not to be off by more than 15 minutes - */ - real_seconds = nowtime % 60; - real_minutes = nowtime / 60; - if (((abs(real_minutes - mostek_minutes) + 15)/30) & 1) - real_minutes += 30; /* correct for half hour time zone */ - real_minutes %= 60; - - if (abs(real_minutes - mostek_minutes) < 30) { - regs->creg |= MSTK_CREG_WRITE; - MSTK_SET_REG_SEC(regs,real_seconds); - MSTK_SET_REG_MIN(regs,real_minutes); - regs->creg &= ~MSTK_CREG_WRITE; - spin_unlock_irqrestore(&mostek_lock, flags); - return 0; - } else { - spin_unlock_irqrestore(&mostek_lock, flags); - return -1; - } -} |
