From d59e014361f325e7c0136da5ec4b6a6575b2aa32 Mon Sep 17 00:00:00 2001 From: nyan Date: Wed, 24 Oct 2001 14:46:40 +0000 Subject: Added the pmc driver which supports power management controller of old NEC PC-98NOTE. Submitted by: chi@bd.mbn.or.jp (Chiharu Shibata) MFC after: 1 week --- sys/conf/files.pc98 | 1 + sys/modules/Makefile | 1 + sys/modules/pmc/Makefile | 13 +++ sys/pc98/cbus/pmc.c | 257 ++++++++++++++++++++++++++++++++++++++++++++ sys/pc98/conf/GENERIC | 1 + sys/pc98/conf/GENERIC.hints | 3 + sys/pc98/pc98/pmc.c | 257 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 533 insertions(+) create mode 100644 sys/modules/pmc/Makefile create mode 100644 sys/pc98/cbus/pmc.c create mode 100644 sys/pc98/pc98/pmc.c diff --git a/sys/conf/files.pc98 b/sys/conf/files.pc98 index ba9b63c..3751430 100644 --- a/sys/conf/files.pc98 +++ b/sys/conf/files.pc98 @@ -395,6 +395,7 @@ pc98/pc98/pc98gdc.c optional gdc pc98/pc98/pc98kbd.c optional pckbd pc98/pc98/pc98_machdep.c standard pc98/pc98/pcaudio.c optional pca +pc98/pc98/pmc.c optional pmc pc98/pc98/ppc.c optional ppc pc98/pc98/scgdcrndr.c optional sc gdc pc98/pc98/scterm-sck.c optional sc diff --git a/sys/modules/Makefile b/sys/modules/Makefile index c07fb37..d0c965f 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -136,6 +136,7 @@ SUBDIR+=aac \ # smbfs \ .if ${MACHINE} == "pc98" +SUBDIR+=pmc SUBDIR+=snc .endif diff --git a/sys/modules/pmc/Makefile b/sys/modules/pmc/Makefile new file mode 100644 index 0000000..59994a0 --- /dev/null +++ b/sys/modules/pmc/Makefile @@ -0,0 +1,13 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../pc98/pc98 + +KMOD= pmc +SRCS= pmc.c +SRCS+= bus_if.h device_if.h isa_if.h + +.if ${MACHINE} == "pc98" +CFLAGS+= -DPC98 +.endif + +.include diff --git a/sys/pc98/cbus/pmc.c b/sys/pc98/cbus/pmc.c new file mode 100644 index 0000000..fe201e9 --- /dev/null +++ b/sys/pc98/cbus/pmc.c @@ -0,0 +1,257 @@ +/*- + * PMC (Power Management Controller of NEC PC-98Note) Driver + * + * Copyright (c) 2001 Chiharu Shibata. + * All rights reserved. + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +struct pmc_isa_softc { + struct resource *port_res; + eventhandler_tag evt; + int flags; +}; + +static int pmc_isa_alloc_resources __P((device_t)); +static void pmc_isa_release_resources __P((device_t)); +static int pmc_isa_probe __P((device_t)); +static int pmc_isa_attach __P((device_t)); +static int pmc_isa_detach __P((device_t)); + +#define PMC_ISA_PORT 0x8f0 +#define PMC_ISA_PORTSIZE 4 + +#define sc_inw(sc, port) \ + bus_space_read_2(rman_get_bustag((sc)->port_res), \ + rman_get_bushandle((sc)->port_res), (port)) + +#define sc_outw(sc, port, value) \ + bus_space_write_2(rman_get_bustag((sc)->port_res), \ + rman_get_bushandle((sc)->port_res), (port), (value)) + +static void +pmc_poweroff(void *arg, int howto) +{ + struct pmc_isa_softc *sc = (struct pmc_isa_softc *)arg; + + if (!sc->flags) { + outb(0x5e8e, inb(0x5e8e) & ~0x11); /* FDD LED off */ + } + + if (!(howto & RB_POWEROFF)) { + return; + } + + sc_outw(sc, 0, 0x0044); + sc_outw(sc, 2, 1 << 10); +#if 1 + /* for 9801NS/T */ + sc_outw(sc, 0, 0xf00a); + sc_outw(sc, 2, 1 << 9); +#endif +} + +static int +pmc_isa_alloc_resources(device_t dev) +{ + struct pmc_isa_softc *sc = device_get_softc(dev); + int rid; + + bzero(sc, sizeof(*sc)); + + rid = 0; + sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0ul, ~0ul, PMC_ISA_PORTSIZE, + RF_ACTIVE); + if (sc->port_res == NULL) { + return (ENOMEM); + } + + return 0; +} + +static void +pmc_isa_release_resources(device_t dev) +{ + struct pmc_isa_softc *sc = device_get_softc(dev); + + if (sc->port_res != NULL) { + bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port_res); + } + sc->port_res = NULL; +} + +static int +pmc_isa_probe(device_t dev) +{ + struct pmc_isa_softc *sc = device_get_softc(dev); + u_int port; + u_int16_t save, tmp; + +#if 0 + if (isa_get_vendorid(dev)) { + return ENXIO; + } + if (device_get_unit(dev) > 0) { + printf("pmc: Only one PMC driver supported.\n"); + return ENXIO; + } +#endif + port = isa_get_port(dev); + if (port == -1) { + port = PMC_ISA_PORT; + } + if (bootverbose) { + device_printf(dev, "port = 0x%x\n", port); + } + + if (bus_set_resource(dev, SYS_RES_IOPORT, 0, port, PMC_ISA_PORTSIZE)) { + if (bootverbose) { + device_printf(dev, "bus_set_resource failed\n"); + } + return ENXIO; + } + if (pmc_isa_alloc_resources(dev)) { + if (bootverbose) { + device_printf(dev, "pmc_isa_alloc_resources failed\n"); + } + return ENXIO; + } + + /* Check the existence of PMC */ + sc_outw(sc, 0, 0x0052); + save = sc_inw(sc, 2); + tmp = save & ~0x3f; + sc_outw(sc, 2, tmp); + if (sc_inw(sc, 2) != tmp) { + if (bootverbose) { + device_printf(dev, "failed to clear index(0x0052)\n"); + } + + pmc_isa_release_resources(dev); + return ENXIO; + } + + tmp |= 0x3e; + sc_outw(sc, 2, tmp); + if (sc_inw(sc, 2) != tmp) { + if (bootverbose) { + device_printf(dev, "failed to set index(0x0052)\n"); + } + + pmc_isa_release_resources(dev); + return ENXIO; + } + sc_outw(sc, 2, save); + + pmc_isa_release_resources(dev); + + device_set_desc(dev, "Power Management Controller"); + return 0; +} + +static int +pmc_isa_attach(device_t dev) +{ + struct pmc_isa_softc *sc = device_get_softc(dev); + int error; + + error = pmc_isa_alloc_resources(dev); + if (error) { + device_printf(dev, "resource allocation failed\n"); + return error; + } + + /* Power the system off using PMC */ + sc->evt = EVENTHANDLER_REGISTER(shutdown_final, pmc_poweroff, sc, + SHUTDOWN_PRI_LAST); + sc->flags = device_get_flags(dev); + return 0; +} + +static int +pmc_isa_detach(device_t dev) +{ + struct pmc_isa_softc *sc = device_get_softc(dev); + + if (bootverbose) { + device_printf(dev, "pmc_isa_detach called\n"); + } + + if (sc->evt != NULL) { + EVENTHANDLER_DEREGISTER(shutdown_final, sc->evt); + } + sc->evt = NULL; + + pmc_isa_release_resources(dev); + return 0; +} + +#ifdef KLD_MODULE +static void +pmc_isa_identify(driver_t *drv, device_t dev) +{ + if (BUS_ADD_CHILD(dev, ISA_ORDER_SPECULATIVE, "pmc", 0) == NULL) { + printf("failed to add pmc driver\n"); + } +} +#endif + +static device_method_t pmc_isa_methods[] = { + /* Device interface */ +#ifdef KLD_MODULE + DEVMETHOD(device_identify, pmc_isa_identify), +#endif + DEVMETHOD(device_probe, pmc_isa_probe), + DEVMETHOD(device_attach, pmc_isa_attach), + DEVMETHOD(device_detach, pmc_isa_detach), + {0, 0} +}; + +static driver_t pmc_isa_driver = { + "pmc", + pmc_isa_methods, sizeof(struct pmc_isa_softc), +}; + +devclass_t pmc_devclass; + +DRIVER_MODULE(pmc, isa, pmc_isa_driver, pmc_devclass, 0, 0); diff --git a/sys/pc98/conf/GENERIC b/sys/pc98/conf/GENERIC index f1c93ad..c79c743 100644 --- a/sys/pc98/conf/GENERIC +++ b/sys/pc98/conf/GENERIC @@ -126,6 +126,7 @@ device npx # Power management support (see NOTES for more options) #device apm +#device pmc # Add suspend/resume support for the i8254. #device pmtimer diff --git a/sys/pc98/conf/GENERIC.hints b/sys/pc98/conf/GENERIC.hints index 4f64788..76f2817 100644 --- a/sys/pc98/conf/GENERIC.hints +++ b/sys/pc98/conf/GENERIC.hints @@ -103,6 +103,9 @@ hint.npx.0.irq="8" #hint.apm.0.flags="0x20" #hint.pmtimer.0.at="isa" +#hint.pmc.0.at="isa" +#hint.pmc.0.port="0x8f0" + hint.pcic.0.at="isa" #hint.pcic.0.irq="6" # Default to polling hint.pcic.0.port="0x3e0" diff --git a/sys/pc98/pc98/pmc.c b/sys/pc98/pc98/pmc.c new file mode 100644 index 0000000..fe201e9 --- /dev/null +++ b/sys/pc98/pc98/pmc.c @@ -0,0 +1,257 @@ +/*- + * PMC (Power Management Controller of NEC PC-98Note) Driver + * + * Copyright (c) 2001 Chiharu Shibata. + * All rights reserved. + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +struct pmc_isa_softc { + struct resource *port_res; + eventhandler_tag evt; + int flags; +}; + +static int pmc_isa_alloc_resources __P((device_t)); +static void pmc_isa_release_resources __P((device_t)); +static int pmc_isa_probe __P((device_t)); +static int pmc_isa_attach __P((device_t)); +static int pmc_isa_detach __P((device_t)); + +#define PMC_ISA_PORT 0x8f0 +#define PMC_ISA_PORTSIZE 4 + +#define sc_inw(sc, port) \ + bus_space_read_2(rman_get_bustag((sc)->port_res), \ + rman_get_bushandle((sc)->port_res), (port)) + +#define sc_outw(sc, port, value) \ + bus_space_write_2(rman_get_bustag((sc)->port_res), \ + rman_get_bushandle((sc)->port_res), (port), (value)) + +static void +pmc_poweroff(void *arg, int howto) +{ + struct pmc_isa_softc *sc = (struct pmc_isa_softc *)arg; + + if (!sc->flags) { + outb(0x5e8e, inb(0x5e8e) & ~0x11); /* FDD LED off */ + } + + if (!(howto & RB_POWEROFF)) { + return; + } + + sc_outw(sc, 0, 0x0044); + sc_outw(sc, 2, 1 << 10); +#if 1 + /* for 9801NS/T */ + sc_outw(sc, 0, 0xf00a); + sc_outw(sc, 2, 1 << 9); +#endif +} + +static int +pmc_isa_alloc_resources(device_t dev) +{ + struct pmc_isa_softc *sc = device_get_softc(dev); + int rid; + + bzero(sc, sizeof(*sc)); + + rid = 0; + sc->port_res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0ul, ~0ul, PMC_ISA_PORTSIZE, + RF_ACTIVE); + if (sc->port_res == NULL) { + return (ENOMEM); + } + + return 0; +} + +static void +pmc_isa_release_resources(device_t dev) +{ + struct pmc_isa_softc *sc = device_get_softc(dev); + + if (sc->port_res != NULL) { + bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->port_res); + } + sc->port_res = NULL; +} + +static int +pmc_isa_probe(device_t dev) +{ + struct pmc_isa_softc *sc = device_get_softc(dev); + u_int port; + u_int16_t save, tmp; + +#if 0 + if (isa_get_vendorid(dev)) { + return ENXIO; + } + if (device_get_unit(dev) > 0) { + printf("pmc: Only one PMC driver supported.\n"); + return ENXIO; + } +#endif + port = isa_get_port(dev); + if (port == -1) { + port = PMC_ISA_PORT; + } + if (bootverbose) { + device_printf(dev, "port = 0x%x\n", port); + } + + if (bus_set_resource(dev, SYS_RES_IOPORT, 0, port, PMC_ISA_PORTSIZE)) { + if (bootverbose) { + device_printf(dev, "bus_set_resource failed\n"); + } + return ENXIO; + } + if (pmc_isa_alloc_resources(dev)) { + if (bootverbose) { + device_printf(dev, "pmc_isa_alloc_resources failed\n"); + } + return ENXIO; + } + + /* Check the existence of PMC */ + sc_outw(sc, 0, 0x0052); + save = sc_inw(sc, 2); + tmp = save & ~0x3f; + sc_outw(sc, 2, tmp); + if (sc_inw(sc, 2) != tmp) { + if (bootverbose) { + device_printf(dev, "failed to clear index(0x0052)\n"); + } + + pmc_isa_release_resources(dev); + return ENXIO; + } + + tmp |= 0x3e; + sc_outw(sc, 2, tmp); + if (sc_inw(sc, 2) != tmp) { + if (bootverbose) { + device_printf(dev, "failed to set index(0x0052)\n"); + } + + pmc_isa_release_resources(dev); + return ENXIO; + } + sc_outw(sc, 2, save); + + pmc_isa_release_resources(dev); + + device_set_desc(dev, "Power Management Controller"); + return 0; +} + +static int +pmc_isa_attach(device_t dev) +{ + struct pmc_isa_softc *sc = device_get_softc(dev); + int error; + + error = pmc_isa_alloc_resources(dev); + if (error) { + device_printf(dev, "resource allocation failed\n"); + return error; + } + + /* Power the system off using PMC */ + sc->evt = EVENTHANDLER_REGISTER(shutdown_final, pmc_poweroff, sc, + SHUTDOWN_PRI_LAST); + sc->flags = device_get_flags(dev); + return 0; +} + +static int +pmc_isa_detach(device_t dev) +{ + struct pmc_isa_softc *sc = device_get_softc(dev); + + if (bootverbose) { + device_printf(dev, "pmc_isa_detach called\n"); + } + + if (sc->evt != NULL) { + EVENTHANDLER_DEREGISTER(shutdown_final, sc->evt); + } + sc->evt = NULL; + + pmc_isa_release_resources(dev); + return 0; +} + +#ifdef KLD_MODULE +static void +pmc_isa_identify(driver_t *drv, device_t dev) +{ + if (BUS_ADD_CHILD(dev, ISA_ORDER_SPECULATIVE, "pmc", 0) == NULL) { + printf("failed to add pmc driver\n"); + } +} +#endif + +static device_method_t pmc_isa_methods[] = { + /* Device interface */ +#ifdef KLD_MODULE + DEVMETHOD(device_identify, pmc_isa_identify), +#endif + DEVMETHOD(device_probe, pmc_isa_probe), + DEVMETHOD(device_attach, pmc_isa_attach), + DEVMETHOD(device_detach, pmc_isa_detach), + {0, 0} +}; + +static driver_t pmc_isa_driver = { + "pmc", + pmc_isa_methods, sizeof(struct pmc_isa_softc), +}; + +devclass_t pmc_devclass; + +DRIVER_MODULE(pmc, isa, pmc_isa_driver, pmc_devclass, 0, 0); -- cgit v1.1