linux/arch/i386/kernel/hpet.c
<<
>>
Prefs
   1#include <linux/clocksource.h>
   2#include <linux/clockchips.h>
   3#include <linux/errno.h>
   4#include <linux/hpet.h>
   5#include <linux/init.h>
   6#include <linux/sysdev.h>
   7#include <linux/pm.h>
   8
   9#include <asm/hpet.h>
  10#include <asm/io.h>
  11
  12extern struct clock_event_device *global_clock_event;
  13
  14#define HPET_MASK       CLOCKSOURCE_MASK(32)
  15#define HPET_SHIFT      22
  16
  17/* FSEC = 10^-15 NSEC = 10^-9 */
  18#define FSEC_PER_NSEC   1000000
  19
  20/*
  21 * HPET address is set in acpi/boot.c, when an ACPI entry exists
  22 */
  23unsigned long hpet_address;
  24static void __iomem * hpet_virt_address;
  25
  26static inline unsigned long hpet_readl(unsigned long a)
  27{
  28        return readl(hpet_virt_address + a);
  29}
  30
  31static inline void hpet_writel(unsigned long d, unsigned long a)
  32{
  33        writel(d, hpet_virt_address + a);
  34}
  35
  36/*
  37 * HPET command line enable / disable
  38 */
  39static int boot_hpet_disable;
  40
  41static int __init hpet_setup(char* str)
  42{
  43        if (str) {
  44                if (!strncmp("disable", str, 7))
  45                        boot_hpet_disable = 1;
  46        }
  47        return 1;
  48}
  49__setup("hpet=", hpet_setup);
  50
  51static inline int is_hpet_capable(void)
  52{
  53        return (!boot_hpet_disable && hpet_address);
  54}
  55
  56/*
  57 * HPET timer interrupt enable / disable
  58 */
  59static int hpet_legacy_int_enabled;
  60
  61/**
  62 * is_hpet_enabled - check whether the hpet timer interrupt is enabled
  63 */
  64int is_hpet_enabled(void)
  65{
  66        return is_hpet_capable() && hpet_legacy_int_enabled;
  67}
  68
  69/*
  70 * When the hpet driver (/dev/hpet) is enabled, we need to reserve
  71 * timer 0 and timer 1 in case of RTC emulation.
  72 */
  73#ifdef CONFIG_HPET
  74static void hpet_reserve_platform_timers(unsigned long id)
  75{
  76        struct hpet __iomem *hpet = hpet_virt_address;
  77        struct hpet_timer __iomem *timer = &hpet->hpet_timers[2];
  78        unsigned int nrtimers, i;
  79        struct hpet_data hd;
  80
  81        nrtimers = ((id & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT) + 1;
  82
  83        memset(&hd, 0, sizeof (hd));
  84        hd.hd_phys_address = hpet_address;
  85        hd.hd_address = hpet_virt_address;
  86        hd.hd_nirqs = nrtimers;
  87        hd.hd_flags = HPET_DATA_PLATFORM;
  88        hpet_reserve_timer(&hd, 0);
  89
  90#ifdef CONFIG_HPET_EMULATE_RTC
  91        hpet_reserve_timer(&hd, 1);
  92#endif
  93
  94        hd.hd_irq[0] = HPET_LEGACY_8254;
  95        hd.hd_irq[1] = HPET_LEGACY_RTC;
  96
  97        for (i = 2; i < nrtimers; timer++, i++)
  98                hd.hd_irq[i] = (timer->hpet_config & Tn_INT_ROUTE_CNF_MASK) >>
  99                        Tn_INT_ROUTE_CNF_SHIFT;
 100
 101        hpet_alloc(&hd);
 102
 103}
 104#else
 105static void hpet_reserve_platform_timers(unsigned long id) { }
 106#endif
 107
 108/*
 109 * Common hpet info
 110 */
 111static unsigned long hpet_period;
 112
 113static void hpet_set_mode(enum clock_event_mode mode,
 114                          struct clock_event_device *evt);
 115static int hpet_next_event(unsigned long delta,
 116                           struct clock_event_device *evt);
 117
 118/*
 119 * The hpet clock event device
 120 */
 121static struct clock_event_device hpet_clockevent = {
 122        .name           = "hpet",
 123        .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
 124        .set_mode       = hpet_set_mode,
 125        .set_next_event = hpet_next_event,
 126        .shift          = 32,
 127        .irq            = 0,
 128};
 129
 130static void hpet_start_counter(void)
 131{
 132        unsigned long cfg = hpet_readl(HPET_CFG);
 133
 134        cfg &= ~HPET_CFG_ENABLE;
 135        hpet_writel(cfg, HPET_CFG);
 136        hpet_writel(0, HPET_COUNTER);
 137        hpet_writel(0, HPET_COUNTER + 4);
 138        cfg |= HPET_CFG_ENABLE;
 139        hpet_writel(cfg, HPET_CFG);
 140}
 141
 142static void hpet_enable_int(void)
 143{
 144        unsigned long cfg = hpet_readl(HPET_CFG);
 145
 146        cfg |= HPET_CFG_LEGACY;
 147        hpet_writel(cfg, HPET_CFG);
 148        hpet_legacy_int_enabled = 1;
 149}
 150
 151static void hpet_set_mode(enum clock_event_mode mode,
 152                          struct clock_event_device *evt)
 153{
 154        unsigned long cfg, cmp, now;
 155        uint64_t delta;
 156
 157        switch(mode) {
 158        case CLOCK_EVT_MODE_PERIODIC:
 159                delta = ((uint64_t)(NSEC_PER_SEC/HZ)) * hpet_clockevent.mult;
 160                delta >>= hpet_clockevent.shift;
 161                now = hpet_readl(HPET_COUNTER);
 162                cmp = now + (unsigned long) delta;
 163                cfg = hpet_readl(HPET_T0_CFG);
 164                cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
 165                       HPET_TN_SETVAL | HPET_TN_32BIT;
 166                hpet_writel(cfg, HPET_T0_CFG);
 167                /*
 168                 * The first write after writing TN_SETVAL to the
 169                 * config register sets the counter value, the second
 170                 * write sets the period.
 171                 */
 172                hpet_writel(cmp, HPET_T0_CMP);
 173                udelay(1);
 174                hpet_writel((unsigned long) delta, HPET_T0_CMP);
 175                break;
 176
 177        case CLOCK_EVT_MODE_ONESHOT:
 178                cfg = hpet_readl(HPET_T0_CFG);
 179                cfg &= ~HPET_TN_PERIODIC;
 180                cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
 181                hpet_writel(cfg, HPET_T0_CFG);
 182                break;
 183
 184        case CLOCK_EVT_MODE_UNUSED:
 185        case CLOCK_EVT_MODE_SHUTDOWN:
 186                cfg = hpet_readl(HPET_T0_CFG);
 187                cfg &= ~HPET_TN_ENABLE;
 188                hpet_writel(cfg, HPET_T0_CFG);
 189                break;
 190        }
 191}
 192
 193static int hpet_next_event(unsigned long delta,
 194                           struct clock_event_device *evt)
 195{
 196        unsigned long cnt;
 197
 198        cnt = hpet_readl(HPET_COUNTER);
 199        cnt += delta;
 200        hpet_writel(cnt, HPET_T0_CMP);
 201
 202        return ((long)(hpet_readl(HPET_COUNTER) - cnt ) > 0) ? -ETIME : 0;
 203}
 204
 205/*
 206 * Clock source related code
 207 */
 208static cycle_t read_hpet(void)
 209{
 210        return (cycle_t)hpet_readl(HPET_COUNTER);
 211}
 212
 213static struct clocksource clocksource_hpet = {
 214        .name           = "hpet",
 215        .rating         = 250,
 216        .read           = read_hpet,
 217        .mask           = HPET_MASK,
 218        .shift          = HPET_SHIFT,
 219        .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
 220};
 221
 222/*
 223 * Try to setup the HPET timer
 224 */
 225int __init hpet_enable(void)
 226{
 227        unsigned long id;
 228        uint64_t hpet_freq;
 229        u64 tmp, start, now;
 230        cycle_t t1;
 231
 232        if (!is_hpet_capable())
 233                return 0;
 234
 235        hpet_virt_address = ioremap_nocache(hpet_address, HPET_MMAP_SIZE);
 236
 237        /*
 238         * Read the period and check for a sane value:
 239         */
 240        hpet_period = hpet_readl(HPET_PERIOD);
 241        if (hpet_period < HPET_MIN_PERIOD || hpet_period > HPET_MAX_PERIOD)
 242                goto out_nohpet;
 243
 244        /*
 245         * The period is a femto seconds value. We need to calculate the
 246         * scaled math multiplication factor for nanosecond to hpet tick
 247         * conversion.
 248         */
 249        hpet_freq = 1000000000000000ULL;
 250        do_div(hpet_freq, hpet_period);
 251        hpet_clockevent.mult = div_sc((unsigned long) hpet_freq,
 252                                      NSEC_PER_SEC, 32);
 253        /* Calculate the min / max delta */
 254        hpet_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFFFF,
 255                                                           &hpet_clockevent);
 256        hpet_clockevent.min_delta_ns = clockevent_delta2ns(0x30,
 257                                                           &hpet_clockevent);
 258
 259        /*
 260         * Read the HPET ID register to retrieve the IRQ routing
 261         * information and the number of channels
 262         */
 263        id = hpet_readl(HPET_ID);
 264
 265#ifdef CONFIG_HPET_EMULATE_RTC
 266        /*
 267         * The legacy routing mode needs at least two channels, tick timer
 268         * and the rtc emulation channel.
 269         */
 270        if (!(id & HPET_ID_NUMBER))
 271                goto out_nohpet;
 272#endif
 273
 274        /* Start the counter */
 275        hpet_start_counter();
 276
 277        /* Verify whether hpet counter works */
 278        t1 = read_hpet();
 279        rdtscll(start);
 280
 281        /*
 282         * We don't know the TSC frequency yet, but waiting for
 283         * 200000 TSC cycles is safe:
 284         * 4 GHz == 50us
 285         * 1 GHz == 200us
 286         */
 287        do {
 288                rep_nop();
 289                rdtscll(now);
 290        } while ((now - start) < 200000UL);
 291
 292        if (t1 == read_hpet()) {
 293                printk(KERN_WARNING
 294                       "HPET counter not counting. HPET disabled\n");
 295                goto out_nohpet;
 296        }
 297
 298        /* Initialize and register HPET clocksource
 299         *
 300         * hpet period is in femto seconds per cycle
 301         * so we need to convert this to ns/cyc units
 302         * aproximated by mult/2^shift
 303         *
 304         *  fsec/cyc * 1nsec/1000000fsec = nsec/cyc = mult/2^shift
 305         *  fsec/cyc * 1ns/1000000fsec * 2^shift = mult
 306         *  fsec/cyc * 2^shift * 1nsec/1000000fsec = mult
 307         *  (fsec/cyc << shift)/1000000 = mult
 308         *  (hpet_period << shift)/FSEC_PER_NSEC = mult
 309         */
 310        tmp = (u64)hpet_period << HPET_SHIFT;
 311        do_div(tmp, FSEC_PER_NSEC);
 312        clocksource_hpet.mult = (u32)tmp;
 313
 314        clocksource_register(&clocksource_hpet);
 315
 316
 317        if (id & HPET_ID_LEGSUP) {
 318                hpet_enable_int();
 319                hpet_reserve_platform_timers(id);
 320                /*
 321                 * Start hpet with the boot cpu mask and make it
 322                 * global after the IO_APIC has been initialized.
 323                 */
 324                hpet_clockevent.cpumask =cpumask_of_cpu(0);
 325                clockevents_register_device(&hpet_clockevent);
 326                global_clock_event = &hpet_clockevent;
 327                return 1;
 328        }
 329        return 0;
 330
 331out_nohpet:
 332        iounmap(hpet_virt_address);
 333        hpet_virt_address = NULL;
 334        boot_hpet_disable = 1;
 335        return 0;
 336}
 337
 338
 339#ifdef CONFIG_HPET_EMULATE_RTC
 340
 341/* HPET in LegacyReplacement Mode eats up RTC interrupt line. When, HPET
 342 * is enabled, we support RTC interrupt functionality in software.
 343 * RTC has 3 kinds of interrupts:
 344 * 1) Update Interrupt - generate an interrupt, every sec, when RTC clock
 345 *    is updated
 346 * 2) Alarm Interrupt - generate an interrupt at a specific time of day
 347 * 3) Periodic Interrupt - generate periodic interrupt, with frequencies
 348 *    2Hz-8192Hz (2Hz-64Hz for non-root user) (all freqs in powers of 2)
 349 * (1) and (2) above are implemented using polling at a frequency of
 350 * 64 Hz. The exact frequency is a tradeoff between accuracy and interrupt
 351 * overhead. (DEFAULT_RTC_INT_FREQ)
 352 * For (3), we use interrupts at 64Hz or user specified periodic
 353 * frequency, whichever is higher.
 354 */
 355#include <linux/mc146818rtc.h>
 356#include <linux/rtc.h>
 357
 358#define DEFAULT_RTC_INT_FREQ    64
 359#define DEFAULT_RTC_SHIFT       6
 360#define RTC_NUM_INTS            1
 361
 362static unsigned long hpet_rtc_flags;
 363static unsigned long hpet_prev_update_sec;
 364static struct rtc_time hpet_alarm_time;
 365static unsigned long hpet_pie_count;
 366static unsigned long hpet_t1_cmp;
 367static unsigned long hpet_default_delta;
 368static unsigned long hpet_pie_delta;
 369static unsigned long hpet_pie_limit;
 370
 371/*
 372 * Timer 1 for RTC emulation. We use one shot mode, as periodic mode
 373 * is not supported by all HPET implementations for timer 1.
 374 *
 375 * hpet_rtc_timer_init() is called when the rtc is initialized.
 376 */
 377int hpet_rtc_timer_init(void)
 378{
 379        unsigned long cfg, cnt, delta, flags;
 380
 381        if (!is_hpet_enabled())
 382                return 0;
 383
 384        if (!hpet_default_delta) {
 385                uint64_t clc;
 386
 387                clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC;
 388                clc >>= hpet_clockevent.shift + DEFAULT_RTC_SHIFT;
 389                hpet_default_delta = (unsigned long) clc;
 390        }
 391
 392        if (!(hpet_rtc_flags & RTC_PIE) || hpet_pie_limit)
 393                delta = hpet_default_delta;
 394        else
 395                delta = hpet_pie_delta;
 396
 397        local_irq_save(flags);
 398
 399        cnt = delta + hpet_readl(HPET_COUNTER);
 400        hpet_writel(cnt, HPET_T1_CMP);
 401        hpet_t1_cmp = cnt;
 402
 403        cfg = hpet_readl(HPET_T1_CFG);
 404        cfg &= ~HPET_TN_PERIODIC;
 405        cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
 406        hpet_writel(cfg, HPET_T1_CFG);
 407
 408        local_irq_restore(flags);
 409
 410        return 1;
 411}
 412
 413/*
 414 * The functions below are called from rtc driver.
 415 * Return 0 if HPET is not being used.
 416 * Otherwise do the necessary changes and return 1.
 417 */
 418int hpet_mask_rtc_irq_bit(unsigned long bit_mask)
 419{
 420        if (!is_hpet_enabled())
 421                return 0;
 422
 423        hpet_rtc_flags &= ~bit_mask;
 424        return 1;
 425}
 426
 427int hpet_set_rtc_irq_bit(unsigned long bit_mask)
 428{
 429        unsigned long oldbits = hpet_rtc_flags;
 430
 431        if (!is_hpet_enabled())
 432                return 0;
 433
 434        hpet_rtc_flags |= bit_mask;
 435
 436        if (!oldbits)
 437                hpet_rtc_timer_init();
 438
 439        return 1;
 440}
 441
 442int hpet_set_alarm_time(unsigned char hrs, unsigned char min,
 443                        unsigned char sec)
 444{
 445        if (!is_hpet_enabled())
 446                return 0;
 447
 448        hpet_alarm_time.tm_hour = hrs;
 449        hpet_alarm_time.tm_min = min;
 450        hpet_alarm_time.tm_sec = sec;
 451
 452        return 1;
 453}
 454
 455int hpet_set_periodic_freq(unsigned long freq)
 456{
 457        uint64_t clc;
 458
 459        if (!is_hpet_enabled())
 460                return 0;
 461
 462        if (freq <= DEFAULT_RTC_INT_FREQ)
 463                hpet_pie_limit = DEFAULT_RTC_INT_FREQ / freq;
 464        else {
 465                clc = (uint64_t) hpet_clockevent.mult * NSEC_PER_SEC;
 466                do_div(clc, freq);
 467                clc >>= hpet_clockevent.shift;
 468                hpet_pie_delta = (unsigned long) clc;
 469        }
 470        return 1;
 471}
 472
 473int hpet_rtc_dropped_irq(void)
 474{
 475        return is_hpet_enabled();
 476}
 477
 478static void hpet_rtc_timer_reinit(void)
 479{
 480        unsigned long cfg, delta;
 481        int lost_ints = -1;
 482
 483        if (unlikely(!hpet_rtc_flags)) {
 484                cfg = hpet_readl(HPET_T1_CFG);
 485                cfg &= ~HPET_TN_ENABLE;
 486                hpet_writel(cfg, HPET_T1_CFG);
 487                return;
 488        }
 489
 490        if (!(hpet_rtc_flags & RTC_PIE) || hpet_pie_limit)
 491                delta = hpet_default_delta;
 492        else
 493                delta = hpet_pie_delta;
 494
 495        /*
 496         * Increment the comparator value until we are ahead of the
 497         * current count.
 498         */
 499        do {
 500                hpet_t1_cmp += delta;
 501                hpet_writel(hpet_t1_cmp, HPET_T1_CMP);
 502                lost_ints++;
 503        } while ((long)(hpet_readl(HPET_COUNTER) - hpet_t1_cmp) > 0);
 504
 505        if (lost_ints) {
 506                if (hpet_rtc_flags & RTC_PIE)
 507                        hpet_pie_count += lost_ints;
 508                if (printk_ratelimit())
 509                        printk(KERN_WARNING "rtc: lost %d interrupts\n",
 510                                lost_ints);
 511        }
 512}
 513
 514irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id)
 515{
 516        struct rtc_time curr_time;
 517        unsigned long rtc_int_flag = 0;
 518
 519        hpet_rtc_timer_reinit();
 520
 521        if (hpet_rtc_flags & (RTC_UIE | RTC_AIE))
 522                rtc_get_rtc_time(&curr_time);
 523
 524        if (hpet_rtc_flags & RTC_UIE &&
 525            curr_time.tm_sec != hpet_prev_update_sec) {
 526                rtc_int_flag = RTC_UF;
 527                hpet_prev_update_sec = curr_time.tm_sec;
 528        }
 529
 530        if (hpet_rtc_flags & RTC_PIE &&
 531            ++hpet_pie_count >= hpet_pie_limit) {
 532                rtc_int_flag |= RTC_PF;
 533                hpet_pie_count = 0;
 534        }
 535
 536        if (hpet_rtc_flags & RTC_PIE &&
 537            (curr_time.tm_sec == hpet_alarm_time.tm_sec) &&
 538            (curr_time.tm_min == hpet_alarm_time.tm_min) &&
 539            (curr_time.tm_hour == hpet_alarm_time.tm_hour))
 540                        rtc_int_flag |= RTC_AF;
 541
 542        if (rtc_int_flag) {
 543                rtc_int_flag |= (RTC_IRQF | (RTC_NUM_INTS << 8));
 544                rtc_interrupt(rtc_int_flag, dev_id);
 545        }
 546        return IRQ_HANDLED;
 547}
 548#endif
 549
 550
 551/*
 552 * Suspend/resume part
 553 */
 554
 555#ifdef CONFIG_PM
 556
 557static int hpet_suspend(struct sys_device *sys_device, pm_message_t state)
 558{
 559        unsigned long cfg = hpet_readl(HPET_CFG);
 560
 561        cfg &= ~(HPET_CFG_ENABLE|HPET_CFG_LEGACY);
 562        hpet_writel(cfg, HPET_CFG);
 563
 564        return 0;
 565}
 566
 567static int hpet_resume(struct sys_device *sys_device)
 568{
 569        unsigned int id;
 570
 571        hpet_start_counter();
 572
 573        id = hpet_readl(HPET_ID);
 574
 575        if (id & HPET_ID_LEGSUP)
 576                hpet_enable_int();
 577
 578        return 0;
 579}
 580
 581static struct sysdev_class hpet_class = {
 582        set_kset_name("hpet"),
 583        .suspend        = hpet_suspend,
 584        .resume         = hpet_resume,
 585};
 586
 587static struct sys_device hpet_device = {
 588        .id             = 0,
 589        .cls            = &hpet_class,
 590};
 591
 592
 593static __init int hpet_register_sysfs(void)
 594{
 595        int err;
 596
 597        if (!is_hpet_capable())
 598                return 0;
 599
 600        err = sysdev_class_register(&hpet_class);
 601
 602        if (!err) {
 603                err = sysdev_register(&hpet_device);
 604                if (err)
 605                        sysdev_class_unregister(&hpet_class);
 606        }
 607
 608        return err;
 609}
 610
 611device_initcall(hpet_register_sysfs);
 612
 613#endif
 614
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.