2 * Copyright (c) 2007 Marc Balmer <mbalmer@openbsd.org>
3 * Copyright (c) 2007 Michael Shalayeff
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
15 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
16 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 * Copyright (c) 2009 The DragonFly Project. All rights reserved.
21 * This code is derived from software contributed to The DragonFly Project
22 * by Alex Hornung <ahornung@gmail.com>
24 * Redistribution and use in source and binary forms, with or without
25 * modification, are permitted provided that the following conditions
28 * 1. Redistributions of source code must retain the above copyright
29 * notice, this list of conditions and the following disclaimer.
30 * 2. Redistributions in binary form must reproduce the above copyright
31 * notice, this list of conditions and the following disclaimer in
32 * the documentation and/or other materials provided with the
34 * 3. Neither the name of The DragonFly Project nor the names of its
35 * contributors may be used to endorse or promote products derived
36 * from this software without specific, prior written permission.
38 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
39 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
40 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
41 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
42 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
43 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
44 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
46 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
47 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
48 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 #include <sys/cdefs.h>
57 #include <sys/param.h>
58 #include <sys/systm.h>
59 #include <sys/systimer.h>
61 #include <sys/kernel.h>
62 #include <sys/module.h>
63 #include <bus/pci/pcireg.h>
64 #include <bus/pci/pcivar.h>
65 #include <bus/pci/pcidevs.h>
66 #include <bus/pci/pcib_private.h>
67 #include <dev/misc/gpio/gpio.h>
68 #include <machine/pc/bios.h>
71 #define AMD5536_TIMER_FREQ 3579545
73 #define AMD5536_REV 0x51400017
74 #define AMD5536_REV_MASK 0xff
75 #define AMD5536_TMC 0x51400050
77 /* Multi-Functional General Purpose Timer */
78 #define MSR_LBAR_MFGPT 0x5140000d
79 #define AMD5536_MFGPT0_CMP1 0x00000000
80 #define AMD5536_MFGPT0_CMP2 0x00000002
81 #define AMD5536_MFGPT0_CNT 0x00000004
82 #define AMD5536_MFGPT0_SETUP 0x00000006
83 #define AMD5536_MFGPT_DIV_MASK 0x000f /* div = 1 << mask */
84 #define AMD5536_MFGPT_CLKSEL 0x0010
85 #define AMD5536_MFGPT_REV_EN 0x0020
86 #define AMD5536_MFGPT_CMP1DIS 0x0000
87 #define AMD5536_MFGPT_CMP1EQ 0x0040
88 #define AMD5536_MFGPT_CMP1GE 0x0080
89 #define AMD5536_MFGPT_CMP1EV 0x00c0
90 #define AMD5536_MFGPT_CMP2DIS 0x0000
91 #define AMD5536_MFGPT_CMP2EQ 0x0100
92 #define AMD5536_MFGPT_CMP2GE 0x0200
93 #define AMD5536_MFGPT_CMP2EV 0x0300
94 #define AMD5536_MFGPT_STOP_EN 0x0800
95 #define AMD5536_MFGPT_SET 0x1000
96 #define AMD5536_MFGPT_CMP1 0x2000
97 #define AMD5536_MFGPT_CMP2 0x4000
98 #define AMD5536_MFGPT_CNT_EN 0x8000
99 #define AMD5536_MFGPT_IRQ 0x51400028
100 #define AMD5536_MFGPT0_C1_IRQM 0x00000001
101 #define AMD5536_MFGPT1_C1_IRQM 0x00000002
102 #define AMD5536_MFGPT2_C1_IRQM 0x00000004
103 #define AMD5536_MFGPT3_C1_IRQM 0x00000008
104 #define AMD5536_MFGPT4_C1_IRQM 0x00000010
105 #define AMD5536_MFGPT5_C1_IRQM 0x00000020
106 #define AMD5536_MFGPT6_C1_IRQM 0x00000040
107 #define AMD5536_MFGPT7_C1_IRQM 0x00000080
108 #define AMD5536_MFGPT0_C2_IRQM 0x00000100
109 #define AMD5536_MFGPT1_C2_IRQM 0x00000200
110 #define AMD5536_MFGPT2_C2_IRQM 0x00000400
111 #define AMD5536_MFGPT3_C2_IRQM 0x00000800
112 #define AMD5536_MFGPT4_C2_IRQM 0x00001000
113 #define AMD5536_MFGPT5_C2_IRQM 0x00002000
114 #define AMD5536_MFGPT6_C2_IRQM 0x00004000
115 #define AMD5536_MFGPT7_C2_IRQM 0x00008000
116 #define AMD5536_MFGPT_NR 0x51400029
117 #define AMD5536_MFGPT0_C1_NMIM 0x00000001
118 #define AMD5536_MFGPT1_C1_NMIM 0x00000002
119 #define AMD5536_MFGPT2_C1_NMIM 0x00000004
120 #define AMD5536_MFGPT3_C1_NMIM 0x00000008
121 #define AMD5536_MFGPT4_C1_NMIM 0x00000010
122 #define AMD5536_MFGPT5_C1_NMIM 0x00000020
123 #define AMD5536_MFGPT6_C1_NMIM 0x00000040
124 #define AMD5536_MFGPT7_C1_NMIM 0x00000080
125 #define AMD5536_MFGPT0_C2_NMIM 0x00000100
126 #define AMD5536_MFGPT1_C2_NMIM 0x00000200
127 #define AMD5536_MFGPT2_C2_NMIM 0x00000400
128 #define AMD5536_MFGPT3_C2_NMIM 0x00000800
129 #define AMD5536_MFGPT4_C2_NMIM 0x00001000
130 #define AMD5536_MFGPT5_C2_NMIM 0x00002000
131 #define AMD5536_MFGPT6_C2_NMIM 0x00004000
132 #define AMD5536_MFGPT7_C2_NMIM 0x00008000
133 #define AMD5536_NMI_LEG 0x00010000
134 #define AMD5536_MFGPT0_C2_RSTEN 0x01000000
135 #define AMD5536_MFGPT1_C2_RSTEN 0x02000000
136 #define AMD5536_MFGPT2_C2_RSTEN 0x04000000
137 #define AMD5536_MFGPT3_C2_RSTEN 0x08000000
138 #define AMD5536_MFGPT4_C2_RSTEN 0x10000000
139 #define AMD5536_MFGPT5_C2_RSTEN 0x20000000
140 #define AMD5536_MFGPT_SETUP 0x5140002b
143 #define MSR_LBAR_GPIO 0x5140000c
144 #define AMD5536_GPIO_NPINS 32
145 #define AMD5536_GPIOH_OFFSET 0x80 /* high bank register offset */
146 #define AMD5536_GPIO_OUT_VAL 0x00 /* output value */
147 #define AMD5536_GPIO_OUT_EN 0x04 /* output enable */
148 #define AMD5536_GPIO_OD_EN 0x08 /* open-drain enable */
149 #define AMD5536_GPIO_OUT_INVRT_EN 0x0c /* invert output */
150 #define AMD5536_GPIO_PU_EN 0x18 /* pull-up enable */
151 #define AMD5536_GPIO_PD_EN 0x1c /* pull-down enable */
152 #define AMD5536_GPIO_IN_EN 0x20 /* input enable */
153 #define AMD5536_GPIO_IN_INVRT_EN 0x24 /* invert input */
154 #define AMD5536_GPIO_READ_BACK 0x30 /* read back value */
157 struct cs5536_softc {
158 bus_space_tag_t sc_iot;
159 bus_space_handle_t sc_ioh;
163 bus_space_tag_t sc_gpio_iot;
164 bus_space_handle_t sc_gpio_ioh;
165 struct gpio sc_gpio_gc;
166 gpio_pin_t sc_gpio_pins[AMD5536_GPIO_NPINS];
170 static struct cs5536_softc cs5536_sc;
173 int cs5536_gpio_pin_read(void *arg, int pin);
174 void cs5536_gpio_pin_write(void *arg, int pin, int value);
175 void cs5536_gpio_pin_ctl(void *arg, int pin, int flags);
177 static sysclock_t cs5536_get_timecount(void);
179 static struct bios_oem bios_soekris_55 = {
180 { 0xf0000, 0xf1000 },
182 { "Soekris", 0, 8 }, /* Soekris Engineering. */
183 { "net5", 0, 8 }, /* net5xxx */
184 { "comBIOS", 0, 54 }, /* comBIOS ver. 1.26a 20040819 ... */
189 static struct bios_oem bios_pcengines_55 = {
190 { 0xf9000, 0xfa000 },
192 { "PC Engines ALIX", 0, 28 }, /* PC Engines ALIX */
193 { "tinyBIOS", 0, 28 }, /* tinyBIOS V1.4a (C)1997-2005 */
200 cs5536_gpio_pin_read(void *arg, int pin)
206 port = rdmsr(MSR_LBAR_GPIO);
208 reg = AMD5536_GPIO_IN_EN;
211 off = AMD5536_GPIOH_OFFSET;
214 data = bus_space_read_4(cs5536_sc.sc_gpio_iot, cs5536_sc.sc_gpio_ioh, reg);
216 if (data & (1 << pin))
217 reg = AMD5536_GPIO_READ_BACK + off;
219 reg = AMD5536_GPIO_OUT_VAL + off;
221 data = bus_space_read_4(cs5536_sc.sc_gpio_iot, cs5536_sc.sc_gpio_ioh, reg);
223 return data & 1 << pin ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
227 cs5536_gpio_pin_write(void *arg, int pin, int value)
233 port = rdmsr(MSR_LBAR_GPIO);
235 reg = AMD5536_GPIO_OUT_VAL;
238 reg += AMD5536_GPIOH_OFFSET;
244 data = 1 << (pin + 16);
246 bus_space_write_4(cs5536_sc.sc_gpio_iot, cs5536_sc.sc_gpio_ioh, reg, data);
247 //write_data_4(port, reg, data);
248 //outl(port + reg, data);
252 cs5536_gpio_pin_ctl(void *arg, int pin, int flags)
254 int n, reg[7], val[7], nreg = 0, off = 0;
258 off = AMD5536_GPIOH_OFFSET;
261 reg[nreg] = AMD5536_GPIO_IN_EN + off;
262 if (flags & GPIO_PIN_INPUT)
263 val[nreg++] = 1 << pin;
265 val[nreg++] = 1 << (pin + 16);
267 reg[nreg] = AMD5536_GPIO_OUT_EN + off;
268 if (flags & GPIO_PIN_OUTPUT)
269 val[nreg++] = 1 << pin;
271 val[nreg++] = 1 << (pin + 16);
273 reg[nreg] = AMD5536_GPIO_OD_EN + off;
274 if (flags & GPIO_PIN_OPENDRAIN)
275 val[nreg++] = 1 << pin;
277 val[nreg++] = 1 << (pin + 16);
279 reg[nreg] = AMD5536_GPIO_PU_EN + off;
280 if (flags & GPIO_PIN_PULLUP)
281 val[nreg++] = 1 << pin;
283 val[nreg++] = 1 << (pin + 16);
285 reg[nreg] = AMD5536_GPIO_PD_EN + off;
286 if (flags & GPIO_PIN_PULLDOWN)
287 val[nreg++] = 1 << pin;
289 val[nreg++] = 1 << (pin + 16);
291 reg[nreg] = AMD5536_GPIO_IN_INVRT_EN + off;
292 if (flags & GPIO_PIN_INVIN)
293 val[nreg++] = 1 << pin;
295 val[nreg++] = 1 << (pin + 16);
297 reg[nreg] = AMD5536_GPIO_OUT_INVRT_EN + off;
298 if (flags & GPIO_PIN_INVOUT)
299 val[nreg++] = 1 << pin;
301 val[nreg++] = 1 << (pin + 16);
304 for (n = 0; n < nreg; n++)
305 bus_space_write_4(cs5536_sc.sc_gpio_iot, cs5536_sc.sc_gpio_ioh, reg[n],
311 cs5536_cputimer_construct(struct cputimer *timer, sysclock_t oldclock)
314 timer->base = oldclock - cs5536_get_timecount();
317 static struct cputimer cs5536_timer = {
318 SLIST_ENTRY_INITIALIZER,
322 cs5536_get_timecount,
323 cputimer_default_fromhz,
324 cputimer_default_fromus,
325 cs5536_cputimer_construct,
326 cputimer_default_destruct,
332 cs5536_get_timecount(void)
334 return (cs5536_timer.base + rdmsr(AMD5536_TMC));
337 #ifdef WATCHDOG_ENABLE
339 cs5536_watchdog(void *unused, int period)
344 bus_space_write_2(cs5536_sc.sc_iot, cs5536_sc.sc_ioh, AMD5536_MFGPT0_SETUP,
345 AMD5536_MFGPT_CNT_EN | AMD5536_MFGPT_CMP2);
347 bus_space_write_2(cs5536_sc.sc_iot, cs5536_sc.sc_ioh, AMD5536_MFGPT0_CNT, 0);
348 /* set comparator 2 */
349 bus_space_write_2(cs5536_sc.sc_iot, cs5536_sc.sc_ioh, AMD5536_MFGPT0_CMP2, period);
352 wrmsr(AMD5536_MFGPT_NR,
353 rdmsr(AMD5536_MFGPT_NR) | AMD5536_MFGPT0_C2_RSTEN);
355 wrmsr(AMD5536_MFGPT_NR,
356 rdmsr(AMD5536_MFGPT_NR) & ~AMD5536_MFGPT0_C2_RSTEN);
362 static struct watchdog cs5536_wdog = {
363 .name = "AMD CS5536",
364 .wdog_fn = cs5536_watchdog,
366 .period_max = 0xffff,
368 #endif /* WATCHDOG_ENABLE */
371 cs5536_probe(device_t self)
373 static int probed = 0;
378 if (pci_get_vendor(self) == PCI_VENDOR_AMD &&
379 (pci_get_device(self) == PCI_PRODUCT_AMD_CS5536_PCIB ||
380 /* XXX: OpenBSD doesn't attach to this one, but free does, though no counter */
381 pci_get_device(self) == PCI_PRODUCT_AMD_GEODE_LX_PCHB)) {
382 /* device_set_desc(self, ...) */
391 cs5536_attach(device_t self)
393 #define BIOS_OEM_MAXLEN 80
394 static u_char bios_oem[BIOS_OEM_MAXLEN] = "\0";
395 static int attached = 0;
397 #if 0 /* Watchdog stuff */
398 EVENTHANDLER_REGISTER(watchdog_list, cs5536_watchdog, NULL, 0);
404 kprintf("AMD CS5536: rev %d, 32-bit %uHz timer\n",
405 (int)rdmsr(AMD5536_REV) & AMD5536_REV_MASK,
409 cputimer_register(&cs5536_timer);
410 cputimer_select(&cs5536_timer, 0);
412 /* bus_space_map(sc->sc_iot, wa & 0xffff, 64, 0, &sc->sc_ioh)) */
413 cs5536_sc.sc_iot = I386_BUS_SPACE_IO;
414 cs5536_sc.sc_ioh = rdmsr(MSR_LBAR_MFGPT);
416 #ifdef WATCHDOG_ENABLE
417 /* enable watchdog and configure */
418 bus_space_write_2(cs5536_sc.sc_iot, cs5536_sc.sc_ioh, AMD5536_MFGPT0_SETUP,
419 AMD5536_MFGPT_CNT_EN | AMD5536_MFGPT_CMP2EV |
420 AMD5536_MFGPT_CMP2 | AMD5536_MFGPT_DIV_MASK);
421 wdog_register(&cs5536_wdog);
422 #endif /* WATCHDOG_ENABLE */
425 /* bus_space_map(sc->sc_gpio_iot, ga & 0xffff, 0xff, 0,... */
426 cs5536_sc.sc_gpio_iot = I386_BUS_SPACE_IO;
427 cs5536_sc.sc_gpio_ioh = rdmsr(MSR_LBAR_GPIO);
428 for (i = 0; i < AMD5536_GPIO_NPINS; i++) {
429 cs5536_sc.sc_gpio_pins[i].pin_num = i;
430 cs5536_sc.sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT |
431 GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN |
432 GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN |
433 GPIO_PIN_INVIN | GPIO_PIN_INVOUT;
435 /* read initial state */
436 cs5536_sc.sc_gpio_pins[i].pin_state =
437 cs5536_gpio_pin_read(&cs5536_sc, i);
439 cs5536_sc.sc_gpio_gc.driver_name = "cs5536";
440 cs5536_sc.sc_gpio_gc.arg = &cs5536_sc;
441 cs5536_sc.sc_gpio_gc.pin_read = cs5536_gpio_pin_read;
442 cs5536_sc.sc_gpio_gc.pin_write = cs5536_gpio_pin_write;
443 cs5536_sc.sc_gpio_gc.pin_ctl = cs5536_gpio_pin_ctl;
444 cs5536_sc.sc_gpio_gc.pins = cs5536_sc.sc_gpio_pins;
445 cs5536_sc.sc_gpio_gc.npins = AMD5536_GPIO_NPINS;
446 gpio_register(&cs5536_sc.sc_gpio_gc);
448 if (bios_oem_strings(&bios_soekris_55,
449 bios_oem, sizeof bios_oem) > 0 ) {
451 /* Attach led to pin 6 */
452 gpio_consumer_attach("led", "error", &cs5536_sc.sc_gpio_gc,
455 } else if (bios_oem_strings(&bios_pcengines_55,
456 bios_oem, sizeof bios_oem) > 0 ) {
459 gpio_consumer_attach("led", "led1", &cs5536_sc.sc_gpio_gc,
461 gpio_consumer_attach("led", "led2", &cs5536_sc.sc_gpio_gc,
463 gpio_consumer_attach("led", "led3", &cs5536_sc.sc_gpio_gc,
468 * Turn on first LED so we don't make
469 * people think their box just died.
471 cs5536_led_func(&led1b, 1);
475 kprintf("Geode LX: %s\n", bios_oem);
477 kprintf("Geode LX: Unknown OEM bios\n");
480 kprintf("MFGPT bar: %jx\n", rdmsr(MSR_LBAR_MFGPT));
485 static device_method_t cs5536_methods[] = {
486 /* Device interface */
487 DEVMETHOD(device_probe, cs5536_probe),
488 DEVMETHOD(device_attach, cs5536_attach),
489 DEVMETHOD(device_suspend, bus_generic_suspend),
490 DEVMETHOD(device_resume, bus_generic_resume),
491 DEVMETHOD(device_shutdown, bus_generic_shutdown),
495 static driver_t cs5536_driver = {
501 static devclass_t cs5536_devclass;
503 DRIVER_MODULE(cs5536, pci, cs5536_driver, cs5536_devclass, 0, 0);