aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/kernel-parameters.txt3
-rw-r--r--arch/x86/kernel/Makefile_322
-rw-r--r--arch/x86/kernel/geode_32.c4
-rw-r--r--arch/x86/kernel/mfgpt_32.c197
-rw-r--r--include/asm-x86/geode.h50
5 files changed, 255 insertions, 1 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index a57c1f216b21..2128de6d80ea 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -1160,6 +1160,9 @@ and is between 256 and 4096 characters. It is defined in the file
1160 1160
1161 nomce [X86-32] Machine Check Exception 1161 nomce [X86-32] Machine Check Exception
1162 1162
1163 nomfgpt [X86-32] Disable Multi-Function General Purpose
1164 Timer usage (for AMD Geode machines).
1165
1163 noreplace-paravirt [X86-32,PV_OPS] Don't patch paravirt_ops 1166 noreplace-paravirt [X86-32,PV_OPS] Don't patch paravirt_ops
1164 1167
1165 noreplace-smp [X86-32,SMP] Don't replace SMP instructions 1168 noreplace-smp [X86-32,SMP] Don't replace SMP instructions
diff --git a/arch/x86/kernel/Makefile_32 b/arch/x86/kernel/Makefile_32
index c624193740fd..10356443998e 100644
--- a/arch/x86/kernel/Makefile_32
+++ b/arch/x86/kernel/Makefile_32
@@ -39,7 +39,7 @@ obj-$(CONFIG_VM86) += vm86_32.o
39obj-$(CONFIG_EARLY_PRINTK) += early_printk.o 39obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
40obj-$(CONFIG_HPET_TIMER) += hpet_32.o 40obj-$(CONFIG_HPET_TIMER) += hpet_32.o
41obj-$(CONFIG_K8_NB) += k8.o 41obj-$(CONFIG_K8_NB) += k8.o
42obj-$(CONFIG_MGEODE_LX) += geode_32.o 42obj-$(CONFIG_MGEODE_LX) += geode_32.o mfgpt_32.o
43 43
44obj-$(CONFIG_VMI) += vmi_32.o vmiclock_32.o 44obj-$(CONFIG_VMI) += vmi_32.o vmiclock_32.o
45obj-$(CONFIG_PARAVIRT) += paravirt_32.o 45obj-$(CONFIG_PARAVIRT) += paravirt_32.o
diff --git a/arch/x86/kernel/geode_32.c b/arch/x86/kernel/geode_32.c
index 41e8aec4c61d..f12d8c5d9809 100644
--- a/arch/x86/kernel/geode_32.c
+++ b/arch/x86/kernel/geode_32.c
@@ -145,10 +145,14 @@ EXPORT_SYMBOL_GPL(geode_gpio_setup_event);
145 145
146static int __init geode_southbridge_init(void) 146static int __init geode_southbridge_init(void)
147{ 147{
148 int timers;
149
148 if (!is_geode()) 150 if (!is_geode())
149 return -ENODEV; 151 return -ENODEV;
150 152
151 init_lbars(); 153 init_lbars();
154 timers = geode_mfgpt_detect();
155 printk(KERN_INFO "geode: %d MFGPT timers available.\n", timers);
152 return 0; 156 return 0;
153} 157}
154 158
diff --git a/arch/x86/kernel/mfgpt_32.c b/arch/x86/kernel/mfgpt_32.c
new file mode 100644
index 000000000000..3a63a2dd8d27
--- /dev/null
+++ b/arch/x86/kernel/mfgpt_32.c
@@ -0,0 +1,197 @@
1/*
2 * Driver/API for AMD Geode Multi-Function General Purpose Timers (MFGPT)
3 *
4 * Copyright (C) 2006, Advanced Micro Devices, Inc.
5 * Copyright (C) 2007, Andres Salomon <dilinger@debian.org>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2 of the GNU General Public License
9 * as published by the Free Software Foundation.
10 *
11 * The MFGPTs are documented in AMD Geode CS5536 Companion Device Data Book.
12 */
13
14/*
15 * We are using the 32Khz input clock - its the only one that has the
16 * ranges we find desirable. The following table lists the suitable
17 * divisors and the associated hz, minimum interval
18 * and the maximum interval:
19 *
20 * Divisor Hz Min Delta (S) Max Delta (S)
21 * 1 32000 .0005 2.048
22 * 2 16000 .001 4.096
23 * 4 8000 .002 8.192
24 * 8 4000 .004 16.384
25 * 16 2000 .008 32.768
26 * 32 1000 .016 65.536
27 * 64 500 .032 131.072
28 * 128 250 .064 262.144
29 * 256 125 .128 524.288
30 */
31
32#include <linux/kernel.h>
33#include <linux/interrupt.h>
34#include <linux/module.h>
35#include <asm/geode.h>
36
37#define F_AVAIL 0x01
38
39static struct mfgpt_timer_t {
40 int flags;
41 struct module *owner;
42} mfgpt_timers[MFGPT_MAX_TIMERS];
43
44/* Selected from the table above */
45
46#define MFGPT_DIVISOR 16
47#define MFGPT_SCALE 4 /* divisor = 2^(scale) */
48#define MFGPT_HZ (32000 / MFGPT_DIVISOR)
49#define MFGPT_PERIODIC (MFGPT_HZ / HZ)
50
51/* Allow for disabling of MFGPTs */
52static int disable;
53static int __init mfgpt_disable(char *s)
54{
55 disable = 1;
56 return 1;
57}
58__setup("nomfgpt", mfgpt_disable);
59
60/*
61 * Check whether any MFGPTs are available for the kernel to use. In most
62 * cases, firmware that uses AMD's VSA code will claim all timers during
63 * bootup; we certainly don't want to take them if they're already in use.
64 * In other cases (such as with VSAless OpenFirmware), the system firmware
65 * leaves timers available for us to use.
66 */
67int __init geode_mfgpt_detect(void)
68{
69 int count = 0, i;
70 u16 val;
71
72 if (disable) {
73 printk(KERN_INFO "geode-mfgpt: Skipping MFGPT setup\n");
74 return 0;
75 }
76
77 for (i = 0; i < MFGPT_MAX_TIMERS; i++) {
78 val = geode_mfgpt_read(i, MFGPT_REG_SETUP);
79 if (!(val & MFGPT_SETUP_SETUP)) {
80 mfgpt_timers[i].flags = F_AVAIL;
81 count++;
82 }
83 }
84
85 return count;
86}
87
88int geode_mfgpt_toggle_event(int timer, int cmp, int event, int enable)
89{
90 u32 msr, mask, value, dummy;
91 int shift = (cmp == MFGPT_CMP1) ? 0 : 8;
92
93 if (timer < 0 || timer >= MFGPT_MAX_TIMERS)
94 return -EIO;
95
96 /*
97 * The register maps for these are described in sections 6.17.1.x of
98 * the AMD Geode CS5536 Companion Device Data Book.
99 */
100 switch (event) {
101 case MFGPT_EVENT_RESET:
102 /*
103 * XXX: According to the docs, we cannot reset timers above
104 * 6; that is, resets for 7 and 8 will be ignored. Is this
105 * a problem? -dilinger
106 */
107 msr = MFGPT_NR_MSR;
108 mask = 1 << (timer + 24);
109 break;
110
111 case MFGPT_EVENT_NMI:
112 msr = MFGPT_NR_MSR;
113 mask = 1 << (timer + shift);
114 break;
115
116 case MFGPT_EVENT_IRQ:
117 msr = MFGPT_IRQ_MSR;
118 mask = 1 << (timer + shift);
119 break;
120
121 default:
122 return -EIO;
123 }
124
125 rdmsr(msr, value, dummy);
126
127 if (enable)
128 value |= mask;
129 else
130 value &= ~mask;
131
132 wrmsr(msr, value, dummy);
133 return 0;
134}
135
136int geode_mfgpt_set_irq(int timer, int cmp, int irq, int enable)
137{
138 u32 val, dummy;
139 int offset;
140
141 if (timer < 0 || timer >= MFGPT_MAX_TIMERS)
142 return -EIO;
143
144 if (geode_mfgpt_toggle_event(timer, cmp, MFGPT_EVENT_IRQ, enable))
145 return -EIO;
146
147 rdmsr(MSR_PIC_ZSEL_LOW, val, dummy);
148
149 offset = (timer % 4) * 4;
150
151 val &= ~((0xF << offset) | (0xF << (offset + 16)));
152
153 if (enable) {
154 val |= (irq & 0x0F) << (offset);
155 val |= (irq & 0x0F) << (offset + 16);
156 }
157
158 wrmsr(MSR_PIC_ZSEL_LOW, val, dummy);
159 return 0;
160}
161
162static int mfgpt_get(int timer, struct module *owner)
163{
164 mfgpt_timers[timer].flags &= ~F_AVAIL;
165 mfgpt_timers[timer].owner = owner;
166 printk(KERN_INFO "geode-mfgpt: Registered timer %d\n", timer);
167 return timer;
168}
169
170int geode_mfgpt_alloc_timer(int timer, int domain, struct module *owner)
171{
172 int i;
173
174 if (!geode_get_dev_base(GEODE_DEV_MFGPT))
175 return -ENODEV;
176 if (timer >= MFGPT_MAX_TIMERS)
177 return -EIO;
178
179 if (timer < 0) {
180 /* Try to find an available timer */
181 for (i = 0; i < MFGPT_MAX_TIMERS; i++) {
182 if (mfgpt_timers[i].flags & F_AVAIL)
183 return mfgpt_get(i, owner);
184
185 if (i == 5 && domain == MFGPT_DOMAIN_WORKING)
186 break;
187 }
188 } else {
189 /* If they requested a specific timer, try to honor that */
190 if (mfgpt_timers[timer].flags & F_AVAIL)
191 return mfgpt_get(timer, owner);
192 }
193
194 /* No timers available - too bad */
195 return -1;
196}
197
diff --git a/include/asm-x86/geode.h b/include/asm-x86/geode.h
index 6da4bbbea3dc..d94898831bac 100644
--- a/include/asm-x86/geode.h
+++ b/include/asm-x86/geode.h
@@ -156,4 +156,54 @@ static inline int is_geode(void)
156 return (is_geode_gx() || is_geode_lx()); 156 return (is_geode_gx() || is_geode_lx());
157} 157}
158 158
159/* MFGPTs */
160
161#define MFGPT_MAX_TIMERS 8
162#define MFGPT_TIMER_ANY -1
163
164#define MFGPT_DOMAIN_WORKING 1
165#define MFGPT_DOMAIN_STANDBY 2
166#define MFGPT_DOMAIN_ANY (MFGPT_DOMAIN_WORKING | MFGPT_DOMAIN_STANDBY)
167
168#define MFGPT_CMP1 0
169#define MFGPT_CMP2 1
170
171#define MFGPT_EVENT_IRQ 0
172#define MFGPT_EVENT_NMI 1
173#define MFGPT_EVENT_RESET 3
174
175#define MFGPT_REG_CMP1 0
176#define MFGPT_REG_CMP2 2
177#define MFGPT_REG_COUNTER 4
178#define MFGPT_REG_SETUP 6
179
180#define MFGPT_SETUP_CNTEN (1 << 15)
181#define MFGPT_SETUP_CMP2 (1 << 14)
182#define MFGPT_SETUP_CMP1 (1 << 13)
183#define MFGPT_SETUP_SETUP (1 << 12)
184#define MFGPT_SETUP_STOPEN (1 << 11)
185#define MFGPT_SETUP_EXTEN (1 << 10)
186#define MFGPT_SETUP_REVEN (1 << 5)
187#define MFGPT_SETUP_CLKSEL (1 << 4)
188
189static inline void geode_mfgpt_write(int timer, u16 reg, u16 value)
190{
191 u32 base = geode_get_dev_base(GEODE_DEV_MFGPT);
192 outw(value, base + reg + (timer * 8));
193}
194
195static inline u16 geode_mfgpt_read(int timer, u16 reg)
196{
197 u32 base = geode_get_dev_base(GEODE_DEV_MFGPT);
198 return inw(base + reg + (timer * 8));
199}
200
201extern int __init geode_mfgpt_detect(void);
202extern int geode_mfgpt_toggle_event(int timer, int cmp, int event, int enable);
203extern int geode_mfgpt_set_irq(int timer, int cmp, int irq, int enable);
204extern int geode_mfgpt_alloc_timer(int timer, int domain, struct module *owner);
205
206#define geode_mfgpt_setup_irq(t, c, i) geode_mfgpt_set_irq((t), (c), (i), 1)
207#define geode_mfgpt_release_irq(t, c, i) geode_mfgpt_set_irq((t), (c), (i), 0)
208
159#endif 209#endif