aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/mips/include/asm/hpet.h73
-rw-r--r--arch/mips/loongson/Kconfig12
-rw-r--r--arch/mips/loongson/common/time.c5
-rw-r--r--arch/mips/loongson/loongson-3/Makefile2
-rw-r--r--arch/mips/loongson/loongson-3/hpet.c257
-rw-r--r--arch/mips/loongson/loongson-3/irq.c2
6 files changed, 350 insertions, 1 deletions
diff --git a/arch/mips/include/asm/hpet.h b/arch/mips/include/asm/hpet.h
new file mode 100644
index 000000000000..18a8f778bfaa
--- /dev/null
+++ b/arch/mips/include/asm/hpet.h
@@ -0,0 +1,73 @@
1#ifndef _ASM_HPET_H
2#define _ASM_HPET_H
3
4#ifdef CONFIG_RS780_HPET
5
6#define HPET_MMAP_SIZE 1024
7
8#define HPET_ID 0x000
9#define HPET_PERIOD 0x004
10#define HPET_CFG 0x010
11#define HPET_STATUS 0x020
12#define HPET_COUNTER 0x0f0
13
14#define HPET_Tn_CFG(n) (0x100 + 0x20 * n)
15#define HPET_Tn_CMP(n) (0x108 + 0x20 * n)
16#define HPET_Tn_ROUTE(n) (0x110 + 0x20 * n)
17
18#define HPET_T0_IRS 0x001
19#define HPET_T1_IRS 0x002
20#define HPET_T3_IRS 0x004
21
22#define HPET_T0_CFG 0x100
23#define HPET_T0_CMP 0x108
24#define HPET_T0_ROUTE 0x110
25#define HPET_T1_CFG 0x120
26#define HPET_T1_CMP 0x128
27#define HPET_T1_ROUTE 0x130
28#define HPET_T2_CFG 0x140
29#define HPET_T2_CMP 0x148
30#define HPET_T2_ROUTE 0x150
31
32#define HPET_ID_REV 0x000000ff
33#define HPET_ID_NUMBER 0x00001f00
34#define HPET_ID_64BIT 0x00002000
35#define HPET_ID_LEGSUP 0x00008000
36#define HPET_ID_VENDOR 0xffff0000
37#define HPET_ID_NUMBER_SHIFT 8
38#define HPET_ID_VENDOR_SHIFT 16
39
40#define HPET_CFG_ENABLE 0x001
41#define HPET_CFG_LEGACY 0x002
42#define HPET_LEGACY_8254 2
43#define HPET_LEGACY_RTC 8
44
45#define HPET_TN_LEVEL 0x0002
46#define HPET_TN_ENABLE 0x0004
47#define HPET_TN_PERIODIC 0x0008
48#define HPET_TN_PERIODIC_CAP 0x0010
49#define HPET_TN_64BIT_CAP 0x0020
50#define HPET_TN_SETVAL 0x0040
51#define HPET_TN_32BIT 0x0100
52#define HPET_TN_ROUTE 0x3e00
53#define HPET_TN_FSB 0x4000
54#define HPET_TN_FSB_CAP 0x8000
55#define HPET_TN_ROUTE_SHIFT 9
56
57/* Max HPET Period is 10^8 femto sec as in HPET spec */
58#define HPET_MAX_PERIOD 100000000UL
59/*
60 * Min HPET period is 10^5 femto sec just for safety. If it is less than this,
61 * then 32 bit HPET counter wrapsaround in less than 0.5 sec.
62 */
63#define HPET_MIN_PERIOD 100000UL
64
65#define HPET_ADDR 0x20000
66#define HPET_MMIO_ADDR 0x90000e0000020000
67#define HPET_FREQ 14318780
68#define HPET_COMPARE_VAL ((HPET_FREQ + HZ / 2) / HZ)
69#define HPET_T0_IRQ 0
70
71extern void __init setup_hpet_timer(void);
72#endif /* CONFIG_RS780_HPET */
73#endif /* _ASM_HPET_H */
diff --git a/arch/mips/loongson/Kconfig b/arch/mips/loongson/Kconfig
index 72f830ac0782..156de85b82cd 100644
--- a/arch/mips/loongson/Kconfig
+++ b/arch/mips/loongson/Kconfig
@@ -108,6 +108,18 @@ config CS5536_MFGPT
108 108
109 If unsure, say Yes. 109 If unsure, say Yes.
110 110
111config RS780_HPET
112 bool "RS780/SBX00 HPET Timer"
113 depends on LOONGSON_MACH3X
114 select MIPS_EXTERNAL_TIMER
115 help
116 This option enables the hpet timer of AMD RS780/SBX00.
117
118 If you want to enable the Loongson3 CPUFreq Driver, Please enable
119 this option at first, otherwise, You will get wrong system time.
120
121 If unsure, say Yes.
122
111config LOONGSON_SUSPEND 123config LOONGSON_SUSPEND
112 bool 124 bool
113 default y 125 default y
diff --git a/arch/mips/loongson/common/time.c b/arch/mips/loongson/common/time.c
index 262a1f65b05e..e1a5382ad47e 100644
--- a/arch/mips/loongson/common/time.c
+++ b/arch/mips/loongson/common/time.c
@@ -12,6 +12,7 @@
12 */ 12 */
13#include <asm/mc146818-time.h> 13#include <asm/mc146818-time.h>
14#include <asm/time.h> 14#include <asm/time.h>
15#include <asm/hpet.h>
15 16
16#include <loongson.h> 17#include <loongson.h>
17#include <cs5536/cs5536_mfgpt.h> 18#include <cs5536/cs5536_mfgpt.h>
@@ -21,7 +22,11 @@ void __init plat_time_init(void)
21 /* setup mips r4k timer */ 22 /* setup mips r4k timer */
22 mips_hpt_frequency = cpu_clock_freq / 2; 23 mips_hpt_frequency = cpu_clock_freq / 2;
23 24
25#ifdef CONFIG_RS780_HPET
26 setup_hpet_timer();
27#else
24 setup_mfgpt0_timer(); 28 setup_mfgpt0_timer();
29#endif
25} 30}
26 31
27void read_persistent_clock(struct timespec *ts) 32void read_persistent_clock(struct timespec *ts)
diff --git a/arch/mips/loongson/loongson-3/Makefile b/arch/mips/loongson/loongson-3/Makefile
index 69809a3d8f34..622fead5ebc9 100644
--- a/arch/mips/loongson/loongson-3/Makefile
+++ b/arch/mips/loongson/loongson-3/Makefile
@@ -6,3 +6,5 @@ obj-y += irq.o cop2-ex.o platform.o
6obj-$(CONFIG_SMP) += smp.o 6obj-$(CONFIG_SMP) += smp.o
7 7
8obj-$(CONFIG_NUMA) += numa.o 8obj-$(CONFIG_NUMA) += numa.o
9
10obj-$(CONFIG_RS780_HPET) += hpet.o
diff --git a/arch/mips/loongson/loongson-3/hpet.c b/arch/mips/loongson/loongson-3/hpet.c
new file mode 100644
index 000000000000..e898d68668a9
--- /dev/null
+++ b/arch/mips/loongson/loongson-3/hpet.c
@@ -0,0 +1,257 @@
1#include <linux/init.h>
2#include <linux/pci.h>
3#include <linux/percpu.h>
4#include <linux/delay.h>
5#include <linux/spinlock.h>
6#include <linux/interrupt.h>
7
8#include <asm/hpet.h>
9#include <asm/time.h>
10
11#define SMBUS_CFG_BASE (loongson_sysconf.ht_control_base + 0x0300a000)
12#define SMBUS_PCI_REG40 0x40
13#define SMBUS_PCI_REG64 0x64
14#define SMBUS_PCI_REGB4 0xb4
15
16static DEFINE_SPINLOCK(hpet_lock);
17DEFINE_PER_CPU(struct clock_event_device, hpet_clockevent_device);
18
19static unsigned int smbus_read(int offset)
20{
21 return *(volatile unsigned int *)(SMBUS_CFG_BASE + offset);
22}
23
24static void smbus_write(int offset, int data)
25{
26 *(volatile unsigned int *)(SMBUS_CFG_BASE + offset) = data;
27}
28
29static void smbus_enable(int offset, int bit)
30{
31 unsigned int cfg = smbus_read(offset);
32
33 cfg |= bit;
34 smbus_write(offset, cfg);
35}
36
37static int hpet_read(int offset)
38{
39 return *(volatile unsigned int *)(HPET_MMIO_ADDR + offset);
40}
41
42static void hpet_write(int offset, int data)
43{
44 *(volatile unsigned int *)(HPET_MMIO_ADDR + offset) = data;
45}
46
47static void hpet_start_counter(void)
48{
49 unsigned int cfg = hpet_read(HPET_CFG);
50
51 cfg |= HPET_CFG_ENABLE;
52 hpet_write(HPET_CFG, cfg);
53}
54
55static void hpet_stop_counter(void)
56{
57 unsigned int cfg = hpet_read(HPET_CFG);
58
59 cfg &= ~HPET_CFG_ENABLE;
60 hpet_write(HPET_CFG, cfg);
61}
62
63static void hpet_reset_counter(void)
64{
65 hpet_write(HPET_COUNTER, 0);
66 hpet_write(HPET_COUNTER + 4, 0);
67}
68
69static void hpet_restart_counter(void)
70{
71 hpet_stop_counter();
72 hpet_reset_counter();
73 hpet_start_counter();
74}
75
76static void hpet_enable_legacy_int(void)
77{
78 /* Do nothing on Loongson-3 */
79}
80
81static void hpet_set_mode(enum clock_event_mode mode,
82 struct clock_event_device *evt)
83{
84 int cfg = 0;
85
86 spin_lock(&hpet_lock);
87 switch (mode) {
88 case CLOCK_EVT_MODE_PERIODIC:
89 pr_info("set clock event to periodic mode!\n");
90 /* stop counter */
91 hpet_stop_counter();
92
93 /* enables the timer0 to generate a periodic interrupt */
94 cfg = hpet_read(HPET_T0_CFG);
95 cfg &= ~HPET_TN_LEVEL;
96 cfg |= HPET_TN_ENABLE | HPET_TN_PERIODIC |
97 HPET_TN_SETVAL | HPET_TN_32BIT;
98 hpet_write(HPET_T0_CFG, cfg);
99
100 /* set the comparator */
101 hpet_write(HPET_T0_CMP, HPET_COMPARE_VAL);
102 udelay(1);
103 hpet_write(HPET_T0_CMP, HPET_COMPARE_VAL);
104
105 /* start counter */
106 hpet_start_counter();
107 break;
108 case CLOCK_EVT_MODE_SHUTDOWN:
109 case CLOCK_EVT_MODE_UNUSED:
110 cfg = hpet_read(HPET_T0_CFG);
111 cfg &= ~HPET_TN_ENABLE;
112 hpet_write(HPET_T0_CFG, cfg);
113 break;
114 case CLOCK_EVT_MODE_ONESHOT:
115 pr_info("set clock event to one shot mode!\n");
116 cfg = hpet_read(HPET_T0_CFG);
117 /* set timer0 type
118 * 1 : periodic interrupt
119 * 0 : non-periodic(oneshot) interrupt
120 */
121 cfg &= ~HPET_TN_PERIODIC;
122 cfg |= HPET_TN_ENABLE | HPET_TN_32BIT;
123 hpet_write(HPET_T0_CFG, cfg);
124 break;
125 case CLOCK_EVT_MODE_RESUME:
126 hpet_enable_legacy_int();
127 break;
128 }
129 spin_unlock(&hpet_lock);
130}
131
132static int hpet_next_event(unsigned long delta,
133 struct clock_event_device *evt)
134{
135 unsigned int cnt;
136 int res;
137
138 cnt = hpet_read(HPET_COUNTER);
139 cnt += delta;
140 hpet_write(HPET_T0_CMP, cnt);
141
142 res = ((int)(hpet_read(HPET_COUNTER) - cnt) > 0) ? -ETIME : 0;
143 return res;
144}
145
146static irqreturn_t hpet_irq_handler(int irq, void *data)
147{
148 int is_irq;
149 struct clock_event_device *cd;
150 unsigned int cpu = smp_processor_id();
151
152 is_irq = hpet_read(HPET_STATUS);
153 if (is_irq & HPET_T0_IRS) {
154 /* clear the TIMER0 irq status register */
155 hpet_write(HPET_STATUS, HPET_T0_IRS);
156 cd = &per_cpu(hpet_clockevent_device, cpu);
157 cd->event_handler(cd);
158 return IRQ_HANDLED;
159 }
160 return IRQ_NONE;
161}
162
163static struct irqaction hpet_irq = {
164 .handler = hpet_irq_handler,
165 .flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_TIMER,
166 .name = "hpet",
167};
168
169/*
170 * hpet address assignation and irq setting should be done in bios.
171 * but pmon don't do this, we just setup here directly.
172 * The operation under is normal. unfortunately, hpet_setup process
173 * is before pci initialize.
174 *
175 * {
176 * struct pci_dev *pdev;
177 *
178 * pdev = pci_get_device(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
179 * pci_write_config_word(pdev, SMBUS_PCI_REGB4, HPET_ADDR);
180 *
181 * ...
182 * }
183 */
184static void hpet_setup(void)
185{
186 /* set hpet base address */
187 smbus_write(SMBUS_PCI_REGB4, HPET_ADDR);
188
189 /* enable decodeing of access to HPET MMIO*/
190 smbus_enable(SMBUS_PCI_REG40, (1 << 28));
191
192 /* HPET irq enable */
193 smbus_enable(SMBUS_PCI_REG64, (1 << 10));
194
195 hpet_enable_legacy_int();
196}
197
198void __init setup_hpet_timer(void)
199{
200 unsigned int cpu = smp_processor_id();
201 struct clock_event_device *cd;
202
203 hpet_setup();
204
205 cd = &per_cpu(hpet_clockevent_device, cpu);
206 cd->name = "hpet";
207 cd->rating = 320;
208 cd->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
209 cd->set_mode = hpet_set_mode;
210 cd->set_next_event = hpet_next_event;
211 cd->irq = HPET_T0_IRQ;
212 cd->cpumask = cpumask_of(cpu);
213 clockevent_set_clock(cd, HPET_FREQ);
214 cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
215 cd->min_delta_ns = 5000;
216
217 clockevents_register_device(cd);
218 setup_irq(HPET_T0_IRQ, &hpet_irq);
219 pr_info("hpet clock event device register\n");
220}
221
222static cycle_t hpet_read_counter(struct clocksource *cs)
223{
224 return (cycle_t)hpet_read(HPET_COUNTER);
225}
226
227static void hpet_suspend(struct clocksource *cs)
228{
229}
230
231static void hpet_resume(struct clocksource *cs)
232{
233 hpet_setup();
234 hpet_restart_counter();
235}
236
237static struct clocksource csrc_hpet = {
238 .name = "hpet",
239 /* mips clocksource rating is less than 300, so hpet is better. */
240 .rating = 300,
241 .read = hpet_read_counter,
242 .mask = CLOCKSOURCE_MASK(32),
243 /* oneshot mode work normal with this flag */
244 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
245 .suspend = hpet_suspend,
246 .resume = hpet_resume,
247 .mult = 0,
248 .shift = 10,
249};
250
251int __init init_hpet_clocksource(void)
252{
253 csrc_hpet.mult = clocksource_hz2mult(HPET_FREQ, csrc_hpet.shift);
254 return clocksource_register_hz(&csrc_hpet, HPET_FREQ);
255}
256
257arch_initcall(init_hpet_clocksource);
diff --git a/arch/mips/loongson/loongson-3/irq.c b/arch/mips/loongson/loongson-3/irq.c
index 5813d941f9fc..21221edda7a9 100644
--- a/arch/mips/loongson/loongson-3/irq.c
+++ b/arch/mips/loongson/loongson-3/irq.c
@@ -9,7 +9,7 @@
9 9
10#include "smp.h" 10#include "smp.h"
11 11
12unsigned int ht_irq[] = {1, 3, 4, 5, 6, 7, 8, 12, 14, 15}; 12unsigned int ht_irq[] = {0, 1, 3, 4, 5, 6, 7, 8, 12, 14, 15};
13 13
14static void ht_irqdispatch(void) 14static void ht_irqdispatch(void)
15{ 15{