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