⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 time.c

📁 xen 3.2.2 源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/****************************************************************************** * arch/x86/time.c *  * Per-CPU time calibration and management. *  * Copyright (c) 2002-2005, K A Fraser *  * Portions from Linux are: * Copyright (c) 1991, 1992, 1995  Linus Torvalds */#include <xen/config.h>#include <xen/errno.h>#include <xen/event.h>#include <xen/sched.h>#include <xen/lib.h>#include <xen/config.h>#include <xen/init.h>#include <xen/time.h>#include <xen/timer.h>#include <xen/smp.h>#include <xen/irq.h>#include <xen/softirq.h>#include <asm/io.h>#include <asm/msr.h>#include <asm/mpspec.h>#include <asm/processor.h>#include <asm/fixmap.h>#include <asm/mc146818rtc.h>#include <asm/div64.h>#include <asm/hpet.h>#include <io_ports.h>/* opt_clocksource: Force clocksource to one of: pit, hpet, cyclone, acpi. */static char opt_clocksource[10];string_param("clocksource", opt_clocksource);#define EPOCH MILLISECS(1000)unsigned long cpu_khz;  /* CPU clock frequency in kHz. */unsigned long hpet_address;DEFINE_SPINLOCK(rtc_lock);volatile unsigned long jiffies;static u32 wc_sec, wc_nsec; /* UTC time at last 'time update'. */static DEFINE_SPINLOCK(wc_lock);struct time_scale {    int shift;    u32 mul_frac;};struct cpu_time {    u64 local_tsc_stamp;    s_time_t stime_local_stamp;    s_time_t stime_master_stamp;    struct time_scale tsc_scale;    struct timer calibration_timer;};struct platform_timesource {    char *name;    u64 frequency;    u32 (*read_counter)(void);    int counter_bits;};static DEFINE_PER_CPU(struct cpu_time, cpu_time);/* * Protected by platform_timer_lock, which must be acquired with interrupts * disabled because plt_overflow() is called from PIT ch0 interrupt context. */static s_time_t stime_platform_stamp;static u64 platform_timer_stamp;static DEFINE_SPINLOCK(platform_timer_lock);/* * Folding platform timer into 64-bit software counter is a really critical * operation! We therefore do it directly in PIT ch0 interrupt handler. */static u32 plt_overflow_jiffies;static void plt_overflow(void);/* * 32-bit division of integer dividend and integer divisor yielding * 32-bit fractional quotient. */static inline u32 div_frac(u32 dividend, u32 divisor){    u32 quotient, remainder;    ASSERT(dividend < divisor);    asm (         "divl %4"        : "=a" (quotient), "=d" (remainder)        : "0" (0), "1" (dividend), "r" (divisor) );    return quotient;}/* * 32-bit multiplication of multiplicand and fractional multiplier * yielding 32-bit product (radix point at same position as in multiplicand). */static inline u32 mul_frac(u32 multiplicand, u32 multiplier){    u32 product_int, product_frac;    asm (        "mul %3"        : "=a" (product_frac), "=d" (product_int)        : "0" (multiplicand), "r" (multiplier) );    return product_int;}/* * Scale a 64-bit delta by scaling and multiplying by a 32-bit fraction, * yielding a 64-bit result. */static inline u64 scale_delta(u64 delta, struct time_scale *scale){    u64 product;#ifdef CONFIG_X86_32    u32 tmp1, tmp2;#endif    if ( scale->shift < 0 )        delta >>= -scale->shift;    else        delta <<= scale->shift;#ifdef CONFIG_X86_32    asm (        "mul  %5       ; "        "mov  %4,%%eax ; "        "mov  %%edx,%4 ; "        "mul  %5       ; "        "xor  %5,%5    ; "        "add  %4,%%eax ; "        "adc  %5,%%edx ; "        : "=A" (product), "=r" (tmp1), "=r" (tmp2)        : "a" ((u32)delta), "1" ((u32)(delta >> 32)), "2" (scale->mul_frac) );#else    asm (        "mul %%rdx ; shrd $32,%%rdx,%%rax"        : "=a" (product) : "0" (delta), "d" ((u64)scale->mul_frac) );#endif    return product;}void timer_interrupt(int irq, void *dev_id, struct cpu_user_regs *regs){    ASSERT(local_irq_is_enabled());    /* Update jiffies counter. */    (*(volatile unsigned long *)&jiffies)++;    /* Rough hack to allow accurate timers to sort-of-work with no APIC. */    if ( !cpu_has_apic )        raise_softirq(TIMER_SOFTIRQ);    if ( --plt_overflow_jiffies == 0 )        plt_overflow();}static struct irqaction irq0 = { timer_interrupt, "timer", NULL};/* ------ Calibrate the TSC -------  * Return processor ticks per second / CALIBRATE_FRAC. */#define CLOCK_TICK_RATE 1193180 /* system crystal frequency (Hz) */#define CALIBRATE_FRAC  20      /* calibrate over 50ms */#define CALIBRATE_LATCH ((CLOCK_TICK_RATE+(CALIBRATE_FRAC/2))/CALIBRATE_FRAC)static u64 init_pit_and_calibrate_tsc(void){    u64 start, end;    unsigned long count;    /* Set PIT channel 0 to HZ Hz. */#define LATCH (((CLOCK_TICK_RATE)+(HZ/2))/HZ)    outb_p(0x34, PIT_MODE);        /* binary, mode 2, LSB/MSB, ch 0 */    outb_p(LATCH & 0xff, PIT_CH0); /* LSB */    outb(LATCH >> 8, PIT_CH0);     /* MSB */    /* Set the Gate high, disable speaker */    outb((inb(0x61) & ~0x02) | 0x01, 0x61);    /*     * Now let's take care of CTC channel 2     *     * Set the Gate high, program CTC channel 2 for mode 0, (interrupt on     * terminal count mode), binary count, load 5 * LATCH count, (LSB and MSB)     * to begin countdown.     */    outb(0xb0, PIT_MODE);           /* binary, mode 0, LSB/MSB, Ch 2 */    outb(CALIBRATE_LATCH & 0xff, PIT_CH2); /* LSB of count */    outb(CALIBRATE_LATCH >> 8, PIT_CH2);   /* MSB of count */    rdtscll(start);    for ( count = 0; (inb(0x61) & 0x20) == 0; count++ )        continue;    rdtscll(end);    /* Error if the CTC doesn't behave itself. */    if ( count == 0 )        return 0;    return ((end - start) * (u64)CALIBRATE_FRAC);}static void set_time_scale(struct time_scale *ts, u64 ticks_per_sec){    u64 tps64 = ticks_per_sec;    u32 tps32;    int shift = 0;    ASSERT(tps64 != 0);    while ( tps64 > (MILLISECS(1000)*2) )    {        tps64 >>= 1;        shift--;    }    tps32 = (u32)tps64;    while ( tps32 <= (u32)MILLISECS(1000) )    {        tps32 <<= 1;        shift++;    }    ts->mul_frac = div_frac(MILLISECS(1000), tps32);    ts->shift    = shift;}static atomic_t tsc_calibrate_gang = ATOMIC_INIT(0);static unsigned int tsc_calibrate_status = 0;void calibrate_tsc_bp(void){    while ( atomic_read(&tsc_calibrate_gang) != (num_booting_cpus() - 1) )        mb();    outb(CALIBRATE_LATCH & 0xff, PIT_CH2);    outb(CALIBRATE_LATCH >> 8, PIT_CH2);    tsc_calibrate_status = 1;    wmb();    while ( (inb(0x61) & 0x20) == 0 )        continue;    tsc_calibrate_status = 2;    wmb();    while ( atomic_read(&tsc_calibrate_gang) != 0 )        mb();}void calibrate_tsc_ap(void){    u64 t1, t2, ticks_per_sec;    atomic_inc(&tsc_calibrate_gang);    while ( tsc_calibrate_status < 1 )        mb();    rdtscll(t1);    while ( tsc_calibrate_status < 2 )        mb();    rdtscll(t2);    ticks_per_sec = (t2 - t1) * (u64)CALIBRATE_FRAC;    set_time_scale(&this_cpu(cpu_time).tsc_scale, ticks_per_sec);    atomic_dec(&tsc_calibrate_gang);}static char *freq_string(u64 freq){    static char s[20];    unsigned int x, y;    y = (unsigned int)do_div(freq, 1000000) / 1000;    x = (unsigned int)freq;    snprintf(s, sizeof(s), "%u.%03uMHz", x, y);    return s;}/************************************************************ * PLATFORM TIMER 1: PROGRAMMABLE INTERVAL TIMER (LEGACY PIT) */static u32 read_pit_count(void){    u16 count;    ASSERT(spin_is_locked(&platform_timer_lock));    outb(0x80, PIT_MODE);    count  = inb(PIT_CH2);    count |= inb(PIT_CH2) << 8;    return ~count;}static void init_pit(struct platform_timesource *pts){    pts->name = "PIT";    pts->frequency = CLOCK_TICK_RATE;    pts->read_counter = read_pit_count;    pts->counter_bits = 16;}/************************************************************ * PLATFORM TIMER 2: HIGH PRECISION EVENT TIMER (HPET) */static u32 read_hpet_count(void){    return hpet_read32(HPET_COUNTER);}static int init_hpet(struct platform_timesource *pts){    u64 hpet_rate;    u32 hpet_id, hpet_period, cfg;    int i;    if ( hpet_address == 0 )        return 0;    set_fixmap_nocache(FIX_HPET_BASE, hpet_address);    hpet_id = hpet_read32(HPET_ID);    if ( hpet_id == 0 )    {        printk("BAD HPET vendor id.\n");        return 0;    }    /* Check for sane period (100ps <= period <= 100ns). */    hpet_period = hpet_read32(HPET_PERIOD);    if ( (hpet_period > 100000000) || (hpet_period < 100000) )    {        printk("BAD HPET period %u.\n", hpet_period);        return 0;    }    cfg = hpet_read32(HPET_CFG);    cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY);    hpet_write32(cfg, HPET_CFG);    for ( i = 0; i <= ((hpet_id >> 8) & 31); i++ )    {        cfg = hpet_read32(HPET_T0_CFG + i*0x20);        cfg &= ~HPET_TN_ENABLE;        hpet_write32(cfg & ~HPET_TN_ENABLE, HPET_T0_CFG);    }    cfg = hpet_read32(HPET_CFG);    cfg |= HPET_CFG_ENABLE;    hpet_write32(cfg, HPET_CFG);    hpet_rate = 1000000000000000ULL; /* 10^15 */    (void)do_div(hpet_rate, hpet_period);    pts->name = "HPET";    pts->frequency = hpet_rate;    pts->read_counter = read_hpet_count;    pts->counter_bits = 32;    return 1;}/************************************************************ * PLATFORM TIMER 3: IBM 'CYCLONE' TIMER */int use_cyclone;/* * Although the counter is read via a 64-bit register, I believe it is actually * a 40-bit counter. Since this will wrap, I read only the low 32 bits and * periodically fold into a 64-bit software counter, just as for PIT and HPET. */#define CYCLONE_CBAR_ADDR   0xFEB00CD0#define CYCLONE_PMCC_OFFSET 0x51A0#define CYCLONE_MPMC_OFFSET 0x51D0#define CYCLONE_MPCS_OFFSET 0x51A8#define CYCLONE_TIMER_FREQ  100000000/* Cyclone MPMC0 register. */static volatile u32 *cyclone_timer;static u32 read_cyclone_count(void){    return *cyclone_timer;}static volatile u32 *map_cyclone_reg(unsigned long regaddr){    unsigned long pageaddr = regaddr &  PAGE_MASK;    unsigned long offset   = regaddr & ~PAGE_MASK;    set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr);    return (volatile u32 *)(fix_to_virt(FIX_CYCLONE_TIMER) + offset);}static int init_cyclone(struct platform_timesource *pts){    u32 base;        if ( !use_cyclone )        return 0;    /* Find base address. */    base = *(map_cyclone_reg(CYCLONE_CBAR_ADDR));    if ( base == 0 )    {        printk(KERN_ERR "Cyclone: Could not find valid CBAR value.\n");        return 0;    }     /* Enable timer and map the counter register. */    *(map_cyclone_reg(base + CYCLONE_PMCC_OFFSET)) = 1;    *(map_cyclone_reg(base + CYCLONE_MPCS_OFFSET)) = 1;    cyclone_timer = map_cyclone_reg(base + CYCLONE_MPMC_OFFSET);    pts->name = "IBM Cyclone";    pts->frequency = CYCLONE_TIMER_FREQ;    pts->read_counter = read_cyclone_count;    pts->counter_bits = 32;    return 1;}/************************************************************ * PLATFORM TIMER 4: ACPI PM TIMER */u32 pmtmr_ioport;/* ACPI PM timer ticks at 3.579545 MHz. */#define ACPI_PM_FREQUENCY 3579545static u32 read_pmtimer_count(void){    return inl(pmtmr_ioport);}static int init_pmtimer(struct platform_timesource *pts){    if ( pmtmr_ioport == 0 )        return 0;    pts->name = "ACPI PM Timer";    pts->frequency = ACPI_PM_FREQUENCY;    pts->read_counter = read_pmtimer_count;    pts->counter_bits = 24;    return 1;}/************************************************************ * GENERIC PLATFORM TIMER INFRASTRUCTURE */static struct platform_timesource plt_src; /* details of chosen timesource  */static u32 plt_mask;             /* hardware-width mask                     */static u32 plt_overflow_period;  /* jiffies between calls to plt_overflow() */static struct time_scale plt_scale; /* scale: platform counter -> nanosecs  *//* Protected by platform_timer_lock. */static u64 plt_count64;          /* 64-bit platform counter stamp           */static u32 plt_count;            /* hardware-width platform counter stamp   */static void plt_overflow(void){    u32 count;    unsigned long flags;    spin_lock_irqsave(&platform_timer_lock, flags);    count = plt_src.read_counter();    plt_count64 += (count - plt_count) & plt_mask;    plt_count = count;    plt_overflow_jiffies = plt_overflow_period;    spin_unlock_irqrestore(&platform_timer_lock, flags);}static s_time_t __read_platform_stime(u64 platform_time){    u64 diff = platform_time - platform_timer_stamp;    ASSERT(spin_is_locked(&platform_timer_lock));    return (stime_platform_stamp + scale_delta(diff, &plt_scale));}static s_time_t read_platform_stime(void){    u64 count;    s_time_t stime;    unsigned long flags;    spin_lock_irqsave(&platform_timer_lock, flags);    count = plt_count64 + ((plt_src.read_counter() - plt_count) & plt_mask);    stime = __read_platform_stime(count);    spin_unlock_irqrestore(&platform_timer_lock, flags);    return stime;}static void platform_time_calibration(void){    u64 count;    s_time_t stamp;    unsigned long flags;    spin_lock_irqsave(&platform_timer_lock, flags);    count = plt_count64 + ((plt_src.read_counter() - plt_count) & plt_mask);    stamp = __read_platform_stime(count);    stime_platform_stamp = stamp;    platform_timer_stamp = count;    spin_unlock_irqrestore(&platform_timer_lock, flags);}static void resume_platform_timer(void){    /* No change in platform_stime across suspend/resume. */    platform_timer_stamp = plt_count64;    plt_count = plt_src.read_counter();}static void init_platform_timer(void){    struct platform_timesource *pts = &plt_src;    u64 overflow_period;    int rc = -1;    if ( opt_clocksource[0] != '\0' )    {        if ( !strcmp(opt_clocksource, "pit") )            rc = (init_pit(pts), 1);        else if ( !strcmp(opt_clocksource, "hpet") )            rc = init_hpet(pts);        else if ( !strcmp(opt_clocksource, "cyclone") )            rc = init_cyclone(pts);        else if ( !strcmp(opt_clocksource, "acpi") )

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -