aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
authorAndres Salomon <dilinger@collabora.co.uk>2009-12-14 21:00:37 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2009-12-15 11:53:28 -0500
commit82dca611bb516ec5fb7d04077733d6a4b70f52d1 (patch)
tree67e085277bcc06e0aa7bf305e586ce856ef11078 /drivers/misc
parent3c55494670745e523f69b56edb66ca0b50a470c2 (diff)
cs5535: add a generic MFGPT driver
This is based on the old code on arch/x86/kernel/mfgpt_32.c, except it's not x86 specific, it's modular, and it makes use of a PCI BAR rather than a random MSR. Currently module unloading is not supported; it's uncertain whether or not it can be made work with the hardware. [akpm@linux-foundation.org: add X86 dependency] Signed-off-by: Andres Salomon <dilinger@collabora.co.uk> Cc: Jordan Crouse <jordan@cosmicpenguin.net> Cc: Ingo Molnar <mingo@elte.hu> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: john stultz <johnstul@us.ibm.com> Cc: Chris Ball <cjb@laptop.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/Kconfig24
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/cs5535-mfgpt.c370
3 files changed, 395 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 724d1188a322..d6d6d846f0d6 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -187,6 +187,30 @@ config SGI_XP
187 this feature will allow for direct communication between SSIs 187 this feature will allow for direct communication between SSIs
188 based on a network adapter and DMA messaging. 188 based on a network adapter and DMA messaging.
189 189
190config CS5535_MFGPT
191 tristate "CS5535/CS5536 Geode Multi-Function General Purpose Timer (MFGPT) support"
192 depends on PCI && !MGEODE_LX
193 depends on X86
194 default n
195 help
196 This driver provides access to MFGPT functionality for other
197 drivers that need timers. MFGPTs are available in the CS5535 and
198 CS5536 companion chips that are found in AMD Geode and several
199 other platforms. They have a better resolution and max interval
200 than the generic PIT, and are suitable for use as high-res timers.
201 You probably don't want to enable this manually; other drivers that
202 make use of it should enable it.
203
204config CS5535_MFGPT_DEFAULT_IRQ
205 int
206 default 7
207 help
208 MFGPTs on the CS5535 require an interrupt. The selected IRQ
209 can be overridden as a module option as well as by driver that
210 use the cs5535_mfgpt_ API; however, different architectures might
211 want to use a different IRQ by default. This is here for
212 architectures to set as necessary.
213
190config HP_ILO 214config HP_ILO
191 tristate "Channel interface driver for HP iLO/iLO2 processor" 215 tristate "Channel interface driver for HP iLO/iLO2 processor"
192 depends on PCI 216 depends on PCI
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index e76b77977442..049ff2482f30 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
18obj-$(CONFIG_KGDB_TESTS) += kgdbts.o 18obj-$(CONFIG_KGDB_TESTS) += kgdbts.o
19obj-$(CONFIG_SGI_XP) += sgi-xp/ 19obj-$(CONFIG_SGI_XP) += sgi-xp/
20obj-$(CONFIG_SGI_GRU) += sgi-gru/ 20obj-$(CONFIG_SGI_GRU) += sgi-gru/
21obj-$(CONFIG_CS5535_MFGPT) += cs5535-mfgpt.o
21obj-$(CONFIG_HP_ILO) += hpilo.o 22obj-$(CONFIG_HP_ILO) += hpilo.o
22obj-$(CONFIG_ISL29003) += isl29003.o 23obj-$(CONFIG_ISL29003) += isl29003.o
23obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o 24obj-$(CONFIG_EP93XX_PWM) += ep93xx_pwm.o
diff --git a/drivers/misc/cs5535-mfgpt.c b/drivers/misc/cs5535-mfgpt.c
new file mode 100644
index 000000000000..8110460558ff
--- /dev/null
+++ b/drivers/misc/cs5535-mfgpt.c
@@ -0,0 +1,370 @@
1/*
2 * Driver for the CS5535/CS5536 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 * Copyright (C) 2009 Andres Salomon <dilinger@collabora.co.uk>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of version 2 of the GNU General Public License
10 * as published by the Free Software Foundation.
11 *
12 * The MFGPTs are documented in AMD Geode CS5536 Companion Device Data Book.
13 */
14
15#include <linux/kernel.h>
16#include <linux/spinlock.h>
17#include <linux/interrupt.h>
18#include <linux/module.h>
19#include <linux/pci.h>
20#include <linux/cs5535.h>
21
22#define DRV_NAME "cs5535-mfgpt"
23#define MFGPT_BAR 2
24
25static int mfgpt_reset_timers;
26module_param_named(mfgptfix, mfgpt_reset_timers, int, 0644);
27MODULE_PARM_DESC(mfgptfix, "Reset the MFGPT timers during init; "
28 "required by some broken BIOSes (ie, TinyBIOS < 0.99).");
29
30struct cs5535_mfgpt_timer {
31 struct cs5535_mfgpt_chip *chip;
32 int nr;
33};
34
35static struct cs5535_mfgpt_chip {
36 DECLARE_BITMAP(avail, MFGPT_MAX_TIMERS);
37 resource_size_t base;
38
39 struct pci_dev *pdev;
40 spinlock_t lock;
41 int initialized;
42} cs5535_mfgpt_chip;
43
44int cs5535_mfgpt_toggle_event(struct cs5535_mfgpt_timer *timer, int cmp,
45 int event, int enable)
46{
47 uint32_t msr, mask, value, dummy;
48 int shift = (cmp == MFGPT_CMP1) ? 0 : 8;
49
50 if (!timer) {
51 WARN_ON(1);
52 return -EIO;
53 }
54
55 /*
56 * The register maps for these are described in sections 6.17.1.x of
57 * the AMD Geode CS5536 Companion Device Data Book.
58 */
59 switch (event) {
60 case MFGPT_EVENT_RESET:
61 /*
62 * XXX: According to the docs, we cannot reset timers above
63 * 6; that is, resets for 7 and 8 will be ignored. Is this
64 * a problem? -dilinger
65 */
66 msr = MSR_MFGPT_NR;
67 mask = 1 << (timer->nr + 24);
68 break;
69
70 case MFGPT_EVENT_NMI:
71 msr = MSR_MFGPT_NR;
72 mask = 1 << (timer->nr + shift);
73 break;
74
75 case MFGPT_EVENT_IRQ:
76 msr = MSR_MFGPT_IRQ;
77 mask = 1 << (timer->nr + shift);
78 break;
79
80 default:
81 return -EIO;
82 }
83
84 rdmsr(msr, value, dummy);
85
86 if (enable)
87 value |= mask;
88 else
89 value &= ~mask;
90
91 wrmsr(msr, value, dummy);
92 return 0;
93}
94EXPORT_SYMBOL_GPL(cs5535_mfgpt_toggle_event);
95
96int cs5535_mfgpt_set_irq(struct cs5535_mfgpt_timer *timer, int cmp, int *irq,
97 int enable)
98{
99 uint32_t zsel, lpc, dummy;
100 int shift;
101
102 if (!timer) {
103 WARN_ON(1);
104 return -EIO;
105 }
106
107 /*
108 * Unfortunately, MFGPTs come in pairs sharing their IRQ lines. If VSA
109 * is using the same CMP of the timer's Siamese twin, the IRQ is set to
110 * 2, and we mustn't use nor change it.
111 * XXX: Likewise, 2 Linux drivers might clash if the 2nd overwrites the
112 * IRQ of the 1st. This can only happen if forcing an IRQ, calling this
113 * with *irq==0 is safe. Currently there _are_ no 2 drivers.
114 */
115 rdmsr(MSR_PIC_ZSEL_LOW, zsel, dummy);
116 shift = ((cmp == MFGPT_CMP1 ? 0 : 4) + timer->nr % 4) * 4;
117 if (((zsel >> shift) & 0xF) == 2)
118 return -EIO;
119
120 /* Choose IRQ: if none supplied, keep IRQ already set or use default */
121 if (!*irq)
122 *irq = (zsel >> shift) & 0xF;
123 if (!*irq)
124 *irq = CONFIG_CS5535_MFGPT_DEFAULT_IRQ;
125
126 /* Can't use IRQ if it's 0 (=disabled), 2, or routed to LPC */
127 if (*irq < 1 || *irq == 2 || *irq > 15)
128 return -EIO;
129 rdmsr(MSR_PIC_IRQM_LPC, lpc, dummy);
130 if (lpc & (1 << *irq))
131 return -EIO;
132
133 /* All chosen and checked - go for it */
134 if (cs5535_mfgpt_toggle_event(timer, cmp, MFGPT_EVENT_IRQ, enable))
135 return -EIO;
136 if (enable) {
137 zsel = (zsel & ~(0xF << shift)) | (*irq << shift);
138 wrmsr(MSR_PIC_ZSEL_LOW, zsel, dummy);
139 }
140
141 return 0;
142}
143EXPORT_SYMBOL_GPL(cs5535_mfgpt_set_irq);
144
145struct cs5535_mfgpt_timer *cs5535_mfgpt_alloc_timer(int timer_nr, int domain)
146{
147 struct cs5535_mfgpt_chip *mfgpt = &cs5535_mfgpt_chip;
148 struct cs5535_mfgpt_timer *timer = NULL;
149 unsigned long flags;
150 int max;
151
152 if (!mfgpt->initialized)
153 goto done;
154
155 /* only allocate timers from the working domain if requested */
156 if (domain == MFGPT_DOMAIN_WORKING)
157 max = 6;
158 else
159 max = MFGPT_MAX_TIMERS;
160
161 if (timer_nr >= max) {
162 /* programmer error. silly programmers! */
163 WARN_ON(1);
164 goto done;
165 }
166
167 spin_lock_irqsave(&mfgpt->lock, flags);
168 if (timer_nr < 0) {
169 unsigned long t;
170
171 /* try to find any available timer */
172 t = find_first_bit(mfgpt->avail, max);
173 /* set timer_nr to -1 if no timers available */
174 timer_nr = t < max ? (int) t : -1;
175 } else {
176 /* check if the requested timer's available */
177 if (test_bit(timer_nr, mfgpt->avail))
178 timer_nr = -1;
179 }
180
181 if (timer_nr >= 0)
182 /* if timer_nr is not -1, it's an available timer */
183 __clear_bit(timer_nr, mfgpt->avail);
184 spin_unlock_irqrestore(&mfgpt->lock, flags);
185
186 if (timer_nr < 0)
187 goto done;
188
189 timer = kmalloc(sizeof(*timer), GFP_KERNEL);
190 if (!timer) {
191 /* aw hell */
192 spin_lock_irqsave(&mfgpt->lock, flags);
193 __set_bit(timer_nr, mfgpt->avail);
194 spin_unlock_irqrestore(&mfgpt->lock, flags);
195 goto done;
196 }
197 timer->chip = mfgpt;
198 timer->nr = timer_nr;
199 dev_info(&mfgpt->pdev->dev, "registered timer %d\n", timer_nr);
200
201done:
202 return timer;
203}
204EXPORT_SYMBOL_GPL(cs5535_mfgpt_alloc_timer);
205
206/*
207 * XXX: This frees the timer memory, but never resets the actual hardware
208 * timer. The old geode_mfgpt code did this; it would be good to figure
209 * out a way to actually release the hardware timer. See comments below.
210 */
211void cs5535_mfgpt_free_timer(struct cs5535_mfgpt_timer *timer)
212{
213 kfree(timer);
214}
215EXPORT_SYMBOL_GPL(cs5535_mfgpt_free_timer);
216
217uint16_t cs5535_mfgpt_read(struct cs5535_mfgpt_timer *timer, uint16_t reg)
218{
219 return inw(timer->chip->base + reg + (timer->nr * 8));
220}
221EXPORT_SYMBOL_GPL(cs5535_mfgpt_read);
222
223void cs5535_mfgpt_write(struct cs5535_mfgpt_timer *timer, uint16_t reg,
224 uint16_t value)
225{
226 outw(value, timer->chip->base + reg + (timer->nr * 8));
227}
228EXPORT_SYMBOL_GPL(cs5535_mfgpt_write);
229
230/*
231 * This is a sledgehammer that resets all MFGPT timers. This is required by
232 * some broken BIOSes which leave the system in an unstable state
233 * (TinyBIOS 0.98, for example; fixed in 0.99). It's uncertain as to
234 * whether or not this secret MSR can be used to release individual timers.
235 * Jordan tells me that he and Mitch once played w/ it, but it's unclear
236 * what the results of that were (and they experienced some instability).
237 */
238static void __init reset_all_timers(void)
239{
240 uint32_t val, dummy;
241
242 /* The following undocumented bit resets the MFGPT timers */
243 val = 0xFF; dummy = 0;
244 wrmsr(MSR_MFGPT_SETUP, val, dummy);
245}
246
247/*
248 * Check whether any MFGPTs are available for the kernel to use. In most
249 * cases, firmware that uses AMD's VSA code will claim all timers during
250 * bootup; we certainly don't want to take them if they're already in use.
251 * In other cases (such as with VSAless OpenFirmware), the system firmware
252 * leaves timers available for us to use.
253 */
254static int __init scan_timers(struct cs5535_mfgpt_chip *mfgpt)
255{
256 struct cs5535_mfgpt_timer timer = { .chip = mfgpt };
257 unsigned long flags;
258 int timers = 0;
259 uint16_t val;
260 int i;
261
262 /* bios workaround */
263 if (mfgpt_reset_timers)
264 reset_all_timers();
265
266 /* just to be safe, protect this section w/ lock */
267 spin_lock_irqsave(&mfgpt->lock, flags);
268 for (i = 0; i < MFGPT_MAX_TIMERS; i++) {
269 timer.nr = i;
270 val = cs5535_mfgpt_read(&timer, MFGPT_REG_SETUP);
271 if (!(val & MFGPT_SETUP_SETUP)) {
272 __set_bit(i, mfgpt->avail);
273 timers++;
274 }
275 }
276 spin_unlock_irqrestore(&mfgpt->lock, flags);
277
278 return timers;
279}
280
281static int __init cs5535_mfgpt_probe(struct pci_dev *pdev,
282 const struct pci_device_id *pci_id)
283{
284 int err, t;
285
286 /* There are two ways to get the MFGPT base address; one is by
287 * fetching it from MSR_LBAR_MFGPT, the other is by reading the
288 * PCI BAR info. The latter method is easier (especially across
289 * different architectures), so we'll stick with that for now. If
290 * it turns out to be unreliable in the face of crappy BIOSes, we
291 * can always go back to using MSRs.. */
292
293 err = pci_enable_device_io(pdev);
294 if (err) {
295 dev_err(&pdev->dev, "can't enable device IO\n");
296 goto done;
297 }
298
299 err = pci_request_region(pdev, MFGPT_BAR, DRV_NAME);
300 if (err) {
301 dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", MFGPT_BAR);
302 goto done;
303 }
304
305 /* set up the driver-specific struct */
306 cs5535_mfgpt_chip.base = pci_resource_start(pdev, MFGPT_BAR);
307 cs5535_mfgpt_chip.pdev = pdev;
308 spin_lock_init(&cs5535_mfgpt_chip.lock);
309
310 dev_info(&pdev->dev, "allocated PCI BAR #%d: base 0x%llx\n", MFGPT_BAR,
311 (unsigned long long) cs5535_mfgpt_chip.base);
312
313 /* detect the available timers */
314 t = scan_timers(&cs5535_mfgpt_chip);
315 dev_info(&pdev->dev, DRV_NAME ": %d MFGPT timers available\n", t);
316 cs5535_mfgpt_chip.initialized = 1;
317 return 0;
318
319done:
320 return err;
321}
322
323static struct pci_device_id cs5535_mfgpt_pci_tbl[] = {
324 { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },
325 { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
326 { 0, },
327};
328MODULE_DEVICE_TABLE(pci, cs5535_mfgpt_pci_tbl);
329
330/*
331 * Just like with the cs5535-gpio driver, we can't use the standard PCI driver
332 * registration stuff. It only allows only one driver to bind to each PCI
333 * device, and we want the GPIO and MFGPT drivers to be able to share a PCI
334 * device. Instead, we manually scan for the PCI device, request a single
335 * region, and keep track of the devices that we're using.
336 */
337
338static int __init cs5535_mfgpt_scan_pci(void)
339{
340 struct pci_dev *pdev;
341 int err = -ENODEV;
342 int i;
343
344 for (i = 0; i < ARRAY_SIZE(cs5535_mfgpt_pci_tbl); i++) {
345 pdev = pci_get_device(cs5535_mfgpt_pci_tbl[i].vendor,
346 cs5535_mfgpt_pci_tbl[i].device, NULL);
347 if (pdev) {
348 err = cs5535_mfgpt_probe(pdev,
349 &cs5535_mfgpt_pci_tbl[i]);
350 if (err)
351 pci_dev_put(pdev);
352
353 /* we only support a single CS5535/6 southbridge */
354 break;
355 }
356 }
357
358 return err;
359}
360
361static int __init cs5535_mfgpt_init(void)
362{
363 return cs5535_mfgpt_scan_pci();
364}
365
366module_init(cs5535_mfgpt_init);
367
368MODULE_AUTHOR("Andres Salomon <dilinger@collabora.co.uk>");
369MODULE_DESCRIPTION("CS5535/CS5536 MFGPT timer driver");
370MODULE_LICENSE("GPL");