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 static unsigned 144 geode_get_timecount(struct timecounter *tc) 145 { 146 return (inl(geode_counter)); 147 } 148 149 static struct timecounter geode_timecounter = { 150 geode_get_timecount, 151 NULL, 152 0xffffffff, 153 27000000, 154 "Geode", 155 1000 156 }; 157 158 static uint64_t 159 geode_cputicks(void) 160 { 161 unsigned c; 162 static unsigned last; 163 static uint64_t offset; 164 165 c = inl(geode_counter); 166 if (c < last) 167 offset += (1LL << 32); 168 last = c; 169 return (offset | c); 170 } 171 172 /* 173 * The GEODE watchdog runs from a 32kHz frequency. One period of that is 174 * 31250 nanoseconds which we round down to 2^14 nanoseconds. The watchdog 175 * consists of a power-of-two prescaler and a 16 bit counter, so the math 176 * is quite simple. The max timeout is 14 + 16 + 13 = 2^43 nsec ~= 2h26m. 177 */ 178 static void 179 geode_watchdog(void *foo __unused, u_int cmd, int *error) 180 { 181 u_int u, p, r; 182 183 u = cmd & WD_INTERVAL; 184 if (u >= 14 && u <= 43) { 185 u -= 14; 186 if (u > 16) { 187 p = u - 16; 188 u -= p; 189 } else { 190 p = 0; 191 } 192 if (u == 16) 193 u = (1 << u) - 1; 194 else 195 u = 1 << u; 196 r = inw(cba + 2) & 0xff00; 197 outw(cba + 2, p | 0xf0 | r); 198 outw(cba, u); 199 *error = 0; 200 } else { 201 outw(cba, 0); 202 } 203 } 204 205 /* 206 * We run MFGPT0 off the 32kHz frequency and prescale by 16384 giving a 207 * period of half a second. 208 * Range becomes 2^30 (= 1 sec) to 2^44 (almost 5 hours) 209 */ 210 static void 211 cs5536_watchdog(void *foo __unused, u_int cmd, int *error) 212 { 213 u_int u, p, s; 214 uint16_t a; 215 uint32_t m; 216 217 a = rdmsr(0x5140000d); 218 219 u = cmd & WD_INTERVAL; 220 if (u >= 30 && u <= 44) { 221 p = 1 << (u - 29); 222 223 /* Set up MFGPT0, 32khz, prescaler 16k, C2 event */ 224 outw(a + 6, 0x030e); 225 /* set comparator 2 */ 226 outw(a + 2, p); 227 /* reset counter */ 228 outw(a + 4, 0); 229 /* Arm reset mechanism */ 230 m = rdmsr(0x51400029); 231 m |= (1 << 24); 232 wrmsr(0x51400029, m); 233 /* Start counter */ 234 outw(a + 6, 0x8000); 235 236 *error = 0; 237 } else { 238 /* 239 * MFGPT_SETUP is write-once 240 * Check if the counter has been setup 241 */ 242 s = inw(a + 6); 243 if (s & (1 << 12)) { 244 /* Stop and reset counter */ 245 outw(a + 6, 0); 246 outw(a + 4, 0); 247 } 248 } 249 } 250 251 /* 252 * The Advantech PCM-582x watchdog expects 0x1 at I/O port 0x0443 253 * every 1.6 secs +/- 30%. Writing 0x0 disables the watchdog 254 * NB: reading the I/O port enables the timer as well 255 */ 256 static void 257 advantech_watchdog(void *foo __unused, u_int cmd, int *error) 258 { 259 u_int u; 260 261 u = cmd & WD_INTERVAL; 262 if (u > 0 && u <= WD_TO_1SEC) { 263 outb(0x0443, 1); 264 *error = 0; 265 } else { 266 outb(0x0443, 0); 267 } 268 } 269 270 static int 271 geode_probe(device_t self) 272 { 273 #define BIOS_OEM_MAXLEN 80 274 static u_char bios_oem[BIOS_OEM_MAXLEN] = "\0"; 275 276 switch (pci_get_devid(self)) { 277 case 0x0515100b: 278 if (geode_counter == 0) { 279 /* 280 * The address of the CBA is written to this register 281 * by the bios, see p161 in data sheet. 282 */ 283 cba = pci_read_config(self, 0x64, 4); 284 if (bootverbose) 285 printf("Geode CBA@ 0x%x\n", cba); 286 geode_counter = cba + 0x08; 287 outl(cba + 0x0d, 2); 288 if (bootverbose) 289 printf("Geode rev: %02x %02x\n", 290 inb(cba + 0x3c), inb(cba + 0x3d)); 291 tc_init(&geode_timecounter); 292 EVENTHANDLER_REGISTER(watchdog_list, geode_watchdog, 293 NULL, 0); 294 set_cputicker(geode_cputicks, 27000000, false); 295 } 296 break; 297 case 0x0510100b: 298 gpio = pci_read_config(self, PCIR_BAR(0), 4); 299 gpio &= ~0x1f; 300 if (bootverbose) 301 printf("Geode GPIO@ = %x\n", gpio); 302 if (bios_oem_strings(&bios_soekris, 303 bios_oem, sizeof bios_oem) > 0 ) { 304 led1b = 20; 305 led1 = led_create(led_func, &led1b, "error"); 306 } else if (bios_oem_strings(&bios_pcengines, 307 bios_oem, sizeof bios_oem) > 0 ) { 308 led1b = -2; 309 led2b = -3; 310 led3b = -18; 311 led1 = led_create(led_func, &led1b, "led1"); 312 led2 = led_create(led_func, &led2b, "led2"); 313 led3 = led_create(led_func, &led3b, "led3"); 314 /* 315 * Turn on first LED so we don't make 316 * people think their box just died. 317 */ 318 led_func(&led1b, 1); 319 } 320 if (*bios_oem) 321 printf("Geode %s\n", bios_oem); 322 break; 323 case 0x01011078: 324 if (bios_oem_strings(&bios_advantech, 325 bios_oem, sizeof bios_oem) > 0 ) { 326 printf("Geode %s\n", bios_oem); 327 EVENTHANDLER_REGISTER(watchdog_list, advantech_watchdog, 328 NULL, 0); 329 } 330 break; 331 case 0x20801022: 332 if (bios_oem_strings(&bios_soekris_55, 333 bios_oem, sizeof bios_oem) > 0 ) { 334 led1b = 6; 335 led1 = led_create(cs5536_led_func, &led1b, "error"); 336 } else if (bios_oem_strings(&bios_pcengines_55, 337 bios_oem, sizeof bios_oem) > 0 ) { 338 led1b = -6; 339 led2b = -25; 340 led3b = -27; 341 led1 = led_create(cs5536_led_func, &led1b, "led1"); 342 led2 = led_create(cs5536_led_func, &led2b, "led2"); 343 led3 = led_create(cs5536_led_func, &led3b, "led3"); 344 /* 345 * Turn on first LED so we don't make 346 * people think their box just died. 347 */ 348 cs5536_led_func(&led1b, 1); 349 } 350 if (*bios_oem) 351 printf("Geode LX: %s\n", bios_oem); 352 if (bootverbose) 353 printf("MFGPT bar: %jx\n", rdmsr(0x5140000d)); 354 EVENTHANDLER_REGISTER(watchdog_list, cs5536_watchdog, NULL, 0); 355 break; 356 } 357 return (ENXIO); 358 } 359 360 static int 361 geode_attach(device_t self) 362 { 363 364 return(ENODEV); 365 } 366 367 static device_method_t geode_methods[] = { 368 /* Device interface */ 369 DEVMETHOD(device_probe, geode_probe), 370 DEVMETHOD(device_attach, geode_attach), 371 DEVMETHOD(device_suspend, bus_generic_suspend), 372 DEVMETHOD(device_resume, bus_generic_resume), 373 DEVMETHOD(device_shutdown, bus_generic_shutdown), 374 {0, 0} 375 }; 376 377 static driver_t geode_driver = { 378 "geode", 379 geode_methods, 380 0, 381 }; 382 383 DRIVER_MODULE(geode, pci, geode_driver, 0, 0); 384