?? rtl_time.c
字號:
/* * rtl_time.c * * PPC-specific clock support * * Copyright (C) 1999 Cort Dougan <cort@fsmlabs.com> */#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/timex.h>#include <linux/mc146818rtc.h>#include <asm/smp.h>#include <asm/system.h>#include <asm/io.h>#include <asm/time.h>#include <rtl_core.h>#include <rtl_time.h>#include <rtl.h>static void _decr_uninit (clockid_t clock);static int _decr_init (clockid_t clock);static hrtime_t _decr_gettime (struct rtl_clock *);static int _decr_settimer (struct rtl_clock *, hrtime_t interval);static int _decr_settimermode (struct rtl_clock *, int mode);unsigned int decr_intercept(struct pt_regs *);unsigned long linux_decrs = 0;unsigned long linux_decrementer_count = 0, rtl_decrementer_count;unsigned long last_tb = 0;/* * Handle >=2.4.0 vs. earlier differences * --- * decrementer_count changed to tb_ticks_per_jiffy in 2.4.0 * * The interrupt handler in linux/ppc is more complicated with later * versions so we have a flag to just tell it to not touch the decr. * This should prevent others from messing with the code without * understanding it, too. * -- Cort <cort@fsmlabs.com> */#if LINUX_2_4_0_FINAL_OR_LATER#define decrementer_count tb_ticks_per_jiffyextern unsigned long disarm_decr[NR_CPUS];#else /* LINUX_2_4_0_FINAL_OR_LATER */extern unsigned decrementer_count;#endif /* LINUX_2_4_0_FINAL_OR_LATER */void default_handler(struct pt_regs *regs);MODULE_AUTHOR("Cort Dougan <cort@fsmlabs.com>");MODULE_DESCRIPTION("RTLinux PPC Timer Module");struct rtl_clock decr_clock ={ _decr_init, _decr_uninit, _decr_gettime, NULL, /* sethrtime */ _decr_settimer, _decr_settimermode, default_handler, /* handler */ RTL_CLOCK_MODE_ONESHOT, /* mode */};hrtime_t _decr_gettime (struct rtl_clock *clock){ unsigned long tbu, tbl, tbu1; hrtime_t tb, ret; /* * Possible bug pointed out by Niklaus Giger where * the timebase goes from ~0x0 to 0x0. This * catches that case now. -- Cort */ do { asm volatile("mftbu %2\n\t" "mftb %0\n\t" "mftbu %1\n\t" : "=r" (tbl), "=r" (tbu), "=r" (tbu1) ); } while (tbu != tbu1); tb = (hrtime_t)tbl + ((hrtime_t)tbu<<32); /* * Changed this calculation so we take into account the * fact that decrementer ticks are not even multiples of * ns's. * * First we get the number of linux ticks that have occurred * so far, which is an even multiple of ns's (NSECS_PER_SEC/HZ). * Then, get the next fraction of a linux tick and compute * the number of ns as best we can with 64-bit arithmetic. * -- Cort */ ret = (tb / linux_decrementer_count) * ((hrtime_t)NSECS_PER_SEC/HZ); ret += ((tb % linux_decrementer_count) * ((hrtime_t)NSECS_PER_SEC/HZ)) / linux_decrementer_count; return ret;}static int _decr_settimer(struct rtl_clock *clock, hrtime_t interval){ unsigned long t; if (interval) { /* * ns to decr ticks, rounds down -- Cort * fixed to round up as required by posix -- Michael * fixed to compute ticks with 64-bit arithmetic so * we don't lose precision. We need this precision * because PPC decrementer ticks are not always multiples * of ns's. Still rounds up. * -- Cort */ t = (ulong)((interval * (hrtime_t)linux_decrementer_count) / ((hrtime_t)NSECS_PER_SEC/(hrtime_t)HZ)) + 1; if (decr_clock.mode == RTL_CLOCK_MODE_PERIODIC) clock->resolution = interval; else clock->resolution = 1; /* sanity check to make sure we're not setting a bad value */ if (t < 1) t = 1; /* * If next tick is set for a time beyond when Linux wants a tick * then just set it for when Linux wants it. This skips the * case when we're already overdue for a Linux tick. * -- Cort */ else if ( (linux_decrs < linux_decrementer_count) && (t > (linux_decrementer_count - linux_decrs)) ) { t = linux_decrementer_count - linux_decrs; } rtl_decrementer_count = t; } /* * actually setup the decrementer, taking into account the * time that has elapsed since the decrementer crossed 0 */ { unsigned long dval; dval = get_dec(); set_dec(rtl_decrementer_count); decr_clock.arch.istimerset = 1; } return 0;}static int _decr_settimermode(struct rtl_clock *clock, int mode){ clock->mode = mode; return 0;}clockid_t rtl_getbestclock (unsigned int cpu){ return &decr_clock;}hrtime_t gethrtime(void){ return _decr_gettime(&decr_clock);}hrtime_t gethrtimeres(void){ return (NSECS_PER_SEC/HZ)/linux_decrementer_count;}int init_module (void){ /* keep a copy of the decrementer count that Linux wants */ linux_decrementer_count = decrementer_count; rtl_init_standard_clocks(); return 0;}void cleanup_module(void){ rtl_cleanup_standard_clocks(); decrementer_count = linux_decrementer_count;}unsigned int decr_intercept(struct pt_regs *regs){ unsigned long dval; int d; void (*handler)( struct pt_regs *regs) = decr_clock.handler; /* * Compute the # ticks since last interrupt so * we can keep track of how many Linux has accrued */ { unsigned long diff, tb; asm volatile("mftb %0" : "=r" (tb) ); diff = tb - last_tb; last_tb = tb; linux_decrs += diff; } /* * If it's one-shot clear the handler and reset the * timer. */ if ( decr_clock.mode == RTL_CLOCK_MODE_ONESHOT ) { decr_clock.arch.istimerset = 0; } else { dval = get_dec(); while ((d = (int)get_dec()) == dval) ; if ( (int)(d + rtl_decrementer_count) < 500 ) { printk("_decr_intercept(): d %08x decr_count %08lx too low: time overrun likely\n", d, rtl_decrementer_count); set_dec(rtl_decrementer_count); } else set_dec(d + rtl_decrementer_count); } /* * Give Linux a timer interrupt if one is due. * * Linux/ppc >=2.4.0 handles all pending interrupts in a single * call to the interrupt handler by looking at the timebase * so for it we just give it one pend. For earlier versions * of Linux we have to give it a timer interrupt for each * on that it has missed. * -- Cort <cort@fsmlabs.com> */#if LINUX_2_4_0_FINAL_OR_LATER if ( linux_decrs >= linux_decrementer_count ) { rtl_local_pend_vec(0,0); while ( linux_decrs >= linux_decrementer_count ) linux_decrs -= linux_decrementer_count; }#else if ( linux_decrs >= linux_decrementer_count ) { if ( !rtl_local_ispending_irq(0) ) { rtl_local_pend_vec(0,0); linux_decrs -= linux_decrementer_count; } }#endif /* LINUX_2_4_0_FINAL_OR_LATER */ if ( handler != default_handler ) handler(regs); if (!decr_clock.arch.istimerset) { /* setup the next tick to be when Linux wants one * since we don't have anything pending */ if ( linux_decrementer_count > linux_decrs) set_dec(linux_decrementer_count - linux_decrs); else set_dec(linux_decrementer_count); decr_clock.arch.istimerset = 1; } return 0;}static int _decr_init (clockid_t clock){ rtl_decrementer_count = linux_decrementer_count; /* we're taking over control of the decr so tell linux to leave it alone */#if LINUX_2_4_0_FINAL_OR_LATER disarm_decr[smp_processor_id()] = 1;#else decrementer_count = 0;#endif asm volatile("mftb %0" : "=r" (last_tb) ); if ( rtl_request_local_irq(0, decr_intercept, 0) ) { printk("Failed getting local timer irq\n"); return -1; } set_dec(rtl_decrementer_count); return 0;}static void _decr_uninit (clockid_t clock){ rtl_free_local_irq(0,0); /* give decr control back to linux */#if LINUX_2_4_0_FINAL_OR_LATER disarm_decr[smp_processor_id()] = 0;#else decrementer_count = linux_decrementer_count;#endif clock->handler = RTL_CLOCK_DEFAULTS.handler;}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -