From: Jun S. <ju...@us...> - 2001-09-22 04:27:18
|
Update of /cvsroot/linux-mips/linux/arch/mips/vr4181/common In directory usw-pr-cvs1:/tmp/cvs-serv27047/arch/mips/vr4181/common Added Files: Makefile irq.c serial.c time.c Log Message: Initial re-structuring of vr41xx directory. More are coming to actually get Osprey/Eagle building and running. --- NEW FILE: Makefile --- # # Makefile for common code of NEC vr4181 based boards # # Note! Dependencies are done automagically by 'make dep', which also # removes any old dependencies. DON'T put your own dependencies here # unless it's something special (ie not a .c file). # .S.s: $(CPP) $(CFLAGS) $< -o $*.s .S.o: $(CC) $(CFLAGS) -c $< -o $*.o O_TARGET:= vr4181.o obj-y := irq.o kbd_no.o serial.o time.o include $(TOPDIR)/Rules.make --- NEW FILE: irq.c --- /* * linux/arch/mips/vr41xx/irq.c * * Code to handle VR4181 IRQs plus some generic interrupt stuff. * * Copyright (C) 1992 Linus Torvalds * Copyright (C) 1994, 1995, 1996, 1997 Ralf Baechle * Copyright (C) 1999 Bradley D. LaRonde * Copyright (C) 1999, 2000 Michael Klar * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * */ #include <linux/init.h> #include <linux/kernel_stat.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/malloc.h> #include <linux/random.h> #include <linux/pm.h> #include <asm/irq.h> #include <asm/mipsregs.h> #include <asm/gdb-stub.h> #include <asm/vr4181/vr4181.h> /* [jsun] HACK */ #define CONFIG_CPU_VR4181 y #define DEVICE_IRQ_MASKL 0xffff extern asmlinkage void vr41xx_handle_irq(void); extern void breakpoint(void); // This is used elsewhere unsigned long spurious_count = 0; static unsigned short irq_mask_probe[(VR4181_IRQ_MAX + 9)/16]; #define BIT_MASK(bit) ( 1 << (bit) ) #define MAKE_CP0_STATUS_IRQ_MASK(irq) ( BIT_MASK( (irq) + 8)) static inline void mask_irq(unsigned int irq) { if (irq < 8) { // it's a cpu interrupt unsigned short newstatus = read_32bit_cp0_register(CP0_STATUS); newstatus &= ~((unsigned short)1 << (irq + 8)); set_cp0_status(ST0_IM, newstatus); } else { if (irq < 40) { // it's an ICU interrupt if (irq < 24) { *VR4181_MSYSINT1REG &= ~((unsigned short)1 << (irq - 8)); } else { *VR4181_MSYSINT2REG &= ~((unsigned short)1 << (irq - 24)); } } else { // it's a GPIO interrupt #ifdef CONFIG_CPU_VR4181 *VR4181_GPINTMSK |= (unsigned short)1 << (irq - 40); #else if (irq < 56) { *VR4181_MGIUINTLREG &= ~((unsigned short)1 << (irq - 40)); } else { *VR4181_MGIUINTHREG &= ~((unsigned short)1 << (irq - 56)); } #endif } } } static inline void unmask_irq(unsigned int irq) { if (irq < 8) { // it's a cpu interrupt unsigned short newstatus = read_32bit_cp0_register(CP0_STATUS); newstatus |= ((unsigned short)1 << (irq + 8)); set_cp0_status(ST0_IM, newstatus); } else { if (irq < 40) { // it's an ICU interrupt if (irq < 24) { *VR4181_MSYSINT1REG |= (unsigned short)1 << (irq - 8); } else { *VR4181_MSYSINT2REG |= (unsigned short)1 << (irq - 24); } } else { // it's a GPIO interrupt (also ack edge-triggered or hold ints) #ifdef CONFIG_CPU_VR4181 if (!((irq < 48 ? *VR4181_GPINTTYPL : *VR4181_GPINTTYPH) & ((unsigned short)2 << ((irq & 0x7) * 2)))) *VR4181_GPINTSTAT = (unsigned short)1 << (irq - 40); *VR4181_GPINTMSK &= ~((unsigned short)1 << (irq - 40)); #else if (irq < 56) { if (*VR4181_GIUINTHTSELL & ((unsigned short)1 << (irq - 40))) *VR4181_GIUINTSTATL = (unsigned short)1 << (irq - 40); *VR4181_MGIUINTLREG |= (unsigned short)1 << (irq - 40); } else { if (*VR4181_GIUINTHTSELH & ((unsigned short)1 << (irq - 56))) *VR4181_GIUINTSTATH = (unsigned short)1 << (irq - 56); *VR4181_MGIUINTHREG |= (unsigned short)1 << (irq - 56); } #endif } } } /* * Per-IRQ information, by not initializing, this gets filled with NULLs: */ typedef struct { struct irqaction* irq_action; // info on low-level handler int depth; // < 0: enabled, >= 0: disabled } irq_info_t; static irq_info_t irq_info[VR4181_IRQ_MAX + 1]; void inline disable_irq_nosync(unsigned int irq) { disable_irq(irq); } void disable_irq(unsigned int irq_nr) { unsigned long flags; if (++irq_info[irq_nr].depth >= 0) { save_and_cli(flags); mask_irq(irq_nr); restore_flags(flags); } } void enable_irq(unsigned int irq_nr) { unsigned long flags; #ifdef CONFIG_CPU_VR4122 if (irq_nr == VR4181_IRQ_GPIO14) return; #endif if (--irq_info[irq_nr].depth < 0) { save_and_cli(flags); unmask_irq(irq_nr); restore_flags(flags); } } int get_irq_list(char *buf) { // make a human-readable list of irq actions // used by /proc/interrupts int i, len = 0; struct irqaction *action; for (i = 0; i <= VR4181_IRQ_MAX; i++) { action = irq_info[i].irq_action; if (!action) continue; len += sprintf(buf + len, "%2d: %8d %c %s", i, kstat.irqs[0][i], (action->flags & SA_INTERRUPT) ? '+' : ' ', action->name); for (action = action->next; action; action = action->next) { len += sprintf(buf + len, ",%s %s", (action->flags & SA_INTERRUPT) ? " +" : "", action->name); } len += sprintf(buf + len, "\n"); } return len; } atomic_t __mips_bh_counter; asmlinkage void do_IRQ(int irq, struct pt_regs *regs) { // This handles all IRQ's that have been installed. // It is called from int-handler.S. // Actions without SA_INTERRUPT run with interrupts enabled // and use the full signal-handling return. // Actions with SA_INTERRUPT run with interrupts disabled. struct irqaction *action; int cpu = smp_processor_id(); irq_enter(cpu, irq); kstat.irqs[cpu][irq]++; // don't interrupt on this same irq again until we're finished // also, it gets left masked if there is no action (see below) mask_irq(irq); action = irq_info[irq].irq_action; if (action != 0) { unsigned long flags = 0; if (!(action->flags & SA_INTERRUPT)) __sti(); do { // handle it action->handler(irq, action->dev_id, regs); flags |= action->flags; action = action->next; } while (action); if (flags & SA_SAMPLE_RANDOM) add_interrupt_randomness(irq); __cli(); unmask_irq(irq); } irq_exit(cpu, irq); // Unmasking and softirq handling is done for us // currently by ret_from_irq in entry.S. } int add_irq_action(int irq, struct irqaction *new) { // put the specified action (new) at the end of the list of actions for this irq int shared = 0; struct irqaction *old, **p; unsigned long flags; p = &irq_info[irq].irq_action; if ((old = *p) != NULL) { if (!(old->flags & new->flags & SA_SHIRQ)) return -EBUSY; // Shared interrupts must be all same type if ((old->flags ^ new->flags) & SA_INTERRUPT) return -EBUSY; do { p = &old->next; old = *p; } while (old); shared = 1; } if (new->flags & SA_SAMPLE_RANDOM) rand_initialize_irq(irq); save_and_cli(flags); *p = new; if (!shared) { irq_info[irq].depth = -1; unmask_irq(irq); } restore_flags(flags); return 0; } int request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *), unsigned long irqflags, const char *devname, void *dev_id) { int retval; struct irqaction *action; // some assertiveness if (irq > VR4181_IRQ_MAX) return -EINVAL; if (!handler) return -EINVAL; // allocate a new action struct action = (struct irqaction *) kmalloc(sizeof(struct irqaction), GFP_KERNEL); if (!action) return -ENOMEM; // initialize it action->handler = handler; action->flags = irqflags; action->mask = 0; action->name = devname; action->next = NULL; action->dev_id = dev_id; // add it to the list of actions for this irq retval = add_irq_action(irq, action); if (retval) kfree(action); return retval; } void free_irq(unsigned int irq, void *dev_id) { struct irqaction *action, **p; unsigned long flags; if (irq > VR4181_IRQ_MAX) { printk("Error - trying to free invalid IRQ %d\n", irq); return; } for (p = &irq_info[irq].irq_action; (action = *p) != NULL; p = &action->next) { if (action->dev_id != dev_id) continue; // Found it - now free it save_and_cli(flags); *p = action->next; if (!irq_info[irq].irq_action) { irq_info[irq].depth = 0; mask_irq(irq); } restore_flags(flags); kfree(action); return; } } unsigned long probe_irq_on(void) { int i; unsigned long delay; // Note the lower 8 bits of irq_mask_probe[0] are not used. Also, // we don't probe for IRQ 0, since no way to report (0 = no IRQ found) irq_mask_probe[0] = read_32bit_cp0_register(CP0_STATUS); irq_mask_probe[1] = *VR4181_MSYSINT1REG; irq_mask_probe[2] = *VR4181_MSYSINT2REG; #ifdef CONFIG_CPU_VR4181 irq_mask_probe[3] = ~*VR4181_GPINTMSK; #else irq_mask_probe[3] = *VR4181_MGIUINTLREG; irq_mask_probe[4] = *VR4181_MGIUINTHREG; #endif for (i = VR4181_IRQ_MAX; i > 0; i--) if (!irq_info[i].irq_action && i != VR4181_IRQ_TIMER) enable_irq(i); // Wait for spurious interrupts to mask themselves out again... for (delay = jiffies + HZ/10; time_before(jiffies, delay); ) barrier(); // about 100ms of delay irq_mask_probe[0] |= ~read_32bit_cp0_register(CP0_STATUS); irq_mask_probe[1] |= ~*VR4181_MSYSINT1REG; irq_mask_probe[2] |= ~*VR4181_MSYSINT2REG; #ifdef CONFIG_CPU_VR4181 irq_mask_probe[3] |= *VR4181_GPINTMSK; #else irq_mask_probe[3] |= ~*VR4181_MGIUINTLREG; irq_mask_probe[4] |= ~*VR4181_MGIUINTHREG; #endif return 0x12345678; } int probe_irq_off(unsigned long unused) { int i, irq_found, nr_irqs; unsigned short tmp; unsigned long flags; if (unused != 0x12345678) printk("Bad IRQ probe detected\n"); // The following mess unmasks the interrupts we enabled to autoprobe // and finishes bit-processing irq_mask_probe at the same time save_and_cli(flags); tmp = read_32bit_cp0_register(CP0_STATUS); set_cp0_status(ST0_IM, tmp & irq_mask_probe[0]); irq_mask_probe[0] |= tmp; tmp = *VR4181_MSYSINT1REG; *VR4181_MSYSINT1REG = tmp & irq_mask_probe[1]; irq_mask_probe[1] |= tmp; tmp = *VR4181_MSYSINT2REG; *VR4181_MSYSINT2REG = tmp & irq_mask_probe[2]; irq_mask_probe[2] |= tmp; #ifdef CONFIG_CPU_VR4181 tmp = ~*VR4181_GPINTMSK; *VR4181_GPINTMSK = ~(tmp & irq_mask_probe[3]); irq_mask_probe[3] |= tmp; #else tmp = *VR4181_MGIUINTLREG; *VR4181_MGIUINTLREG = tmp & irq_mask_probe[3]; irq_mask_probe[3] |= tmp; tmp = *VR4181_MGIUINTHREG; *VR4181_MGIUINTHREG = tmp & irq_mask_probe[4]; irq_mask_probe[4] |= tmp; #endif restore_flags(flags); nr_irqs = 0; irq_found = 0; for (i = VR4181_IRQ_MAX; i > 0; i--) if (!(irq_mask_probe[(i + 8)/16] & ((unsigned short)1 << ((i + 8) & 15)))) { irq_found = i; nr_irqs++; } if (nr_irqs > 1) irq_found = -irq_found; return irq_found; } #ifdef CONFIG_PM // // Unlike the real pm_request callbacks, this one doesn't get registered // with PM, and only gets called from do_hibernate and do_wakeup, because // it has to happen in a certain order. It also assumes ints disabled. // void do_pm_irq_request(pm_request_t rqst) { static unsigned short irq_mask[(VR4181_IRQ_MAX + 9)/16]; unsigned int status; switch (rqst) { case PM_RESUME: status = read_32bit_cp0_register(CP0_STATUS) & 0xffff00ff; write_32bit_cp0_register(CP0_STATUS, status | irq_mask[0]); *VR4181_MSYSINT1REG = irq_mask[1]; *VR4181_MSYSINT2REG = irq_mask[2]; #ifdef CONFIG_CPU_VR4181 *VR4181_GPINTMSK = irq_mask[3]; #else *VR4181_MGIUINTLREG = irq_mask[3]; *VR4181_MGIUINTHREG = irq_mask[4]; #endif break; case PM_SUSPEND: irq_mask[0] = read_32bit_cp0_register(CP0_STATUS) & 0xff00; irq_mask[1] = *VR4181_MSYSINT1REG; irq_mask[2] = *VR4181_MSYSINT2REG; #ifdef CONFIG_CPU_VR4181 irq_mask[3] = *VR4181_GPINTMSK; #else irq_mask[3] = *VR4181_MGIUINTLREG; irq_mask[4] = *VR4181_MGIUINTHREG; #endif break; } } #endif // CONFIG_PM static struct irqaction cascade = { NULL, SA_INTERRUPT, 0, "cascade", NULL, NULL }; static struct irqaction reserved = { NULL, SA_INTERRUPT, 0, "reserved", NULL, NULL }; void __init init_IRQ(void) { set_except_vector(0, vr41xx_handle_irq); // Default all ICU IRQs to off ... *VR4181_MSYSINT1REG = 0; *VR4181_MSYSINT2REG = 0; // We initialize the level 2 ICU registers to all bits disabled. // After this, these registers are the resposibility of whatever // driver requests the IRQ of the corresponding level 1 ICU bit. // (except GIU, where each level 2 bit has its own IRQ) #ifdef CONFIG_CPU_VR4122 *VR4181_MPCIINTREG = 0; *VR4181_MSCUINTREG = 0; *VR4181_MCSIINTREG = 0; #else *VR4181_MPIUINTREG = 0; *VR4181_MAIUINTREG = 0; *VR4181_MKIUINTREG = 0; #endif #ifdef CONFIG_CPU_VR4181 *VR4181_GPINTMSK = 0xffff; #else *VR4181_MGIUINTLREG = 0; *VR4181_MDSIUINTREG = 0; *VR4181_MGIUINTHREG = 0; *VR4181_MFIRINTREG = 0; #endif barrier(); // // NOTE: This may break autodetection on some devices. If so, an IRQ mask needs // to be defined in vr41xx-platdep.h to disable some GPIO lines from interrupting, // or the broken autodetect IRQ needs to be defined explicitly. This define is // for documentation purposes, only turn it off for test/debug: // #define LET_ALL_GPIO_INPUTS_CAUSE_INTERRUPTS #ifdef LET_ALL_GPIO_INPUTS_CAUSE_INTERRUPTS // Initialize secondary IRQ mask to enable any GPIO line configured as an input. // Note that the int won't be active until enabled in the primary mask, too. #ifdef CONFIG_CPU_VR4181 { unsigned int bits; bits = (unsigned int)(*VR4181_GPMD1REG | 0xaaaa) << 16; bits |= *VR4181_GPMD0REG | 0xaaaa; bits &= bits >> 1; bits |= 0xcccccccc; bits &= bits >> 2; bits |= 0xf0f0f0f0; bits &= bits >> 4; bits |= 0xff00; bits &= bits >> 8; *VR4181_SECIRQMASKL = ~bits & DEVICE_IRQ_MASKL; } #else *VR4181_SECIRQMASKL = ~*VR4181_GIUIOSELL & DEVICE_IRQ_MASKL; *VR4181_SECIRQMASKH = ~*VR4181_GIUIOSELH & DEVICE_IRQ_MASKH; #endif #endif // LET_ALL_GPIO_INPUTS_CAUSE_INTERRUPTS // These don't really add handlers, these IRQs are never reported by the int // vector handler. What these do is register the IRQ as non-sharable add_irq_action(VR4181_IRQ_INT0, &cascade); add_irq_action(VR4181_IRQ_GIU, &cascade); add_irq_action(VR4181_IRQ_RTCL1, &reserved); add_irq_action(VR4181_IRQ_RTCL2, &reserved); #ifdef CONFIG_REMOTE_DEBUG printk("Setting debug traps - please connect the remote debugger.\n"); set_debug_traps(); // you may move this line to whereever you want breakpoint(); #endif } #ifdef CONFIG_PCMCIA /* * dummy functions needed for PCMCIA support for non-ISA systems (vr4122) - mikemac * */ /* * Return a mask of triggered interrupts (this * can handle only legacy ISA interrupts). */ unsigned int probe_irq_mask(unsigned long val) { return val; } #ifndef CONFIG_ISA #define CS_IN_USE 0x1e int try_irq(u_int Attributes, int irq, int specific) { return CS_IN_USE; } #endif /* CONFIG_ISA */ #endif /* CONFIG_PCMCIA */ --- NEW FILE: serial.c --- /* * linux/drivers/char/serial.c * Serial (SIU) driver for NEC VR41xx CPUs, VR4102 and up only. * * Based almost entirely on linux/drivers/char/serial.c * Modified for VR41xx by Michael Klar, mf...@po... * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * */ #include <linux/config.h> #include <linux/version.h> /* [jsun] HACK */ #define MAX_VR_PORT 2 [...3799 lines suppressed...] char mygetDebugChar(void) { if (!initialized) { /* need to init device first */ DbgInitSerial(); initialized = 1; } while ( !(*VR4181_SIULS & UART_LSR_DR) ) ; barrier(); return(*VR4181_SIURB); } /* Local variables: compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -D__SMP__ -pipe -fno-strength-reduce -march=i686 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c" End: */ --- NEW FILE: time.c --- /* * linux/arch/mips/vr41xx/time.c * * VR4181 timer interrupt using real-time clock. * * Copyright (C) 1991, 1992, 1995 Linus Torvalds * Copyright (C) 1999 Bradley D. LaRonde * Copyright (C) 2000 Michael Klar * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * */ #include <linux/config.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/timex.h> #include <linux/pm.h> #include <asm/vr4181/vr4181.h> extern int add_irq_action(int irq, struct irqaction *new); extern volatile unsigned long wall_jiffies; extern rwlock_t xtime_lock; #define USECS_PER_JIFFY (1000000/HZ) #define COUNTS_PER_JIFFY ((32768 + HZ/2) / HZ) spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED; #ifdef CONFIG_RTC unsigned long epoch_adj; #else #define epoch_adj 0 #endif // 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; } unsigned long get_rtc_time(volatile unsigned short *reg) { 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(reg + 1); barrier(); regh = read_time_reg(reg + 2); barrier(); regl = read_time_reg(reg); } while (regm != read_time_reg(reg + 1)); return ((regh << 17) | (regm << 1) | (regl >> 15)) + epoch_adj; } void set_rtc_time(unsigned long settime, volatile unsigned short *reg) { unsigned short intreg; unsigned long flags, timeval = settime - epoch_adj; spin_lock_irqsave(&rtc_lock, flags); intreg = *VR4181_RTCINTREG & 0x05; barrier(); *reg = timeval << 15; *(reg + 1) = timeval >> 1; *(reg + 2) = 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); } // must be called with ints disabled: // static unsigned long do_gettimeoffset(void) { unsigned short count; unsigned long offset; count = read_time_reg(VR4181_RTCL1CNTLREG); if (count == 1) offset = 0; else offset = (COUNTS_PER_JIFFY - count + 1) * 1000000 / 32768; // detect if the counter wrapped, but int not serviced yet, being // careful not to adjust if int happen after count was read just now if (*VR4181_RTCINTREG & 0x0002 && (jiffies == wall_jiffies) && count != 2) offset += USECS_PER_JIFFY; return offset; } // This version of gettimeofday has about 30us resolution void do_gettimeofday(struct timeval *tv) { unsigned long flags, lost; read_lock_irqsave(&xtime_lock, flags); *tv = xtime; tv->tv_usec += do_gettimeoffset(); // xtime is atomically updated in timer_bh, wall_jiffies is // updated in timer_bh, jiffies is updated in timer int, so this // will be nonzero if the timer bottom half hasn't executed yet: lost = jiffies - wall_jiffies; read_unlock_irqrestore(&xtime_lock, flags); while (lost--) tv->tv_usec += USECS_PER_JIFFY; while (tv->tv_usec >= 1000000) { tv->tv_usec -= 1000000; tv->tv_sec++; } } void do_settimeofday(struct timeval *tv) { write_lock_irq(&xtime_lock); // undo whatever correction gettimeofday would have done tv->tv_usec -= do_gettimeoffset(); tv->tv_usec -= (jiffies - wall_jiffies) * USECS_PER_JIFFY; if (tv->tv_usec < 0) { tv->tv_usec += 1000000; tv->tv_sec--; } xtime = *tv; time_adjust = 0; // stop active adjtime() time_status |= STA_UNSYNC; time_maxerror = NTP_PHASE_LIMIT; time_esterror = NTP_PHASE_LIMIT; write_unlock_irq(&xtime_lock); } static unsigned long last_rtc_update; /* * timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick */ static void timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) { // Clear the interrupt. *VR4181_RTCINTREG = 0x2; do_timer(regs); // If we have an externally synchronized Linux clock, then update // CMOS clock accordingly every ~11 minutes. if ((time_status & STA_UNSYNC) == 0 && xtime.tv_sec > last_rtc_update + 660) { set_rtc_time(xtime.tv_sec, VR4181_ETIMELREG); last_rtc_update = xtime.tv_sec; } } struct irqaction timer_action = { timer_interrupt, SA_INTERRUPT, 0, "timer", NULL, NULL }; #ifdef CONFIG_PM static int pm_time_request(struct pm_dev *dev, pm_request_t rqst, void *data) { unsigned long flags; switch (rqst) { case PM_SUSPEND: disable_irq(VR4181_IRQ_INT1); break; case PM_RESUME: write_lock_irqsave(&xtime_lock, flags); enable_irq(VR4181_IRQ_INT1); xtime.tv_sec = get_rtc_time(VR4181_ETIMELREG); xtime.tv_usec = 0; // this shouldn't be necessary, but just to be safe... *VR4181_RTCL1LREG = COUNTS_PER_JIFFY; *VR4181_RTCL1HREG = 0; write_unlock_irqrestore(&xtime_lock, flags); break; } return 0; } static int __init pm_time_init(void) { pm_register(PM_SYS_DEV, PM_SYS_UNKNOWN, pm_time_request); return 0; } __initcall(pm_time_init); #endif void __init time_init(void) { // Set default time near beginning of Linux VR epoch. xtime.tv_sec = get_rtc_time(VR4181_ETIMELREG); xtime.tv_usec = 0; // Use RTCLong1 for the system timer. // It has it's own cpu interrupt, is not T-Clock dependent, // and has sufficient resolution. // Set the RTCLong1 counter (32.768kHz) to expire in 1 / HZ second *VR4181_RTCL1LREG = COUNTS_PER_JIFFY; *VR4181_RTCL1HREG = 0; // and ack any pending ints *VR4181_RTCINTREG = 0x2; // Grab the IRQ for the cpu interrupt, not the ICU interrupt // The RTCLong1 ICU interrupt is always left unmasked // This can't use request_irq() because it's too early for kmalloc add_irq_action(VR4181_IRQ_INT1, &timer_action); } |