geode.c revision 331722
1/*- 2 * Copyright (c) 2003-2004 Poul-Henning Kamp 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD: stable/11/sys/i386/i386/geode.c 331722 2018-03-29 02:50:57Z eadler $"); 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/timetc.h> 33#include <sys/bus.h> 34#include <sys/kernel.h> 35#include <sys/module.h> 36#include <sys/watchdog.h> 37#include <dev/pci/pcireg.h> 38#include <dev/pci/pcivar.h> 39#include <dev/led/led.h> 40#include <machine/pc/bios.h> 41 42static struct bios_oem bios_soekris = { 43 { 0xf0000, 0xf1000 }, 44 { 45 { "Soekris", 0, 8 }, /* Soekris Engineering. */ 46 { "net4", 0, 8 }, /* net45xx */ 47 { "comBIOS", 0, 54 }, /* comBIOS ver. 1.26a 20040819 ... */ 48 { NULL, 0, 0 }, 49 } 50}; 51 52static struct bios_oem bios_soekris_55 = { 53 { 0xf0000, 0xf1000 }, 54 { 55 { "Soekris", 0, 8 }, /* Soekris Engineering. */ 56 { "net5", 0, 8 }, /* net5xxx */ 57 { "comBIOS", 0, 54 }, /* comBIOS ver. 1.26a 20040819 ... */ 58 { NULL, 0, 0 }, 59 } 60}; 61 62static struct bios_oem bios_pcengines = { 63 { 0xf9000, 0xfa000 }, 64 { 65 { "PC Engines WRAP", 0, 28 }, /* PC Engines WRAP.1C v1.03 */ 66 { "tinyBIOS", 0, 28 }, /* tinyBIOS V1.4a (C)1997-2003 */ 67 { NULL, 0, 0 }, 68 } 69}; 70 71static struct bios_oem bios_pcengines_55 = { 72 { 0xf9000, 0xfa000 }, 73 { 74 { "PC Engines ALIX", 0, 28 }, /* PC Engines ALIX */ 75 { "tinyBIOS", 0, 28 }, /* tinyBIOS V1.4a (C)1997-2005 */ 76 { NULL, 0, 0 }, 77 } 78}; 79 80static struct bios_oem bios_advantech = { 81 { 0xfe000, 0xff000 }, 82 { 83 { "**** PCM-582", 5, 33 }, /* PCM-5823 BIOS V1.12 ... */ 84 { "GXm-Cx5530", -11, 35 }, /* 06/07/2002-GXm-Cx5530... */ 85 { NULL, 0, 0 }, 86 } 87}; 88 89static unsigned cba; 90static unsigned gpio; 91static unsigned geode_counter; 92 93static struct cdev *led1, *led2, *led3; 94static int led1b, led2b, led3b; 95 96static void 97led_func(void *ptr, int onoff) 98{ 99 uint32_t u; 100 int bit; 101 102 bit = *(int *)ptr; 103 if (bit < 0) { 104 bit = -bit; 105 onoff = !onoff; 106 } 107 108 u = inl(gpio + 4); 109 if (onoff) 110 u |= 1 << bit; 111 else 112 u &= ~(1 << bit); 113 outl(gpio, u); 114} 115 116static void 117cs5536_led_func(void *ptr, int onoff) 118{ 119 int bit; 120 uint16_t a; 121 122 bit = *(int *)ptr; 123 if (bit < 0) { 124 bit = -bit; 125 onoff = !onoff; 126 } 127 128 a = rdmsr(0x5140000c); 129 if (bit >= 16) { 130 a += 0x80; 131 bit -= 16; 132 } 133 134 if (onoff) 135 outl(a, 1 << bit); 136 else 137 outl(a, 1 << (bit + 16)); 138} 139 140 141static unsigned 142geode_get_timecount(struct timecounter *tc) 143{ 144 return (inl(geode_counter)); 145} 146 147static struct timecounter geode_timecounter = { 148 geode_get_timecount, 149 NULL, 150 0xffffffff, 151 27000000, 152 "Geode", 153 1000 154}; 155 156static uint64_t 157geode_cputicks(void) 158{ 159 unsigned c; 160 static unsigned last; 161 static uint64_t offset; 162 163 c = inl(geode_counter); 164 if (c < last) 165 offset += (1LL << 32); 166 last = c; 167 return (offset | c); 168} 169 170/* 171 * The GEODE watchdog runs from a 32kHz frequency. One period of that is 172 * 31250 nanoseconds which we round down to 2^14 nanoseconds. The watchdog 173 * consists of a power-of-two prescaler and a 16 bit counter, so the math 174 * is quite simple. The max timeout is 14 + 16 + 13 = 2^43 nsec ~= 2h26m. 175 */ 176static void 177geode_watchdog(void *foo __unused, u_int cmd, int *error) 178{ 179 u_int u, p, r; 180 181 u = cmd & WD_INTERVAL; 182 if (u >= 14 && u <= 43) { 183 u -= 14; 184 if (u > 16) { 185 p = u - 16; 186 u -= p; 187 } else { 188 p = 0; 189 } 190 if (u == 16) 191 u = (1 << u) - 1; 192 else 193 u = 1 << u; 194 r = inw(cba + 2) & 0xff00; 195 outw(cba + 2, p | 0xf0 | r); 196 outw(cba, u); 197 *error = 0; 198 } else { 199 outw(cba, 0); 200 } 201} 202 203/* 204 * We run MFGPT0 off the 32kHz frequency and prescale by 16384 giving a 205 * period of half a second. 206 * Range becomes 2^30 (= 1 sec) to 2^44 (almost 5 hours) 207 */ 208static void 209cs5536_watchdog(void *foo __unused, u_int cmd, int *error) 210{ 211 u_int u, p, s; 212 uint16_t a; 213 uint32_t m; 214 215 a = rdmsr(0x5140000d); 216 217 u = cmd & WD_INTERVAL; 218 if (u >= 30 && u <= 44) { 219 p = 1 << (u - 29); 220 221 /* Set up MFGPT0, 32khz, prescaler 16k, C2 event */ 222 outw(a + 6, 0x030e); 223 /* set comparator 2 */ 224 outw(a + 2, p); 225 /* reset counter */ 226 outw(a + 4, 0); 227 /* Arm reset mechanism */ 228 m = rdmsr(0x51400029); 229 m |= (1 << 24); 230 wrmsr(0x51400029, m); 231 /* Start counter */ 232 outw(a + 6, 0x8000); 233 234 *error = 0; 235 } else { 236 /* 237 * MFGPT_SETUP is write-once 238 * Check if the counter has been setup 239 */ 240 s = inw(a + 6); 241 if (s & (1 << 12)) { 242 /* Stop and reset counter */ 243 outw(a + 6, 0); 244 outw(a + 4, 0); 245 } 246 } 247} 248 249/* 250 * The Advantech PCM-582x watchdog expects 0x1 at I/O port 0x0443 251 * every 1.6 secs +/- 30%. Writing 0x0 disables the watchdog 252 * NB: reading the I/O port enables the timer as well 253 */ 254static void 255advantech_watchdog(void *foo __unused, u_int cmd, int *error) 256{ 257 u_int u; 258 259 u = cmd & WD_INTERVAL; 260 if (u > 0 && u <= WD_TO_1SEC) { 261 outb(0x0443, 1); 262 *error = 0; 263 } else { 264 outb(0x0443, 0); 265 } 266} 267 268static int 269geode_probe(device_t self) 270{ 271#define BIOS_OEM_MAXLEN 80 272 static u_char bios_oem[BIOS_OEM_MAXLEN] = "\0"; 273 274 switch (pci_get_devid(self)) { 275 case 0x0515100b: 276 if (geode_counter == 0) { 277 /* 278 * The address of the CBA is written to this register 279 * by the bios, see p161 in data sheet. 280 */ 281 cba = pci_read_config(self, 0x64, 4); 282 if (bootverbose) 283 printf("Geode CBA@ 0x%x\n", cba); 284 geode_counter = cba + 0x08; 285 outl(cba + 0x0d, 2); 286 if (bootverbose) 287 printf("Geode rev: %02x %02x\n", 288 inb(cba + 0x3c), inb(cba + 0x3d)); 289 tc_init(&geode_timecounter); 290 EVENTHANDLER_REGISTER(watchdog_list, geode_watchdog, 291 NULL, 0); 292 set_cputicker(geode_cputicks, 27000000, 0); 293 } 294 break; 295 case 0x0510100b: 296 gpio = pci_read_config(self, PCIR_BAR(0), 4); 297 gpio &= ~0x1f; 298 if (bootverbose) 299 printf("Geode GPIO@ = %x\n", gpio); 300 if (bios_oem_strings(&bios_soekris, 301 bios_oem, sizeof bios_oem) > 0 ) { 302 led1b = 20; 303 led1 = led_create(led_func, &led1b, "error"); 304 } else if (bios_oem_strings(&bios_pcengines, 305 bios_oem, sizeof bios_oem) > 0 ) { 306 led1b = -2; 307 led2b = -3; 308 led3b = -18; 309 led1 = led_create(led_func, &led1b, "led1"); 310 led2 = led_create(led_func, &led2b, "led2"); 311 led3 = led_create(led_func, &led3b, "led3"); 312 /* 313 * Turn on first LED so we don't make 314 * people think their box just died. 315 */ 316 led_func(&led1b, 1); 317 } 318 if (*bios_oem) 319 printf("Geode %s\n", bios_oem); 320 break; 321 case 0x01011078: 322 if (bios_oem_strings(&bios_advantech, 323 bios_oem, sizeof bios_oem) > 0 ) { 324 printf("Geode %s\n", bios_oem); 325 EVENTHANDLER_REGISTER(watchdog_list, advantech_watchdog, 326 NULL, 0); 327 } 328 break; 329 case 0x20801022: 330 if (bios_oem_strings(&bios_soekris_55, 331 bios_oem, sizeof bios_oem) > 0 ) { 332 led1b = 6; 333 led1 = led_create(cs5536_led_func, &led1b, "error"); 334 } else if (bios_oem_strings(&bios_pcengines_55, 335 bios_oem, sizeof bios_oem) > 0 ) { 336 led1b = -6; 337 led2b = -25; 338 led3b = -27; 339 led1 = led_create(cs5536_led_func, &led1b, "led1"); 340 led2 = led_create(cs5536_led_func, &led2b, "led2"); 341 led3 = led_create(cs5536_led_func, &led3b, "led3"); 342 /* 343 * Turn on first LED so we don't make 344 * people think their box just died. 345 */ 346 cs5536_led_func(&led1b, 1); 347 } 348 if (*bios_oem) 349 printf("Geode LX: %s\n", bios_oem); 350 if (bootverbose) 351 printf("MFGPT bar: %jx\n", rdmsr(0x5140000d)); 352 EVENTHANDLER_REGISTER(watchdog_list, cs5536_watchdog, NULL, 0); 353 break; 354 } 355 return (ENXIO); 356} 357 358static int 359geode_attach(device_t self) 360{ 361 362 return(ENODEV); 363} 364 365static device_method_t geode_methods[] = { 366 /* Device interface */ 367 DEVMETHOD(device_probe, geode_probe), 368 DEVMETHOD(device_attach, geode_attach), 369 DEVMETHOD(device_suspend, bus_generic_suspend), 370 DEVMETHOD(device_resume, bus_generic_resume), 371 DEVMETHOD(device_shutdown, bus_generic_shutdown), 372 {0, 0} 373}; 374 375static driver_t geode_driver = { 376 "geode", 377 geode_methods, 378 0, 379}; 380 381static devclass_t geode_devclass; 382 383DRIVER_MODULE(geode, pci, geode_driver, geode_devclass, 0, 0); 384