direct-io.hg

changeset 15258:bd94f75fe469

x86: Clean up interface to platform timers to extract more common code.
Signed-off-by: Keir Fraser <keir@xensource.com>
author kfraser@localhost.localdomain
date Mon Jun 11 15:10:23 2007 +0100 (2007-06-11)
parents ed254cf78f7c
children 35e38c9048c8
files xen/arch/x86/time.c
line diff
     1.1 --- a/xen/arch/x86/time.c	Mon Jun 11 14:56:50 2007 +0100
     1.2 +++ b/xen/arch/x86/time.c	Mon Jun 11 15:10:23 2007 +0100
     1.3 @@ -57,25 +57,29 @@ struct cpu_time {
     1.4      struct timer calibration_timer;
     1.5  };
     1.6  
     1.7 +struct platform_timesource {
     1.8 +    char *name;
     1.9 +    u64 frequency;
    1.10 +    u32 (*read_counter)(void);
    1.11 +    int counter_bits;
    1.12 +};
    1.13 +
    1.14  static DEFINE_PER_CPU(struct cpu_time, cpu_time);
    1.15  
    1.16  /*
    1.17   * Protected by platform_timer_lock, which must be acquired with interrupts
    1.18 - * disabled because pit_overflow() is called from PIT ch0 interrupt context.
    1.19 + * disabled because plt_overflow() is called from PIT ch0 interrupt context.
    1.20   */
    1.21  static s_time_t stime_platform_stamp;
    1.22  static u64 platform_timer_stamp;
    1.23 -static struct time_scale platform_timer_scale;
    1.24  static DEFINE_SPINLOCK(platform_timer_lock);
    1.25 -static u64 (*read_platform_count)(void);
    1.26  
    1.27  /*
    1.28 - * Folding 16-bit PIT into 64-bit software counter is a really critical
    1.29 - * operation! We therefore do it directly in PIT ch0 interrupt handler,
    1.30 - * based on this flag.
    1.31 + * Folding platform timer into 64-bit software counter is a really critical
    1.32 + * operation! We therefore do it directly in PIT ch0 interrupt handler.
    1.33   */
    1.34 -static int using_pit;
    1.35 -static void pit_overflow(void);
    1.36 +static u32 plt_overflow_jiffies;
    1.37 +static void plt_overflow(void);
    1.38  
    1.39  /*
    1.40   * 32-bit division of integer dividend and integer divisor yielding
    1.41 @@ -153,8 +157,8 @@ void timer_interrupt(int irq, void *dev_
    1.42      if ( !cpu_has_apic )
    1.43          raise_softirq(TIMER_SOFTIRQ);
    1.44  
    1.45 -    if ( using_pit )
    1.46 -        pit_overflow();
    1.47 +    if ( --plt_overflow_jiffies == 0 )
    1.48 +        plt_overflow();
    1.49  }
    1.50  
    1.51  static struct irqaction irq0 = { timer_interrupt, "timer", NULL};
    1.52 @@ -281,76 +285,34 @@ static char *freq_string(u64 freq)
    1.53   * PLATFORM TIMER 1: PROGRAMMABLE INTERVAL TIMER (LEGACY PIT)
    1.54   */
    1.55  
    1.56 -/* Protected by platform_timer_lock. */
    1.57 -static u64 pit_counter64;
    1.58 -static u16 pit_stamp;
    1.59 -
    1.60 -static u16 pit_read_counter(void)
    1.61 +static u32 read_pit_count(void)
    1.62  {
    1.63      u16 count;
    1.64      ASSERT(spin_is_locked(&platform_timer_lock));
    1.65      outb(0x80, PIT_MODE);
    1.66      count  = inb(PIT_CH2);
    1.67      count |= inb(PIT_CH2) << 8;
    1.68 -    return count;
    1.69 -}
    1.70 -
    1.71 -static void pit_overflow(void)
    1.72 -{
    1.73 -    u16 counter;
    1.74 -
    1.75 -    spin_lock_irq(&platform_timer_lock);
    1.76 -    counter = pit_read_counter();
    1.77 -    pit_counter64 += (u16)(pit_stamp - counter);
    1.78 -    pit_stamp = counter;
    1.79 -    spin_unlock_irq(&platform_timer_lock);
    1.80 +    return ~count;
    1.81  }
    1.82  
    1.83 -static u64 read_pit_count(void)
    1.84 -{
    1.85 -    return pit_counter64 + (u16)(pit_stamp - pit_read_counter());
    1.86 -}
    1.87 -
    1.88 -static void init_pit(void)
    1.89 +static void init_pit(struct platform_timesource *pts)
    1.90  {
    1.91 -    read_platform_count = read_pit_count;
    1.92 -
    1.93 -    pit_overflow();
    1.94 -    platform_timer_stamp = pit_counter64;
    1.95 -    set_time_scale(&platform_timer_scale, CLOCK_TICK_RATE);
    1.96 -
    1.97 -    printk("Platform timer is %s PIT\n", freq_string(CLOCK_TICK_RATE));
    1.98 -    using_pit = 1;
    1.99 +    pts->name = "PIT";
   1.100 +    pts->frequency = CLOCK_TICK_RATE;
   1.101 +    pts->read_counter = read_pit_count;
   1.102 +    pts->counter_bits = 16;
   1.103  }
   1.104  
   1.105  /************************************************************
   1.106   * PLATFORM TIMER 2: HIGH PRECISION EVENT TIMER (HPET)
   1.107   */
   1.108  
   1.109 -/* Protected by platform_timer_lock. */
   1.110 -static u64 hpet_counter64, hpet_overflow_period;
   1.111 -static u32 hpet_stamp;
   1.112 -static struct timer hpet_overflow_timer;
   1.113 -
   1.114 -static void hpet_overflow(void *unused)
   1.115 +static u32 read_hpet_count(void)
   1.116  {
   1.117 -    u32 counter;
   1.118 -
   1.119 -    spin_lock_irq(&platform_timer_lock);
   1.120 -    counter = hpet_read32(HPET_COUNTER);
   1.121 -    hpet_counter64 += (u32)(counter - hpet_stamp);
   1.122 -    hpet_stamp = counter;
   1.123 -    spin_unlock_irq(&platform_timer_lock);
   1.124 -
   1.125 -    set_timer(&hpet_overflow_timer, NOW() + hpet_overflow_period);
   1.126 +    return hpet_read32(HPET_COUNTER);
   1.127  }
   1.128  
   1.129 -static u64 read_hpet_count(void)
   1.130 -{
   1.131 -    return hpet_counter64 + (u32)(hpet_read32(HPET_COUNTER) - hpet_stamp);
   1.132 -}
   1.133 -
   1.134 -static int init_hpet(void)
   1.135 +static int init_hpet(struct platform_timesource *pts)
   1.136  {
   1.137      u64 hpet_rate;
   1.138      u32 hpet_id, hpet_period, cfg;
   1.139 @@ -391,29 +353,13 @@ static int init_hpet(void)
   1.140      cfg |= HPET_CFG_ENABLE;
   1.141      hpet_write32(cfg, HPET_CFG);
   1.142  
   1.143 -    read_platform_count = read_hpet_count;
   1.144 -
   1.145      hpet_rate = 1000000000000000ULL; /* 10^15 */
   1.146      (void)do_div(hpet_rate, hpet_period);
   1.147 -    set_time_scale(&platform_timer_scale, hpet_rate);
   1.148  
   1.149 -    /* Trigger overflow avoidance roughly when counter increments 2^31. */
   1.150 -    if ( (hpet_rate >> 31) != 0 )
   1.151 -    {
   1.152 -        hpet_overflow_period = MILLISECS(1000);
   1.153 -        (void)do_div(hpet_overflow_period, (u32)(hpet_rate >> 31) + 1);
   1.154 -    }
   1.155 -    else
   1.156 -    {
   1.157 -        hpet_overflow_period = MILLISECS(1000) << 31;
   1.158 -        (void)do_div(hpet_overflow_period, (u32)hpet_rate);
   1.159 -    }
   1.160 -
   1.161 -    init_timer(&hpet_overflow_timer, hpet_overflow, NULL, 0);
   1.162 -    hpet_overflow(NULL);
   1.163 -    platform_timer_stamp = hpet_counter64;
   1.164 -
   1.165 -    printk("Platform timer is %s HPET\n", freq_string(hpet_rate));
   1.166 +    pts->name = "HPET";
   1.167 +    pts->frequency = hpet_rate;
   1.168 +    pts->read_counter = read_hpet_count;
   1.169 +    pts->counter_bits = 32;
   1.170  
   1.171      return 1;
   1.172  }
   1.173 @@ -435,28 +381,12 @@ int use_cyclone;
   1.174  #define CYCLONE_MPCS_OFFSET 0x51A8
   1.175  #define CYCLONE_TIMER_FREQ  100000000
   1.176  
   1.177 -/* Protected by platform_timer_lock. */
   1.178 -static u64 cyclone_counter64;
   1.179 -static u32 cyclone_stamp;
   1.180 -static struct timer cyclone_overflow_timer;
   1.181 -static volatile u32 *cyclone_timer; /* Cyclone MPMC0 register */
   1.182 -
   1.183 -static void cyclone_overflow(void *unused)
   1.184 -{
   1.185 -    u32 counter;
   1.186 +/* Cyclone MPMC0 register. */
   1.187 +static volatile u32 *cyclone_timer;
   1.188  
   1.189 -    spin_lock_irq(&platform_timer_lock);
   1.190 -    counter = *cyclone_timer;
   1.191 -    cyclone_counter64 += (u32)(counter - cyclone_stamp);
   1.192 -    cyclone_stamp = counter;
   1.193 -    spin_unlock_irq(&platform_timer_lock);
   1.194 -
   1.195 -    set_timer(&cyclone_overflow_timer, NOW() + MILLISECS(20000));
   1.196 -}
   1.197 -
   1.198 -static u64 read_cyclone_count(void)
   1.199 +static u32 read_cyclone_count(void)
   1.200  {
   1.201 -    return cyclone_counter64 + (u32)(*cyclone_timer - cyclone_stamp);
   1.202 +    return *cyclone_timer;
   1.203  }
   1.204  
   1.205  static volatile u32 *map_cyclone_reg(unsigned long regaddr)
   1.206 @@ -467,7 +397,7 @@ static volatile u32 *map_cyclone_reg(uns
   1.207      return (volatile u32 *)(fix_to_virt(FIX_CYCLONE_TIMER) + offset);
   1.208  }
   1.209  
   1.210 -static int init_cyclone(void)
   1.211 +static int init_cyclone(struct platform_timesource *pts)
   1.212  {
   1.213      u32 base;
   1.214      
   1.215 @@ -487,15 +417,10 @@ static int init_cyclone(void)
   1.216      *(map_cyclone_reg(base + CYCLONE_MPCS_OFFSET)) = 1;
   1.217      cyclone_timer = map_cyclone_reg(base + CYCLONE_MPMC_OFFSET);
   1.218  
   1.219 -    read_platform_count = read_cyclone_count;
   1.220 -
   1.221 -    init_timer(&cyclone_overflow_timer, cyclone_overflow, NULL, 0);
   1.222 -    cyclone_overflow(NULL);
   1.223 -    platform_timer_stamp = cyclone_counter64;
   1.224 -    set_time_scale(&platform_timer_scale, CYCLONE_TIMER_FREQ);
   1.225 -
   1.226 -    printk("Platform timer is %s IBM Cyclone\n",
   1.227 -           freq_string(CYCLONE_TIMER_FREQ));
   1.228 +    pts->name = "IBM Cyclone";
   1.229 +    pts->frequency = CYCLONE_TIMER_FREQ;
   1.230 +    pts->read_counter = read_cyclone_count;
   1.231 +    pts->counter_bits = 32;
   1.232  
   1.233      return 1;
   1.234  }
   1.235 @@ -506,50 +431,23 @@ static int init_cyclone(void)
   1.236  
   1.237  u32 pmtmr_ioport;
   1.238  
   1.239 -/* Protected by platform_timer_lock. */
   1.240 -static u64 pmtimer_counter64;
   1.241 -static u32 pmtimer_stamp;
   1.242 -static struct timer pmtimer_overflow_timer;
   1.243 -
   1.244  /* ACPI PM timer ticks at 3.579545 MHz. */
   1.245  #define ACPI_PM_FREQUENCY 3579545
   1.246  
   1.247 -/* Deltas are 24-bit unsigned values, as counter may be only 24 bits wide. */
   1.248 -#define pmtimer_delta(c) ((u32)(((c) - pmtimer_stamp) & ((1U<<24)-1)))
   1.249 -
   1.250 -static void pmtimer_overflow(void *unused)
   1.251 +static u32 read_pmtimer_count(void)
   1.252  {
   1.253 -    u32 counter;
   1.254 -
   1.255 -    spin_lock_irq(&platform_timer_lock);
   1.256 -    counter = inl(pmtmr_ioport);
   1.257 -    pmtimer_counter64 += pmtimer_delta(counter);
   1.258 -    pmtimer_stamp = counter;
   1.259 -    spin_unlock_irq(&platform_timer_lock);
   1.260 -
   1.261 -    /* Trigger overflow avoidance roughly when counter increments 2^23. */
   1.262 -    set_timer(&pmtimer_overflow_timer, NOW() + MILLISECS(2000));
   1.263 +    return inl(pmtmr_ioport);
   1.264  }
   1.265  
   1.266 -static u64 read_pmtimer_count(void)
   1.267 -{
   1.268 -    return pmtimer_counter64 + pmtimer_delta(inl(pmtmr_ioport));
   1.269 -}
   1.270 -
   1.271 -static int init_pmtimer(void)
   1.272 +static int init_pmtimer(struct platform_timesource *pts)
   1.273  {
   1.274      if ( pmtmr_ioport == 0 )
   1.275          return 0;
   1.276  
   1.277 -    read_platform_count = read_pmtimer_count;
   1.278 -
   1.279 -    init_timer(&pmtimer_overflow_timer, pmtimer_overflow, NULL, 0);
   1.280 -    pmtimer_overflow(NULL);
   1.281 -    platform_timer_stamp = pmtimer_counter64;
   1.282 -    set_time_scale(&platform_timer_scale, ACPI_PM_FREQUENCY);
   1.283 -
   1.284 -    printk("Platform timer is %s ACPI PM Timer\n",
   1.285 -           freq_string(ACPI_PM_FREQUENCY));
   1.286 +    pts->name = "ACPI PM Timer";
   1.287 +    pts->frequency = ACPI_PM_FREQUENCY;
   1.288 +    pts->read_counter = read_pmtimer_count;
   1.289 +    pts->counter_bits = 24;
   1.290  
   1.291      return 1;
   1.292  }
   1.293 @@ -558,21 +456,43 @@ static int init_pmtimer(void)
   1.294   * GENERIC PLATFORM TIMER INFRASTRUCTURE
   1.295   */
   1.296  
   1.297 +static struct platform_timesource plt_src; /* details of chosen timesource  */
   1.298 +static u32 plt_mask;             /* hardware-width mask                     */
   1.299 +static u32 plt_overflow_period;  /* jiffies between calls to plt_overflow() */
   1.300 +static struct time_scale plt_scale; /* scale: platform counter -> nanosecs  */
   1.301 +
   1.302 +/* Protected by platform_timer_lock. */
   1.303 +static u64 plt_count64;          /* 64-bit platform counter stamp           */
   1.304 +static u32 plt_count;            /* hardware-width platform counter stamp   */
   1.305 +
   1.306 +static void plt_overflow(void)
   1.307 +{
   1.308 +    u32 count;
   1.309 +    unsigned long flags;
   1.310 +
   1.311 +    spin_lock_irqsave(&platform_timer_lock, flags);
   1.312 +    count = plt_src.read_counter();
   1.313 +    plt_count64 += (count - plt_count) & plt_mask;
   1.314 +    plt_count = count;
   1.315 +    plt_overflow_jiffies = plt_overflow_period;
   1.316 +    spin_unlock_irqrestore(&platform_timer_lock, flags);
   1.317 +}
   1.318 +
   1.319  static s_time_t __read_platform_stime(u64 platform_time)
   1.320  {
   1.321      u64 diff = platform_time - platform_timer_stamp;
   1.322      ASSERT(spin_is_locked(&platform_timer_lock));
   1.323 -    return (stime_platform_stamp + scale_delta(diff, &platform_timer_scale));
   1.324 +    return (stime_platform_stamp + scale_delta(diff, &plt_scale));
   1.325  }
   1.326  
   1.327  static s_time_t read_platform_stime(void)
   1.328  {
   1.329 -    u64 counter;
   1.330 +    u64 count;
   1.331      s_time_t stime;
   1.332  
   1.333      spin_lock_irq(&platform_timer_lock);
   1.334 -    counter = read_platform_count();
   1.335 -    stime   = __read_platform_stime(counter);
   1.336 +    count = plt_count64 + ((plt_src.read_counter() - plt_count) & plt_mask);
   1.337 +    stime = __read_platform_stime(count);
   1.338      spin_unlock_irq(&platform_timer_lock);
   1.339  
   1.340      return stime;
   1.341 @@ -580,42 +500,60 @@ static s_time_t read_platform_stime(void
   1.342  
   1.343  static void platform_time_calibration(void)
   1.344  {
   1.345 -    u64 counter;
   1.346 +    u64 count;
   1.347      s_time_t stamp;
   1.348  
   1.349      spin_lock_irq(&platform_timer_lock);
   1.350 -    counter = read_platform_count();
   1.351 -    stamp   = __read_platform_stime(counter);
   1.352 +    count = plt_count64 + ((plt_src.read_counter() - plt_count) & plt_mask);
   1.353 +    stamp = __read_platform_stime(count);
   1.354      stime_platform_stamp = stamp;
   1.355 -    platform_timer_stamp = counter;
   1.356 +    platform_timer_stamp = count;
   1.357      spin_unlock_irq(&platform_timer_lock);
   1.358  }
   1.359  
   1.360  static void init_platform_timer(void)
   1.361  {
   1.362 +    struct platform_timesource *pts = &plt_src;
   1.363 +    u64 overflow_period;
   1.364 +    int rc = -1;
   1.365 +
   1.366      if ( opt_clocksource[0] != '\0' )
   1.367      {
   1.368 -        int rc = -1;
   1.369 -
   1.370          if ( !strcmp(opt_clocksource, "pit") )
   1.371 -            rc = (init_pit(), 1);
   1.372 +            rc = (init_pit(pts), 1);
   1.373          else if ( !strcmp(opt_clocksource, "hpet") )
   1.374 -            rc = init_hpet();
   1.375 +            rc = init_hpet(pts);
   1.376          else if ( !strcmp(opt_clocksource, "cyclone") )
   1.377 -            rc = init_cyclone();
   1.378 +            rc = init_cyclone(pts);
   1.379          else if ( !strcmp(opt_clocksource, "acpi") )
   1.380 -            rc = init_pmtimer();
   1.381 +            rc = init_pmtimer(pts);
   1.382  
   1.383 -        if ( rc == 1 )
   1.384 -            return;
   1.385 -
   1.386 -        printk("WARNING: %s clocksource '%s'.\n",
   1.387 -               (rc == 0) ? "Could not initialise" : "Unrecognised",
   1.388 -               opt_clocksource);
   1.389 +        if ( rc <= 0 )
   1.390 +            printk("WARNING: %s clocksource '%s'.\n",
   1.391 +                   (rc == 0) ? "Could not initialise" : "Unrecognised",
   1.392 +                   opt_clocksource);
   1.393      }
   1.394  
   1.395 -    if ( !init_cyclone() && !init_hpet() && !init_pmtimer() )
   1.396 -        init_pit();
   1.397 +    if ( (rc <= 0) &&
   1.398 +         !init_cyclone(pts) &&
   1.399 +         !init_hpet(pts) &&
   1.400 +         !init_pmtimer(pts) )
   1.401 +        init_pit(pts);
   1.402 +
   1.403 +    plt_mask = (u32)~0u >> (32 - pts->counter_bits);
   1.404 +
   1.405 +    set_time_scale(&plt_scale, pts->frequency);
   1.406 +
   1.407 +    overflow_period = scale_delta(1ull << (pts->counter_bits-1), &plt_scale);
   1.408 +    do_div(overflow_period, MILLISECS(1000/HZ));
   1.409 +    plt_overflow_period = overflow_period;
   1.410 +    plt_overflow();
   1.411 +    printk("Platform timer overflows in %d jiffies.\n", plt_overflow_period);
   1.412 +
   1.413 +    platform_timer_stamp = plt_count64;
   1.414 +
   1.415 +    printk("Platform timer is %s %s\n",
   1.416 +           freq_string(pts->frequency), pts->name);
   1.417  }
   1.418  
   1.419