Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <linux/init.h>
0003 #include <linux/pci.h>
0004 #include <linux/percpu.h>
0005 #include <linux/delay.h>
0006 #include <linux/spinlock.h>
0007 #include <linux/interrupt.h>
0008 
0009 #include <asm/hpet.h>
0010 #include <asm/time.h>
0011 
0012 #define SMBUS_CFG_BASE      (loongson_sysconf.ht_control_base + 0x0300a000)
0013 #define SMBUS_PCI_REG40     0x40
0014 #define SMBUS_PCI_REG64     0x64
0015 #define SMBUS_PCI_REGB4     0xb4
0016 
0017 #define HPET_MIN_CYCLES     16
0018 #define HPET_MIN_PROG_DELTA (HPET_MIN_CYCLES * 12)
0019 
0020 static DEFINE_SPINLOCK(hpet_lock);
0021 DEFINE_PER_CPU(struct clock_event_device, hpet_clockevent_device);
0022 
0023 static unsigned int smbus_read(int offset)
0024 {
0025     return *(volatile unsigned int *)(SMBUS_CFG_BASE + offset);
0026 }
0027 
0028 static void smbus_write(int offset, int data)
0029 {
0030     *(volatile unsigned int *)(SMBUS_CFG_BASE + offset) = data;
0031 }
0032 
0033 static void smbus_enable(int offset, int bit)
0034 {
0035     unsigned int cfg = smbus_read(offset);
0036 
0037     cfg |= bit;
0038     smbus_write(offset, cfg);
0039 }
0040 
0041 static int hpet_read(int offset)
0042 {
0043     return *(volatile unsigned int *)(HPET_MMIO_ADDR + offset);
0044 }
0045 
0046 static void hpet_write(int offset, int data)
0047 {
0048     *(volatile unsigned int *)(HPET_MMIO_ADDR + offset) = data;
0049 }
0050 
0051 static void hpet_start_counter(void)
0052 {
0053     unsigned int cfg = hpet_read(HPET_CFG);
0054 
0055     cfg |= HPET_CFG_ENABLE;
0056     hpet_write(HPET_CFG, cfg);
0057 }
0058 
0059 static void hpet_stop_counter(void)
0060 {
0061     unsigned int cfg = hpet_read(HPET_CFG);
0062 
0063     cfg &= ~HPET_CFG_ENABLE;
0064     hpet_write(HPET_CFG, cfg);
0065 }
0066 
0067 static void hpet_reset_counter(void)
0068 {
0069     hpet_write(HPET_COUNTER, 0);
0070     hpet_write(HPET_COUNTER + 4, 0);
0071 }
0072 
0073 static void hpet_restart_counter(void)
0074 {
0075     hpet_stop_counter();
0076     hpet_reset_counter();
0077     hpet_start_counter();
0078 }
0079 
0080 static void hpet_enable_legacy_int(void)
0081 {
0082     /* Do nothing on Loongson-3 */
0083 }
0084 
0085 static int hpet_set_state_periodic(struct clock_event_device *evt)
0086 {
0087     int cfg;
0088 
0089     spin_lock(&hpet_lock);
0090 
0091     pr_info("set clock event to periodic mode!\n");
0092     /* stop counter */
0093     hpet_stop_counter();
0094 
0095     /* enables the timer0 to generate a periodic interrupt */
0096     cfg = hpet_read(HPET_T0_CFG);
0097     cfg &= ~HPET_TN_LEVEL;
0098     cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL |
0099         HPET_TN_32BIT;
0100     hpet_write(HPET_T0_CFG, cfg);
0101 
0102     /* set the comparator */
0103     hpet_write(HPET_T0_CMP, HPET_COMPARE_VAL);
0104     udelay(1);
0105     hpet_write(HPET_T0_CMP, HPET_COMPARE_VAL);
0106 
0107     /* start counter */
0108     hpet_start_counter();
0109 
0110     spin_unlock(&hpet_lock);
0111     return 0;
0112 }
0113 
0114 static int hpet_set_state_shutdown(struct clock_event_device *evt)
0115 {
0116     int cfg;
0117 
0118     spin_lock(&hpet_lock);
0119 
0120     cfg = hpet_read(HPET_T0_CFG);
0121     cfg &= ~HPET_TN_ENABLE;
0122     hpet_write(HPET_T0_CFG, cfg);
0123 
0124     spin_unlock(&hpet_lock);
0125     return 0;
0126 }
0127 
0128 static int hpet_set_state_oneshot(struct clock_event_device *evt)
0129 {
0130     int cfg;
0131 
0132     spin_lock(&hpet_lock);
0133 
0134     pr_info("set clock event to one shot mode!\n");
0135     cfg = hpet_read(HPET_T0_CFG);
0136     /*
0137      * set timer0 type
0138      * 1 : periodic interrupt
0139      * 0 : non-periodic(oneshot) interrupt
0140      */
0141     cfg &= ~HPET_TN_PERIODIC;
0142     cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
0143     hpet_write(HPET_T0_CFG, cfg);
0144 
0145     spin_unlock(&hpet_lock);
0146     return 0;
0147 }
0148 
0149 static int hpet_tick_resume(struct clock_event_device *evt)
0150 {
0151     spin_lock(&hpet_lock);
0152     hpet_enable_legacy_int();
0153     spin_unlock(&hpet_lock);
0154 
0155     return 0;
0156 }
0157 
0158 static int hpet_next_event(unsigned long delta,
0159         struct clock_event_device *evt)
0160 {
0161     u32 cnt;
0162     s32 res;
0163 
0164     cnt = hpet_read(HPET_COUNTER);
0165     cnt += (u32) delta;
0166     hpet_write(HPET_T0_CMP, cnt);
0167 
0168     res = (s32)(cnt - hpet_read(HPET_COUNTER));
0169 
0170     return res < HPET_MIN_CYCLES ? -ETIME : 0;
0171 }
0172 
0173 static irqreturn_t hpet_irq_handler(int irq, void *data)
0174 {
0175     int is_irq;
0176     struct clock_event_device *cd;
0177     unsigned int cpu = smp_processor_id();
0178 
0179     is_irq = hpet_read(HPET_STATUS);
0180     if (is_irq & HPET_T0_IRS) {
0181         /* clear the TIMER0 irq status register */
0182         hpet_write(HPET_STATUS, HPET_T0_IRS);
0183         cd = &per_cpu(hpet_clockevent_device, cpu);
0184         cd->event_handler(cd);
0185         return IRQ_HANDLED;
0186     }
0187     return IRQ_NONE;
0188 }
0189 
0190 /*
0191  * hpet address assignation and irq setting should be done in bios.
0192  * but pmon don't do this, we just setup here directly.
0193  * The operation under is normal. unfortunately, hpet_setup process
0194  * is before pci initialize.
0195  *
0196  * {
0197  *  struct pci_dev *pdev;
0198  *
0199  *  pdev = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
0200  *  pci_write_config_word(pdev, SMBUS_PCI_REGB4, HPET_ADDR);
0201  *
0202  *  ...
0203  * }
0204  */
0205 static void hpet_setup(void)
0206 {
0207     /* set hpet base address */
0208     smbus_write(SMBUS_PCI_REGB4, HPET_ADDR);
0209 
0210     /* enable decoding of access to HPET MMIO*/
0211     smbus_enable(SMBUS_PCI_REG40, (1 << 28));
0212 
0213     /* HPET irq enable */
0214     smbus_enable(SMBUS_PCI_REG64, (1 << 10));
0215 
0216     hpet_enable_legacy_int();
0217 }
0218 
0219 void __init setup_hpet_timer(void)
0220 {
0221     unsigned long flags = IRQF_NOBALANCING | IRQF_TIMER;
0222     unsigned int cpu = smp_processor_id();
0223     struct clock_event_device *cd;
0224 
0225     hpet_setup();
0226 
0227     cd = &per_cpu(hpet_clockevent_device, cpu);
0228     cd->name = "hpet";
0229     cd->rating = 100;
0230     cd->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
0231     cd->set_state_shutdown = hpet_set_state_shutdown;
0232     cd->set_state_periodic = hpet_set_state_periodic;
0233     cd->set_state_oneshot = hpet_set_state_oneshot;
0234     cd->tick_resume = hpet_tick_resume;
0235     cd->set_next_event = hpet_next_event;
0236     cd->irq = HPET_T0_IRQ;
0237     cd->cpumask = cpumask_of(cpu);
0238     clockevent_set_clock(cd, HPET_FREQ);
0239     cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
0240     cd->max_delta_ticks = 0x7fffffff;
0241     cd->min_delta_ns = clockevent_delta2ns(HPET_MIN_PROG_DELTA, cd);
0242     cd->min_delta_ticks = HPET_MIN_PROG_DELTA;
0243 
0244     clockevents_register_device(cd);
0245     if (request_irq(HPET_T0_IRQ, hpet_irq_handler, flags, "hpet", NULL))
0246         pr_err("Failed to request irq %d (hpet)\n", HPET_T0_IRQ);
0247     pr_info("hpet clock event device register\n");
0248 }
0249 
0250 static u64 hpet_read_counter(struct clocksource *cs)
0251 {
0252     return (u64)hpet_read(HPET_COUNTER);
0253 }
0254 
0255 static void hpet_suspend(struct clocksource *cs)
0256 {
0257 }
0258 
0259 static void hpet_resume(struct clocksource *cs)
0260 {
0261     hpet_setup();
0262     hpet_restart_counter();
0263 }
0264 
0265 static struct clocksource csrc_hpet = {
0266     .name = "hpet",
0267     /* mips clocksource rating is less than 300, so hpet is better. */
0268     .rating = 300,
0269     .read = hpet_read_counter,
0270     .mask = CLOCKSOURCE_MASK(32),
0271     /* oneshot mode work normal with this flag */
0272     .flags = CLOCK_SOURCE_IS_CONTINUOUS,
0273     .suspend = hpet_suspend,
0274     .resume = hpet_resume,
0275     .mult = 0,
0276     .shift = 10,
0277 };
0278 
0279 int __init init_hpet_clocksource(void)
0280 {
0281     csrc_hpet.mult = clocksource_hz2mult(HPET_FREQ, csrc_hpet.shift);
0282     return clocksource_register_hz(&csrc_hpet, HPET_FREQ);
0283 }
0284 
0285 arch_initcall(init_hpet_clocksource);