Module Name: src Committed By: bouyer Date: Mon Apr 4 17:09:40 UTC 2011
Modified Files: src/sys/arch/i386/conf: ALL GENERIC src/sys/arch/x86/pci: files.pci Added Files: src/sys/arch/x86/pci: rdcpcib.c Log Message: Add a driver for RDC's vortex86/PMX-1000 SoC PCI/ISA bridge, with support for the integrated watchdog timer. To generate a diff of this commit: cvs rdiff -u -r1.302 -r1.303 src/sys/arch/i386/conf/ALL cvs rdiff -u -r1.1028 -r1.1029 src/sys/arch/i386/conf/GENERIC cvs rdiff -u -r1.10 -r1.11 src/sys/arch/x86/pci/files.pci cvs rdiff -u -r0 -r1.1 src/sys/arch/x86/pci/rdcpcib.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/i386/conf/ALL diff -u src/sys/arch/i386/conf/ALL:1.302 src/sys/arch/i386/conf/ALL:1.303 --- src/sys/arch/i386/conf/ALL:1.302 Mon Apr 4 14:33:51 2011 +++ src/sys/arch/i386/conf/ALL Mon Apr 4 17:09:39 2011 @@ -1,4 +1,4 @@ -# $NetBSD: ALL,v 1.302 2011/04/04 14:33:51 bouyer Exp $ +# $NetBSD: ALL,v 1.303 2011/04/04 17:09:39 bouyer Exp $ # From NetBSD: GENERIC,v 1.787 2006/10/01 18:37:54 bouyer Exp # # ALL machine description file @@ -17,7 +17,7 @@ options INCLUDE_CONFIG_FILE # embed config file in kernel binary -#ident "ALL-$Revision: 1.302 $" +#ident "ALL-$Revision: 1.303 $" maxusers 64 # estimated number of users @@ -487,6 +487,7 @@ gscpcib* at pci? dev ? function ? # NS Geode PCI-ISA w/ GPIO support viapcib* at pci? dev ? function ? # VIA VT8235 PCI-ISA w/ SMBus support iic* at viapcib? +rdcpcib* at pci? dev ? function ? # RDC Vortex86/PMX-1000 PCI-ISA w/ pchb* at pci? dev ? function ? # PCI-Host bridges pceb* at pci? dev ? function ? # PCI-EISA bridges pcib* at pci? dev ? function ? # PCI-ISA bridges @@ -509,6 +510,7 @@ isa0 at piixpcib? isa0 at gscpcib? isa0 at viapcib? +isa0 at rdcpcib? isa0 at mainbus? isa0 at pceb? isa0 at pcib? Index: src/sys/arch/i386/conf/GENERIC diff -u src/sys/arch/i386/conf/GENERIC:1.1028 src/sys/arch/i386/conf/GENERIC:1.1029 --- src/sys/arch/i386/conf/GENERIC:1.1028 Mon Apr 4 14:33:51 2011 +++ src/sys/arch/i386/conf/GENERIC Mon Apr 4 17:09:39 2011 @@ -1,4 +1,4 @@ -# $NetBSD: GENERIC,v 1.1028 2011/04/04 14:33:51 bouyer Exp $ +# $NetBSD: GENERIC,v 1.1029 2011/04/04 17:09:39 bouyer Exp $ # # GENERIC machine description file # @@ -22,7 +22,7 @@ options INCLUDE_CONFIG_FILE # embed config file in kernel binary -#ident "GENERIC-$Revision: 1.1028 $" +#ident "GENERIC-$Revision: 1.1029 $" maxusers 64 # estimated number of users @@ -467,6 +467,8 @@ #gscpcib* at pci? dev ? function ? # NS Geode PCI-ISA w/ GPIO support viapcib* at pci? dev ? function ? # VIA VT8235 PCI-ISA w/ SMBus support iic* at viapcib? +rdcpcib* at pci? dev ? function ? # RDC Vortex86/PMX-1000 PCI-ISA w/ + # watchdog pchb* at pci? dev ? function ? # PCI-Host bridges pceb* at pci? dev ? function ? # PCI-EISA bridges pcib* at pci? dev ? function ? # PCI-ISA bridges @@ -489,6 +491,7 @@ #isa0 at piixpcib? #isa0 at gscpcib? isa0 at viapcib? +isa0 at rdcpcib? isa0 at mainbus? isa0 at pceb? isa0 at pcib? Index: src/sys/arch/x86/pci/files.pci diff -u src/sys/arch/x86/pci/files.pci:1.10 src/sys/arch/x86/pci/files.pci:1.11 --- src/sys/arch/x86/pci/files.pci:1.10 Fri Jul 23 00:43:21 2010 +++ src/sys/arch/x86/pci/files.pci Mon Apr 4 17:09:39 2011 @@ -1,4 +1,4 @@ -# $NetBSD: files.pci,v 1.10 2010/07/23 00:43:21 jakllsch Exp $ +# $NetBSD: files.pci,v 1.11 2011/04/04 17:09:39 bouyer Exp $ device aapic attach aapic at pci @@ -16,7 +16,7 @@ device pcib: isabus attach pcib at pci file arch/x86/pci/pcib.c pcib | ichlpcib | gscpcib | piixpcib | - viapcib | amdpcib | gcscpcib + viapcib | amdpcib | gcscpcib | rdcpcib device amdpcib {} : isabus attach amdpcib at pci @@ -31,6 +31,10 @@ file arch/x86/pci/amdtemp.c amdtemp # PCI-LPC bridges +device rdcpcib: isabus, sysmon_wdog +attach rdcpcib at pci +file arch/x86/pci/rdcpcib.c rdcpcib + define fwhichbus {} define hpetichbus {} device ichlpcib: acpipmtimer, isabus, sysmon_wdog, fwhichbus, hpetichbus, gpiobus Added files: Index: src/sys/arch/x86/pci/rdcpcib.c diff -u /dev/null src/sys/arch/x86/pci/rdcpcib.c:1.1 --- /dev/null Mon Apr 4 17:09:40 2011 +++ src/sys/arch/x86/pci/rdcpcib.c Mon Apr 4 17:09:39 2011 @@ -0,0 +1,301 @@ +/* $NetBSD: rdcpcib.c,v 1.1 2011/04/04 17:09:39 bouyer Exp $ */ + +/* + * Copyright (c) 2011 Manuel Bouyer. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * driver for the RDC vortex86/PMX-1000 SoC PCI-ISA bridge, which also drives + * the watchdog timer + */ + + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: rdcpcib.c,v 1.1 2011/04/04 17:09:39 bouyer Exp $"); + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/sysctl.h> +#include <sys/timetc.h> +#include <sys/gpio.h> +#include <machine/bus.h> + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> +#include <dev/pci/pcidevs.h> + +#include <dev/gpio/gpiovar.h> +#include <dev/sysmon/sysmonvar.h> + +#include "pcibvar.h" + +/* + * special registers: iospace-registers for indirect access to timer and GPIO + */ +#define RDC_IND_BASE 0x22 +#define RDC_IND_SIZE 0x2 +#define RDC_IND_ADDR 0 +#define RDC_IND_DATA 1 + +struct rdcpcib_softc { + struct pcib_softc rdc_pcib; + + /* indirect registers mapping */ + bus_space_tag_t rdc_iot; + bus_space_handle_t rdc_ioh; + + /* Watchdog suppoprt */ + struct sysmon_wdog rdc_smw; +}; + +static int rdcpcibmatch(device_t, cfdata_t, void *); +static void rdcpcibattach(device_t, device_t, void *); +static int rdcpcibdetach(device_t, int); + +static uint8_t rdc_ind_read(struct rdcpcib_softc *, uint8_t); +static void rdc_ind_write(struct rdcpcib_softc *, uint8_t, uint8_t); + +static void rdc_wdtimer_configure(device_t); +static int rdc_wdtimer_unconfigure(device_t, int); +static int rdc_wdtimer_setmode(struct sysmon_wdog *); +static int rdc_wdtimer_tickle(struct sysmon_wdog *); +static void rdc_wdtimer_stop(struct rdcpcib_softc *); +static void rdc_wdtimer_start(struct rdcpcib_softc *); + +CFATTACH_DECL2_NEW(rdcpcib, sizeof(struct rdcpcib_softc), + rdcpcibmatch, rdcpcibattach, rdcpcibdetach, NULL, + pcibrescan, pcibchilddet); + +static int +rdcpcibmatch(device_t parent, cfdata_t match, void *aux) +{ + struct pci_attach_args *pa = aux; + + if (PCI_CLASS(pa->pa_class) != PCI_CLASS_BRIDGE || + PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_BRIDGE_ISA) + return 0; + + if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_RDC && + PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_RDC_PCIB) + return 10; + + return 0; +} + +static void +rdcpcibattach(device_t parent, device_t self, void *aux) +{ + struct rdcpcib_softc *sc = device_private(self); + + /* generic PCI/ISA bridge */ + pcibattach(parent, self, aux); + + /* map indirect registers */ + sc->rdc_iot = x86_bus_space_io; + if (bus_space_map(sc->rdc_iot, RDC_IND_BASE, RDC_IND_SIZE, 0, + &sc->rdc_ioh) != 0) { + aprint_error_dev(self, "couldn't map indirect registers\n"); + return; + } + + /* Set up the watchdog. */ + rdc_wdtimer_configure(self); + + /* Install power handler XXX */ + if (!pmf_device_register(self, NULL, NULL)) + aprint_error_dev(self, "couldn't establish power handler\n"); +} + +static int +rdcpcibdetach(device_t self, int flags) +{ + struct rdcpcib_softc *sc = device_private(self); + int rc; + + pmf_device_deregister(self); + + if ((rc = rdc_wdtimer_unconfigure(self, flags)) != 0) + return rc; + + bus_space_unmap(sc->rdc_iot, sc->rdc_ioh, RDC_IND_SIZE); + return pcibdetach(self, flags); +} + +/* indirect registers read/write */ +static uint8_t +rdc_ind_read(struct rdcpcib_softc *sc, uint8_t addr) +{ + bus_space_write_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_ADDR, addr); + return bus_space_read_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_DATA); +} + +static void +rdc_ind_write(struct rdcpcib_softc *sc, uint8_t addr, uint8_t data) +{ + bus_space_write_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_ADDR, addr); + bus_space_write_1(sc->rdc_iot, sc->rdc_ioh, RDC_IND_DATA, data); +} + +/* + * watchdog timer registers + */ + +/* control */ +#define RDC_WDT0_CTRL 0x37 +#define RDC_WDT0_CTRL_EN 0x40 + +/* signal select */ +#define RDC_WDT0_SSEL 0x38 +#define RDC_WDT0_SSEL_MSK 0xf0 +#define RDC_WDT0_SSEL_NMI 0xc0 +#define RDC_WDT0_SSEL_RST 0xd0 + +/* counter */ +#define RDC_WDT0_CNTL 0x39 +#define RDC_WDT0_CNTH 0x3A +#define RDC_WDT0_CNTU 0x3B +#define RDC_WDT0_FREQ 32768 /* Hz */ +#define RDC_WDT0_PERIOD_MAX (1 << 24) + +/* clear counter */ +#define RDC_WDT0_CTRL1 0x3c +#define RDC_WDT0_CTRL1_RELOAD 0x40 +#define RDC_WDT0_CRTL1_FIRE 0x80 + + +/* + * Initialize the watchdog timer. + */ +static void +rdc_wdtimer_configure(device_t self) +{ + struct rdcpcib_softc *sc = device_private(self); + uint8_t reg; + + /* Explicitly stop the timer. */ + rdc_wdtimer_stop(sc); + + /* + * Register the driver with the sysmon watchdog framework. + */ + sc->rdc_smw.smw_name = device_xname(self); + sc->rdc_smw.smw_cookie = sc; + sc->rdc_smw.smw_setmode = rdc_wdtimer_setmode; + sc->rdc_smw.smw_tickle = rdc_wdtimer_tickle; + sc->rdc_smw.smw_period = RDC_WDT0_PERIOD_MAX / RDC_WDT0_FREQ; + + if (sysmon_wdog_register(&sc->rdc_smw)) { + aprint_error_dev(self, "unable to register wdt" + "as a sysmon watchdog device.\n"); + return; + } + + aprint_verbose_dev(self, "watchdog timer configured.\n"); + reg = rdc_ind_read(sc, RDC_WDT0_CTRL1); + if (reg & RDC_WDT0_CRTL1_FIRE) { + aprint_error_dev(self, "watchdog fired bit set, clearing\n"); + rdc_ind_write(sc, RDC_WDT0_CTRL1, reg & ~RDC_WDT0_CRTL1_FIRE); + } +} + +static int +rdc_wdtimer_unconfigure(device_t self, int flags) +{ + struct rdcpcib_softc *sc = device_private(self); + int rc; + + if ((rc = sysmon_wdog_unregister(&sc->rdc_smw)) != 0) { + if (rc == ERESTART) + rc = EINTR; + return rc; + } + + /* Explicitly stop the timer. */ + rdc_wdtimer_stop(sc); + + return 0; +} + + +/* + * Sysmon watchdog callbacks. + */ +static int +rdc_wdtimer_setmode(struct sysmon_wdog *smw) +{ + struct rdcpcib_softc *sc = smw->smw_cookie; + unsigned int period; + + if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { + /* Stop the timer. */ + rdc_wdtimer_stop(sc); + } else { + period = smw->smw_period * RDC_WDT0_FREQ; + if (period < 1 || + period > RDC_WDT0_PERIOD_MAX) + return EINVAL; + period = period - 1; + + /* Stop the timer, */ + rdc_wdtimer_stop(sc); + + /* set the timeout, */ + rdc_ind_write(sc, RDC_WDT0_CNTL, (period >> 0) & 0xff); + rdc_ind_write(sc, RDC_WDT0_CNTH, (period >> 8) & 0xff); + rdc_ind_write(sc, RDC_WDT0_CNTU, (period >> 16) & 0xff); + + /* and start the timer again */ + rdc_wdtimer_start(sc); + } + return 0; +} + +static int +rdc_wdtimer_tickle(struct sysmon_wdog *smw) +{ + struct rdcpcib_softc *sc = smw->smw_cookie; + uint8_t reg; + + reg = rdc_ind_read(sc, RDC_WDT0_CTRL1); + rdc_ind_write(sc, RDC_WDT0_CTRL1, reg | RDC_WDT0_CTRL1_RELOAD); + return 0; +} + +static void +rdc_wdtimer_stop(struct rdcpcib_softc *sc) +{ + uint8_t reg; + reg = rdc_ind_read(sc, RDC_WDT0_CTRL); + rdc_ind_write(sc, RDC_WDT0_CTRL, reg & ~RDC_WDT0_CTRL_EN); +} + +static void +rdc_wdtimer_start(struct rdcpcib_softc *sc) +{ + uint8_t reg; + rdc_ind_write(sc, RDC_WDT0_SSEL, RDC_WDT0_SSEL_RST); + reg = rdc_ind_read(sc, RDC_WDT0_CTRL); + rdc_ind_write(sc, RDC_WDT0_CTRL, reg | RDC_WDT0_CTRL_EN); +}