1 /* $NetBSD: elan520.c,v 1.14 2006/02/19 14:59:23 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Device driver for the AMD Elan SC520 System Controller. This attaches 41 * where the "pchb" driver might normally attach, and provides support for 42 * extra features on the SC520, such as the watchdog timer and GPIO. 43 * 44 * Information about the GP bus echo bug work-around is from code posted 45 * to the "soekris-tech" mailing list by Jasper Wallace. 46 */ 47 48 #include <sys/cdefs.h> 49 50 __KERNEL_RCSID(0, "$NetBSD: elan520.c,v 1.14 2006/02/19 14:59:23 thorpej Exp $"); 51 52 #include <sys/param.h> 53 #include <sys/systm.h> 54 #include <sys/device.h> 55 #include <sys/wdog.h> 56 #include <sys/gpio.h> 57 58 #include <uvm/uvm_extern.h> 59 60 #include <machine/bus.h> 61 62 #include <dev/pci/pcivar.h> 63 64 #include <dev/pci/pcidevs.h> 65 66 #include "gpio.h" 67 #if NGPIO > 0 68 #include <dev/gpio/gpiovar.h> 69 #endif 70 71 #include <arch/i386/pci/elan520reg.h> 72 73 #include <dev/sysmon/sysmonvar.h> 74 75 struct elansc_softc { 76 struct device sc_dev; 77 bus_space_tag_t sc_memt; 78 bus_space_handle_t sc_memh; 79 int sc_echobug; 80 81 struct sysmon_wdog sc_smw; 82 #if NGPIO > 0 83 /* GPIO interface */ 84 struct gpio_chipset_tag sc_gpio_gc; 85 gpio_pin_t sc_gpio_pins[ELANSC_PIO_NPINS]; 86 #endif 87 }; 88 89 #if NGPIO > 0 90 static int elansc_gpio_pin_read(void *, int); 91 static void elansc_gpio_pin_write(void *, int, int); 92 static void elansc_gpio_pin_ctl(void *, int, int); 93 #endif 94 95 static void 96 elansc_wdogctl_write(struct elansc_softc *sc, uint16_t val) 97 { 98 int s; 99 uint8_t echo_mode = 0; /* XXX: gcc */ 100 101 s = splhigh(); 102 103 /* Switch off GP bus echo mode if we need to. */ 104 if (sc->sc_echobug) { 105 echo_mode = bus_space_read_1(sc->sc_memt, sc->sc_memh, 106 MMCR_GPECHO); 107 bus_space_write_1(sc->sc_memt, sc->sc_memh, 108 MMCR_GPECHO, echo_mode & ~GPECHO_GP_ECHO_ENB); 109 } 110 111 /* Unlock the register. */ 112 bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL, 113 WDTMRCTL_UNLOCK1); 114 bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL, 115 WDTMRCTL_UNLOCK2); 116 117 /* Write the value. */ 118 bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL, val); 119 120 /* Switch GP bus echo mode back. */ 121 if (sc->sc_echobug) 122 bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_GPECHO, 123 echo_mode); 124 125 splx(s); 126 } 127 128 static void 129 elansc_wdogctl_reset(struct elansc_softc *sc) 130 { 131 int s; 132 uint8_t echo_mode = 0/* XXX: gcc */; 133 134 s = splhigh(); 135 136 /* Switch off GP bus echo mode if we need to. */ 137 if (sc->sc_echobug) { 138 echo_mode = bus_space_read_1(sc->sc_memt, sc->sc_memh, 139 MMCR_GPECHO); 140 bus_space_write_1(sc->sc_memt, sc->sc_memh, 141 MMCR_GPECHO, echo_mode & ~GPECHO_GP_ECHO_ENB); 142 } 143 144 /* Reset the watchdog. */ 145 bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL, 146 WDTMRCTL_RESET1); 147 bus_space_write_2(sc->sc_memt, sc->sc_memh, MMCR_WDTMRCTL, 148 WDTMRCTL_RESET2); 149 150 /* Switch GP bus echo mode back. */ 151 if (sc->sc_echobug) 152 bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_GPECHO, 153 echo_mode); 154 155 splx(s); 156 } 157 158 static const struct { 159 int period; /* whole seconds */ 160 uint16_t exp; /* exponent select */ 161 } elansc_wdog_periods[] = { 162 { 1, WDTMRCTL_EXP_SEL25 }, 163 { 2, WDTMRCTL_EXP_SEL26 }, 164 { 4, WDTMRCTL_EXP_SEL27 }, 165 { 8, WDTMRCTL_EXP_SEL28 }, 166 { 16, WDTMRCTL_EXP_SEL29 }, 167 { 32, WDTMRCTL_EXP_SEL30 }, 168 { 0, 0 }, 169 }; 170 171 static int 172 elansc_wdog_setmode(struct sysmon_wdog *smw) 173 { 174 struct elansc_softc *sc = smw->smw_cookie; 175 int i; 176 uint16_t exp_sel = 0; /* XXX: gcc */ 177 178 if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { 179 elansc_wdogctl_write(sc, 180 WDTMRCTL_WRST_ENB | WDTMRCTL_EXP_SEL30); 181 } else { 182 if (smw->smw_period == WDOG_PERIOD_DEFAULT) { 183 smw->smw_period = 32; 184 exp_sel = WDTMRCTL_EXP_SEL30; 185 } else { 186 for (i = 0; elansc_wdog_periods[i].period != 0; i++) { 187 if (elansc_wdog_periods[i].period == 188 smw->smw_period) { 189 exp_sel = elansc_wdog_periods[i].exp; 190 break; 191 } 192 } 193 if (elansc_wdog_periods[i].period == 0) 194 return (EINVAL); 195 } 196 elansc_wdogctl_write(sc, WDTMRCTL_ENB | 197 WDTMRCTL_WRST_ENB | exp_sel); 198 elansc_wdogctl_reset(sc); 199 } 200 return (0); 201 } 202 203 static int 204 elansc_wdog_tickle(struct sysmon_wdog *smw) 205 { 206 struct elansc_softc *sc = smw->smw_cookie; 207 208 elansc_wdogctl_reset(sc); 209 return (0); 210 } 211 212 static int 213 elansc_match(struct device *parent, struct cfdata *match, void *aux) 214 { 215 struct pci_attach_args *pa = aux; 216 217 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_AMD && 218 PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_AMD_SC520_SC) 219 return (10); /* beat pchb */ 220 221 return (0); 222 } 223 224 static const char *elansc_speeds[] = { 225 "(reserved 00)", 226 "100MHz", 227 "133MHz", 228 "(reserved 11)", 229 }; 230 231 static void 232 elansc_attach(struct device *parent, struct device *self, void *aux) 233 { 234 struct elansc_softc *sc = (void *) self; 235 struct pci_attach_args *pa = aux; 236 uint16_t rev; 237 uint8_t ressta, cpuctl; 238 #if NGPIO > 0 239 struct gpiobus_attach_args gba; 240 int pin; 241 int reg, shift; 242 uint16_t data; 243 #endif 244 245 aprint_naive(": System Controller\n"); 246 aprint_normal(": AMD Elan SC520 System Controller\n"); 247 248 sc->sc_memt = pa->pa_memt; 249 if (bus_space_map(sc->sc_memt, MMCR_BASE_ADDR, PAGE_SIZE, 0, 250 &sc->sc_memh) != 0) { 251 aprint_error("%s: unable to map registers\n", 252 sc->sc_dev.dv_xname); 253 return; 254 } 255 256 rev = bus_space_read_2(sc->sc_memt, sc->sc_memh, MMCR_REVID); 257 cpuctl = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_CPUCTL); 258 259 aprint_normal("%s: product %d stepping %d.%d, CPU clock %s\n", 260 sc->sc_dev.dv_xname, 261 (rev & REVID_PRODID) >> REVID_PRODID_SHIFT, 262 (rev & REVID_MAJSTEP) >> REVID_MAJSTEP_SHIFT, 263 (rev & REVID_MINSTEP), 264 elansc_speeds[cpuctl & CPUCTL_CPU_CLK_SPD_MASK]); 265 266 /* 267 * SC520 rev A1 has a bug that affects the watchdog timer. If 268 * the GP bus echo mode is enabled, writing to the watchdog control 269 * register is blocked. 270 * 271 * The BIOS in some systems (e.g. the Soekris net4501) enables 272 * GP bus echo for various reasons, so we need to switch it off 273 * when we talk to the watchdog timer. 274 * 275 * XXX The step 1.1 (B1?) in my Soekris net4501 also has this 276 * XXX problem, so we'll just enable it for all Elan SC520s 277 * XXX for now. --thorpej@NetBSD.org 278 */ 279 if (1 || rev == ((PRODID_ELAN_SC520 << REVID_PRODID_SHIFT) | 280 (0 << REVID_MAJSTEP_SHIFT) | (1))) 281 sc->sc_echobug = 1; 282 283 /* 284 * Determine cause of the last reset, and issue a warning if it 285 * was due to watchdog expiry. 286 */ 287 ressta = bus_space_read_1(sc->sc_memt, sc->sc_memh, MMCR_RESSTA); 288 if (ressta & RESSTA_WDT_RST_DET) 289 aprint_error( 290 "%s: WARNING: LAST RESET DUE TO WATCHDOG EXPIRATION!\n", 291 sc->sc_dev.dv_xname); 292 bus_space_write_1(sc->sc_memt, sc->sc_memh, MMCR_RESSTA, ressta); 293 294 /* 295 * Hook up the watchdog timer. 296 */ 297 sc->sc_smw.smw_name = sc->sc_dev.dv_xname; 298 sc->sc_smw.smw_cookie = sc; 299 sc->sc_smw.smw_setmode = elansc_wdog_setmode; 300 sc->sc_smw.smw_tickle = elansc_wdog_tickle; 301 sc->sc_smw.smw_period = 32; /* actually 32.54 */ 302 if (sysmon_wdog_register(&sc->sc_smw) != 0) 303 aprint_error("%s: unable to register watchdog with sysmon\n", 304 sc->sc_dev.dv_xname); 305 306 /* Set up the watchdog registers with some defaults. */ 307 elansc_wdogctl_write(sc, WDTMRCTL_WRST_ENB | WDTMRCTL_EXP_SEL30); 308 309 /* ...and clear it. */ 310 elansc_wdogctl_reset(sc); 311 312 #if NGPIO > 0 313 /* Initialize GPIO pins array */ 314 for (pin = 0; pin < ELANSC_PIO_NPINS; pin++) { 315 sc->sc_gpio_pins[pin].pin_num = pin; 316 sc->sc_gpio_pins[pin].pin_caps = GPIO_PIN_INPUT | 317 GPIO_PIN_OUTPUT; 318 319 /* Read initial state */ 320 reg = (pin < 16 ? MMCR_PIODIR15_0 : MMCR_PIODIR31_16); 321 shift = pin % 16; 322 data = bus_space_read_2(sc->sc_memt, sc->sc_memh, reg); 323 if ((data & (1 << shift)) == 0) 324 sc->sc_gpio_pins[pin].pin_flags = GPIO_PIN_INPUT; 325 else 326 sc->sc_gpio_pins[pin].pin_flags = GPIO_PIN_OUTPUT; 327 if (elansc_gpio_pin_read(sc, pin) == 0) 328 sc->sc_gpio_pins[pin].pin_state = GPIO_PIN_LOW; 329 else 330 sc->sc_gpio_pins[pin].pin_state = GPIO_PIN_HIGH; 331 } 332 333 /* Create controller tag */ 334 sc->sc_gpio_gc.gp_cookie = sc; 335 sc->sc_gpio_gc.gp_pin_read = elansc_gpio_pin_read; 336 sc->sc_gpio_gc.gp_pin_write = elansc_gpio_pin_write; 337 sc->sc_gpio_gc.gp_pin_ctl = elansc_gpio_pin_ctl; 338 339 gba.gba_gc = &sc->sc_gpio_gc; 340 gba.gba_pins = sc->sc_gpio_pins; 341 gba.gba_npins = ELANSC_PIO_NPINS; 342 343 /* Attach GPIO framework */ 344 config_found_ia(&sc->sc_dev, "gpiobus", &gba, gpiobus_print); 345 #endif /* NGPIO */ 346 } 347 348 CFATTACH_DECL(elansc, sizeof(struct elansc_softc), 349 elansc_match, elansc_attach, NULL, NULL); 350 351 #if NGPIO > 0 352 static int 353 elansc_gpio_pin_read(void *arg, int pin) 354 { 355 struct elansc_softc *sc = arg; 356 int reg, shift; 357 uint16_t data; 358 359 reg = (pin < 16 ? MMCR_PIODATA15_0 : MMCR_PIODATA31_16); 360 shift = pin % 16; 361 data = bus_space_read_2(sc->sc_memt, sc->sc_memh, reg); 362 363 return ((data >> shift) & 0x1); 364 } 365 366 static void 367 elansc_gpio_pin_write(void *arg, int pin, int value) 368 { 369 struct elansc_softc *sc = arg; 370 int reg, shift; 371 uint16_t data; 372 373 reg = (pin < 16 ? MMCR_PIODATA15_0 : MMCR_PIODATA31_16); 374 shift = pin % 16; 375 data = bus_space_read_2(sc->sc_memt, sc->sc_memh, reg); 376 if (value == 0) 377 data &= ~(1 << shift); 378 else if (value == 1) 379 data |= (1 << shift); 380 381 bus_space_write_2(sc->sc_memt, sc->sc_memh, reg, data); 382 } 383 384 static void 385 elansc_gpio_pin_ctl(void *arg, int pin, int flags) 386 { 387 struct elansc_softc *sc = arg; 388 int reg, shift; 389 uint16_t data; 390 391 reg = (pin < 16 ? MMCR_PIODIR15_0 : MMCR_PIODIR31_16); 392 shift = pin % 16; 393 data = bus_space_read_2(sc->sc_memt, sc->sc_memh, reg); 394 if (flags & GPIO_PIN_INPUT) 395 data &= ~(1 << shift); 396 if (flags & GPIO_PIN_OUTPUT) 397 data |= (1 << shift); 398 399 bus_space_write_2(sc->sc_memt, sc->sc_memh, reg, data); 400 } 401 #endif /* NGPIO */ 402