aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/loongson
diff options
context:
space:
mode:
authorWu Zhangjin <wuzhangjin@gmail.com>2009-11-16 12:32:57 -0500
committerRalf Baechle <ralf@linux-mips.org>2009-12-16 20:57:19 -0500
commit916daba8a9f2617ded8b9255e6b39f066ef60178 (patch)
tree63aa70b44e483bc30154130707e4d7529493543c /arch/mips/loongson
parent6e552c9b3aa7ba3be57b9569ec92a38af5c65e48 (diff)
MIPS: Lemote 2F: Add cs5536 MFGPT timer support
CPUFreq support for Loongson 2F requires an external timer. Because the frequency of the MIPS Timer is related to the CPU frequency which itself is variable another timer of constant frequency is required. Export the mfgpt0 counter disable / enable operations for the coming suspend support to suspend / resume the timer. Signed-off-by: Wu Zhangjin <wuzhangjin@gmail.com> Cc: linux-mips@linux-mips.org Cc: cpufreq@vger.kernel.org, Cc: Dave Jones <davej@redhat.com>, Cc: Dominik Brodowski <linux@dominikbrodowski.net>, Cc: yanh@lemote.com Cc: huhb@lemote.com, Patchwork: http://patchwork.linux-mips.org/patch/658/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/loongson')
-rw-r--r--arch/mips/loongson/Kconfig11
-rw-r--r--arch/mips/loongson/common/cs5536/Makefile5
-rw-r--r--arch/mips/loongson/common/cs5536/cs5536_mfgpt.c217
-rw-r--r--arch/mips/loongson/common/time.c3
4 files changed, 236 insertions, 0 deletions
diff --git a/arch/mips/loongson/Kconfig b/arch/mips/loongson/Kconfig
index 17e72fde908c..8b5cc138611a 100644
--- a/arch/mips/loongson/Kconfig
+++ b/arch/mips/loongson/Kconfig
@@ -62,6 +62,17 @@ endchoice
62config CS5536 62config CS5536
63 bool 63 bool
64 64
65config CS5536_MFGPT
66 bool "CS5536 MFGPT Timer"
67 depends on CS5536
68 help
69 This option enables the mfgpt0 timer of AMD CS5536.
70
71 If you want to enable the Loongson2 CPUFreq Driver, Please enable
72 this option at first, otherwise, You will get wrong system time.
73
74 If unsure, say Yes.
75
65config LOONGSON_SUSPEND 76config LOONGSON_SUSPEND
66 bool 77 bool
67 default y 78 default y
diff --git a/arch/mips/loongson/common/cs5536/Makefile b/arch/mips/loongson/common/cs5536/Makefile
index 31657ee037d8..510d4cdc2378 100644
--- a/arch/mips/loongson/common/cs5536/Makefile
+++ b/arch/mips/loongson/common/cs5536/Makefile
@@ -5,4 +5,9 @@
5obj-$(CONFIG_CS5536) += cs5536_pci.o cs5536_ide.o cs5536_acc.o cs5536_ohci.o \ 5obj-$(CONFIG_CS5536) += cs5536_pci.o cs5536_ide.o cs5536_acc.o cs5536_ohci.o \
6 cs5536_isa.o cs5536_ehci.o 6 cs5536_isa.o cs5536_ehci.o
7 7
8#
9# Enable cs5536 mfgpt Timer
10#
11obj-$(CONFIG_CS5536_MFGPT) += cs5536_mfgpt.o
12
8EXTRA_CFLAGS += -Werror 13EXTRA_CFLAGS += -Werror
diff --git a/arch/mips/loongson/common/cs5536/cs5536_mfgpt.c b/arch/mips/loongson/common/cs5536/cs5536_mfgpt.c
new file mode 100644
index 000000000000..6cb44dbaeec2
--- /dev/null
+++ b/arch/mips/loongson/common/cs5536/cs5536_mfgpt.c
@@ -0,0 +1,217 @@
1/*
2 * CS5536 General timer functions
3 *
4 * Copyright (C) 2007 Lemote Inc. & Insititute of Computing Technology
5 * Author: Yanhua, yanh@lemote.com
6 *
7 * Copyright (C) 2009 Lemote Inc.
8 * Author: Wu zhangjin, wuzj@lemote.com
9 *
10 * Reference: AMD Geode(TM) CS5536 Companion Device Data Book
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 2 of the License, or (at your
15 * option) any later version.
16 */
17
18#include <linux/io.h>
19#include <linux/init.h>
20#include <linux/module.h>
21#include <linux/jiffies.h>
22#include <linux/spinlock.h>
23#include <linux/interrupt.h>
24#include <linux/clockchips.h>
25
26#include <asm/time.h>
27
28#include <cs5536/cs5536_mfgpt.h>
29
30DEFINE_SPINLOCK(mfgpt_lock);
31EXPORT_SYMBOL(mfgpt_lock);
32
33static u32 mfgpt_base;
34
35/*
36 * Initialize the MFGPT timer.
37 *
38 * This is also called after resume to bring the MFGPT into operation again.
39 */
40
41/* disable counter */
42void disable_mfgpt0_counter(void)
43{
44 outw(inw(MFGPT0_SETUP) & 0x7fff, MFGPT0_SETUP);
45}
46EXPORT_SYMBOL(disable_mfgpt0_counter);
47
48/* enable counter, comparator2 to event mode, 14.318MHz clock */
49void enable_mfgpt0_counter(void)
50{
51 outw(0xe310, MFGPT0_SETUP);
52}
53EXPORT_SYMBOL(enable_mfgpt0_counter);
54
55static void init_mfgpt_timer(enum clock_event_mode mode,
56 struct clock_event_device *evt)
57{
58 spin_lock(&mfgpt_lock);
59
60 switch (mode) {
61 case CLOCK_EVT_MODE_PERIODIC:
62 outw(COMPARE, MFGPT0_CMP2); /* set comparator2 */
63 outw(0, MFGPT0_CNT); /* set counter to 0 */
64 enable_mfgpt0_counter();
65 break;
66
67 case CLOCK_EVT_MODE_SHUTDOWN:
68 case CLOCK_EVT_MODE_UNUSED:
69 if (evt->mode == CLOCK_EVT_MODE_PERIODIC ||
70 evt->mode == CLOCK_EVT_MODE_ONESHOT)
71 disable_mfgpt0_counter();
72 break;
73
74 case CLOCK_EVT_MODE_ONESHOT:
75 /* The oneshot mode have very high deviation, Not use it! */
76 break;
77
78 case CLOCK_EVT_MODE_RESUME:
79 /* Nothing to do here */
80 break;
81 }
82 spin_unlock(&mfgpt_lock);
83}
84
85static struct clock_event_device mfgpt_clockevent = {
86 .name = "mfgpt",
87 .features = CLOCK_EVT_FEAT_PERIODIC,
88 .set_mode = init_mfgpt_timer,
89 .irq = CS5536_MFGPT_INTR,
90};
91
92static irqreturn_t timer_interrupt(int irq, void *dev_id)
93{
94 u32 basehi;
95
96 /*
97 * get MFGPT base address
98 *
99 * NOTE: do not remove me, it's need for the value of mfgpt_base is
100 * variable
101 */
102 _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
103
104 /* ack */
105 outw(inw(MFGPT0_SETUP) | 0x4000, MFGPT0_SETUP);
106
107 mfgpt_clockevent.event_handler(&mfgpt_clockevent);
108
109 return IRQ_HANDLED;
110}
111
112static struct irqaction irq5 = {
113 .handler = timer_interrupt,
114 .flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_TIMER,
115 .name = "timer"
116};
117
118/*
119 * Initialize the conversion factor and the min/max deltas of the clock event
120 * structure and register the clock event source with the framework.
121 */
122void __init setup_mfgpt0_timer(void)
123{
124 u32 basehi;
125 struct clock_event_device *cd = &mfgpt_clockevent;
126 unsigned int cpu = smp_processor_id();
127
128 cd->cpumask = cpumask_of(cpu);
129 clockevent_set_clock(cd, MFGPT_TICK_RATE);
130 cd->max_delta_ns = clockevent_delta2ns(0xffff, cd);
131 cd->min_delta_ns = clockevent_delta2ns(0xf, cd);
132
133 /* Enable MFGPT0 Comparator 2 Output to the Interrupt Mapper */
134 _wrmsr(DIVIL_MSR_REG(MFGPT_IRQ), 0, 0x100);
135
136 /* Enable Interrupt Gate 5 */
137 _wrmsr(DIVIL_MSR_REG(PIC_ZSEL_LOW), 0, 0x50000);
138
139 /* get MFGPT base address */
140 _rdmsr(DIVIL_MSR_REG(DIVIL_LBAR_MFGPT), &basehi, &mfgpt_base);
141
142 clockevents_register_device(cd);
143
144 setup_irq(CS5536_MFGPT_INTR, &irq5);
145}
146
147/*
148 * Since the MFGPT overflows every tick, its not very useful
149 * to just read by itself. So use jiffies to emulate a free
150 * running counter:
151 */
152static cycle_t mfgpt_read(struct clocksource *cs)
153{
154 unsigned long flags;
155 int count;
156 u32 jifs;
157 static int old_count;
158 static u32 old_jifs;
159
160 spin_lock_irqsave(&mfgpt_lock, flags);
161 /*
162 * Although our caller may have the read side of xtime_lock,
163 * this is now a seqlock, and we are cheating in this routine
164 * by having side effects on state that we cannot undo if
165 * there is a collision on the seqlock and our caller has to
166 * retry. (Namely, old_jifs and old_count.) So we must treat
167 * jiffies as volatile despite the lock. We read jiffies
168 * before latching the timer count to guarantee that although
169 * the jiffies value might be older than the count (that is,
170 * the counter may underflow between the last point where
171 * jiffies was incremented and the point where we latch the
172 * count), it cannot be newer.
173 */
174 jifs = jiffies;
175 /* read the count */
176 count = inw(MFGPT0_CNT);
177
178 /*
179 * It's possible for count to appear to go the wrong way for this
180 * reason:
181 *
182 * The timer counter underflows, but we haven't handled the resulting
183 * interrupt and incremented jiffies yet.
184 *
185 * Previous attempts to handle these cases intelligently were buggy, so
186 * we just do the simple thing now.
187 */
188 if (count < old_count && jifs == old_jifs)
189 count = old_count;
190
191 old_count = count;
192 old_jifs = jifs;
193
194 spin_unlock_irqrestore(&mfgpt_lock, flags);
195
196 return (cycle_t) (jifs * COMPARE) + count;
197}
198
199static struct clocksource clocksource_mfgpt = {
200 .name = "mfgpt",
201 .rating = 120, /* Functional for real use, but not desired */
202 .read = mfgpt_read,
203 .mask = CLOCKSOURCE_MASK(32),
204 .mult = 0,
205 .shift = 22,
206};
207
208int __init init_mfgpt_clocksource(void)
209{
210 if (num_possible_cpus() > 1) /* MFGPT does not scale! */
211 return 0;
212
213 clocksource_mfgpt.mult = clocksource_hz2mult(MFGPT_TICK_RATE, 22);
214 return clocksource_register(&clocksource_mfgpt);
215}
216
217arch_initcall(init_mfgpt_clocksource);
diff --git a/arch/mips/loongson/common/time.c b/arch/mips/loongson/common/time.c
index 6e08c8270abe..35f0b66a94f5 100644
--- a/arch/mips/loongson/common/time.c
+++ b/arch/mips/loongson/common/time.c
@@ -14,11 +14,14 @@
14#include <asm/time.h> 14#include <asm/time.h>
15 15
16#include <loongson.h> 16#include <loongson.h>
17#include <cs5536/cs5536_mfgpt.h>
17 18
18void __init plat_time_init(void) 19void __init plat_time_init(void)
19{ 20{
20 /* setup mips r4k timer */ 21 /* setup mips r4k timer */
21 mips_hpt_frequency = cpu_clock_freq / 2; 22 mips_hpt_frequency = cpu_clock_freq / 2;
23
24 setup_mfgpt0_timer();
22} 25}
23 26
24void read_persistent_clock(struct timespec *ts) 27void read_persistent_clock(struct timespec *ts)