📄 rtl_time.c
字号:
/* * rtl_time.c * * architecture-dependent clock support * * Written by Michael Barabanov * Copyright Finite State Machine Labs Inc. 1998-1999 * Released under the terms of the GPL Version 2 * */#include <rtl_conf.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/config.h>#include <asm/smp.h>#include <asm/io.h>#include <linux/errno.h>#include <asm/system.h>#include <linux/irq.h>#include <asm/hw_irq.h>#include <linux/sched.h>#include <linux/timex.h>#include <linux/mc146818rtc.h>#include <rtl.h>#include <rtl_core.h>#include <rtl_debug.h>#include <rtl_sync.h>#include <rtl_time.h>/* #define CONFIG_RTL_FAST_8254 */int notsc=0;MODULE_PARM(notsc,"i");int _8254_latency = 0;#ifndef cpu_has_tsc#define cpu_has_tsc \ (boot_cpu_data.x86_capability & X86_FEATURE_TSC)#endifhrtime_t _gethrtime(struct rtl_clock *c){ return gethrtime();}struct rtl_clock _i8254_clock;#ifdef CONFIG_X86_LOCAL_APICstruct rtl_clock _apic_clock[NR_CPUS];#endifextern void (*kd_mksound)(unsigned int hz, unsigned int ticks);static hrtime_t (*rtl_do_get_time)(void);static hrtime_t hrtime_resolution;static long LATCH_NS;static long MAX_LATCH_ONESHOT;/* getting global time from 8254 */#define READ_CNT0(var) \do { var = inb(0x40); var |= (inb(0x40) << 8); } while (0)#define READ_CNT2(var) \do { var = inb(0x42); var |= (inb(0x42) << 8); } while (0)#define LATCH_CNT0() \outb(0xd2, 0x43);#define LATCH_CNT0_AND_2() \outb(0xda, 0x43);#define LATCH_CNT2() \outb(0xd8, 0x43);#define WRITE_COUNTER_ZERO16(x) do { \ outb(x&0xff,0x40); outb((x>>8)&0xff,0x40);\ clock_counter =x; \} while (0)#define WRITE_COUNTER_ZERO8(x) do { \ outb(x&0xff,0x40); \ clock_counter =x; \} while (0)#ifdef CONFIG_RTL_FAST_8254#define WRITE_COUNTER_ZERO_ONESHOT(x) WRITE_COUNTER_ZERO8(x)#else #define WRITE_COUNTER_ZERO_ONESHOT(x) WRITE_COUNTER_ZERO16(x)#endifstatic volatile int last_c2;unsigned long scaler_8254_to_hrtime; /* =8380965 ns*100 */unsigned long scaler_hrtime_to_8254;#define LATCH2 0x8000/*static */spinlock_t lock8254;/*static */spinlock_t lock_linuxtime;hrtime_t gethrtime(void){ return rtl_do_get_time();}hrtime_t gethrtimeres(void){ return hrtime_resolution;}hrtime_t base_time;hrtime_t last_8254_time;long offset_time;hrtime_t global_8254_gettime (void){ register unsigned int c2; int flags; long t; rtl_spin_lock_irqsave (&lock8254, flags); LATCH_CNT2(); READ_CNT2(c2); offset_time += ((c2 < last_c2) ? (last_c2 - c2) / 2 : (last_c2 - c2 + LATCH2) / 2); last_c2 = c2; if (offset_time >= CLOCK_TICK_RATE) { offset_time -= CLOCK_TICK_RATE; base_time += HRTICKS_PER_SEC; }#if HRTICKS_PER_SEC != CLOCK_TICK_RATE __asm__("shl $10, %%eax\n\t" "mul %%ebx\n\t" :"=d" (t) : "b" (scaler_8254_to_hrtime), "a" (offset_time));#else t = offset_time;#endif last_8254_time = base_time + t; rtl_spin_unlock_irqrestore (&lock8254, flags); return last_8254_time;}/* getting global time from Pentium TSC */static unsigned long scaler_pentium_to_hrtime = 0;int can_change_latch2;int I8253_channel2_free(void){ return can_change_latch2;}hrtime_t pent_gettime(void){ hrtime_t t; /* time = counter * scaler_pentium_to_hrtime / 2^32 * 2^5; */ /* Why 2^5? Because the slowest Pentiums run at 60 MHz */ __asm__("rdtsc\n\t" "mov %%edx, %%ecx\n\t" "mul %%ebx\n\t" /* multiply the low 32 bits of the counter by the scaler_pentium */ "mov %%ecx, %%eax\n\t" "mov %%edx, %%ecx\n\t" /* save the high 32 bits of the product */ "mul %%ebx\n\t" /* now the high 32 bits of the counter */ "add %%ecx, %%eax\n\t" "adc $0, %%edx\n\t"#if HRTICKS_PER_SEC == NSECS_PER_SEC "shld $5, %%eax, %%edx\n\t" "shl $5, %%eax\n\t"#endif :"=A" (t) : "b" (scaler_pentium_to_hrtime) : "cx"); return t;}/* #ifndef CONFIG_X86_TSC */extern unsigned long (*do_gettimeoffset)(void);static unsigned long (*save_do_gettimeoffset)(void);/* we can't allow Linux to read the clocks; it must use this function */#include <linux/timex.h>#define TICK_SIZE tick#define printll(x) rtl_printf("%x%08x ", (unsigned) ((x) >> 32), (unsigned) (x))static int rtl_save_jiffies;static unsigned long do_rt_gettimeoffset(void){ int count; hrtime_t diff; long flags; diff = gethrtime() + LATCH_NS; rtl_spin_lock_irqsave (&lock_linuxtime, flags); if (/* ;rtl_global_ispending_irq(0) || */ jiffies == rtl_save_jiffies) { diff += LATCH_NS; } diff -= _i8254_clock.arch.linux_time; rtl_spin_unlock_irqrestore (&lock_linuxtime, flags);/* count = (((long)(now - _i8254_clock.arch.linux_time)) * TICK_SIZE) / LATCH_NS; */ count = muldiv((long)diff, TICK_SIZE, LATCH_NS); /* rtl_printf("%d ", count); */ return count;}/* #endif */static void rtl_kd_nosound(unsigned long ignored){ int flags; rtl_no_interrupts(flags); outb(inb_p(0x61) & 0xfd, 0x61); rtl_restore_interrupts(flags);}static void rtl_kd_mksound(unsigned int hz, unsigned int ticks){ static struct timer_list sound_timer = { function: rtl_kd_nosound }; unsigned int count = 0; if (hz > 20 && hz < 32767) count = 1193180 / hz; cli(); del_timer(&sound_timer); if (count) { int flags; rtl_spin_lock_irqsave (&lock8254, flags); outb_p(inb_p(0x61)|3, 0x61); if (can_change_latch2) { outb_p(0xB6, 0x43); outb_p(count & 0xff, 0x42); outb((count >> 8) & 0xff, 0x42); } rtl_spin_unlock_irqrestore (&lock8254, flags); if (ticks) { sound_timer.expires = jiffies+ticks; add_timer(&sound_timer); } } else rtl_kd_nosound(0); sti(); return;}static void (*save_kd_mksound)(unsigned int hz, unsigned int ticks);static void uninit_hrtime (void){ rtl_kd_mksound(0, 0); kd_mksound = save_kd_mksound;}#define wait_value(x) do {; } while ((inb(0x61) & 0x20) != (x))#define wait_cycle() do { wait_value(0); wait_value(0x20); } while (0)#define CLATCH (1024 * 32)#define NLOOPS 50#ifdef CONFIG_X86_LOCAL_APICstatic unsigned long scaler_hrtime_to_apic;/* this is defined in arch/i386/kernel/smp.c */#define APIC_DIVISOR 16static long apic_ticks_per_sec;#endif/* scaler_pentium == 2^32 / (2^5 * (cpu clocks per ns)) */static void do_calibration(int do_tsc){#ifdef CONFIG_X86_LOCAL_APIC long temp = 0; long a1 = 0; long a2 = 0; long save_apic = 0; long result_apic;#endif long long t1 = 0; long long t2 = 0; long pps; int j; long result = 0; rtl_irqstate_t flags; rtl_no_interrupts(flags);#ifdef CONFIG_X86_LOCAL_APIC if (smp_found_config) { save_apic = apic_read(APIC_TMICT); apic_write(APIC_TMICT, 1000000000/APIC_DIVISOR); }#endif outb((inb(0x61) & ~0x02) | 0x01, 0x61); outb_p(0xb6, 0x43); /* binary, mode 3, LSB/MSB, ch 2 */ outb(CLATCH & 0xff, 0x42); /* LSB of count */ outb(CLATCH >> 8, 0x42); /* MSB of count */ wait_cycle(); if (do_tsc) rdtscll(t1);#ifdef CONFIG_X86_LOCAL_APIC if (smp_found_config) { a1 = apic_read(APIC_TMCCT); }#endif for (j = 0; j < NLOOPS; j++) { wait_cycle();/* rtl_allow_interrupts(); *//* rtl_stop_interrupts(); */ } if (do_tsc) rdtscll(t2);#ifdef CONFIG_X86_LOCAL_APIC if (smp_found_config) { a2 = apic_read(APIC_TMCCT); } result_apic = a1 - a2;#endif if (do_tsc) result = t2 - t1;#ifdef CONFIG_X86_LOCAL_APIC if (smp_found_config) { temp = apic_read(APIC_TMICT); apic_write(APIC_TMICT, save_apic); }#endif rtl_restore_interrupts(flags); if (do_tsc) { pps = muldiv (result, CLOCK_TICK_RATE, CLATCH * NLOOPS);#if HRTICKS_PER_SEC == NSECS_PER_SEC scaler_pentium_to_hrtime = muldiv (1 << 27, HRTICKS_PER_SEC, pps);#else scaler_pentium_to_hrtime = muldiv (1 << 31, HRTICKS_PER_SEC * 2, pps);#endif } else { scaler_pentium_to_hrtime = 0; }#ifdef CONFIG_X86_LOCAL_APIC if (smp_found_config) { temp = muldiv (result_apic, CLOCK_TICK_RATE, CLATCH * NLOOPS);#if HRTICKS_PER_SEC == NSECS_PER_SEC scaler_hrtime_to_apic = muldiv (temp, 1 << 31, HRTICKS_PER_SEC / 2);#else scaler_hrtime_to_apic = muldiv (temp, 1 << (31 - 10), HRTICKS_PER_SEC / 2);#endif/* printk("sca=%ld, temp=%ld resapic=%ld\n", scaler_hrtime_to_apic, temp, result_apic); */ apic_ticks_per_sec = temp; /* printk("pps apic=%ld %ld\n", temp * APIC_DIVISOR, scaler_hrtime_to_apic); */ }#endif/* printk("pps=%ld\n", pps); */}static void init_hrtime (void){ int flags;#ifdef CONFIG_VT kd_mksound(0, 0); /* clear the possibly pending sound timer */#endif save_kd_mksound = kd_mksound; kd_mksound = rtl_kd_mksound;#if HRTICKS_PER_SEC != CLOCK_TICK_RATE scaler_8254_to_hrtime = muldiv (HRTICKS_PER_SEC, 1 << 22, CLOCK_TICK_RATE); scaler_hrtime_to_8254 = muldiv (CLOCK_TICK_RATE, 1 << 31, HRTICKS_PER_SEC / 2); LATCH_NS = muldiv (LATCH, HRTICKS_PER_SEC, CLOCK_TICK_RATE);#else LATCH_NS = LATCH;#endif MAX_LATCH_ONESHOT = LATCH_NS * 3 / 4;#ifdef CONFIG_RTL_FAST_8254 MAX_LATCH_ONESHOT = muldiv (250, HRTICKS_PER_SEC, CLOCK_TICK_RATE);#endif rtl_no_interrupts(flags); if (cpu_has_tsc && !notsc) { can_change_latch2 = 1; do_calibration(1); rtl_do_get_time = pent_gettime; hrtime_resolution = 32; /* printk("scaler_pentium_to_hrtime = %d\n", (int) scaler_pentium_to_hrtime); */ } else { do_calibration(0); can_change_latch2 = 0; /* program channel 2 of the 8254 chip for periodic counting */ outb_p(0xb6, 0x43); /* binary, mode 3, LSB/MSB, ch 2 */ outb_p(LATCH2 & 0xff, 0x42); outb_p((LATCH2 >> 8) & 0xff, 0x42); outb_p((inb_p(0x61) & 0xfd) | 1, 0x61); /* shut up the speaker and enable counting */ LATCH_CNT2(); READ_CNT2(last_c2); offset_time = 0; base_time = 0; rtl_do_get_time = global_8254_gettime; hrtime_resolution = HRTICKS_PER_SEC / CLOCK_TICK_RATE; } /* current_time.tv_sec = xtime.tv_sec; current_time.tv_nsec = 0; rtl_time_offset = 0; rtl_time_offset = rtl_timespec_to_rtime(¤t_time) - rtl_get_time(); */ rtl_restore_interrupts(flags);}void rtl_clock_clear(clockid_t h){ h->uninit(h);}static hrtime_t periodic_gethrtime (struct rtl_clock *c) { return c->value; }static hrtime_t oneshot_gethrtime (struct rtl_clock *c) { return gethrtime(); }/* the 8254 clock */static unsigned int clock_counter; /* current latch value */static inline long RTIME_to_8254_ticks(long t){#if HRTICKS_PER_SEC != CLOCK_TICK_RATE int dummy; __asm__("mull %2" :"=a" (dummy), "=d" (t) :"g" (scaler_hrtime_to_8254), "0" (t) );#endif return (t);}void _8254_checklinuxirq (void){ /* TODO must periodically call gethrtime if it's based on i8254 */ /* or maybe not (for speed reasons) * anyway, here's a way to do it: * set a bit on every gethrtime(); * clear it in periodic irq * periodically check: if it's not set, gethrtime() * * alternatively, use the fact that in periodic * mode we can add periods to 8254 ticks count */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -