Annotation of sys/arch/i386/pci/elan520.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: elan520.c,v 1.13 2007/05/23 11:55:11 markus Exp $ */
2: /* $NetBSD: elan520.c,v 1.4 2002/10/02 05:47:15 thorpej Exp $ */
3:
4: /*-
5: * Copyright (c) 2002 The NetBSD Foundation, Inc.
6: * All rights reserved.
7: *
8: * This code is derived from software contributed to The NetBSD Foundation
9: * by Jason R. Thorpe.
10: *
11: * Redistribution and use in source and binary forms, with or without
12: * modification, are permitted provided that the following conditions
13: * are met:
14: * 1. Redistributions of source code must retain the above copyright
15: * notice, this list of conditions and the following disclaimer.
16: * 2. Redistributions in binary form must reproduce the above copyright
17: * notice, this list of conditions and the following disclaimer in the
18: * documentation and/or other materials provided with the distribution.
19: * 3. All advertising materials mentioning features or use of this software
20: * must display the following acknowledgement:
21: * This product includes software developed by the NetBSD
22: * Foundation, Inc. and its contributors.
23: * 4. Neither the name of The NetBSD Foundation nor the names of its
24: * contributors may be used to endorse or promote products derived
25: * from this software without specific prior written permission.
26: *
27: * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29: * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37: * POSSIBILITY OF SUCH DAMAGE.
38: */
39:
40: /*
41: * Device driver for the AMD Elan SC520 System Controller. This attaches
42: * where the "pchb" driver might normally attach, and provides support for
43: * extra features on the SC520, such as the watchdog timer and GPIO.
44: */
45:
46: #include <sys/param.h>
47: #include <sys/systm.h>
48: #include <sys/device.h>
49: #include <sys/gpio.h>
50: #include <sys/sysctl.h>
51: #include <sys/time.h>
52: #include <sys/timetc.h>
53:
54: #include <machine/bus.h>
55:
56: #include <dev/pci/pcivar.h>
57: #include <dev/pci/pcidevs.h>
58:
59: #include <dev/gpio/gpiovar.h>
60:
61: #include <arch/i386/pci/elan520reg.h>
62:
63: struct elansc_softc {
64: struct device sc_dev;
65: bus_space_tag_t sc_memt;
66: bus_space_handle_t sc_memh;
67:
68: /* GPIO interface */
69: struct gpio_chipset_tag sc_gpio_gc;
70: gpio_pin_t sc_gpio_pins[ELANSC_PIO_NPINS];
71:
72: /* GP timer */
73: struct timecounter sc_tc;
74: } *elansc;
75:
76: int elansc_match(struct device *, void *, void *);
77: void elansc_attach(struct device *, struct device *, void *);
78: void elansc_update_cpuspeed(void);
79: void elansc_setperf(int);
80: int elansc_cpuspeed(int *);
81:
82: void elansc_wdogctl(struct elansc_softc *, int, uint16_t);
83: #define elansc_wdogctl_reset(sc) elansc_wdogctl(sc, 1, 0)
84: #define elansc_wdogctl_write(sc, val) elansc_wdogctl(sc, 0, val)
85: int elansc_wdogctl_cb(void *, int);
86:
87: int elansc_gpio_pin_read(void *, int);
88: void elansc_gpio_pin_write(void *, int, int);
89: void elansc_gpio_pin_ctl(void *, int, int);
90:
91: u_int elansc_tc_read(struct timecounter *);
92:
93: struct cfattach elansc_ca = {
94: sizeof(struct elansc_softc), elansc_match, elansc_attach
95: };
96:
97: struct cfdriver elansc_cd = {
98: NULL, "elansc", DV_DULL
99: };
100:
101: static int cpuspeed;
102:
103: int
104: elansc_match(struct device *parent, void *match, void *aux)
105: {
106: struct pci_attach_args *pa = aux;
107:
108: if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMD &&
109: PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_ELANSC520)
110: return (10); /* beat pchb */
111:
112: return (0);
113: }
114:
115: static const char *elansc_speeds[] = {
116: "(reserved 00)",
117: "100MHz",
118: "133MHz",
119: "(reserved 11)",
120: };
121:
122: #define RSTBITS "\20\x07SCP\x06HRST\x05SRST\x04WDT\x03SD\x02PRGRST\x01PWRGOOD"
123:
124: void
125: elansc_attach(struct device *parent, struct device *self, void *aux)
126: {
127: struct elansc_softc *sc = (void *) self;
128: struct pci_attach_args *pa = aux;
129: struct gpiobus_attach_args gba;
130: struct timecounter *tc;
131: uint16_t rev, data;
132: uint8_t ressta, cpuctl, tmr;
133: int pin, reg, shift;
134:
135: sc->sc_memt = pa->pa_memt;
136: if (bus_space_map(sc->sc_memt, MMCR_BASE_ADDR, NBPG, 0,
137: &sc->sc_memh) != 0) {
138: printf(": unable to map registers\n");
139: return;
140: }
141:
142: rev = bus_space_read_2(sc->sc_memt, sc->sc_memh, MMCR_REVID);
143: cpuctl = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_CPUCTL);
144: ressta = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_RESSTA);
145:
146: printf(": product %d stepping %d.%d, CPU clock %s, reset %b\n",
147: (rev & REVID_PRODID) >> REVID_PRODID_SHIFT,
148: (rev & REVID_MAJSTEP) >> REVID_MAJSTEP_SHIFT,
149: (rev & REVID_MINSTEP),
150: elansc_speeds[cpuctl & CPUCTL_CPU_CLK_SPD_MASK],
151: ressta, RSTBITS);
152:
153: /*
154: * Determine cause of the last reset, and issue a warning if it
155: * was due to watchdog expiry.
156: */
157: if (ressta & RESSTA_WDT_RST_DET)
158: printf("%s: WARNING: LAST RESET DUE TO WATCHDOG EXPIRATION!\n",
159: sc->sc_dev.dv_xname);
160: bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_RESSTA, ressta);
161:
162: /* Set up the watchdog registers with some defaults. */
163: elansc_wdogctl_write(sc, WDTMRCTL_WRST_ENB | WDTMRCTL_EXP_SEL30);
164:
165: /* ...and clear it. */
166: elansc_wdogctl_reset(sc);
167:
168: wdog_register(sc, elansc_wdogctl_cb);
169: elansc = sc;
170: cpu_setperf = elansc_setperf;
171: cpu_cpuspeed = elansc_cpuspeed;
172: elansc_update_cpuspeed();
173:
174: /* Initialize GPIO pins array */
175: for (pin = 0; pin < ELANSC_PIO_NPINS; pin++) {
176: sc->sc_gpio_pins[pin].pin_num = pin;
177: sc->sc_gpio_pins[pin].pin_caps = GPIO_PIN_INPUT |
178: GPIO_PIN_OUTPUT;
179:
180: /* Read initial state */
181: reg = (pin < 16 ? MMCR_PIODIR15_0 : MMCR_PIODIR31_16);
182: shift = pin % 16;
183: data = bus_space_read_2(sc->sc_memt, sc->sc_memh, reg);
184: if ((data & (1 << shift)) == 0)
185: sc->sc_gpio_pins[pin].pin_flags = GPIO_PIN_INPUT;
186: else
187: sc->sc_gpio_pins[pin].pin_flags = GPIO_PIN_OUTPUT;
188: if (elansc_gpio_pin_read(sc, pin) == 0)
189: sc->sc_gpio_pins[pin].pin_state = GPIO_PIN_LOW;
190: else
191: sc->sc_gpio_pins[pin].pin_state = GPIO_PIN_HIGH;
192: }
193:
194: /* Create controller tag */
195: sc->sc_gpio_gc.gp_cookie = sc;
196: sc->sc_gpio_gc.gp_pin_read = elansc_gpio_pin_read;
197: sc->sc_gpio_gc.gp_pin_write = elansc_gpio_pin_write;
198: sc->sc_gpio_gc.gp_pin_ctl = elansc_gpio_pin_ctl;
199:
200: gba.gba_name = "gpio";
201: gba.gba_gc = &sc->sc_gpio_gc;
202: gba.gba_pins = sc->sc_gpio_pins;
203: gba.gba_npins = ELANSC_PIO_NPINS;
204:
205: /* Attach GPIO framework */
206: config_found(&sc->sc_dev, &gba, gpiobus_print);
207:
208: /* Disable GP1/2, clear the current count, and set the period to max */
209: bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR1CTL,
210: GPTMRCTL_ENB_WR | GPTMRCTL_CONT_CMP |
211: GPTMRCTL_PSC_SEL | GPTMRCTL_RTG);
212: bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR1CNT, 0);
213: bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR1MAXCMPA, 0);
214:
215: bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR2CTL,
216: GPTMRCTL_ENB_WR | GPTMRCTL_CONT_CMP);
217: bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR2CNT, 0);
218: bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR2MAXCMPA, 0);
219:
220: tmr = bus_space_read_1(sc->sc_memt, sc->sc_memh, SWTMRCFG);
221:
222: /* Enable GP1/2 */
223: bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR1CTL,
224: GPTMRCTL_ENB | GPTMRCTL_ENB_WR | GPTMRCTL_CONT_CMP |
225: GPTMRCTL_PSC_SEL | GPTMRCTL_RTG);
226: bus_space_write_2(sc->sc_memt, sc->sc_memh, GPTMR2CTL,
227: GPTMRCTL_ENB | GPTMRCTL_ENB_WR | GPTMRCTL_CONT_CMP);
228:
229: /* Attach timer */
230: tc = &sc->sc_tc;
231: tc->tc_get_timecount = elansc_tc_read;
232: tc->tc_poll_pps = NULL;
233: tc->tc_counter_mask = ~0;
234: tc->tc_frequency = (tmr & 1) ? (33000000 / 4) : (33333333 / 4);
235: tc->tc_name = sc->sc_dev.dv_xname;
236: tc->tc_quality = 1000;
237: tc->tc_priv = sc;
238: tc_init(tc);
239: }
240:
241: u_int
242: elansc_tc_read(struct timecounter *tc)
243: {
244: struct elansc_softc *sc = tc->tc_priv;
245: u_int32_t m1, m2, l;
246:
247: do {
248: m1 = bus_space_read_2(sc->sc_memt, sc->sc_memh, GPTMR1CNT);
249: l = bus_space_read_2(sc->sc_memt, sc->sc_memh, GPTMR2CNT);
250: m2 = bus_space_read_2(sc->sc_memt, sc->sc_memh, GPTMR1CNT);
251: } while (m1 != m2);
252:
253: return ((m1 << 16) | l);
254: }
255:
256: void
257: elansc_wdogctl(struct elansc_softc *sc, int do_reset, uint16_t val)
258: {
259: int s;
260: uint8_t echo_mode;
261:
262: s = splhigh();
263:
264: /* Switch off GP bus echo mode. */
265: echo_mode = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_GPECHO);
266: bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_GPECHO,
267: echo_mode & ~GPECHO_GP_ECHO_ENB);
268:
269: if (do_reset) {
270: /* Reset the watchdog. */
271: bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL,
272: WDTMRCTL_RESET1);
273: bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL,
274: WDTMRCTL_RESET2);
275: } else {
276: /* Unlock the register. */
277: bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL,
278: WDTMRCTL_UNLOCK1);
279: bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL,
280: WDTMRCTL_UNLOCK2);
281:
282: /* Write the value. */
283: bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL,
284: val);
285: }
286:
287: /* Switch GP bus echo mode back. */
288: bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_GPECHO, echo_mode);
289:
290: splx(s);
291: }
292:
293: static const struct {
294: int period; /* whole seconds */
295: uint16_t exp; /* exponent select */
296: } elansc_wdog_periods[] = {
297: { 1, WDTMRCTL_EXP_SEL25 },
298: { 2, WDTMRCTL_EXP_SEL26 },
299: { 4, WDTMRCTL_EXP_SEL27 },
300: { 8, WDTMRCTL_EXP_SEL28 },
301: { 16, WDTMRCTL_EXP_SEL29 },
302: { 32, WDTMRCTL_EXP_SEL30 },
303: };
304:
305: int
306: elansc_wdogctl_cb(void *self, int period)
307: {
308: struct elansc_softc *sc = self;
309: int i;
310:
311: if (period == 0) {
312: elansc_wdogctl_write(sc,
313: WDTMRCTL_WRST_ENB | WDTMRCTL_EXP_SEL30);
314: } else {
315: for (i = 0; i < (sizeof(elansc_wdog_periods) /
316: sizeof(elansc_wdog_periods[0])) - 1; i++)
317: if (elansc_wdog_periods[i].period >= period)
318: break;
319: period = elansc_wdog_periods[i].period;
320: elansc_wdogctl_write(sc, WDTMRCTL_ENB |
321: WDTMRCTL_WRST_ENB | elansc_wdog_periods[i].exp);
322: elansc_wdogctl_reset(sc);
323: }
324: return (period);
325: }
326:
327: void
328: elansc_update_cpuspeed(void)
329: {
330: #ifdef I586_CPU
331: static const int elansc_mhz[] = { 0, 100, 133, 999 };
332: #endif
333: uint8_t cpuctl;
334:
335: cpuctl = bus_space_read_1(elansc->sc_memt, elansc->sc_memh,
336: MMCR_CPUCTL);
337: #ifdef I586_CPU
338: cpuspeed = elansc_mhz[cpuctl & CPUCTL_CPU_CLK_SPD_MASK];
339: #endif
340: }
341:
342: void
343: elansc_setperf(int level)
344: {
345: uint32_t eflags;
346: uint8_t cpuctl, speed;
347:
348: level = (level > 50) ? 100 : 0;
349:
350: cpuctl = bus_space_read_1(elansc->sc_memt, elansc->sc_memh,
351: MMCR_CPUCTL);
352: speed = (level == 100) ? 2 : 1;
353: if ((cpuctl & CPUCTL_CPU_CLK_SPD_MASK) == speed)
354: return;
355:
356: eflags = read_eflags();
357: disable_intr();
358: bus_space_write_1(elansc->sc_memt, elansc->sc_memh, MMCR_CPUCTL,
359: (cpuctl & ~CPUCTL_CPU_CLK_SPD_MASK) | speed);
360: enable_intr();
361: write_eflags(eflags);
362:
363: elansc_update_cpuspeed();
364: }
365:
366: int
367: elansc_cpuspeed(int *freq)
368: {
369: *freq = cpuspeed;
370: return (0);
371: }
372:
373: int
374: elansc_gpio_pin_read(void *arg, int pin)
375: {
376: struct elansc_softc *sc = arg;
377: int reg, shift;
378: u_int16_t data;
379:
380: reg = (pin < 16 ? MMCR_PIODATA15_0 : MMCR_PIODATA31_16);
381: shift = pin % 16;
382: data = bus_space_read_2(sc->sc_memt, sc->sc_memh, reg);
383:
384: return ((data >> shift) & 0x1);
385: }
386:
387: void
388: elansc_gpio_pin_write(void *arg, int pin, int value)
389: {
390: struct elansc_softc *sc = arg;
391: int reg, shift;
392: u_int16_t data;
393:
394: reg = (pin < 16 ? MMCR_PIODATA15_0 : MMCR_PIODATA31_16);
395: shift = pin % 16;
396: data = bus_space_read_2(sc->sc_memt, sc->sc_memh, reg);
397: if (value == 0)
398: data &= ~(1 << shift);
399: else if (value == 1)
400: data |= (1 << shift);
401:
402: bus_space_write_2(sc->sc_memt, sc->sc_memh, reg, data);
403: }
404:
405: void
406: elansc_gpio_pin_ctl(void *arg, int pin, int flags)
407: {
408: struct elansc_softc *sc = arg;
409: int reg, shift;
410: u_int16_t data;
411:
412: reg = (pin < 16 ? MMCR_PIODIR15_0 : MMCR_PIODIR31_16);
413: shift = pin % 16;
414: data = bus_space_read_2(sc->sc_memt, sc->sc_memh, reg);
415: if (flags & GPIO_PIN_INPUT)
416: data &= ~(1 << shift);
417: if (flags & GPIO_PIN_OUTPUT)
418: data |= (1 << shift);
419:
420: bus_space_write_2(sc->sc_memt, sc->sc_memh, reg, data);
421: }
CVSweb