* "A Kernel Model for Precision Timekeeping" by Dave Mills
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/irq.h>
-#include <asm/leds.h>
-
-u64 jiffies_64 = INITIAL_JIFFIES;
-
-EXPORT_SYMBOL(jiffies_64);
-
-extern unsigned long wall_jiffies;
+#include <asm/ioc.h>
/* this needs a better home */
-spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
+DEFINE_SPINLOCK(rtc_lock);
/* change this if you have some constant time drift */
#define USECS_PER_JIFFY (1000000/HZ)
*/
int (*set_rtc)(void) = dummy_set_rtc;
-static unsigned long dummy_gettimeoffset(void)
+/*
+ * Get time offset based on IOCs timer.
+ * FIXME - if this is called with interrutps off, why the shennanigans
+ * below ?
+ */
+static unsigned long gettimeoffset(void)
{
- return 0;
+ unsigned int count1, count2, status;
+ long offset;
+
+ ioc_writeb (0, IOC_T0LATCH);
+ barrier ();
+ count1 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
+ barrier ();
+ status = ioc_readb(IOC_IRQREQA);
+ barrier ();
+ ioc_writeb (0, IOC_T0LATCH);
+ barrier ();
+ count2 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
+
+ offset = count2;
+ if (count2 < count1) {
+ /*
+ * We have not had an interrupt between reading count1
+ * and count2.
+ */
+ if (status & (1 << 5))
+ offset -= LATCH;
+ } else if (count2 > count1) {
+ /*
+ * We have just had another interrupt between reading
+ * count1 and count2.
+ */
+ offset -= LATCH;
+ }
+
+ offset = (LATCH - offset) * (tick_nsec / 1000);
+ return (offset + LATCH/2) / LATCH;
}
/*
- * hook for getting the time offset. Note that it is
- * always called with interrupts disabled.
+ * Scheduler clock - returns current time in nanosec units.
*/
-unsigned long (*gettimeoffset)(void) = dummy_gettimeoffset;
+unsigned long long sched_clock(void)
+{
+ return (unsigned long long)jiffies * (1000000000 / HZ);
+}
static unsigned long next_rtc_update;
*/
static inline void do_set_rtc(void)
{
- if (time_status & STA_UNSYNC || set_rtc == NULL)
+ if (!ntp_synced() || set_rtc == NULL)
return;
//FIXME - timespec.tv_sec is a time_t not unsigned long
{
unsigned long flags;
unsigned long seq;
- unsigned long usec, sec, lost;
+ unsigned long usec, sec;
do {
seq = read_seqbegin_irqsave(&xtime_lock, flags);
usec = gettimeoffset();
-
- lost = jiffies - wall_jiffies;
- if (lost)
- usec += lost * USECS_PER_JIFFY;
-
sec = xtime.tv_sec;
usec += xtime.tv_nsec / 1000;
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
* wall time. Discover what correction gettimeofday() would have
* done, and then undo it!
*/
- tv->tv_nsec -= 1000 * (gettimeoffset() +
- (jiffies - wall_jiffies) * USECS_PER_JIFFY);
+ tv->tv_nsec -= 1000 * gettimeoffset();
while (tv->tv_nsec < 0) {
tv->tv_nsec += NSEC_PER_SEC;
xtime.tv_sec = tv->tv_sec;
xtime.tv_nsec = tv->tv_nsec;
- time_adjust = 0; /* stop active adjtime() */
- time_status |= STA_UNSYNC;
- time_maxerror = NTP_PHASE_LIMIT;
- time_esterror = NTP_PHASE_LIMIT;
+ ntp_clear();
write_sequnlock_irq(&xtime_lock);
clock_was_set();
return 0;
static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- do_timer(regs);
+ do_timer(1);
+#ifndef CONFIG_SMP
+ update_process_times(user_mode(regs));
+#endif
do_set_rtc(); //FIME - EVERY timer IRQ?
profile_tick(CPU_PROFILING, regs);
return IRQ_HANDLED; //FIXME - is this right?
static struct irqaction timer_irq = {
.name = "timer",
- .flags = SA_INTERRUPT,
+ .flags = IRQF_DISABLED,
.handler = timer_interrupt,
};
*/
void __init time_init(void)
{
- ioctime_init();
+ ioc_writeb(LATCH & 255, IOC_T0LTCHL);
+ ioc_writeb(LATCH >> 8, IOC_T0LTCHH);
+ ioc_writeb(0, IOC_T0GO);
+
setup_irq(IRQ_TIMER, &timer_irq);
}