From a75ee873df5802d7c89b1ea5a1d04acd6318a42d Mon Sep 17 00:00:00 2001 From: Alex Hornung Date: Sun, 4 Oct 2009 13:09:57 +0100 Subject: [PATCH] geode/cs5536 - Add support for wdog/gpio/timers/identfication * Add drivers for geode lx and cs5536 chips to register watchdogs, timers and gpio these chips have. * Add missing defines for the geode and cs5536 timers and pci device ids. Partially-Obtained-from: OpenBSD Partially-Obtained-from: FreeBSD --- sys/bus/pci/pcidevs.h | 3 + sys/platform/pc32/i386/cs5536.c | 503 ++++++++++++++++++++++++++++++++ sys/platform/pc32/i386/geode.c | 243 +++++++++++++++ sys/sys/systimer.h | 4 + 4 files changed, 753 insertions(+) create mode 100644 sys/platform/pc32/i386/cs5536.c create mode 100644 sys/platform/pc32/i386/geode.c diff --git a/sys/bus/pci/pcidevs.h b/sys/bus/pci/pcidevs.h index f3daf04c55..ac1340d6d7 100644 --- a/sys/bus/pci/pcidevs.h +++ b/sys/bus/pci/pcidevs.h @@ -824,6 +824,8 @@ #define PCI_PRODUCT_AMD_PCNET_PCI 0x2000 /* PCnet-PCI Ethernet */ #define PCI_PRODUCT_AMD_PCNET_HOME 0x2001 /* PCnet-Home HomePNA Ethernet */ #define PCI_PRODUCT_AMD_PCSCSI_PCI 0x2020 /* PCscsi-PCI SCSI */ +#define PCI_PRODUCT_AMD_GEODE_LX_PCHB 0x2080 /* Geode LX */ +#define PCI_PRODUCT_AMD_CS5536_PCIB 0x2090 /* CS5536 ISA */ #define PCI_PRODUCT_AMD_SC520_SC 0x3000 /* Elan SC520 System Controller */ #define PCI_PRODUCT_AMD_SC751_SC 0x7006 /* AMD751 System Controller */ #define PCI_PRODUCT_AMD_SC751_PPB 0x7007 /* AMD751 PCI-to-PCI Bridge */ @@ -2081,6 +2083,7 @@ #define PCI_PRODUCT_NS_DP83815 0x0020 /* DP83815 10/100 Ethernet */ #define PCI_PRODUCT_NS_DP83820 0x0022 /* DP83820 10/100/1000 Ethernet */ #define PCI_PRODUCT_NS_NS87410 0xd001 /* NS87410 */ +#define PCI_PRODUCT_NS_SCx200_XBUS 0x0505 /* SCx200 X-BUS */ #define PCI_PRODUCT_NS_SC1100_ISA 0x0510 /* SC1100 PCI-ISA bridge */ #define PCI_PRODUCT_NS_SC1100_ACPI 0x0511 /* SC1100 SMI/ACPI */ #define PCI_PRODUCT_NS_SC1100_IDE 0x0512 /* SC1100 PCI IDE */ diff --git a/sys/platform/pc32/i386/cs5536.c b/sys/platform/pc32/i386/cs5536.c new file mode 100644 index 0000000000..b2765c3890 --- /dev/null +++ b/sys/platform/pc32/i386/cs5536.c @@ -0,0 +1,503 @@ +/* + * Copyright (c) 2007 Marc Balmer + * Copyright (c) 2007 Michael Shalayeff + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * + * Copyright (c) 2009 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Alex Hornung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include "opt_cpu.h" +#include "use_gpio.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AMD5536_TIMER_FREQ 3579545 + +#define AMD5536_REV 0x51400017 +#define AMD5536_REV_MASK 0xff +#define AMD5536_TMC 0x51400050 + +/* Multi-Functional General Purpose Timer */ +#define MSR_LBAR_MFGPT 0x5140000d +#define AMD5536_MFGPT0_CMP1 0x00000000 +#define AMD5536_MFGPT0_CMP2 0x00000002 +#define AMD5536_MFGPT0_CNT 0x00000004 +#define AMD5536_MFGPT0_SETUP 0x00000006 +#define AMD5536_MFGPT_DIV_MASK 0x000f /* div = 1 << mask */ +#define AMD5536_MFGPT_CLKSEL 0x0010 +#define AMD5536_MFGPT_REV_EN 0x0020 +#define AMD5536_MFGPT_CMP1DIS 0x0000 +#define AMD5536_MFGPT_CMP1EQ 0x0040 +#define AMD5536_MFGPT_CMP1GE 0x0080 +#define AMD5536_MFGPT_CMP1EV 0x00c0 +#define AMD5536_MFGPT_CMP2DIS 0x0000 +#define AMD5536_MFGPT_CMP2EQ 0x0100 +#define AMD5536_MFGPT_CMP2GE 0x0200 +#define AMD5536_MFGPT_CMP2EV 0x0300 +#define AMD5536_MFGPT_STOP_EN 0x0800 +#define AMD5536_MFGPT_SET 0x1000 +#define AMD5536_MFGPT_CMP1 0x2000 +#define AMD5536_MFGPT_CMP2 0x4000 +#define AMD5536_MFGPT_CNT_EN 0x8000 +#define AMD5536_MFGPT_IRQ 0x51400028 +#define AMD5536_MFGPT0_C1_IRQM 0x00000001 +#define AMD5536_MFGPT1_C1_IRQM 0x00000002 +#define AMD5536_MFGPT2_C1_IRQM 0x00000004 +#define AMD5536_MFGPT3_C1_IRQM 0x00000008 +#define AMD5536_MFGPT4_C1_IRQM 0x00000010 +#define AMD5536_MFGPT5_C1_IRQM 0x00000020 +#define AMD5536_MFGPT6_C1_IRQM 0x00000040 +#define AMD5536_MFGPT7_C1_IRQM 0x00000080 +#define AMD5536_MFGPT0_C2_IRQM 0x00000100 +#define AMD5536_MFGPT1_C2_IRQM 0x00000200 +#define AMD5536_MFGPT2_C2_IRQM 0x00000400 +#define AMD5536_MFGPT3_C2_IRQM 0x00000800 +#define AMD5536_MFGPT4_C2_IRQM 0x00001000 +#define AMD5536_MFGPT5_C2_IRQM 0x00002000 +#define AMD5536_MFGPT6_C2_IRQM 0x00004000 +#define AMD5536_MFGPT7_C2_IRQM 0x00008000 +#define AMD5536_MFGPT_NR 0x51400029 +#define AMD5536_MFGPT0_C1_NMIM 0x00000001 +#define AMD5536_MFGPT1_C1_NMIM 0x00000002 +#define AMD5536_MFGPT2_C1_NMIM 0x00000004 +#define AMD5536_MFGPT3_C1_NMIM 0x00000008 +#define AMD5536_MFGPT4_C1_NMIM 0x00000010 +#define AMD5536_MFGPT5_C1_NMIM 0x00000020 +#define AMD5536_MFGPT6_C1_NMIM 0x00000040 +#define AMD5536_MFGPT7_C1_NMIM 0x00000080 +#define AMD5536_MFGPT0_C2_NMIM 0x00000100 +#define AMD5536_MFGPT1_C2_NMIM 0x00000200 +#define AMD5536_MFGPT2_C2_NMIM 0x00000400 +#define AMD5536_MFGPT3_C2_NMIM 0x00000800 +#define AMD5536_MFGPT4_C2_NMIM 0x00001000 +#define AMD5536_MFGPT5_C2_NMIM 0x00002000 +#define AMD5536_MFGPT6_C2_NMIM 0x00004000 +#define AMD5536_MFGPT7_C2_NMIM 0x00008000 +#define AMD5536_NMI_LEG 0x00010000 +#define AMD5536_MFGPT0_C2_RSTEN 0x01000000 +#define AMD5536_MFGPT1_C2_RSTEN 0x02000000 +#define AMD5536_MFGPT2_C2_RSTEN 0x04000000 +#define AMD5536_MFGPT3_C2_RSTEN 0x08000000 +#define AMD5536_MFGPT4_C2_RSTEN 0x10000000 +#define AMD5536_MFGPT5_C2_RSTEN 0x20000000 +#define AMD5536_MFGPT_SETUP 0x5140002b + +/* GPIO */ +#define MSR_LBAR_GPIO 0x5140000c +#define AMD5536_GPIO_NPINS 32 +#define AMD5536_GPIOH_OFFSET 0x80 /* high bank register offset */ +#define AMD5536_GPIO_OUT_VAL 0x00 /* output value */ +#define AMD5536_GPIO_OUT_EN 0x04 /* output enable */ +#define AMD5536_GPIO_OD_EN 0x08 /* open-drain enable */ +#define AMD5536_GPIO_OUT_INVRT_EN 0x0c /* invert output */ +#define AMD5536_GPIO_PU_EN 0x18 /* pull-up enable */ +#define AMD5536_GPIO_PD_EN 0x1c /* pull-down enable */ +#define AMD5536_GPIO_IN_EN 0x20 /* input enable */ +#define AMD5536_GPIO_IN_INVRT_EN 0x24 /* invert input */ +#define AMD5536_GPIO_READ_BACK 0x30 /* read back value */ + + +struct cs5536_softc { + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + +#if NGPIO > 0 + /* GPIO interface */ + bus_space_tag_t sc_gpio_iot; + bus_space_handle_t sc_gpio_ioh; + struct gpio sc_gpio_gc; + gpio_pin_t sc_gpio_pins[AMD5536_GPIO_NPINS]; +#endif +}; + +static struct cs5536_softc cs5536_sc; + +#if NGPIO > 0 +int cs5536_gpio_pin_read(void *arg, int pin); +void cs5536_gpio_pin_write(void *arg, int pin, int value); +void cs5536_gpio_pin_ctl(void *arg, int pin, int flags); +#endif +static sysclock_t cs5536_get_timecount(void); + +static struct bios_oem bios_soekris_55 = { + { 0xf0000, 0xf1000 }, + { + { "Soekris", 0, 8 }, /* Soekris Engineering. */ + { "net5", 0, 8 }, /* net5xxx */ + { "comBIOS", 0, 54 }, /* comBIOS ver. 1.26a 20040819 ... */ + { NULL, 0, 0 }, + } +}; + +static struct bios_oem bios_pcengines_55 = { + { 0xf9000, 0xfa000 }, + { + { "PC Engines ALIX", 0, 28 }, /* PC Engines ALIX */ + { "tinyBIOS", 0, 28 }, /* tinyBIOS V1.4a (C)1997-2005 */ + { NULL, 0, 0 }, + } +}; + +#if NGPIO > 0 +int +cs5536_gpio_pin_read(void *arg, int pin) +{ + uint32_t data; + uint16_t port; + int reg, off = 0; + + port = rdmsr(MSR_LBAR_GPIO); + + reg = AMD5536_GPIO_IN_EN; + if (pin > 15) { + pin &= 0x0f; + off = AMD5536_GPIOH_OFFSET; + } + reg += off; + data = bus_space_read_4(cs5536_sc.sc_gpio_iot, cs5536_sc.sc_gpio_ioh, reg); + + if (data & (1 << pin)) + reg = AMD5536_GPIO_READ_BACK + off; + else + reg = AMD5536_GPIO_OUT_VAL + off; + + data = bus_space_read_4(cs5536_sc.sc_gpio_iot, cs5536_sc.sc_gpio_ioh, reg); + + return data & 1 << pin ? GPIO_PIN_HIGH : GPIO_PIN_LOW; +} + +void +cs5536_gpio_pin_write(void *arg, int pin, int value) +{ + uint32_t data; + uint16_t port; + int reg; + + port = rdmsr(MSR_LBAR_GPIO); + + reg = AMD5536_GPIO_OUT_VAL; + if (pin > 15) { + pin &= 0x0f; + reg += AMD5536_GPIOH_OFFSET; + } + + if (value == 1) + data = 1 << pin; + else + data = 1 << (pin + 16); + + bus_space_write_4(cs5536_sc.sc_gpio_iot, cs5536_sc.sc_gpio_ioh, reg, data); + //write_data_4(port, reg, data); + //outl(port + reg, data); +} + +void +cs5536_gpio_pin_ctl(void *arg, int pin, int flags) +{ + int n, reg[7], val[7], nreg = 0, off = 0; + + if (pin > 15) { + pin &= 0x0f; + off = AMD5536_GPIOH_OFFSET; + } + + reg[nreg] = AMD5536_GPIO_IN_EN + off; + if (flags & GPIO_PIN_INPUT) + val[nreg++] = 1 << pin; + else + val[nreg++] = 1 << (pin + 16); + + reg[nreg] = AMD5536_GPIO_OUT_EN + off; + if (flags & GPIO_PIN_OUTPUT) + val[nreg++] = 1 << pin; + else + val[nreg++] = 1 << (pin + 16); + + reg[nreg] = AMD5536_GPIO_OD_EN + off; + if (flags & GPIO_PIN_OPENDRAIN) + val[nreg++] = 1 << pin; + else + val[nreg++] = 1 << (pin + 16); + + reg[nreg] = AMD5536_GPIO_PU_EN + off; + if (flags & GPIO_PIN_PULLUP) + val[nreg++] = 1 << pin; + else + val[nreg++] = 1 << (pin + 16); + + reg[nreg] = AMD5536_GPIO_PD_EN + off; + if (flags & GPIO_PIN_PULLDOWN) + val[nreg++] = 1 << pin; + else + val[nreg++] = 1 << (pin + 16); + + reg[nreg] = AMD5536_GPIO_IN_INVRT_EN + off; + if (flags & GPIO_PIN_INVIN) + val[nreg++] = 1 << pin; + else + val[nreg++] = 1 << (pin + 16); + + reg[nreg] = AMD5536_GPIO_OUT_INVRT_EN + off; + if (flags & GPIO_PIN_INVOUT) + val[nreg++] = 1 << pin; + else + val[nreg++] = 1 << (pin + 16); + + /* set flags */ + for (n = 0; n < nreg; n++) + bus_space_write_4(cs5536_sc.sc_gpio_iot, cs5536_sc.sc_gpio_ioh, reg[n], + val[n]); +} +#endif /* NGPIO */ + +static void +cs5536_cputimer_construct(struct cputimer *timer, sysclock_t oldclock) +{ + timer->base = 0; + timer->base = oldclock - cs5536_get_timecount(); +} + +static struct cputimer cs5536_timer = { + SLIST_ENTRY_INITIALIZER, + "CS5536", + CPUTIMER_PRI_CS5536, + CPUTIMER_GEODE, + cs5536_get_timecount, + cputimer_default_fromhz, + cputimer_default_fromus, + cs5536_cputimer_construct, + cputimer_default_destruct, + AMD5536_TIMER_FREQ, + 0, 0, 0 +}; + +static sysclock_t +cs5536_get_timecount(void) +{ + return (cs5536_timer.base + rdmsr(AMD5536_TMC)); +} + +#ifdef WATCHDOG_ENABLE +static int +cs5536_watchdog(void *unused, int period) +{ + if (period > 0xffff) + period = 0xffff; + + bus_space_write_2(cs5536_sc.sc_iot, cs5536_sc.sc_ioh, AMD5536_MFGPT0_SETUP, + AMD5536_MFGPT_CNT_EN | AMD5536_MFGPT_CMP2); + /* reset counter */ + bus_space_write_2(cs5536_sc.sc_iot, cs5536_sc.sc_ioh, AMD5536_MFGPT0_CNT, 0); + /* set comparator 2 */ + bus_space_write_2(cs5536_sc.sc_iot, cs5536_sc.sc_ioh, AMD5536_MFGPT0_CMP2, period); + + if (period) { + wrmsr(AMD5536_MFGPT_NR, + rdmsr(AMD5536_MFGPT_NR) | AMD5536_MFGPT0_C2_RSTEN); + } else { + wrmsr(AMD5536_MFGPT_NR, + rdmsr(AMD5536_MFGPT_NR) & ~AMD5536_MFGPT0_C2_RSTEN); + } + + return period; +} + +static struct watchdog cs5536_wdog = { + .name = "AMD CS5536", + .wdog_fn = cs5536_watchdog, + .arg = NULL, + .period_max = 0xffff, +}; +#endif /* WATCHDOG_ENABLE */ + +static int +cs5536_probe(device_t self) +{ + static int probed = 0; + + if (probed) + return ENXIO; + + if (pci_get_vendor(self) == PCI_VENDOR_AMD && + (pci_get_device(self) == PCI_PRODUCT_AMD_CS5536_PCIB || + /* XXX: OpenBSD doesn't attach to this one, but free does, though no counter */ + pci_get_device(self) == PCI_PRODUCT_AMD_GEODE_LX_PCHB)) { + /* device_set_desc(self, ...) */ + probed = 1; + return 0; + } + + return ENXIO; +} + +static int +cs5536_attach(device_t self) +{ +#define BIOS_OEM_MAXLEN 80 + static u_char bios_oem[BIOS_OEM_MAXLEN] = "\0"; + static int attached = 0; + int i; +#if 0 /* Watchdog stuff */ + EVENTHANDLER_REGISTER(watchdog_list, cs5536_watchdog, NULL, 0); +#endif + if (attached) + return ENODEV; + + attached = 1; + kprintf("AMD CS5536: rev %d, 32-bit %uHz timer\n", + (int)rdmsr(AMD5536_REV) & AMD5536_REV_MASK, + cs5536_timer.freq); + + /* enable timer */ + cputimer_register(&cs5536_timer); + cputimer_select(&cs5536_timer, 0); + + /* bus_space_map(sc->sc_iot, wa & 0xffff, 64, 0, &sc->sc_ioh)) */ + cs5536_sc.sc_iot = I386_BUS_SPACE_IO; + cs5536_sc.sc_ioh = rdmsr(MSR_LBAR_MFGPT); + +#ifdef WATCHDOG_ENABLE + /* enable watchdog and configure */ + bus_space_write_2(cs5536_sc.sc_iot, cs5536_sc.sc_ioh, AMD5536_MFGPT0_SETUP, + AMD5536_MFGPT_CNT_EN | AMD5536_MFGPT_CMP2EV | + AMD5536_MFGPT_CMP2 | AMD5536_MFGPT_DIV_MASK); + wdog_register(&cs5536_wdog); +#endif /* WATCHDOG_ENABLE */ + +#if NGPIO > 0 + /* bus_space_map(sc->sc_gpio_iot, ga & 0xffff, 0xff, 0,... */ + cs5536_sc.sc_gpio_iot = I386_BUS_SPACE_IO; + cs5536_sc.sc_gpio_ioh = rdmsr(MSR_LBAR_GPIO); + for (i = 0; i < AMD5536_GPIO_NPINS; i++) { + cs5536_sc.sc_gpio_pins[i].pin_num = i; + cs5536_sc.sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT | + GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN | + GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | + GPIO_PIN_INVIN | GPIO_PIN_INVOUT; + + /* read initial state */ + cs5536_sc.sc_gpio_pins[i].pin_state = + cs5536_gpio_pin_read(&cs5536_sc, i); + } + cs5536_sc.sc_gpio_gc.driver_name = "cs5536"; + cs5536_sc.sc_gpio_gc.arg = &cs5536_sc; + cs5536_sc.sc_gpio_gc.pin_read = cs5536_gpio_pin_read; + cs5536_sc.sc_gpio_gc.pin_write = cs5536_gpio_pin_write; + cs5536_sc.sc_gpio_gc.pin_ctl = cs5536_gpio_pin_ctl; + cs5536_sc.sc_gpio_gc.pins = cs5536_sc.sc_gpio_pins; + cs5536_sc.sc_gpio_gc.npins = AMD5536_GPIO_NPINS; + gpio_register(&cs5536_sc.sc_gpio_gc); +#endif + if (bios_oem_strings(&bios_soekris_55, + bios_oem, sizeof bios_oem) > 0 ) { +#if NGPIO > 0 + /* Attach led to pin 6 */ + gpio_consumer_attach("led", "error", &cs5536_sc.sc_gpio_gc, + 6, 1); +#endif + } else if (bios_oem_strings(&bios_pcengines_55, + bios_oem, sizeof bios_oem) > 0 ) { +#if NGPIO > 0 + /* Attach leds */ + gpio_consumer_attach("led", "led1", &cs5536_sc.sc_gpio_gc, + 6, 1); + gpio_consumer_attach("led", "led2", &cs5536_sc.sc_gpio_gc, + 25, 1); + gpio_consumer_attach("led", "led3", &cs5536_sc.sc_gpio_gc, + 27, 1); +#endif +#if 0 + /* + * Turn on first LED so we don't make + * people think their box just died. + */ + cs5536_led_func(&led1b, 1); +#endif + } + if (*bios_oem) + kprintf("Geode LX: %s\n", bios_oem); + else + kprintf("Geode LX: Unknown OEM bios\n"); + + if (bootverbose) + kprintf("MFGPT bar: %jx\n", rdmsr(MSR_LBAR_MFGPT)); + + return 0; +} + +static device_method_t cs5536_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, cs5536_probe), + DEVMETHOD(device_attach, cs5536_attach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + {0, 0} +}; + +static driver_t cs5536_driver = { + "cs5536", + cs5536_methods, + 0, +}; + +static devclass_t cs5536_devclass; + +DRIVER_MODULE(cs5536, pci, cs5536_driver, cs5536_devclass, 0, 0); diff --git a/sys/platform/pc32/i386/geode.c b/sys/platform/pc32/i386/geode.c new file mode 100644 index 0000000000..3afa49b7a7 --- /dev/null +++ b/sys/platform/pc32/i386/geode.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2003 Markus Friedl + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * + * Copyright (c) 2009 The DragonFly Project. All rights reserved. + * + * This code is derived from software contributed to The DragonFly Project + * by Alex Hornung + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name of The DragonFly Project nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific, prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#include "opt_cpu.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Geode SC1100 Information Appliance On a Chip + * http://www.national.com/ds.cgi/SC/SC1100.pdf + */ + +/* Configuration Space Register Map */ + +#define SC1100_F5_SCRATCHPAD 0x64 + +#define GCB_WDTO 0x0000 /* WATCHDOG Timeout */ +#define GCB_WDCNFG 0x0002 /* WATCHDOG Configuration */ +#define GCB_WDSTS 0x0004 /* WATCHDOG Status */ +#define GCB_TSC 0x0008 /* Cyclecounter at 27MHz */ +#define GCB_TSCNFG 0x000c /* config for the above */ +#define GCB_IID 0x003c /* IA On a Chip ID */ +#define GCB_REV 0x003d /* Revision */ +#define GCB_CBA 0x003e /* Configuration Base Address */ + +/* Watchdog */ + +#define WD32KPD_ENABLE 0x0000 +#define WD32KPD_DISABLE 0x0100 +#define WDTYPE1_RESET 0x0030 +#define WDTYPE2_RESET 0x00c0 +#define WDPRES_DIV_512 0x0009 +#define WDPRES_DIV_8192 0x000d +#define WDCNFG_MASK 0x00ff +#define WDOVF_CLEAR 0x0001 + +/* cyclecounter */ +#define TSC_ENABLE 0x0200 +#define GEODE_TIMER_FREQ 27000000 + +struct geode_softc { + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +static struct geode_softc geode_sc; +static sysclock_t geode_get_timecount(void); + +static void +geode_cputimer_construct(struct cputimer *timer, sysclock_t oldclock) +{ + timer->base = 0; + timer->base = oldclock - geode_get_timecount(); +} + +static struct cputimer geode_timer = { + SLIST_ENTRY_INITIALIZER, + "Geode", + CPUTIMER_PRI_GEODE, + CPUTIMER_GEODE, + geode_get_timecount, + cputimer_default_fromhz, + cputimer_default_fromus, + geode_cputimer_construct, + cputimer_default_destruct, + GEODE_TIMER_FREQ, + 0, 0, 0 +}; + +static sysclock_t +geode_get_timecount(void) +{ + + return (geode_timer.base + + bus_space_read_4(geode_sc.sc_iot, geode_sc.sc_ioh, GCB_TSC)); +} + + +#ifdef WATCHDOG_ENABLE +static int +geode_watchdog(void *unused, int period) +{ + uint32_t data; + uint16_t port; + int reg; + + if (period > 0x03ff) + period = 0x03ff; + + bus_space_write_2(geode_sc.sc_iot, geode_sc.sc_ioh, GCB_WDTO, period * 64); + + return period; +} + +static struct watchdog geode_wdog = { + .name = "Geode SC1100", + .wdog_fn = geode_watchdog, + .arg = NULL, + .period_max = 0x03ff, +}; +#endif /* WATCHDOG_ENABLE */ + +static int +geode_probe(device_t self) +{ + if (pci_get_vendor(self) == PCI_VENDOR_NS && + (pci_get_device(self) == PCI_PRODUCT_NS_SC1100_XBUS || + pci_get_device(self) == PCI_PRODUCT_NS_SCx200_XBUS)) { + /* device_set_desc(self, ...) */ + return 0; + } + + return ENXIO; +} +static int +geode_attach(device_t self) +{ + u_int32_t reg; + u_int16_t cnfg, cba; + u_int8_t sts, rev, iid; + + /* + * The address of the CBA is written to this register + * by the bios, see p161 in data sheet. + */ + reg = pci_read_config(self, SC1100_F5_SCRATCHPAD, 4); + if (reg == 0) + return ENODEV; + + /* bus_space_map(sc->sc_iot, reg, 64, 0, &sc->sc_ioh)) */ + geode_sc.sc_iot = I386_BUS_SPACE_IO; + geode_sc.sc_ioh = reg; + + cba = bus_space_read_2(geode_sc.sc_iot, geode_sc.sc_ioh, GCB_CBA); + if (cba != reg) { + kprintf("Geode LX: cba mismatch: cba 0x%x != reg 0x%x\n", cba, reg); + return ENODEV; + } + + /* outl(cba + 0x0d, 2); ??? */ + sts = bus_space_read_1(geode_sc.sc_iot, geode_sc.sc_ioh, GCB_WDSTS); + cnfg = bus_space_read_2(geode_sc.sc_iot, geode_sc.sc_ioh, GCB_WDCNFG); + iid = bus_space_read_1(geode_sc.sc_iot, geode_sc.sc_ioh, GCB_IID); + rev = bus_space_read_1(geode_sc.sc_iot, geode_sc.sc_ioh, GCB_REV); +#define WDSTSBITS "\20\x04WDRST\x03WDSMI\x02WDINT\x01WDOVF" + kprintf("Geode LX: iid %d revision %d wdstatus %b\n", iid, rev, sts, WDSTSBITS); + + /* enable timer */ + bus_space_write_4(geode_sc.sc_iot, geode_sc.sc_iot, GCB_TSCNFG, TSC_ENABLE); + cputimer_register(&geode_timer); + cputimer_select(&geode_timer, 0); + +#ifdef WATCHDOG_ENABLE + /* enable watchdog and configure */ + bus_space_write_2(geode_sc.sc_iot, geode_sc.sc_ioh, GCB_WDTO, 0); + sts |= WDOVF_CLEAR; + bus_space_write_1(geode_sc.sc_iot, geode_sc.sc_ioh, GCB_WDSTS, sts); + cnfg &= ~WDCNFG_MASK; + cnfg |= WDTYPE1_RESET | WDPRES_DIV_512; + bus_space_write_2(geode_sc.sc_iot, geode_sc.sc_ioh, GCB_WDCNFG, cnfg); + wdog_register(&geode_wdog); +#endif /* WATCHDOG_ENABLE */ + + return 0; +} + +static device_method_t geode_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, geode_probe), + DEVMETHOD(device_attach, geode_attach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + {0, 0} +}; + +static driver_t geode_driver = { + "geode", + geode_methods, + 0, +}; + +static devclass_t geode_devclass; + +DRIVER_MODULE(geode, pci, geode_driver, geode_devclass, 0, 0); diff --git a/sys/sys/systimer.h b/sys/sys/systimer.h index 484dafeb42..43e43e6fab 100644 --- a/sys/sys/systimer.h +++ b/sys/sys/systimer.h @@ -126,11 +126,15 @@ extern struct cputimer *sys_cputimer; #define CPUTIMER_ACPI 3 #define CPUTIMER_VKERNEL 4 #define CPUTIMER_HPET 5 +#define CPUTIMER_GEODE 6 +#define CPUTIMER_CS5536 7 #define CPUTIMER_PRI_DUMMY -10 #define CPUTIMER_PRI_8254 0 #define CPUTIMER_PRI_ACPI 10 #define CPUTIMER_PRI_HPET 15 +#define CPUTIMER_PRI_CS5536 17 +#define CPUTIMER_PRI_GEODE 18 #define CPUTIMER_PRI_VKERNEL 20 void cputimer_select(struct cputimer *, int); -- 2.41.0