linux/arch/i386/kernel/vmiclock.c
<<
>>
Prefs
   1/*
   2 * VMI paravirtual timer support routines.
   3 *
   4 * Copyright (C) 2007, VMware, Inc.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful, but
  12 * WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
  14 * NON INFRINGEMENT.  See the GNU General Public License for more
  15 * details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20 *
  21 */
  22
  23#include <linux/smp.h>
  24#include <linux/interrupt.h>
  25#include <linux/cpumask.h>
  26#include <linux/clocksource.h>
  27#include <linux/clockchips.h>
  28
  29#include <asm/vmi.h>
  30#include <asm/vmi_time.h>
  31#include <asm/arch_hooks.h>
  32#include <asm/apicdef.h>
  33#include <asm/apic.h>
  34#include <asm/timer.h>
  35
  36#include <irq_vectors.h>
  37#include "io_ports.h"
  38
  39#define VMI_ONESHOT  (VMI_ALARM_IS_ONESHOT  | VMI_CYCLES_REAL | vmi_get_alarm_wiring())
  40#define VMI_PERIODIC (VMI_ALARM_IS_PERIODIC | VMI_CYCLES_REAL | vmi_get_alarm_wiring())
  41
  42static DEFINE_PER_CPU(struct clock_event_device, local_events);
  43
  44static inline u32 vmi_counter(u32 flags)
  45{
  46        /* Given VMI_ONESHOT or VMI_PERIODIC, return the corresponding
  47         * cycle counter. */
  48        return flags & VMI_ALARM_COUNTER_MASK;
  49}
  50
  51/* paravirt_ops.get_wallclock = vmi_get_wallclock */
  52unsigned long vmi_get_wallclock(void)
  53{
  54        unsigned long long wallclock;
  55        wallclock = vmi_timer_ops.get_wallclock(); // nsec
  56        (void)do_div(wallclock, 1000000000);       // sec
  57
  58        return wallclock;
  59}
  60
  61/* paravirt_ops.set_wallclock = vmi_set_wallclock */
  62int vmi_set_wallclock(unsigned long now)
  63{
  64        return 0;
  65}
  66
  67/* paravirt_ops.get_scheduled_cycles = vmi_get_sched_cycles */
  68unsigned long long vmi_get_sched_cycles(void)
  69{
  70        return vmi_timer_ops.get_cycle_counter(VMI_CYCLES_AVAILABLE);
  71}
  72
  73/* paravirt_ops.get_cpu_khz = vmi_cpu_khz */
  74unsigned long vmi_cpu_khz(void)
  75{
  76        unsigned long long khz;
  77        khz = vmi_timer_ops.get_cycle_frequency();
  78        (void)do_div(khz, 1000);
  79        return khz;
  80}
  81
  82static inline unsigned int vmi_get_timer_vector(void)
  83{
  84#ifdef CONFIG_X86_IO_APIC
  85        return FIRST_DEVICE_VECTOR;
  86#else
  87        return FIRST_EXTERNAL_VECTOR;
  88#endif
  89}
  90
  91/** vmi clockchip */
  92#ifdef CONFIG_X86_LOCAL_APIC
  93static unsigned int startup_timer_irq(unsigned int irq)
  94{
  95        unsigned long val = apic_read(APIC_LVTT);
  96        apic_write(APIC_LVTT, vmi_get_timer_vector());
  97
  98        return (val & APIC_SEND_PENDING);
  99}
 100
 101static void mask_timer_irq(unsigned int irq)
 102{
 103        unsigned long val = apic_read(APIC_LVTT);
 104        apic_write(APIC_LVTT, val | APIC_LVT_MASKED);
 105}
 106
 107static void unmask_timer_irq(unsigned int irq)
 108{
 109        unsigned long val = apic_read(APIC_LVTT);
 110        apic_write(APIC_LVTT, val & ~APIC_LVT_MASKED);
 111}
 112
 113static void ack_timer_irq(unsigned int irq)
 114{
 115        ack_APIC_irq();
 116}
 117
 118static struct irq_chip vmi_chip __read_mostly = {
 119        .name           = "VMI-LOCAL",
 120        .startup        = startup_timer_irq,
 121        .mask           = mask_timer_irq,
 122        .unmask         = unmask_timer_irq,
 123        .ack            = ack_timer_irq
 124};
 125#endif
 126
 127/** vmi clockevent */
 128#define VMI_ALARM_WIRED_IRQ0    0x00000000
 129#define VMI_ALARM_WIRED_LVTT    0x00010000
 130static int vmi_wiring = VMI_ALARM_WIRED_IRQ0;
 131
 132static inline int vmi_get_alarm_wiring(void)
 133{
 134        return vmi_wiring;
 135}
 136
 137static void vmi_timer_set_mode(enum clock_event_mode mode,
 138                               struct clock_event_device *evt)
 139{
 140        cycle_t now, cycles_per_hz;
 141        BUG_ON(!irqs_disabled());
 142
 143        switch (mode) {
 144        case CLOCK_EVT_MODE_ONESHOT:
 145                break;
 146        case CLOCK_EVT_MODE_PERIODIC:
 147                cycles_per_hz = vmi_timer_ops.get_cycle_frequency();
 148                (void)do_div(cycles_per_hz, HZ);
 149                now = vmi_timer_ops.get_cycle_counter(vmi_counter(VMI_PERIODIC));
 150                vmi_timer_ops.set_alarm(VMI_PERIODIC, now, cycles_per_hz);
 151                break;
 152        case CLOCK_EVT_MODE_UNUSED:
 153        case CLOCK_EVT_MODE_SHUTDOWN:
 154                switch (evt->mode) {
 155                case CLOCK_EVT_MODE_ONESHOT:
 156                        vmi_timer_ops.cancel_alarm(VMI_ONESHOT);
 157                        break;
 158                case CLOCK_EVT_MODE_PERIODIC:
 159                        vmi_timer_ops.cancel_alarm(VMI_PERIODIC);
 160                        break;
 161                default:
 162                        break;
 163                }
 164                break;
 165        default:
 166                break;
 167        }
 168}
 169
 170static int vmi_timer_next_event(unsigned long delta,
 171                                struct clock_event_device *evt)
 172{
 173        /* Unfortunately, set_next_event interface only passes relative
 174         * expiry, but we want absolute expiry.  It'd be better if were
 175         * were passed an aboslute expiry, since a bunch of time may
 176         * have been stolen between the time the delta is computed and
 177         * when we set the alarm below. */
 178        cycle_t now = vmi_timer_ops.get_cycle_counter(vmi_counter(VMI_ONESHOT));
 179
 180        BUG_ON(evt->mode != CLOCK_EVT_MODE_ONESHOT);
 181        vmi_timer_ops.set_alarm(VMI_ONESHOT, now + delta, 0);
 182        return 0;
 183}
 184
 185static struct clock_event_device vmi_clockevent = {
 186        .name           = "vmi-timer",
 187        .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
 188        .shift          = 22,
 189        .set_mode       = vmi_timer_set_mode,
 190        .set_next_event = vmi_timer_next_event,
 191        .rating         = 1000,
 192        .irq            = 0,
 193};
 194
 195static irqreturn_t vmi_timer_interrupt(int irq, void *dev_id)
 196{
 197        struct clock_event_device *evt = &__get_cpu_var(local_events);
 198        evt->event_handler(evt);
 199        return IRQ_HANDLED;
 200}
 201
 202static struct irqaction vmi_clock_action  = {
 203        .name           = "vmi-timer",
 204        .handler        = vmi_timer_interrupt,
 205        .flags          = IRQF_DISABLED | IRQF_NOBALANCING,
 206        .mask           = CPU_MASK_ALL,
 207};
 208
 209static void __devinit vmi_time_init_clockevent(void)
 210{
 211        cycle_t cycles_per_msec;
 212        struct clock_event_device *evt;
 213
 214        int cpu = smp_processor_id();
 215        evt = &__get_cpu_var(local_events);
 216
 217        /* Use cycles_per_msec since div_sc params are 32-bits. */
 218        cycles_per_msec = vmi_timer_ops.get_cycle_frequency();
 219        (void)do_div(cycles_per_msec, 1000);
 220
 221        memcpy(evt, &vmi_clockevent, sizeof(*evt));
 222        /* Must pick .shift such that .mult fits in 32-bits.  Choosing
 223         * .shift to be 22 allows 2^(32-22) cycles per nano-seconds
 224         * before overflow. */
 225        evt->mult = div_sc(cycles_per_msec, NSEC_PER_MSEC, evt->shift);
 226        /* Upper bound is clockevent's use of ulong for cycle deltas. */
 227        evt->max_delta_ns = clockevent_delta2ns(ULONG_MAX, evt);
 228        evt->min_delta_ns = clockevent_delta2ns(1, evt);
 229        evt->cpumask = cpumask_of_cpu(cpu);
 230
 231        printk(KERN_WARNING "vmi: registering clock event %s. mult=%lu shift=%u\n",
 232               evt->name, evt->mult, evt->shift);
 233        clockevents_register_device(evt);
 234}
 235
 236void __init vmi_time_init(void)
 237{
 238        /* Disable PIT: BIOSes start PIT CH0 with 18.2hz peridic. */
 239        outb_p(0x3a, PIT_MODE); /* binary, mode 5, LSB/MSB, ch 0 */
 240
 241        vmi_time_init_clockevent();
 242        setup_irq(0, &vmi_clock_action);
 243}
 244
 245#ifdef CONFIG_X86_LOCAL_APIC
 246void __devinit vmi_time_bsp_init(void)
 247{
 248        /*
 249         * On APIC systems, we want local timers to fire on each cpu.  We do
 250         * this by programming LVTT to deliver timer events to the IRQ handler
 251         * for IRQ-0, since we can't re-use the APIC local timer handler
 252         * without interfering with that code.
 253         */
 254        clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL);
 255        local_irq_disable();
 256#ifdef CONFIG_X86_SMP
 257        /*
 258         * XXX handle_percpu_irq only defined for SMP; we need to switch over
 259         * to using it, since this is a local interrupt, which each CPU must
 260         * handle individually without locking out or dropping simultaneous
 261         * local timers on other CPUs.  We also don't want to trigger the
 262         * quirk workaround code for interrupts which gets invoked from
 263         * handle_percpu_irq via eoi, so we use our own IRQ chip.
 264         */
 265        set_irq_chip_and_handler_name(0, &vmi_chip, handle_percpu_irq, "lvtt");
 266#else
 267        set_irq_chip_and_handler_name(0, &vmi_chip, handle_edge_irq, "lvtt");
 268#endif
 269        vmi_wiring = VMI_ALARM_WIRED_LVTT;
 270        apic_write(APIC_LVTT, vmi_get_timer_vector());
 271        local_irq_enable();
 272        clockevents_notify(CLOCK_EVT_NOTIFY_RESUME, NULL);
 273}
 274
 275void __devinit vmi_time_ap_init(void)
 276{
 277        vmi_time_init_clockevent();
 278        apic_write(APIC_LVTT, vmi_get_timer_vector());
 279}
 280#endif
 281
 282/** vmi clocksource */
 283
 284static cycle_t read_real_cycles(void)
 285{
 286        return vmi_timer_ops.get_cycle_counter(VMI_CYCLES_REAL);
 287}
 288
 289static struct clocksource clocksource_vmi = {
 290        .name                   = "vmi-timer",
 291        .rating                 = 450,
 292        .read                   = read_real_cycles,
 293        .mask                   = CLOCKSOURCE_MASK(64),
 294        .mult                   = 0, /* to be set */
 295        .shift                  = 22,
 296        .flags                  = CLOCK_SOURCE_IS_CONTINUOUS,
 297};
 298
 299static int __init init_vmi_clocksource(void)
 300{
 301        cycle_t cycles_per_msec;
 302
 303        if (!vmi_timer_ops.get_cycle_frequency)
 304                return 0;
 305        /* Use khz2mult rather than hz2mult since hz arg is only 32-bits. */
 306        cycles_per_msec = vmi_timer_ops.get_cycle_frequency();
 307        (void)do_div(cycles_per_msec, 1000);
 308
 309        /* Note that clocksource.{mult, shift} converts in the opposite direction
 310         * as clockevents.  */
 311        clocksource_vmi.mult = clocksource_khz2mult(cycles_per_msec,
 312                                                    clocksource_vmi.shift);
 313
 314        printk(KERN_WARNING "vmi: registering clock source khz=%lld\n", cycles_per_msec);
 315        return clocksource_register(&clocksource_vmi);
 316
 317}
 318module_init(init_vmi_clocksource);
 319
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.