linux/arch/mips/loongson64/loongson-3/hpet.c
<<
>>
Prefs
   1#include <linux/init.h>
   2#include <linux/pci.h>
   3#include <linux/percpu.h>
   4#include <linux/delay.h>
   5#include <linux/spinlock.h>
   6#include <linux/interrupt.h>
   7
   8#include <asm/hpet.h>
   9#include <asm/time.h>
  10
  11#define SMBUS_CFG_BASE          (loongson_sysconf.ht_control_base + 0x0300a000)
  12#define SMBUS_PCI_REG40         0x40
  13#define SMBUS_PCI_REG64         0x64
  14#define SMBUS_PCI_REGB4         0xb4
  15
  16#define HPET_MIN_CYCLES         64
  17#define HPET_MIN_PROG_DELTA     (HPET_MIN_CYCLES + (HPET_MIN_CYCLES >> 1))
  18
  19static DEFINE_SPINLOCK(hpet_lock);
  20DEFINE_PER_CPU(struct clock_event_device, hpet_clockevent_device);
  21
  22static unsigned int smbus_read(int offset)
  23{
  24        return *(volatile unsigned int *)(SMBUS_CFG_BASE + offset);
  25}
  26
  27static void smbus_write(int offset, int data)
  28{
  29        *(volatile unsigned int *)(SMBUS_CFG_BASE + offset) = data;
  30}
  31
  32static void smbus_enable(int offset, int bit)
  33{
  34        unsigned int cfg = smbus_read(offset);
  35
  36        cfg |= bit;
  37        smbus_write(offset, cfg);
  38}
  39
  40static int hpet_read(int offset)
  41{
  42        return *(volatile unsigned int *)(HPET_MMIO_ADDR + offset);
  43}
  44
  45static void hpet_write(int offset, int data)
  46{
  47        *(volatile unsigned int *)(HPET_MMIO_ADDR + offset) = data;
  48}
  49
  50static void hpet_start_counter(void)
  51{
  52        unsigned int cfg = hpet_read(HPET_CFG);
  53
  54        cfg |= HPET_CFG_ENABLE;
  55        hpet_write(HPET_CFG, cfg);
  56}
  57
  58static void hpet_stop_counter(void)
  59{
  60        unsigned int cfg = hpet_read(HPET_CFG);
  61
  62        cfg &= ~HPET_CFG_ENABLE;
  63        hpet_write(HPET_CFG, cfg);
  64}
  65
  66static void hpet_reset_counter(void)
  67{
  68        hpet_write(HPET_COUNTER, 0);
  69        hpet_write(HPET_COUNTER + 4, 0);
  70}
  71
  72static void hpet_restart_counter(void)
  73{
  74        hpet_stop_counter();
  75        hpet_reset_counter();
  76        hpet_start_counter();
  77}
  78
  79static void hpet_enable_legacy_int(void)
  80{
  81        /* Do nothing on Loongson-3 */
  82}
  83
  84static int hpet_set_state_periodic(struct clock_event_device *evt)
  85{
  86        int cfg;
  87
  88        spin_lock(&hpet_lock);
  89
  90        pr_info("set clock event to periodic mode!\n");
  91        /* stop counter */
  92        hpet_stop_counter();
  93
  94        /* enables the timer0 to generate a periodic interrupt */
  95        cfg = hpet_read(HPET_T0_CFG);
  96        cfg &= ~HPET_TN_LEVEL;
  97        cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL |
  98                HPET_TN_32BIT;
  99        hpet_write(HPET_T0_CFG, cfg);
 100
 101        /* set the comparator */
 102        hpet_write(HPET_T0_CMP, HPET_COMPARE_VAL);
 103        udelay(1);
 104        hpet_write(HPET_T0_CMP, HPET_COMPARE_VAL);
 105
 106        /* start counter */
 107        hpet_start_counter();
 108
 109        spin_unlock(&hpet_lock);
 110        return 0;
 111}
 112
 113static int hpet_set_state_shutdown(struct clock_event_device *evt)
 114{
 115        int cfg;
 116
 117        spin_lock(&hpet_lock);
 118
 119        cfg = hpet_read(HPET_T0_CFG);
 120        cfg &= ~HPET_TN_ENABLE;
 121        hpet_write(HPET_T0_CFG, cfg);
 122
 123        spin_unlock(&hpet_lock);
 124        return 0;
 125}
 126
 127static int hpet_set_state_oneshot(struct clock_event_device *evt)
 128{
 129        int cfg;
 130
 131        spin_lock(&hpet_lock);
 132
 133        pr_info("set clock event to one shot mode!\n");
 134        cfg = hpet_read(HPET_T0_CFG);
 135        /*
 136         * set timer0 type
 137         * 1 : periodic interrupt
 138         * 0 : non-periodic(oneshot) interrupt
 139         */
 140        cfg &= ~HPET_TN_PERIODIC;
 141        cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
 142        hpet_write(HPET_T0_CFG, cfg);
 143
 144        spin_unlock(&hpet_lock);
 145        return 0;
 146}
 147
 148static int hpet_tick_resume(struct clock_event_device *evt)
 149{
 150        spin_lock(&hpet_lock);
 151        hpet_enable_legacy_int();
 152        spin_unlock(&hpet_lock);
 153
 154        return 0;
 155}
 156
 157static int hpet_next_event(unsigned long delta,
 158                struct clock_event_device *evt)
 159{
 160        unsigned int cnt;
 161        int res;
 162
 163        cnt = hpet_read(HPET_COUNTER);
 164        cnt += delta;
 165        hpet_write(HPET_T0_CMP, cnt);
 166
 167        res = (int)(cnt - hpet_read(HPET_COUNTER));
 168
 169        return res < HPET_MIN_CYCLES ? -ETIME : 0;
 170}
 171
 172static irqreturn_t hpet_irq_handler(int irq, void *data)
 173{
 174        int is_irq;
 175        struct clock_event_device *cd;
 176        unsigned int cpu = smp_processor_id();
 177
 178        is_irq = hpet_read(HPET_STATUS);
 179        if (is_irq & HPET_T0_IRS) {
 180                /* clear the TIMER0 irq status register */
 181                hpet_write(HPET_STATUS, HPET_T0_IRS);
 182                cd = &per_cpu(hpet_clockevent_device, cpu);
 183                cd->event_handler(cd);
 184                return IRQ_HANDLED;
 185        }
 186        return IRQ_NONE;
 187}
 188
 189static struct irqaction hpet_irq = {
 190        .handler = hpet_irq_handler,
 191        .flags = IRQF_NOBALANCING | IRQF_TIMER,
 192        .name = "hpet",
 193};
 194
 195/*
 196 * hpet address assignation and irq setting should be done in bios.
 197 * but pmon don't do this, we just setup here directly.
 198 * The operation under is normal. unfortunately, hpet_setup process
 199 * is before pci initialize.
 200 *
 201 * {
 202 *      struct pci_dev *pdev;
 203 *
 204 *      pdev = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
 205 *      pci_write_config_word(pdev, SMBUS_PCI_REGB4, HPET_ADDR);
 206 *
 207 *      ...
 208 * }
 209 */
 210static void hpet_setup(void)
 211{
 212        /* set hpet base address */
 213        smbus_write(SMBUS_PCI_REGB4, HPET_ADDR);
 214
 215        /* enable decodeing of access to HPET MMIO*/
 216        smbus_enable(SMBUS_PCI_REG40, (1 << 28));
 217
 218        /* HPET irq enable */
 219        smbus_enable(SMBUS_PCI_REG64, (1 << 10));
 220
 221        hpet_enable_legacy_int();
 222}
 223
 224void __init setup_hpet_timer(void)
 225{
 226        unsigned int cpu = smp_processor_id();
 227        struct clock_event_device *cd;
 228
 229        hpet_setup();
 230
 231        cd = &per_cpu(hpet_clockevent_device, cpu);
 232        cd->name = "hpet";
 233        cd->rating = 320;
 234        cd->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
 235        cd->set_state_shutdown = hpet_set_state_shutdown;
 236        cd->set_state_periodic = hpet_set_state_periodic;
 237        cd->set_state_oneshot = hpet_set_state_oneshot;
 238        cd->tick_resume = hpet_tick_resume;
 239        cd->set_next_event = hpet_next_event;
 240        cd->irq = HPET_T0_IRQ;
 241        cd->cpumask = cpumask_of(cpu);
 242        clockevent_set_clock(cd, HPET_FREQ);
 243        cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
 244        cd->min_delta_ns = clockevent_delta2ns(HPET_MIN_PROG_DELTA, cd);
 245
 246        clockevents_register_device(cd);
 247        setup_irq(HPET_T0_IRQ, &hpet_irq);
 248        pr_info("hpet clock event device register\n");
 249}
 250
 251static cycle_t hpet_read_counter(struct clocksource *cs)
 252{
 253        return (cycle_t)hpet_read(HPET_COUNTER);
 254}
 255
 256static void hpet_suspend(struct clocksource *cs)
 257{
 258}
 259
 260static void hpet_resume(struct clocksource *cs)
 261{
 262        hpet_setup();
 263        hpet_restart_counter();
 264}
 265
 266static struct clocksource csrc_hpet = {
 267        .name = "hpet",
 268        /* mips clocksource rating is less than 300, so hpet is better. */
 269        .rating = 300,
 270        .read = hpet_read_counter,
 271        .mask = CLOCKSOURCE_MASK(32),
 272        /* oneshot mode work normal with this flag */
 273        .flags = CLOCK_SOURCE_IS_CONTINUOUS,
 274        .suspend = hpet_suspend,
 275        .resume = hpet_resume,
 276        .mult = 0,
 277        .shift = 10,
 278};
 279
 280int __init init_hpet_clocksource(void)
 281{
 282        csrc_hpet.mult = clocksource_hz2mult(HPET_FREQ, csrc_hpet.shift);
 283        return clocksource_register_hz(&csrc_hpet, HPET_FREQ);
 284}
 285
 286arch_initcall(init_hpet_clocksource);
 287