geode.c revision 172216
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: head/sys/i386/i386/geode.c 172216 2007-09-18 09:19:44Z phk $"); 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_advantech = { 72 { 0xfe000, 0xff000 }, 73 { 74 { "**** PCM-582", 5, 33 }, /* PCM-5823 BIOS V1.12 ... */ 75 { "GXm-Cx5530", -11, 35 }, /* 06/07/2002-GXm-Cx5530... */ 76 { NULL, 0, 0 }, 77 } 78}; 79 80static unsigned cba; 81static unsigned gpio; 82static unsigned geode_counter; 83 84static struct cdev *led1, *led2, *led3; 85static int led1b, led2b, led3b; 86 87static void 88led_func(void *ptr, int onoff) 89{ 90 uint32_t u; 91 int bit; 92 93 bit = *(int *)ptr; 94 if (bit < 0) { 95 bit = -bit; 96 onoff = !onoff; 97 } 98 99 u = inl(gpio + 4); 100 if (onoff) 101 u |= 1 << bit; 102 else 103 u &= ~(1 << bit); 104 outl(gpio, u); 105} 106 107static void 108cs5536_led_func(void *ptr, int onoff) 109{ 110 int bit; 111 uint16_t a; 112 113 bit = *(int *)ptr; 114 if (bit < 0) { 115 bit = -bit; 116 onoff = !onoff; 117 } 118 119 a = rdmsr(0x5140000c); 120 if (onoff) 121 outl(a, 1 << bit); 122 else 123 outl(a, 1 << (bit + 16)); 124} 125 126 127static unsigned 128geode_get_timecount(struct timecounter *tc) 129{ 130 return (inl(geode_counter)); 131} 132 133static struct timecounter geode_timecounter = { 134 geode_get_timecount, 135 NULL, 136 0xffffffff, 137 27000000, 138 "Geode", 139 1000 140}; 141 142static uint64_t 143geode_cputicks(void) 144{ 145 unsigned c; 146 static unsigned last; 147 static uint64_t offset; 148 149 c = inl(geode_counter); 150 if (c < last) 151 offset += (1LL << 32); 152 last = c; 153 return (offset | c); 154} 155 156/* 157 * The GEODE watchdog runs from a 32kHz frequency. One period of that is 158 * 31250 nanoseconds which we round down to 2^14 nanoseconds. The watchdog 159 * consists of a power-of-two prescaler and a 16 bit counter, so the math 160 * is quite simple. The max timeout is 14 + 16 + 13 = 2^43 nsec ~= 2h26m. 161 */ 162static void 163geode_watchdog(void *foo __unused, u_int cmd, int *error) 164{ 165 u_int u, p, r; 166 167 u = cmd & WD_INTERVAL; 168 if (u >= 14 && u <= 43) { 169 u -= 14; 170 if (u > 16) { 171 p = u - 16; 172 u -= p; 173 } else { 174 p = 0; 175 } 176 if (u == 16) 177 u = (1 << u) - 1; 178 else 179 u = 1 << u; 180 r = inw(cba + 2) & 0xff00; 181 outw(cba + 2, p | 0xf0 | r); 182 outw(cba, u); 183 *error = 0; 184 } else { 185 outw(cba, 0); 186 } 187} 188 189/* 190 * We run MFGPT0 off the 32kHz frequency and prescale by 16384 giving a 191 * period of half a second. 192 * Range becomes 2^30 (= 1 sec) to 2^44 (almost 5 hours) 193 */ 194static void 195cs5536_watchdog(void *foo __unused, u_int cmd, int *error) 196{ 197 u_int u, p; 198 uint16_t a; 199 uint32_t m; 200 201 a = rdmsr(0x5140000d); 202 m = rdmsr(0x51400029); 203 m &= ~(1 << 24); 204 wrmsr(0x51400029, m); 205 206 u = cmd & WD_INTERVAL; 207 if (u >= 30 && u <= 44) { 208 p = 1 << (u - 29); 209 210 /* Set up MFGPT0, 32khz, prescaler 16k, C2 event */ 211 outw(a + 6, 0x030e); 212 /* set comparator 2 */ 213 outw(a + 2, p); 214 /* reset counter */ 215 outw(a + 4, 0); 216 /* Arm reset mechanism */ 217 m |= (1 << 24); 218 wrmsr(0x51400029, m); 219 /* Start counter */ 220 outw(a + 6, 0x8000); 221 222 *error = 0; 223 } 224} 225 226/* 227 * The Advantech PCM-582x watchdog expects 0x1 at I/O port 0x0443 228 * every 1.6 secs +/- 30%. Writing 0x0 disables the watchdog 229 * NB: reading the I/O port enables the timer as well 230 */ 231static void 232advantech_watchdog(void *foo __unused, u_int cmd, int *error) 233{ 234 u_int u; 235 236 u = cmd & WD_INTERVAL; 237 if (u > 0 && u <= WD_TO_1SEC) { 238 outb(0x0443, 1); 239 *error = 0; 240 } else { 241 outb(0x0443, 0); 242 } 243} 244 245static int 246geode_probe(device_t self) 247{ 248#define BIOS_OEM_MAXLEN 80 249 static u_char bios_oem[BIOS_OEM_MAXLEN] = "\0"; 250 251 switch (pci_get_devid(self)) { 252 case 0x0515100b: 253 if (geode_counter == 0) { 254 /* 255 * The address of the CBA is written to this register 256 * by the bios, see p161 in data sheet. 257 */ 258 cba = pci_read_config(self, 0x64, 4); 259 printf("Geode CBA@ 0x%x\n", cba); 260 geode_counter = cba + 0x08; 261 outl(cba + 0x0d, 2); 262 printf("Geode rev: %02x %02x\n", 263 inb(cba + 0x3c), inb(cba + 0x3d)); 264 tc_init(&geode_timecounter); 265 EVENTHANDLER_REGISTER(watchdog_list, geode_watchdog, 266 NULL, 0); 267 set_cputicker(geode_cputicks, 27000000, 0); 268 } 269 break; 270 case 0x0510100b: 271 gpio = pci_read_config(self, PCIR_BAR(0), 4); 272 gpio &= ~0x1f; 273 printf("Geode GPIO@ = %x\n", gpio); 274 if ( bios_oem_strings(&bios_soekris, 275 bios_oem, BIOS_OEM_MAXLEN) > 0 ) { 276 led1b = 20; 277 led1 = led_create(led_func, &led1b, "error"); 278 } else if ( bios_oem_strings(&bios_pcengines, 279 bios_oem, BIOS_OEM_MAXLEN) > 0 ) { 280 led1b = -2; 281 led2b = -3; 282 led3b = -18; 283 led1 = led_create(led_func, &led1b, "led1"); 284 led2 = led_create(led_func, &led2b, "led2"); 285 led3 = led_create(led_func, &led3b, "led3"); 286 /* 287 * Turn on first LED so we don't make 288 * people think their box just died. 289 */ 290 led_func(&led1b, 1); 291 } 292 if ( strlen(bios_oem) ) 293 printf("Geode %s\n", bios_oem); 294 break; 295 case 0x01011078: 296 if ( bios_oem_strings(&bios_advantech, 297 bios_oem, BIOS_OEM_MAXLEN) > 0 ) { 298 printf("Geode %s\n", bios_oem); 299 EVENTHANDLER_REGISTER(watchdog_list, advantech_watchdog, 300 NULL, 0); 301 } 302 break; 303 case 0x20801022: 304 if ( bios_oem_strings(&bios_soekris_55, 305 bios_oem, BIOS_OEM_MAXLEN) > 0 ) { 306 printf("Geode LX: %s\n", bios_oem); 307 led1b = 6; 308 led1 = led_create(cs5536_led_func, &led1b, "error"); 309 } 310 printf("MFGPT bar: %jx\n", rdmsr(0x5140000d)); 311 EVENTHANDLER_REGISTER(watchdog_list, cs5536_watchdog, 312 NULL, 0); 313 break; 314 } 315 return (ENXIO); 316} 317 318static int 319geode_attach(device_t self) 320{ 321 322 return(ENODEV); 323} 324 325static device_method_t geode_methods[] = { 326 /* Device interface */ 327 DEVMETHOD(device_probe, geode_probe), 328 DEVMETHOD(device_attach, geode_attach), 329 DEVMETHOD(device_suspend, bus_generic_suspend), 330 DEVMETHOD(device_resume, bus_generic_resume), 331 DEVMETHOD(device_shutdown, bus_generic_shutdown), 332 {0, 0} 333}; 334 335static driver_t geode_driver = { 336 "geode", 337 geode_methods, 338 0, 339}; 340 341static devclass_t geode_devclass; 342 343DRIVER_MODULE(geode, pci, geode_driver, geode_devclass, 0, 0); 344