1119608Sphk/*- 2126349Sphk * Copyright (c) 2003-2004 Poul-Henning Kamp 3119608Sphk * All rights reserved. 4119608Sphk * 5119608Sphk * Redistribution and use in source and binary forms, with or without 6119608Sphk * modification, are permitted provided that the following conditions 7119608Sphk * are met: 8119608Sphk * 1. Redistributions of source code must retain the above copyright 9119608Sphk * notice, this list of conditions and the following disclaimer. 10119608Sphk * 2. Redistributions in binary form must reproduce the above copyright 11119608Sphk * notice, this list of conditions and the following disclaimer in the 12119608Sphk * documentation and/or other materials provided with the distribution. 13119608Sphk * 14119608Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15119608Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16119608Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17119608Sphk * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18119608Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19119608Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20119608Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21119608Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22119608Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23119608Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24119608Sphk * SUCH DAMAGE. 25119608Sphk */ 26119608Sphk 27119608Sphk#include <sys/cdefs.h> 28119608Sphk__FBSDID("$FreeBSD: releng/10.3/sys/i386/i386/geode.c 208111 2010-05-15 10:31:11Z phk $"); 29119608Sphk 30119608Sphk#include <sys/param.h> 31119608Sphk#include <sys/systm.h> 32119608Sphk#include <sys/timetc.h> 33119608Sphk#include <sys/bus.h> 34119608Sphk#include <sys/kernel.h> 35130041Sphk#include <sys/module.h> 36126387Sphk#include <sys/watchdog.h> 37126349Sphk#include <dev/pci/pcireg.h> 38119608Sphk#include <dev/pci/pcivar.h> 39130041Sphk#include <dev/led/led.h> 40130041Sphk#include <machine/pc/bios.h> 41119608Sphk 42148231Sphkstatic struct bios_oem bios_soekris = { 43176153Sphk { 0xf0000, 0xf1000 }, 44176153Sphk { 45176153Sphk { "Soekris", 0, 8 }, /* Soekris Engineering. */ 46176153Sphk { "net4", 0, 8 }, /* net45xx */ 47176153Sphk { "comBIOS", 0, 54 }, /* comBIOS ver. 1.26a 20040819 ... */ 48176153Sphk { NULL, 0, 0 }, 49176153Sphk } 50148231Sphk}; 51148231Sphk 52172216Sphkstatic struct bios_oem bios_soekris_55 = { 53176153Sphk { 0xf0000, 0xf1000 }, 54176153Sphk { 55176153Sphk { "Soekris", 0, 8 }, /* Soekris Engineering. */ 56176153Sphk { "net5", 0, 8 }, /* net5xxx */ 57176153Sphk { "comBIOS", 0, 54 }, /* comBIOS ver. 1.26a 20040819 ... */ 58176153Sphk { NULL, 0, 0 }, 59176153Sphk } 60172216Sphk}; 61172216Sphk 62148231Sphkstatic struct bios_oem bios_pcengines = { 63176153Sphk { 0xf9000, 0xfa000 }, 64176153Sphk { 65176153Sphk { "PC Engines WRAP", 0, 28 }, /* PC Engines WRAP.1C v1.03 */ 66176153Sphk { "tinyBIOS", 0, 28 }, /* tinyBIOS V1.4a (C)1997-2003 */ 67176153Sphk { NULL, 0, 0 }, 68176153Sphk } 69148231Sphk}; 70148231Sphk 71176153Sphkstatic struct bios_oem bios_pcengines_55 = { 72176153Sphk { 0xf9000, 0xfa000 }, 73176153Sphk { 74176153Sphk { "PC Engines ALIX", 0, 28 }, /* PC Engines ALIX */ 75176153Sphk { "tinyBIOS", 0, 28 }, /* tinyBIOS V1.4a (C)1997-2005 */ 76176153Sphk { NULL, 0, 0 }, 77176153Sphk } 78176153Sphk}; 79176153Sphk 80148231Sphkstatic struct bios_oem bios_advantech = { 81176153Sphk { 0xfe000, 0xff000 }, 82176153Sphk { 83176153Sphk { "**** PCM-582", 5, 33 }, /* PCM-5823 BIOS V1.12 ... */ 84176153Sphk { "GXm-Cx5530", -11, 35 }, /* 06/07/2002-GXm-Cx5530... */ 85176153Sphk { NULL, 0, 0 }, 86176153Sphk } 87148231Sphk}; 88148231Sphk 89119608Sphkstatic unsigned cba; 90126349Sphkstatic unsigned gpio; 91119608Sphkstatic unsigned geode_counter; 92119608Sphk 93130585Sphkstatic struct cdev *led1, *led2, *led3; 94130041Sphkstatic int led1b, led2b, led3b; 95126349Sphk 96126349Sphkstatic void 97130041Sphkled_func(void *ptr, int onoff) 98126349Sphk{ 99126349Sphk uint32_t u; 100130041Sphk int bit; 101126349Sphk 102130041Sphk bit = *(int *)ptr; 103130041Sphk if (bit < 0) { 104130041Sphk bit = -bit; 105130041Sphk onoff = !onoff; 106130041Sphk } 107130041Sphk 108126349Sphk u = inl(gpio + 4); 109126349Sphk if (onoff) 110130041Sphk u |= 1 << bit; 111126349Sphk else 112130041Sphk u &= ~(1 << bit); 113126349Sphk outl(gpio, u); 114126349Sphk} 115126349Sphk 116172216Sphkstatic void 117172216Sphkcs5536_led_func(void *ptr, int onoff) 118172216Sphk{ 119172216Sphk int bit; 120172216Sphk uint16_t a; 121126349Sphk 122172216Sphk bit = *(int *)ptr; 123172216Sphk if (bit < 0) { 124172216Sphk bit = -bit; 125172216Sphk onoff = !onoff; 126172216Sphk } 127172216Sphk 128172216Sphk a = rdmsr(0x5140000c); 129176153Sphk if (bit >= 16) { 130176153Sphk a += 0x80; 131176153Sphk bit -= 16; 132176153Sphk } 133176153Sphk 134172216Sphk if (onoff) 135172216Sphk outl(a, 1 << bit); 136172216Sphk else 137172216Sphk outl(a, 1 << (bit + 16)); 138172216Sphk} 139172216Sphk 140172216Sphk 141119608Sphkstatic unsigned 142119608Sphkgeode_get_timecount(struct timecounter *tc) 143119608Sphk{ 144119608Sphk return (inl(geode_counter)); 145119608Sphk} 146119608Sphk 147119608Sphkstatic struct timecounter geode_timecounter = { 148119608Sphk geode_get_timecount, 149119608Sphk NULL, 150119608Sphk 0xffffffff, 151119608Sphk 27000000, 152119608Sphk "Geode", 153119608Sphk 1000 154119608Sphk}; 155119608Sphk 156155534Sphkstatic uint64_t 157155534Sphkgeode_cputicks(void) 158155534Sphk{ 159155534Sphk unsigned c; 160155534Sphk static unsigned last; 161155534Sphk static uint64_t offset; 162155534Sphk 163155534Sphk c = inl(geode_counter); 164155534Sphk if (c < last) 165155534Sphk offset += (1LL << 32); 166155534Sphk last = c; 167155534Sphk return (offset | c); 168155534Sphk} 169155534Sphk 170126387Sphk/* 171126387Sphk * The GEODE watchdog runs from a 32kHz frequency. One period of that is 172126387Sphk * 31250 nanoseconds which we round down to 2^14 nanoseconds. The watchdog 173126387Sphk * consists of a power-of-two prescaler and a 16 bit counter, so the math 174126387Sphk * is quite simple. The max timeout is 14 + 16 + 13 = 2^43 nsec ~= 2h26m. 175126387Sphk */ 176126387Sphkstatic void 177126387Sphkgeode_watchdog(void *foo __unused, u_int cmd, int *error) 178126387Sphk{ 179126387Sphk u_int u, p, r; 180126387Sphk 181126387Sphk u = cmd & WD_INTERVAL; 182167950Sn_hibma if (u >= 14 && u <= 43) { 183126387Sphk u -= 14; 184126387Sphk if (u > 16) { 185126387Sphk p = u - 16; 186126387Sphk u -= p; 187126387Sphk } else { 188126387Sphk p = 0; 189126387Sphk } 190126387Sphk if (u == 16) 191126387Sphk u = (1 << u) - 1; 192126387Sphk else 193126387Sphk u = 1 << u; 194126387Sphk r = inw(cba + 2) & 0xff00; 195126387Sphk outw(cba + 2, p | 0xf0 | r); 196126387Sphk outw(cba, u); 197126387Sphk *error = 0; 198126387Sphk } else { 199126387Sphk outw(cba, 0); 200126387Sphk } 201126387Sphk} 202126387Sphk 203148231Sphk/* 204172216Sphk * We run MFGPT0 off the 32kHz frequency and prescale by 16384 giving a 205172216Sphk * period of half a second. 206172216Sphk * Range becomes 2^30 (= 1 sec) to 2^44 (almost 5 hours) 207172216Sphk */ 208172216Sphkstatic void 209172216Sphkcs5536_watchdog(void *foo __unused, u_int cmd, int *error) 210172216Sphk{ 211208111Sphk u_int u, p, s; 212172216Sphk uint16_t a; 213172216Sphk uint32_t m; 214172216Sphk 215172216Sphk a = rdmsr(0x5140000d); 216172216Sphk 217172216Sphk u = cmd & WD_INTERVAL; 218172216Sphk if (u >= 30 && u <= 44) { 219172216Sphk p = 1 << (u - 29); 220172216Sphk 221172216Sphk /* Set up MFGPT0, 32khz, prescaler 16k, C2 event */ 222172216Sphk outw(a + 6, 0x030e); 223172216Sphk /* set comparator 2 */ 224172216Sphk outw(a + 2, p); 225172216Sphk /* reset counter */ 226172216Sphk outw(a + 4, 0); 227172216Sphk /* Arm reset mechanism */ 228208111Sphk m = rdmsr(0x51400029); 229172216Sphk m |= (1 << 24); 230172216Sphk wrmsr(0x51400029, m); 231172216Sphk /* Start counter */ 232172216Sphk outw(a + 6, 0x8000); 233172216Sphk 234172216Sphk *error = 0; 235208111Sphk } else { 236208111Sphk /* 237208111Sphk * MFGPT_SETUP is write-once 238208111Sphk * Check if the counter has been setup 239208111Sphk */ 240208111Sphk s = inw(a + 6); 241208111Sphk if (s & (1 << 12)) { 242208111Sphk /* Stop and reset counter */ 243208111Sphk outw(a + 6, 0); 244208111Sphk outw(a + 4, 0); 245208111Sphk } 246172216Sphk } 247172216Sphk} 248172216Sphk 249172216Sphk/* 250148231Sphk * The Advantech PCM-582x watchdog expects 0x1 at I/O port 0x0443 251148231Sphk * every 1.6 secs +/- 30%. Writing 0x0 disables the watchdog 252148231Sphk * NB: reading the I/O port enables the timer as well 253148231Sphk */ 254148231Sphkstatic void 255148231Sphkadvantech_watchdog(void *foo __unused, u_int cmd, int *error) 256148231Sphk{ 257167950Sn_hibma u_int u; 258167950Sn_hibma 259167950Sn_hibma u = cmd & WD_INTERVAL; 260167950Sn_hibma if (u > 0 && u <= WD_TO_1SEC) { 261156335Sphk outb(0x0443, 1); 262156335Sphk *error = 0; 263156335Sphk } else { 264156335Sphk outb(0x0443, 0); 265156335Sphk } 266148231Sphk} 267148231Sphk 268119608Sphkstatic int 269119608Sphkgeode_probe(device_t self) 270119608Sphk{ 271148231Sphk#define BIOS_OEM_MAXLEN 80 272148231Sphk static u_char bios_oem[BIOS_OEM_MAXLEN] = "\0"; 273119608Sphk 274172216Sphk switch (pci_get_devid(self)) { 275172216Sphk case 0x0515100b: 276126349Sphk if (geode_counter == 0) { 277126349Sphk /* 278126349Sphk * The address of the CBA is written to this register 279126349Sphk * by the bios, see p161 in data sheet. 280126349Sphk */ 281126349Sphk cba = pci_read_config(self, 0x64, 4); 282176153Sphk if (bootverbose) 283176153Sphk printf("Geode CBA@ 0x%x\n", cba); 284126349Sphk geode_counter = cba + 0x08; 285126349Sphk outl(cba + 0x0d, 2); 286176153Sphk if (bootverbose) 287176153Sphk printf("Geode rev: %02x %02x\n", 288176153Sphk inb(cba + 0x3c), inb(cba + 0x3d)); 289126349Sphk tc_init(&geode_timecounter); 290126387Sphk EVENTHANDLER_REGISTER(watchdog_list, geode_watchdog, 291126387Sphk NULL, 0); 292155534Sphk set_cputicker(geode_cputicks, 27000000, 0); 293126349Sphk } 294172216Sphk break; 295172216Sphk case 0x0510100b: 296126349Sphk gpio = pci_read_config(self, PCIR_BAR(0), 4); 297126349Sphk gpio &= ~0x1f; 298176153Sphk if (bootverbose) 299176153Sphk printf("Geode GPIO@ = %x\n", gpio); 300176153Sphk if (bios_oem_strings(&bios_soekris, 301176153Sphk bios_oem, sizeof bios_oem) > 0 ) { 302130041Sphk led1b = 20; 303130041Sphk led1 = led_create(led_func, &led1b, "error"); 304176153Sphk } else if (bios_oem_strings(&bios_pcengines, 305176153Sphk bios_oem, sizeof bios_oem) > 0 ) { 306130041Sphk led1b = -2; 307130041Sphk led2b = -3; 308130041Sphk led3b = -18; 309130041Sphk led1 = led_create(led_func, &led1b, "led1"); 310130041Sphk led2 = led_create(led_func, &led2b, "led2"); 311130041Sphk led3 = led_create(led_func, &led3b, "led3"); 312130041Sphk /* 313148231Sphk * Turn on first LED so we don't make 314148231Sphk * people think their box just died. 315148231Sphk */ 316130041Sphk led_func(&led1b, 1); 317130041Sphk } 318176153Sphk if (*bios_oem) 319148231Sphk printf("Geode %s\n", bios_oem); 320172216Sphk break; 321172216Sphk case 0x01011078: 322176153Sphk if (bios_oem_strings(&bios_advantech, 323176153Sphk bios_oem, sizeof bios_oem) > 0 ) { 324148231Sphk printf("Geode %s\n", bios_oem); 325148231Sphk EVENTHANDLER_REGISTER(watchdog_list, advantech_watchdog, 326148231Sphk NULL, 0); 327148231Sphk } 328172216Sphk break; 329172216Sphk case 0x20801022: 330176153Sphk if (bios_oem_strings(&bios_soekris_55, 331176153Sphk bios_oem, sizeof bios_oem) > 0 ) { 332172216Sphk led1b = 6; 333172216Sphk led1 = led_create(cs5536_led_func, &led1b, "error"); 334176153Sphk } else if (bios_oem_strings(&bios_pcengines_55, 335176153Sphk bios_oem, sizeof bios_oem) > 0 ) { 336176153Sphk led1b = -6; 337176153Sphk led2b = -25; 338176153Sphk led3b = -27; 339176153Sphk led1 = led_create(cs5536_led_func, &led1b, "led1"); 340176153Sphk led2 = led_create(cs5536_led_func, &led2b, "led2"); 341176153Sphk led3 = led_create(cs5536_led_func, &led3b, "led3"); 342176153Sphk /* 343176153Sphk * Turn on first LED so we don't make 344176153Sphk * people think their box just died. 345176153Sphk */ 346176153Sphk cs5536_led_func(&led1b, 1); 347172216Sphk } 348176153Sphk if (*bios_oem) 349176153Sphk printf("Geode LX: %s\n", bios_oem); 350176153Sphk if (bootverbose) 351176153Sphk printf("MFGPT bar: %jx\n", rdmsr(0x5140000d)); 352176153Sphk EVENTHANDLER_REGISTER(watchdog_list, cs5536_watchdog, NULL, 0); 353172216Sphk break; 354126349Sphk } 355119608Sphk return (ENXIO); 356119608Sphk} 357119608Sphk 358119608Sphkstatic int 359119608Sphkgeode_attach(device_t self) 360119608Sphk{ 361119608Sphk 362119608Sphk return(ENODEV); 363119608Sphk} 364119608Sphk 365119608Sphkstatic device_method_t geode_methods[] = { 366119608Sphk /* Device interface */ 367119608Sphk DEVMETHOD(device_probe, geode_probe), 368119608Sphk DEVMETHOD(device_attach, geode_attach), 369119608Sphk DEVMETHOD(device_suspend, bus_generic_suspend), 370119608Sphk DEVMETHOD(device_resume, bus_generic_resume), 371119608Sphk DEVMETHOD(device_shutdown, bus_generic_shutdown), 372119608Sphk {0, 0} 373119608Sphk}; 374119608Sphk 375119608Sphkstatic driver_t geode_driver = { 376119608Sphk "geode", 377119608Sphk geode_methods, 378119608Sphk 0, 379119608Sphk}; 380119608Sphk 381119608Sphkstatic devclass_t geode_devclass; 382119608Sphk 383119608SphkDRIVER_MODULE(geode, pci, geode_driver, geode_devclass, 0, 0); 384