b2765c389064ed7c6b8f974f40cc9662bca44ad4
[dragonfly.git] / sys / platform / pc32 / i386 / cs5536.c
1 /*
2  * Copyright (c) 2007 Marc Balmer <mbalmer@openbsd.org>
3  * Copyright (c) 2007 Michael Shalayeff
4  * All rights reserved.
5  *
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.
9  *
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.
17  *
18  *
19  * Copyright (c) 2009 The DragonFly Project.  All rights reserved.
20  *
21  * This code is derived from software contributed to The DragonFly Project
22  * by Alex Hornung <ahornung@gmail.com>
23  *
24  * Redistribution and use in source and binary forms, with or without
25  * modification, are permitted provided that the following conditions
26  * are met:
27  *
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
33  *    distribution.
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.
37  *
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
49  * SUCH DAMAGE.
50  *
51  */
52
53 #include "opt_cpu.h"
54 #include "use_gpio.h"
55 #include <sys/cdefs.h>
56
57 #include <sys/param.h>
58 #include <sys/systm.h>
59 #include <sys/systimer.h>
60 #include <sys/bus.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>
69 #include <sys/wdog.h>
70
71 #define AMD5536_TIMER_FREQ      3579545
72
73 #define AMD5536_REV             0x51400017
74 #define AMD5536_REV_MASK        0xff
75 #define AMD5536_TMC             0x51400050
76
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
141
142 /* GPIO */
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 */
155
156
157 struct cs5536_softc {
158         bus_space_tag_t         sc_iot;
159         bus_space_handle_t      sc_ioh;
160
161 #if NGPIO > 0
162         /* GPIO interface */
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];
167 #endif
168 };
169
170 static struct cs5536_softc cs5536_sc;
171
172 #if NGPIO > 0
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);
176 #endif
177 static sysclock_t cs5536_get_timecount(void);
178
179 static struct bios_oem bios_soekris_55 = {
180     { 0xf0000, 0xf1000 },
181     {
182         { "Soekris", 0, 8 },            /* Soekris Engineering. */
183         { "net5", 0, 8 },               /* net5xxx */
184         { "comBIOS", 0, 54 },           /* comBIOS ver. 1.26a  20040819 ... */
185         { NULL, 0, 0 },
186     }
187 };
188
189 static struct bios_oem bios_pcengines_55 = {
190     { 0xf9000, 0xfa000 },
191     {
192         { "PC Engines ALIX", 0, 28 },   /* PC Engines ALIX */
193         { "tinyBIOS", 0, 28 },          /* tinyBIOS V1.4a (C)1997-2005 */
194         { NULL, 0, 0 },
195     }
196 };
197
198 #if NGPIO > 0
199 int
200 cs5536_gpio_pin_read(void *arg, int pin)
201 {
202         uint32_t data;
203         uint16_t port;
204         int     reg, off = 0;
205
206         port = rdmsr(MSR_LBAR_GPIO);
207
208         reg = AMD5536_GPIO_IN_EN;
209         if (pin > 15) {
210                 pin &= 0x0f;
211                 off = AMD5536_GPIOH_OFFSET;
212         }
213         reg += off;
214         data = bus_space_read_4(cs5536_sc.sc_gpio_iot, cs5536_sc.sc_gpio_ioh, reg);
215
216         if (data & (1 << pin))
217                 reg = AMD5536_GPIO_READ_BACK + off;
218         else
219                 reg = AMD5536_GPIO_OUT_VAL + off;
220
221         data = bus_space_read_4(cs5536_sc.sc_gpio_iot, cs5536_sc.sc_gpio_ioh, reg);
222
223         return data & 1 << pin ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
224 }
225
226 void
227 cs5536_gpio_pin_write(void *arg, int pin, int value)
228 {
229         uint32_t data;
230         uint16_t port;
231         int     reg;
232
233         port = rdmsr(MSR_LBAR_GPIO);
234
235         reg = AMD5536_GPIO_OUT_VAL;
236         if (pin > 15) {
237                 pin &= 0x0f;
238                 reg += AMD5536_GPIOH_OFFSET;
239         }
240
241         if (value == 1)
242                 data = 1 << pin;
243         else
244                 data = 1 << (pin + 16);
245
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);
249 }
250
251 void
252 cs5536_gpio_pin_ctl(void *arg, int pin, int flags)
253 {
254         int n, reg[7], val[7], nreg = 0, off = 0;
255
256         if (pin > 15) {
257                 pin &= 0x0f;
258                 off = AMD5536_GPIOH_OFFSET;
259         }
260
261         reg[nreg] = AMD5536_GPIO_IN_EN + off;
262         if (flags & GPIO_PIN_INPUT)
263                 val[nreg++] = 1 << pin;
264         else
265                 val[nreg++] = 1 << (pin + 16);
266
267         reg[nreg] = AMD5536_GPIO_OUT_EN + off;
268         if (flags & GPIO_PIN_OUTPUT)
269                 val[nreg++] = 1 << pin;
270         else
271                 val[nreg++] = 1 << (pin + 16);
272
273         reg[nreg] = AMD5536_GPIO_OD_EN + off;
274         if (flags & GPIO_PIN_OPENDRAIN)
275                 val[nreg++] = 1 << pin;
276         else
277                 val[nreg++] = 1 << (pin + 16);
278
279         reg[nreg] = AMD5536_GPIO_PU_EN + off;
280         if (flags & GPIO_PIN_PULLUP)
281                 val[nreg++] = 1 << pin;
282         else
283                 val[nreg++] = 1 << (pin + 16);
284
285         reg[nreg] = AMD5536_GPIO_PD_EN + off;
286         if (flags & GPIO_PIN_PULLDOWN)
287                 val[nreg++] = 1 << pin;
288         else
289                 val[nreg++] = 1 << (pin + 16);
290
291         reg[nreg] = AMD5536_GPIO_IN_INVRT_EN + off;
292         if (flags & GPIO_PIN_INVIN)
293                 val[nreg++] = 1 << pin;
294         else
295                 val[nreg++] = 1 << (pin + 16);
296
297         reg[nreg] = AMD5536_GPIO_OUT_INVRT_EN + off;
298         if (flags & GPIO_PIN_INVOUT)
299                 val[nreg++] = 1 << pin;
300         else
301                 val[nreg++] = 1 << (pin + 16);
302
303         /* set flags */
304         for (n = 0; n < nreg; n++)
305                 bus_space_write_4(cs5536_sc.sc_gpio_iot, cs5536_sc.sc_gpio_ioh, reg[n],
306                     val[n]);
307 }
308 #endif /* NGPIO */
309
310 static void
311 cs5536_cputimer_construct(struct cputimer *timer, sysclock_t oldclock)
312 {
313         timer->base = 0;
314         timer->base = oldclock - cs5536_get_timecount();
315 }
316
317 static struct cputimer cs5536_timer = {
318         SLIST_ENTRY_INITIALIZER,
319         "CS5536",
320         CPUTIMER_PRI_CS5536,
321         CPUTIMER_GEODE,
322         cs5536_get_timecount,
323         cputimer_default_fromhz,
324         cputimer_default_fromus,
325         cs5536_cputimer_construct,
326         cputimer_default_destruct,
327         AMD5536_TIMER_FREQ,
328         0, 0, 0
329 };
330
331 static sysclock_t
332 cs5536_get_timecount(void)
333 {
334         return (cs5536_timer.base + rdmsr(AMD5536_TMC));
335 }
336
337 #ifdef WATCHDOG_ENABLE
338 static int
339 cs5536_watchdog(void *unused, int period)
340 {
341         if (period > 0xffff)
342                 period = 0xffff;
343
344         bus_space_write_2(cs5536_sc.sc_iot, cs5536_sc.sc_ioh, AMD5536_MFGPT0_SETUP,
345                     AMD5536_MFGPT_CNT_EN | AMD5536_MFGPT_CMP2);
346         /* reset counter */
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);
350
351         if (period) {
352                 wrmsr(AMD5536_MFGPT_NR,
353                     rdmsr(AMD5536_MFGPT_NR) | AMD5536_MFGPT0_C2_RSTEN);
354         } else {
355                 wrmsr(AMD5536_MFGPT_NR,
356                     rdmsr(AMD5536_MFGPT_NR) & ~AMD5536_MFGPT0_C2_RSTEN);
357         }
358
359         return period;
360 }
361
362 static struct watchdog  cs5536_wdog = {
363         .name           =       "AMD CS5536",
364         .wdog_fn        =       cs5536_watchdog,
365         .arg            =       NULL,
366         .period_max     =       0xffff,
367 };
368 #endif /* WATCHDOG_ENABLE */
369
370 static int
371 cs5536_probe(device_t self)
372 {
373         static int probed = 0;
374
375         if (probed)
376                 return ENXIO;
377
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, ...) */
383                 probed = 1;
384                 return 0;
385         }
386
387         return ENXIO;
388 }
389
390 static int
391 cs5536_attach(device_t self)
392 {
393 #define BIOS_OEM_MAXLEN 80
394         static u_char bios_oem[BIOS_OEM_MAXLEN] = "\0";
395         static int attached = 0;
396         int i;
397 #if 0 /* Watchdog stuff */
398         EVENTHANDLER_REGISTER(watchdog_list, cs5536_watchdog, NULL, 0);
399 #endif
400         if (attached)
401                 return ENODEV;
402
403         attached = 1;
404         kprintf("AMD CS5536: rev %d, 32-bit %uHz timer\n",
405                 (int)rdmsr(AMD5536_REV) & AMD5536_REV_MASK,
406                 cs5536_timer.freq);
407
408         /* enable timer */
409         cputimer_register(&cs5536_timer);
410         cputimer_select(&cs5536_timer, 0);
411
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);
415
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 */
423
424 #if NGPIO > 0
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;
434
435                 /* read initial state */
436                 cs5536_sc.sc_gpio_pins[i].pin_state =
437                     cs5536_gpio_pin_read(&cs5536_sc, i);
438         }
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);
447 #endif
448         if (bios_oem_strings(&bios_soekris_55,
449             bios_oem, sizeof bios_oem) > 0 ) {
450 #if NGPIO > 0
451                 /* Attach led to pin 6 */
452                 gpio_consumer_attach("led", "error", &cs5536_sc.sc_gpio_gc,
453                 6, 1);
454 #endif
455         } else if (bios_oem_strings(&bios_pcengines_55,
456             bios_oem, sizeof bios_oem) > 0 ) {
457 #if NGPIO > 0
458                 /* Attach leds */
459                 gpio_consumer_attach("led", "led1", &cs5536_sc.sc_gpio_gc,
460                 6, 1);
461                 gpio_consumer_attach("led", "led2", &cs5536_sc.sc_gpio_gc,
462                 25, 1);
463                 gpio_consumer_attach("led", "led3", &cs5536_sc.sc_gpio_gc,
464                 27, 1);
465 #endif
466 #if 0
467                 /*
468                 * Turn on first LED so we don't make
469                 * people think their box just died.
470                 */
471                 cs5536_led_func(&led1b, 1);
472 #endif
473         }
474         if (*bios_oem)
475                 kprintf("Geode LX: %s\n", bios_oem);
476         else
477                 kprintf("Geode LX: Unknown OEM bios\n");
478
479         if (bootverbose)
480                 kprintf("MFGPT bar: %jx\n", rdmsr(MSR_LBAR_MFGPT));
481
482         return 0;
483 }
484
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),
492         {0, 0}
493 };
494
495 static driver_t cs5536_driver = {
496         "cs5536",
497         cs5536_methods,
498         0,
499 };
500
501 static devclass_t cs5536_devclass;
502
503 DRIVER_MODULE(cs5536, pci, cs5536_driver, cs5536_devclass, 0, 0);