time.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:3k
- /*
- * Copyright 2001 MontaVista Software Inc.
- * Author: jsun@mvista.com or jsun@junsun.net
- *
- * rtc and time ops for vr4181. Part of code is drived from
- * linux-vr, originally written by Bradley D. LaRonde & Michael Klar.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- */
- #include <linux/kernel.h>
- #include <linux/spinlock.h>
- #include <linux/param.h> /* for HZ */
- #include <linux/time.h>
- #include <linux/interrupt.h>
- #include <asm/system.h>
- #include <asm/time.h>
- #include <asm/vr4181/vr4181.h>
- #define COUNTS_PER_JIFFY ((32768 + HZ/2) / HZ)
- /*
- * RTC ops
- */
- spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
- /* per VR41xx docs, bad data can be read if between 2 counts */
- static inline unsigned short
- read_time_reg(volatile unsigned short *reg)
- {
- unsigned short value;
- do {
- value = *reg;
- barrier();
- } while (value != *reg);
- return value;
- }
- static unsigned long
- vr4181_rtc_get_time(void)
- {
- unsigned short regh, regm, regl;
- // why this crazy order, you ask? to guarantee that neither m
- // nor l wrap before all 3 read
- do {
- regm = read_time_reg(VR4181_ETIMEMREG);
- barrier();
- regh = read_time_reg(VR4181_ETIMEHREG);
- barrier();
- regl = read_time_reg(VR4181_ETIMELREG);
- } while (regm != read_time_reg(VR4181_ETIMEMREG));
- return ((regh << 17) | (regm << 1) | (regl >> 15));
- }
- static int
- vr4181_rtc_set_time(unsigned long timeval)
- {
- unsigned short intreg;
- unsigned long flags;
- spin_lock_irqsave(&rtc_lock, flags);
- intreg = *VR4181_RTCINTREG & 0x05;
- barrier();
- *VR4181_ETIMELREG = timeval << 15;
- *VR4181_ETIMEMREG = timeval >> 1;
- *VR4181_ETIMEHREG = timeval >> 17;
- barrier();
- // assume that any ints that just triggered are invalid, since the
- // time value is written non-atomically in 3 separate regs
- *VR4181_RTCINTREG = 0x05 ^ intreg;
- spin_unlock_irqrestore(&rtc_lock, flags);
- return 0;
- }
- /*
- * timer interrupt routine (wrapper)
- *
- * we need our own interrupt routine because we need to clear
- * RTC1 interrupt.
- */
- static void
- vr4181_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
- {
- /* Clear the interrupt. */
- *VR4181_RTCINTREG = 0x2;
- /* call the generic one */
- timer_interrupt(irq, dev_id, regs);
- }
- /*
- * vr4181_time_init:
- *
- * We pick the following choices:
- * . we use elapsed timer as the RTC. We set some reasonable init data since
- * it does not persist across reset
- * . we use RTC1 as the system timer interrupt source.
- * . we use CPU counter for fast_gettimeoffset and we calivrate the cpu
- * frequency. In other words, we use calibrate_div64_gettimeoffset().
- * . we use our own timer interrupt routine which clears the interrupt
- * and then calls the generic high-level timer interrupt routine.
- *
- */
- extern int setup_irq(unsigned int irq, struct irqaction *irqaction);
- static void
- vr4181_timer_setup(struct irqaction *irq)
- {
- /* over-write the handler to be our own one */
- irq->handler = vr4181_timer_interrupt;
- /* sets up the frequency */
- *VR4181_RTCL1LREG = COUNTS_PER_JIFFY;
- *VR4181_RTCL1HREG = 0;
- /* and ack any pending ints */
- *VR4181_RTCINTREG = 0x2;
- /* setup irqaction */
- setup_irq(VR4181_IRQ_INT1, irq);
- }
- void
- vr4181_init_time(void)
- {
- /* setup hookup functions */
- rtc_get_time = vr4181_rtc_get_time;
- rtc_set_time = vr4181_rtc_set_time;
- board_timer_setup = vr4181_timer_setup;
- }