Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * CS5536 General timer functions
0004  *
0005  * Copyright (C) 2007 Lemote Inc. & Institute of Computing Technology
0006  * Author: Yanhua, yanh@lemote.com
0007  *
0008  * Copyright (C) 2009 Lemote Inc.
0009  * Author: Wu zhangjin, wuzhangjin@gmail.com
0010  *
0011  * Reference: AMD Geode(TM) CS5536 Companion Device Data Book
0012  */
0013 
0014 #include <linux/io.h>
0015 #include <linux/init.h>
0016 #include <linux/export.h>
0017 #include <linux/jiffies.h>
0018 #include <linux/spinlock.h>
0019 #include <linux/interrupt.h>
0020 #include <linux/clockchips.h>
0021 
0022 #include <asm/time.h>
0023 
0024 #include <cs5536/cs5536_mfgpt.h>
0025 
0026 static DEFINE_RAW_SPINLOCK(mfgpt_lock);
0027 
0028 static u32 mfgpt_base;
0029 
0030 /*
0031  * Initialize the MFGPT timer.
0032  *
0033  * This is also called after resume to bring the MFGPT into operation again.
0034  */
0035 
0036 /* disable counter */
0037 void disable_mfgpt0_counter(void)
0038 {
0039     outw(inw(MFGPT0_SETUP) & 0x7fff, MFGPT0_SETUP);
0040 }
0041 EXPORT_SYMBOL(disable_mfgpt0_counter);
0042 
0043 /* enable counter, comparator2 to event mode, 14.318MHz clock */
0044 void enable_mfgpt0_counter(void)
0045 {
0046     outw(0xe310, MFGPT0_SETUP);
0047 }
0048 EXPORT_SYMBOL(enable_mfgpt0_counter);
0049 
0050 static int mfgpt_timer_set_periodic(struct clock_event_device *evt)
0051 {
0052     raw_spin_lock(&mfgpt_lock);
0053 
0054     outw(COMPARE, MFGPT0_CMP2); /* set comparator2 */
0055     outw(0, MFGPT0_CNT);        /* set counter to 0 */
0056     enable_mfgpt0_counter();
0057 
0058     raw_spin_unlock(&mfgpt_lock);
0059     return 0;
0060 }
0061 
0062 static int mfgpt_timer_shutdown(struct clock_event_device *evt)
0063 {
0064     if (clockevent_state_periodic(evt) || clockevent_state_oneshot(evt)) {
0065         raw_spin_lock(&mfgpt_lock);
0066         disable_mfgpt0_counter();
0067         raw_spin_unlock(&mfgpt_lock);
0068     }
0069 
0070     return 0;
0071 }
0072 
0073 static struct clock_event_device mfgpt_clockevent = {
0074     .name = "mfgpt",
0075     .features = CLOCK_EVT_FEAT_PERIODIC,
0076 
0077     /* The oneshot mode have very high deviation, don't use it! */
0078     .set_state_shutdown = mfgpt_timer_shutdown,
0079     .set_state_periodic = mfgpt_timer_set_periodic,
0080     .irq = CS5536_MFGPT_INTR,
0081 };
0082 
0083 static irqreturn_t timer_interrupt(int irq, void *dev_id)
0084 {
0085     u32 basehi;
0086 
0087     /*
0088      * get MFGPT base address
0089      *
0090      * NOTE: do not remove me, it's need for the value of mfgpt_base is
0091      * variable
0092      */
0093     _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
0094 
0095     /* ack */
0096     outw(inw(MFGPT0_SETUP) | 0x4000, MFGPT0_SETUP);
0097 
0098     mfgpt_clockevent.event_handler(&mfgpt_clockevent);
0099 
0100     return IRQ_HANDLED;
0101 }
0102 
0103 /*
0104  * Initialize the conversion factor and the min/max deltas of the clock event
0105  * structure and register the clock event source with the framework.
0106  */
0107 void __init setup_mfgpt0_timer(void)
0108 {
0109     u32 basehi;
0110     struct clock_event_device *cd = &mfgpt_clockevent;
0111     unsigned int cpu = smp_processor_id();
0112 
0113     cd->cpumask = cpumask_of(cpu);
0114     clockevent_set_clock(cd, MFGPT_TICK_RATE);
0115     cd->max_delta_ns = clockevent_delta2ns(0xffff, cd);
0116     cd->max_delta_ticks = 0xffff;
0117     cd->min_delta_ns = clockevent_delta2ns(0xf, cd);
0118     cd->min_delta_ticks = 0xf;
0119 
0120     /* Enable MFGPT0 Comparator 2 Output to the Interrupt Mapper */
0121     _wrmsr(DIVIL_MSR_REG(MFGPT_IRQ), 0, 0x100);
0122 
0123     /* Enable Interrupt Gate 5 */
0124     _wrmsr(DIVIL_MSR_REG(PIC_ZSEL_LOW), 0, 0x50000);
0125 
0126     /* get MFGPT base address */
0127     _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
0128 
0129     clockevents_register_device(cd);
0130 
0131     if (request_irq(CS5536_MFGPT_INTR, timer_interrupt,
0132             IRQF_NOBALANCING | IRQF_TIMER, "timer", NULL))
0133         pr_err("Failed to register timer interrupt\n");
0134 }
0135 
0136 /*
0137  * Since the MFGPT overflows every tick, its not very useful
0138  * to just read by itself. So use jiffies to emulate a free
0139  * running counter:
0140  */
0141 static u64 mfgpt_read(struct clocksource *cs)
0142 {
0143     unsigned long flags;
0144     int count;
0145     u32 jifs;
0146     static int old_count;
0147     static u32 old_jifs;
0148 
0149     raw_spin_lock_irqsave(&mfgpt_lock, flags);
0150     /*
0151      * Although our caller may have the read side of xtime_lock,
0152      * this is now a seqlock, and we are cheating in this routine
0153      * by having side effects on state that we cannot undo if
0154      * there is a collision on the seqlock and our caller has to
0155      * retry.  (Namely, old_jifs and old_count.)  So we must treat
0156      * jiffies as volatile despite the lock.  We read jiffies
0157      * before latching the timer count to guarantee that although
0158      * the jiffies value might be older than the count (that is,
0159      * the counter may underflow between the last point where
0160      * jiffies was incremented and the point where we latch the
0161      * count), it cannot be newer.
0162      */
0163     jifs = jiffies;
0164     /* read the count */
0165     count = inw(MFGPT0_CNT);
0166 
0167     /*
0168      * It's possible for count to appear to go the wrong way for this
0169      * reason:
0170      *
0171      *  The timer counter underflows, but we haven't handled the resulting
0172      *  interrupt and incremented jiffies yet.
0173      *
0174      * Previous attempts to handle these cases intelligently were buggy, so
0175      * we just do the simple thing now.
0176      */
0177     if (count < old_count && jifs == old_jifs)
0178         count = old_count;
0179 
0180     old_count = count;
0181     old_jifs = jifs;
0182 
0183     raw_spin_unlock_irqrestore(&mfgpt_lock, flags);
0184 
0185     return (u64) (jifs * COMPARE) + count;
0186 }
0187 
0188 static struct clocksource clocksource_mfgpt = {
0189     .name = "mfgpt",
0190     .rating = 120, /* Functional for real use, but not desired */
0191     .read = mfgpt_read,
0192     .mask = CLOCKSOURCE_MASK(32),
0193 };
0194 
0195 int __init init_mfgpt_clocksource(void)
0196 {
0197     if (num_possible_cpus() > 1)    /* MFGPT does not scale! */
0198         return 0;
0199 
0200     return clocksource_register_hz(&clocksource_mfgpt, MFGPT_TICK_RATE);
0201 }
0202 
0203 arch_initcall(init_mfgpt_clocksource);