root/arch/mips/loongson64/loongson-3/hpet.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. smbus_read
  2. smbus_write
  3. smbus_enable
  4. hpet_read
  5. hpet_write
  6. hpet_start_counter
  7. hpet_stop_counter
  8. hpet_reset_counter
  9. hpet_restart_counter
  10. hpet_enable_legacy_int
  11. hpet_set_state_periodic
  12. hpet_set_state_shutdown
  13. hpet_set_state_oneshot
  14. hpet_tick_resume
  15. hpet_next_event
  16. hpet_irq_handler
  17. hpet_setup
  18. setup_hpet_timer
  19. hpet_read_counter
  20. hpet_suspend
  21. hpet_resume
  22. init_hpet_clocksource

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

/* [<][>][^][v][top][bottom][index][help] */