1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * CS5536 General timer functions
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2007 Lemote Inc. & Institute of Computing Technology
6*4882a593Smuzhiyun * Author: Yanhua, yanh@lemote.com
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * Copyright (C) 2009 Lemote Inc.
9*4882a593Smuzhiyun * Author: Wu zhangjin, wuzhangjin@gmail.com
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * Reference: AMD Geode(TM) CS5536 Companion Device Data Book
12*4882a593Smuzhiyun */
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include <linux/io.h>
15*4882a593Smuzhiyun #include <linux/init.h>
16*4882a593Smuzhiyun #include <linux/export.h>
17*4882a593Smuzhiyun #include <linux/jiffies.h>
18*4882a593Smuzhiyun #include <linux/spinlock.h>
19*4882a593Smuzhiyun #include <linux/interrupt.h>
20*4882a593Smuzhiyun #include <linux/clockchips.h>
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #include <asm/time.h>
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun #include <cs5536/cs5536_mfgpt.h>
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun static DEFINE_RAW_SPINLOCK(mfgpt_lock);
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun static u32 mfgpt_base;
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun /*
31*4882a593Smuzhiyun * Initialize the MFGPT timer.
32*4882a593Smuzhiyun *
33*4882a593Smuzhiyun * This is also called after resume to bring the MFGPT into operation again.
34*4882a593Smuzhiyun */
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun /* disable counter */
disable_mfgpt0_counter(void)37*4882a593Smuzhiyun void disable_mfgpt0_counter(void)
38*4882a593Smuzhiyun {
39*4882a593Smuzhiyun outw(inw(MFGPT0_SETUP) & 0x7fff, MFGPT0_SETUP);
40*4882a593Smuzhiyun }
41*4882a593Smuzhiyun EXPORT_SYMBOL(disable_mfgpt0_counter);
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun /* enable counter, comparator2 to event mode, 14.318MHz clock */
enable_mfgpt0_counter(void)44*4882a593Smuzhiyun void enable_mfgpt0_counter(void)
45*4882a593Smuzhiyun {
46*4882a593Smuzhiyun outw(0xe310, MFGPT0_SETUP);
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun EXPORT_SYMBOL(enable_mfgpt0_counter);
49*4882a593Smuzhiyun
mfgpt_timer_set_periodic(struct clock_event_device * evt)50*4882a593Smuzhiyun static int mfgpt_timer_set_periodic(struct clock_event_device *evt)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun raw_spin_lock(&mfgpt_lock);
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun outw(COMPARE, MFGPT0_CMP2); /* set comparator2 */
55*4882a593Smuzhiyun outw(0, MFGPT0_CNT); /* set counter to 0 */
56*4882a593Smuzhiyun enable_mfgpt0_counter();
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun raw_spin_unlock(&mfgpt_lock);
59*4882a593Smuzhiyun return 0;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun
mfgpt_timer_shutdown(struct clock_event_device * evt)62*4882a593Smuzhiyun static int mfgpt_timer_shutdown(struct clock_event_device *evt)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun if (clockevent_state_periodic(evt) || clockevent_state_oneshot(evt)) {
65*4882a593Smuzhiyun raw_spin_lock(&mfgpt_lock);
66*4882a593Smuzhiyun disable_mfgpt0_counter();
67*4882a593Smuzhiyun raw_spin_unlock(&mfgpt_lock);
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun return 0;
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun static struct clock_event_device mfgpt_clockevent = {
74*4882a593Smuzhiyun .name = "mfgpt",
75*4882a593Smuzhiyun .features = CLOCK_EVT_FEAT_PERIODIC,
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun /* The oneshot mode have very high deviation, don't use it! */
78*4882a593Smuzhiyun .set_state_shutdown = mfgpt_timer_shutdown,
79*4882a593Smuzhiyun .set_state_periodic = mfgpt_timer_set_periodic,
80*4882a593Smuzhiyun .irq = CS5536_MFGPT_INTR,
81*4882a593Smuzhiyun };
82*4882a593Smuzhiyun
timer_interrupt(int irq,void * dev_id)83*4882a593Smuzhiyun static irqreturn_t timer_interrupt(int irq, void *dev_id)
84*4882a593Smuzhiyun {
85*4882a593Smuzhiyun u32 basehi;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun /*
88*4882a593Smuzhiyun * get MFGPT base address
89*4882a593Smuzhiyun *
90*4882a593Smuzhiyun * NOTE: do not remove me, it's need for the value of mfgpt_base is
91*4882a593Smuzhiyun * variable
92*4882a593Smuzhiyun */
93*4882a593Smuzhiyun _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun /* ack */
96*4882a593Smuzhiyun outw(inw(MFGPT0_SETUP) | 0x4000, MFGPT0_SETUP);
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun mfgpt_clockevent.event_handler(&mfgpt_clockevent);
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun return IRQ_HANDLED;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun /*
104*4882a593Smuzhiyun * Initialize the conversion factor and the min/max deltas of the clock event
105*4882a593Smuzhiyun * structure and register the clock event source with the framework.
106*4882a593Smuzhiyun */
setup_mfgpt0_timer(void)107*4882a593Smuzhiyun void __init setup_mfgpt0_timer(void)
108*4882a593Smuzhiyun {
109*4882a593Smuzhiyun u32 basehi;
110*4882a593Smuzhiyun struct clock_event_device *cd = &mfgpt_clockevent;
111*4882a593Smuzhiyun unsigned int cpu = smp_processor_id();
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun cd->cpumask = cpumask_of(cpu);
114*4882a593Smuzhiyun clockevent_set_clock(cd, MFGPT_TICK_RATE);
115*4882a593Smuzhiyun cd->max_delta_ns = clockevent_delta2ns(0xffff, cd);
116*4882a593Smuzhiyun cd->max_delta_ticks = 0xffff;
117*4882a593Smuzhiyun cd->min_delta_ns = clockevent_delta2ns(0xf, cd);
118*4882a593Smuzhiyun cd->min_delta_ticks = 0xf;
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun /* Enable MFGPT0 Comparator 2 Output to the Interrupt Mapper */
121*4882a593Smuzhiyun _wrmsr(DIVIL_MSR_REG(MFGPT_IRQ), 0, 0x100);
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun /* Enable Interrupt Gate 5 */
124*4882a593Smuzhiyun _wrmsr(DIVIL_MSR_REG(PIC_ZSEL_LOW), 0, 0x50000);
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun /* get MFGPT base address */
127*4882a593Smuzhiyun _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun clockevents_register_device(cd);
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun if (request_irq(CS5536_MFGPT_INTR, timer_interrupt,
132*4882a593Smuzhiyun IRQF_NOBALANCING | IRQF_TIMER, "timer", NULL))
133*4882a593Smuzhiyun pr_err("Failed to register timer interrupt\n");
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun /*
137*4882a593Smuzhiyun * Since the MFGPT overflows every tick, its not very useful
138*4882a593Smuzhiyun * to just read by itself. So use jiffies to emulate a free
139*4882a593Smuzhiyun * running counter:
140*4882a593Smuzhiyun */
mfgpt_read(struct clocksource * cs)141*4882a593Smuzhiyun static u64 mfgpt_read(struct clocksource *cs)
142*4882a593Smuzhiyun {
143*4882a593Smuzhiyun unsigned long flags;
144*4882a593Smuzhiyun int count;
145*4882a593Smuzhiyun u32 jifs;
146*4882a593Smuzhiyun static int old_count;
147*4882a593Smuzhiyun static u32 old_jifs;
148*4882a593Smuzhiyun
149*4882a593Smuzhiyun raw_spin_lock_irqsave(&mfgpt_lock, flags);
150*4882a593Smuzhiyun /*
151*4882a593Smuzhiyun * Although our caller may have the read side of xtime_lock,
152*4882a593Smuzhiyun * this is now a seqlock, and we are cheating in this routine
153*4882a593Smuzhiyun * by having side effects on state that we cannot undo if
154*4882a593Smuzhiyun * there is a collision on the seqlock and our caller has to
155*4882a593Smuzhiyun * retry. (Namely, old_jifs and old_count.) So we must treat
156*4882a593Smuzhiyun * jiffies as volatile despite the lock. We read jiffies
157*4882a593Smuzhiyun * before latching the timer count to guarantee that although
158*4882a593Smuzhiyun * the jiffies value might be older than the count (that is,
159*4882a593Smuzhiyun * the counter may underflow between the last point where
160*4882a593Smuzhiyun * jiffies was incremented and the point where we latch the
161*4882a593Smuzhiyun * count), it cannot be newer.
162*4882a593Smuzhiyun */
163*4882a593Smuzhiyun jifs = jiffies;
164*4882a593Smuzhiyun /* read the count */
165*4882a593Smuzhiyun count = inw(MFGPT0_CNT);
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun /*
168*4882a593Smuzhiyun * It's possible for count to appear to go the wrong way for this
169*4882a593Smuzhiyun * reason:
170*4882a593Smuzhiyun *
171*4882a593Smuzhiyun * The timer counter underflows, but we haven't handled the resulting
172*4882a593Smuzhiyun * interrupt and incremented jiffies yet.
173*4882a593Smuzhiyun *
174*4882a593Smuzhiyun * Previous attempts to handle these cases intelligently were buggy, so
175*4882a593Smuzhiyun * we just do the simple thing now.
176*4882a593Smuzhiyun */
177*4882a593Smuzhiyun if (count < old_count && jifs == old_jifs)
178*4882a593Smuzhiyun count = old_count;
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun old_count = count;
181*4882a593Smuzhiyun old_jifs = jifs;
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun raw_spin_unlock_irqrestore(&mfgpt_lock, flags);
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun return (u64) (jifs * COMPARE) + count;
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun static struct clocksource clocksource_mfgpt = {
189*4882a593Smuzhiyun .name = "mfgpt",
190*4882a593Smuzhiyun .rating = 120, /* Functional for real use, but not desired */
191*4882a593Smuzhiyun .read = mfgpt_read,
192*4882a593Smuzhiyun .mask = CLOCKSOURCE_MASK(32),
193*4882a593Smuzhiyun };
194*4882a593Smuzhiyun
init_mfgpt_clocksource(void)195*4882a593Smuzhiyun int __init init_mfgpt_clocksource(void)
196*4882a593Smuzhiyun {
197*4882a593Smuzhiyun if (num_possible_cpus() > 1) /* MFGPT does not scale! */
198*4882a593Smuzhiyun return 0;
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun return clocksource_register_hz(&clocksource_mfgpt, MFGPT_TICK_RATE);
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun arch_initcall(init_mfgpt_clocksource);
204