Module Name: src Committed By: martin Date: Fri Oct 25 17:39:57 UTC 2019
Modified Files: src/distrib/sets/lists/man: mi src/share/man/man4: Makefile isa.4 src/sys/arch/amd64/conf: GENERIC src/sys/arch/i386/conf: GENERIC src/sys/dev: DEVNAMES src/sys/dev/isa: files.isa Added Files: src/share/man/man4: nct.4 src/sys/dev/isa: nct.c Log Message: Add support for Nuvoton NCT5104D GPIO chips, as found on PC Engines APU systems. From Andrew Doran in PR kern/54648. To generate a diff of this commit: cvs rdiff -u -r1.1657 -r1.1658 src/distrib/sets/lists/man/mi cvs rdiff -u -r1.684 -r1.685 src/share/man/man4/Makefile cvs rdiff -u -r1.46 -r1.47 src/share/man/man4/isa.4 cvs rdiff -u -r0 -r1.1 src/share/man/man4/nct.4 cvs rdiff -u -r1.540 -r1.541 src/sys/arch/amd64/conf/GENERIC cvs rdiff -u -r1.1212 -r1.1213 src/sys/arch/i386/conf/GENERIC cvs rdiff -u -r1.322 -r1.323 src/sys/dev/DEVNAMES cvs rdiff -u -r1.173 -r1.174 src/sys/dev/isa/files.isa cvs rdiff -u -r0 -r1.1 src/sys/dev/isa/nct.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/man/mi diff -u src/distrib/sets/lists/man/mi:1.1657 src/distrib/sets/lists/man/mi:1.1658 --- src/distrib/sets/lists/man/mi:1.1657 Tue Oct 15 18:33:57 2019 +++ src/distrib/sets/lists/man/mi Fri Oct 25 17:39:56 2019 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1657 2019/10/15 18:33:57 christos Exp $ +# $NetBSD: mi,v 1.1658 2019/10/25 17:39:56 martin Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -1493,6 +1493,7 @@ ./usr/share/man/cat4/nadb.0 man-sys-catman .cat ./usr/share/man/cat4/nca.0 man-sys-catman .cat ./usr/share/man/cat4/ncr.0 man-obsolete obsolete +./usr/share/man/cat4/nct.0 man-sys-catman .cat ./usr/share/man/cat4/ne.0 man-sys-catman .cat ./usr/share/man/cat4/nele.0 man-sys-catman .cat ./usr/share/man/cat4/neo.0 man-sys-catman .cat @@ -4626,6 +4627,7 @@ ./usr/share/man/html4/mvsata.html man-sys-htmlman html ./usr/share/man/html4/nadb.html man-sys-htmlman html ./usr/share/man/html4/nca.html man-sys-htmlman html +./usr/share/man/html4/nct.html man-sys-htmlman html ./usr/share/man/html4/ne.html man-sys-htmlman html ./usr/share/man/html4/nele.html man-sys-htmlman html ./usr/share/man/html4/neo.html man-sys-htmlman html @@ -7613,6 +7615,7 @@ ./usr/share/man/man4/nadb.4 man-sys-man .man ./usr/share/man/man4/nca.4 man-sys-man .man ./usr/share/man/man4/ncr.4 man-obsolete obsolete +./usr/share/man/man4/nct.4 man-sys-man .man ./usr/share/man/man4/ne.4 man-sys-man .man ./usr/share/man/man4/nele.4 man-sys-man .man ./usr/share/man/man4/neo.4 man-sys-man .man Index: src/share/man/man4/Makefile diff -u src/share/man/man4/Makefile:1.684 src/share/man/man4/Makefile:1.685 --- src/share/man/man4/Makefile:1.684 Mon Oct 7 11:53:40 2019 +++ src/share/man/man4/Makefile Fri Oct 25 17:39:57 2019 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.684 2019/10/07 11:53:40 msaitoh Exp $ +# $NetBSD: Makefile,v 1.685 2019/10/25 17:39:57 martin Exp $ # @(#)Makefile 8.1 (Berkeley) 6/18/93 MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \ @@ -112,7 +112,7 @@ MAN+= cz.4 epic.4 viaenv.4 # machine-independent ISA devices MAN+= aha.4 ai.4 aic.4 ast.4 ate.4 boca.4 cs.4 cy.4 ec.4 ef.4 \ eg.4 el.4 esp.4 ess.4 ex.4 fmv.4 gus.4 guspnp.4 ix.4 iy.4 \ - le.4 lm.4 mcd.4 nca.4 rtfps.4 sb.4 sea.4 smsc.4 tcom.4 \ + le.4 lm.4 mcd.4 nca.4 nct.4 rtfps.4 sb.4 sea.4 smsc.4 tcom.4 \ wds.4 we.4 wss.4 wt.4 # machine-independent PCMCIA devices Index: src/share/man/man4/isa.4 diff -u src/share/man/man4/isa.4:1.46 src/share/man/man4/isa.4:1.47 --- src/share/man/man4/isa.4:1.46 Mon Jul 3 21:30:58 2017 +++ src/share/man/man4/isa.4 Fri Oct 25 17:39:57 2019 @@ -1,4 +1,4 @@ -.\" $NetBSD: isa.4,v 1.46 2017/07/03 21:30:58 wiz Exp $ +.\" $NetBSD: isa.4,v 1.47 2019/10/25 17:39:57 martin Exp $ .\" .\" Copyright (c) 1997 Jason R. Thorpe. All rights reserved. .\" Copyright (c) 1997 Jonathan Stone @@ -29,7 +29,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd June 10, 2013 +.Dd October 25, 2019 .Dt ISA 4 .Os .Sh NAME @@ -253,6 +253,8 @@ Aztech/PackardBell radio card. EGA graphics boards. .It lm National Semiconductor LM78, LM79 and compatible hardware monitors. +.It nct +Nuvoton NCT5104D SuperIO. .It pcdisplay PC display adapters. .It pcic @@ -353,6 +355,7 @@ It is usually due to a suboptimally code .Xr mcd 4 , .Xr mpu 4 , .Xr nca 4 , +.Xr nct 4 , .Xr ne 4 , .Xr ntwoc 4 , .Xr opl 4 , Index: src/sys/arch/amd64/conf/GENERIC diff -u src/sys/arch/amd64/conf/GENERIC:1.540 src/sys/arch/amd64/conf/GENERIC:1.541 --- src/sys/arch/amd64/conf/GENERIC:1.540 Fri Oct 25 17:25:23 2019 +++ src/sys/arch/amd64/conf/GENERIC Fri Oct 25 17:39:57 2019 @@ -1,4 +1,4 @@ -# $NetBSD: GENERIC,v 1.540 2019/10/25 17:25:23 martin Exp $ +# $NetBSD: GENERIC,v 1.541 2019/10/25 17:39:57 martin Exp $ # # GENERIC machine description file # @@ -22,7 +22,7 @@ include "arch/amd64/conf/std.amd64" options INCLUDE_CONFIG_FILE # embed config file in kernel binary -#ident "GENERIC-$Revision: 1.540 $" +#ident "GENERIC-$Revision: 1.541 $" maxusers 64 # estimated number of users @@ -593,6 +593,9 @@ owtemp* at onewire? # Temperature sen # Soekris 6501 GPIO/LED driver (provides gpiobus, needs gpio) #soekrisgpio0 at isa? port 0x680 +# Nuvoton NCT5104D SuperIO providing GPIO +nct0 at isa? port ? + # SCSI Controllers and Devices # PCI SCSI controllers Index: src/sys/arch/i386/conf/GENERIC diff -u src/sys/arch/i386/conf/GENERIC:1.1212 src/sys/arch/i386/conf/GENERIC:1.1213 --- src/sys/arch/i386/conf/GENERIC:1.1212 Tue Oct 8 18:50:44 2019 +++ src/sys/arch/i386/conf/GENERIC Fri Oct 25 17:39:57 2019 @@ -1,4 +1,4 @@ -# $NetBSD: GENERIC,v 1.1212 2019/10/08 18:50:44 maxv Exp $ +# $NetBSD: GENERIC,v 1.1213 2019/10/25 17:39:57 martin Exp $ # # GENERIC machine description file # @@ -22,7 +22,7 @@ include "arch/i386/conf/std.i386" options INCLUDE_CONFIG_FILE # embed config file in kernel binary -#ident "GENERIC-$Revision: 1.1212 $" +#ident "GENERIC-$Revision: 1.1213 $" maxusers 64 # estimated number of users @@ -737,6 +737,9 @@ gpiopwm* at gpio? # Soekris 6501 GPIO/LED driver (provides gpiobus, needs gpio) #soekrisgpio0 at isa? port 0x680 +# Nuvoton NCT5104D SuperIO providing GPIO +nct0 at isa? port ? + # SCSI Controllers and Devices # PCI SCSI controllers Index: src/sys/dev/DEVNAMES diff -u src/sys/dev/DEVNAMES:1.322 src/sys/dev/DEVNAMES:1.323 --- src/sys/dev/DEVNAMES:1.322 Mon Oct 7 11:53:40 2019 +++ src/sys/dev/DEVNAMES Fri Oct 25 17:39:56 2019 @@ -1,4 +1,4 @@ -# $NetBSD: DEVNAMES,v 1.322 2019/10/07 11:53:40 msaitoh Exp $ +# $NetBSD: DEVNAMES,v 1.323 2019/10/25 17:39:56 martin Exp $ # # This file contains all used device names and defined attributes in # alphabetical order. New devices added to the system somewhere should first @@ -924,6 +924,7 @@ ncr53c9x MI Attribute ncrsc mvme68k ncrscsi atari ncrscsi mac68k +nct MI ne MI necpb arc nele MI Index: src/sys/dev/isa/files.isa diff -u src/sys/dev/isa/files.isa:1.173 src/sys/dev/isa/files.isa:1.174 --- src/sys/dev/isa/files.isa:1.173 Wed May 8 13:40:18 2019 +++ src/sys/dev/isa/files.isa Fri Oct 25 17:39:57 2019 @@ -1,4 +1,4 @@ -# $NetBSD: files.isa,v 1.173 2019/05/08 13:40:18 isaki Exp $ +# $NetBSD: files.isa,v 1.174 2019/10/25 17:39:57 martin Exp $ # # Config file and device description for machine-independent ISA code. # Included by ports that need it. Requires that the SCSI files be @@ -511,6 +511,11 @@ device soekrisgpio: gpiobus attach soekrisgpio at isa file dev/isa/soekrisgpio.c soekrisgpio +# NCT5104D GPIO +device nct: gpiobus +attach nct at isa +file dev/isa/nct.c nct + # # ISA Plug 'n Play autoconfiguration glue. # THIS MUST COME AFTER ALL MI ISA DEVICES ARE DEFINED. This is because Added files: Index: src/share/man/man4/nct.4 diff -u /dev/null src/share/man/man4/nct.4:1.1 --- /dev/null Fri Oct 25 17:39:57 2019 +++ src/share/man/man4/nct.4 Fri Oct 25 17:39:57 2019 @@ -0,0 +1,72 @@ +.\" $NetBSD: nct.4,v 1.1 2019/10/25 17:39:57 martin Exp $ +.\" +.\" Copyright (c) 2019 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Andrew Doran. +.\" +.\" 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +.\" +.Dd October 14, 2019 +.Dt NCT 4 +.Os +.Sh NAME +.Nm nct +.Nd Nuvoton NCT5104D SuperIO driver +.Sh SYNOPSIS +.Cd "nct0 at isa? port ?" +.Cd "nct0 at isa? port 0x2e" +.Cd "nct0 at isa? port 0x4e" +.Cd "gpio* at nct?" +.Sh DESCRIPTION +The +.Nm +driver supports the GPIO functions of the NCT5104D. +The driver does not support the watchdog function of the chip. +The chip's UARTs are driven by the +.Xr com 4 +driver. +.Pp +The probe routine for this device is invasive. +The chip will be probed for only if the device is configured into the kernel +with a fixed port number (0x2e or 0x4e), or if running on a system that +is known to have a NCT5104D, such as the PC Engines APU line of systems. +.Pp +GPIO pins on this chip are shared with the 3rd UART, 4th UART, a clock +input line, and the watchdog timer. +If any these functions have been enabled by the BIOS, the +.Nm +driver will not take control of the corresponding GPIO lines. +At attach time, the driver logs which of the 17 GPIO lines are enabled. +.Sh SEE ALSO +.Xr gpio 4 , +.Xr gpioctl 8 , +.Xr isa 4 +.Sh HISTORY +The +.Nm +driver first appeared in +.Nx 9 . +.Sh CAVEATS +If the chip has not been configured in a complete and accurate manner by +the BIOS, GPIO lines may be needlessly disabled. Index: src/sys/dev/isa/nct.c diff -u /dev/null src/sys/dev/isa/nct.c:1.1 --- /dev/null Fri Oct 25 17:39:57 2019 +++ src/sys/dev/isa/nct.c Fri Oct 25 17:39:57 2019 @@ -0,0 +1,660 @@ +/* $NetBSD: nct.c,v 1.1 2019/10/25 17:39:57 martin Exp $ */ + +/*- + * Copyright (c) 2019 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andrew Doran. + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Nuvoton NCT5104D + * + * - GPIO: full support. + * - Watchdog: no support. Watchdog uses GPIO pins. + * - UARTS: handled by com driver. 3rd & 4th UARTs use GPIO pins. + * + * If asked to probe with a wildcard address, we'll only do so if known to + * be running on a PC Engines APU board. Probe is invasive. + * + * Register access on Super I/O chips typically involves one or two levels + * of indirection, so we try hard to avoid needless register access. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: nct.c,v 1.1 2019/10/25 17:39:57 martin Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/types.h> +#include <sys/device.h> +#include <sys/module.h> +#include <sys/bus.h> +#include <sys/gpio.h> + +#include <machine/autoconf.h> + +#include <dev/isa/isavar.h> + +#include <dev/gpio/gpiovar.h> + +/* + * Hardware interface definition (enough for GPIO only). + */ + +/* I/O basics */ +#define NCT_IOBASE_A 0x2e +#define NCT_IOBASE_B 0x4e +#define NCT_IOSIZE 2 +#define NCT_CHIP_ID_1 0x1061 +#define NCT_CHIP_ID_2 0xc452 /* PC Engines APU1 */ +#define NCT_NUM_PINS 17 + +/* Enable/disable keys */ +#define NCT_KEY_UNLOCK 0x87 +#define NCT_KEY_LOCK 0xaa + +/* I/O ports */ +#define NCT_PORT_SELECT 0 +#define NCT_PORT_DATA 1 + +/* Global registers */ +#define GD_DEVSEL 0x0007 /* logical device select */ +#define GD_MULTIFUN 0x001c /* multi function selection */ +#define GD_MULTIFUN_GPIO1 0x04 /* clr: gpio1 available */ +#define GD_MULTIFUN_GPIO0 0x08 /* clr: gpio0 available */ +#define GD_MULTIFUN_GPIO67 0x10 /* set: gpio67 available */ +#define GD_GLOBOPT 0x0027 /* global option */ +#define GD_GLOBOPT_GPIO67 0x04 /* clr: gpio67 available */ +#define GD_ID_HIGH 0x0020 /* ID high byte */ +#define GD_ID_LOW 0x0021 /* ID low byte */ + +/* Logical device 7 */ +#define LD7_ENABLE 0x0730 /* GPIO function enable */ +#define LD7_ENABLE_GPIO0 0x01 +#define LD7_ENABLE_GPIO1 0x02 +#define LD7_ENABLE_GPIO67 0x40 +#define LD7_GPIO0_DIRECTION 0x07e0 /* clr for output, set for input */ +#define LD7_GPIO0_DATA 0x07e1 /* current status */ +#define LD7_GPIO0_INVERSION 0x07e2 /* set to invert i/o */ +#define LD7_GPIO0_STATUS 0x07e3 /* edge detect, reading clears */ +#define LD7_GPIO1_DIRECTION 0x07e4 /* clr for output, set for input */ +#define LD7_GPIO1_DATA 0x07e5 /* current status */ +#define LD7_GPIO1_INVERSION 0x07e6 /* set to invert i/o */ +#define LD7_GPIO1_STATUS 0x07e7 /* edge detect, reading clears */ +#define LD7_GPIO67_DIRECTION 0x07f8 /* clr for output, set for input */ +#define LD7_GPIO67_DATA 0x07f9 /* current status */ +#define LD7_GPIO67_INVERSION 0x07fa /* set to invert i/o */ +#define LD7_GPIO67_STATUS 0x07fb /* edge detect, reading clears */ + +/* Logical device 8 */ +#define LD8_DEVCFG 0x0830 /* WDT/GPIO device config */ +#define LD8_GPIO0_MULTIFUNC 0x08e0 /* clr: gpio, set: pin unusable */ +#define LD8_GPIO1_MULTIFUNC 0x08e1 /* clr: gpio, set: pin unusable */ +#define LD8_GPIO67_MULTIFUNC 0x08e7 /* clr: gpio, set: pin unusable */ + +/* Logical device 10 */ +#define LDA_UARTC_ENABLE 0x0a30 /* bit 0: UARTC active */ + +/* Logical device 11 */ +#define LDB_UARTD_ENABLE 0x0b30 /* bit 0: UARTD active */ + +/* Logical device 15 */ +#define LDF_GPIO0_OUTMODE 0x0fe0 /* clr: push/pull, set: open drain */ +#define LDF_GPIO1_OUTMODE 0x0fe1 /* clr: push/pull, set: open drain */ +#define LDF_GPIO67_OUTMODE 0x0fe6 /* clr: push/pull, set: open drain */ + +/* + * Internal GPIO bank description, including register addresses and cached + * register content. + */ +struct nct_bank { + /* Pin descriptions */ + u_int8_t nb_firstpin; + u_int8_t nb_numpins; + u_int8_t nb_enabled; + + /* Cached values */ + u_int8_t nb_val_dir; + u_int8_t nb_val_inv; + u_int8_t nb_val_mode; + + /* Register addresses */ + u_int16_t nb_reg_dir; + u_int16_t nb_reg_data; + u_int16_t nb_reg_inv; + u_int16_t nb_reg_stat; + u_int16_t nb_reg_mode; +}; + +/* + * Driver instance. + */ +struct nct_softc { + device_t sc_dev; /* MI device */ + bus_space_tag_t sc_iot; /* I/O tag */ + bus_space_handle_t sc_ioh; /* I/O handle */ + struct gpio_chipset_tag sc_gc; /* GPIO tag */ + gpio_pin_t sc_pins[NCT_NUM_PINS]; /* GPIO pin descr. */ + + /* Access to the remaining members is covered by sc_lock. */ + kmutex_t sc_lock; /* Serialization */ + int sc_curdev; /* Cur. logical dev */ + int sc_curreg; /* Cur. register */ + struct nct_bank sc_bank[3]; /* Bank descriptions */ +}; + +static void nct_attach(device_t, device_t, void *); +static int nct_detach(device_t, int); +static void nct_gpio_ctl(void *, int, int); +static int nct_gpio_read(void *, int); +static void nct_gpio_write(void *, int, int); +static int nct_match(device_t, cfdata_t , void *); +static u_int8_t nct_rd(struct nct_softc *, int); +static struct nct_bank *nct_sel(struct nct_softc *, int, u_int8_t *); +static void nct_wr(struct nct_softc *, int, u_int8_t); + +static inline void +nct_outb(struct nct_softc *sc, int reg, u_int8_t data) +{ + + bus_space_write_1(sc->sc_iot, sc->sc_ioh, reg, data); +} + +static inline u_int8_t +nct_inb(struct nct_softc *sc, int reg) +{ + + return bus_space_read_1(sc->sc_iot, sc->sc_ioh, reg); +} + +CFATTACH_DECL_NEW(nct, + sizeof(struct nct_softc), + nct_match, + nct_attach, + nct_detach, + NULL); + +MODULE(MODULE_CLASS_DRIVER, nct, "gpio"); + +/* + * Module linkage. + */ +#ifdef _MODULE +#include "ioconf.c" +#endif + +static int +nct_modcmd(modcmd_t cmd, void *priv) +{ + int error = 0; + + switch (cmd) { + case MODULE_CMD_INIT: +#ifdef _MODULE + error = config_init_component(cfdriver_ioconf_nct, + cfattach_ioconf_nct, cfdata_ioconf_nct); +#endif + return error; + case MODULE_CMD_FINI: +#ifdef _MODULE + error = config_fini_component(cfdriver_ioconf_nct, + cfattach_ioconf_nct, cfdata_ioconf_nct); +#endif + return error; + default: + return ENOTTY; + } +} + +/* + * Probe for device. + */ +static int +nct_match(device_t parent, cfdata_t match, void *aux) +{ + int ioaddrs[2] = { 0x2e, 0x4e }; + struct isa_attach_args *ia = aux; + bus_space_handle_t ioh; + int nioaddr, i; + u_int8_t low, high; + u_int16_t id; + + /* + * Allow override of I/O base address. If no I/O base address is + * provided, proceed to probe if running on a PC Engines APU. + */ + if (ia->ia_nio > 0 && ia->ia_io[0].ir_addr != ISA_UNKNOWN_PORT) { + ioaddrs[0] = ia->ia_io[0].ir_addr; + nioaddr = 1; + } else if ((strcmp(pmf_get_platform("system-vendor"), "PC Engines") | + strcmp(pmf_get_platform("system-product"), "APU")) == 0) { + nioaddr = __arraycount(ioaddrs); + } else { + nioaddr = 0; + } + + /* + * Probe at the selected addresses, if any. + */ + for (i = 0; i < nioaddr; i++) { + if (bus_space_map(ia->ia_iot, ioaddrs[i], NCT_IOSIZE, 0, + &ioh) != 0) { + continue; + } + /* Unlock chip */ + bus_space_write_1(ia->ia_iot, ioh, NCT_PORT_SELECT, + NCT_KEY_UNLOCK); + bus_space_write_1(ia->ia_iot, ioh, NCT_PORT_SELECT, + NCT_KEY_UNLOCK); + /* Read ID */ + bus_space_write_1(ia->ia_iot, ioh, NCT_PORT_SELECT, GD_ID_LOW); + low = bus_space_read_1(ia->ia_iot, ioh, NCT_PORT_DATA); + bus_space_write_1(ia->ia_iot, ioh, NCT_PORT_SELECT, GD_ID_HIGH); + high = bus_space_read_1(ia->ia_iot, ioh, NCT_PORT_DATA); + id = (u_int16_t)low | ((u_int16_t)high << 8); + bus_space_unmap(ia->ia_iot, ioh, NCT_IOSIZE); + if (id == NCT_CHIP_ID_1 || id == NCT_CHIP_ID_2) { + ia->ia_nirq = 0; + ia->ia_ndrq = 0; + ia->ia_niomem = 0; + ia->ia_nio = 1; + ia->ia_io[0].ir_size = NCT_IOSIZE; + ia->ia_io[0].ir_addr = ioaddrs[i]; + return 1; + } + } + return 0; +} + +/* + * Attach device instance. + */ +static void +nct_attach(device_t parent, device_t self, void *aux) +{ + struct nct_softc *sc = device_private(self); + struct isa_attach_args *ia = aux; + struct gpiobus_attach_args gba; + struct nct_bank *nb; + u_int8_t multifun, enable; + bool apu; + int i, j; + + /* + * Set up register space and basics of our state. + */ + if (bus_space_map(ia->ia_iot, ia->ia_io[0].ir_addr, + ia->ia_io[0].ir_size, 0, &sc->sc_ioh) != 0) { + aprint_normal(": can't map i/o space\n"); + return; + } + aprint_normal(": Nuvoton NCT5104D GPIO\n"); + sc->sc_dev = self; + sc->sc_iot = ia->ia_iot; + sc->sc_curdev = -1; + sc->sc_curreg = -1; + apu = ((strcmp(pmf_get_platform("system-vendor"), "PC Engines") | + strcmp(pmf_get_platform("system-product"), "APU")) == 0); + + /* + * All pin access is funneled through a common, indirect register + * interface. The gpio framework doesn't serialize calls to our + * access methods, so do it internally. This is likely such a + * common requirement that it should be factored out as is done for + * audio devices, allowing the driver to specify the appropriate + * locks. Anyhow, acquire the lock immediately to pacify locking + * assertions. + */ + mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM); + mutex_spin_enter(&sc->sc_lock); + + /* + * Disable watchdog timer and GPIO alternate I/O mapping. + */ + nct_wr(sc, LD8_DEVCFG, 0); + + /* + * Fill out descriptions of GPIO0, GPIO1 and GPIO67. + * Determine which banks and pins are enabled. + */ + multifun = nct_rd(sc, GD_MULTIFUN); + enable = nct_rd(sc, LD7_ENABLE); + + nb = &sc->sc_bank[0]; + nb->nb_firstpin = 0; + nb->nb_numpins = 8; + nb->nb_reg_dir = LD7_GPIO0_DIRECTION; + nb->nb_reg_data = LD7_GPIO0_DATA; + nb->nb_reg_inv = LD7_GPIO0_INVERSION; + nb->nb_reg_stat = LD7_GPIO0_STATUS; + nb->nb_reg_mode = LDF_GPIO0_OUTMODE; + if ((multifun & GD_MULTIFUN_GPIO0) == 0 && + ((nct_rd(sc, LDA_UARTC_ENABLE) & 1) == 0 || apu)) { + nct_wr(sc, LD8_GPIO0_MULTIFUNC, 0); + nb->nb_enabled = 0xff; + enable |= LD7_ENABLE_GPIO0; + } else { + sc->sc_bank[0].nb_enabled = 0; + } + + nb = &sc->sc_bank[1]; + nb->nb_firstpin = 8; + nb->nb_numpins = 8; + nb->nb_reg_dir = LD7_GPIO1_DIRECTION; + nb->nb_reg_data = LD7_GPIO1_DATA; + nb->nb_reg_inv = LD7_GPIO1_INVERSION; + nb->nb_reg_stat = LD7_GPIO1_STATUS; + nb->nb_reg_mode = LDF_GPIO1_OUTMODE; + if ((multifun & GD_MULTIFUN_GPIO1) == 0 && + (nct_rd(sc, LDB_UARTD_ENABLE) & 1) == 0) { + nct_wr(sc, LD8_GPIO1_MULTIFUNC, 0); + nb->nb_enabled = 0xff; + enable |= LD7_ENABLE_GPIO1; + } else { + sc->sc_bank[1].nb_enabled = 0; + } + + nb = &sc->sc_bank[2]; + nb->nb_firstpin = 16; + nb->nb_numpins = 1; + nb->nb_reg_dir = LD7_GPIO67_DIRECTION; + nb->nb_reg_data = LD7_GPIO67_DATA; + nb->nb_reg_stat = LD7_GPIO67_STATUS; + nb->nb_reg_mode = LDF_GPIO67_OUTMODE; + if ((multifun & GD_MULTIFUN_GPIO67) != 0 && + (nct_rd(sc, GD_GLOBOPT) & GD_GLOBOPT_GPIO67) == 0) { + nct_wr(sc, LD8_GPIO67_MULTIFUNC, 0); + nb->nb_enabled = 0x01; + enable |= LD7_ENABLE_GPIO67; + } else { + sc->sc_bank[2].nb_enabled = 0; + } + + /* + * Display enabled pins and enable GPIO devices accordingly. + */ + nct_wr(sc, LD7_ENABLE, enable); + mutex_spin_exit(&sc->sc_lock); + + aprint_normal_dev(self, + "enabled pins: GPIO0(%02x) GPIO1(%02x) GPIO67(%01x)\n", + (unsigned)sc->sc_bank[0].nb_enabled, + (unsigned)sc->sc_bank[1].nb_enabled, + (unsigned)sc->sc_bank[2].nb_enabled); + + /* + * Fill pin descriptions and initialize registers. + */ + memset(sc->sc_pins, 0, sizeof(sc->sc_pins)); + for (i = 0; i < __arraycount(sc->sc_bank); i++) { + nb = &sc->sc_bank[i]; + mutex_spin_enter(&sc->sc_lock); + nb->nb_val_dir = nct_rd(sc, nb->nb_reg_dir); + nb->nb_val_inv = nct_rd(sc, nb->nb_reg_inv); + nb->nb_val_mode = nct_rd(sc, nb->nb_reg_mode); + mutex_spin_exit(&sc->sc_lock); + for (j = 0; j < nb->nb_numpins; j++) { + gpio_pin_t *pin = &sc->sc_pins[nb->nb_firstpin + j]; + pin->pin_num = nb->nb_firstpin + j; + /* Skip pin if not configured as GPIO. */ + if ((nb->nb_enabled & (1 << j)) == 0) { + continue; + } + pin->pin_caps = + GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | + GPIO_PIN_OPENDRAIN | + GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE | + GPIO_PIN_INVIN | GPIO_PIN_INVOUT; + pin->pin_flags = + GPIO_PIN_INPUT | GPIO_PIN_OPENDRAIN; + nct_gpio_ctl(sc, pin->pin_num, pin->pin_flags); + pin->pin_state = nct_gpio_read(sc, pin->pin_num); + } + } + + /* + * Attach to gpio framework, and attach all pins unconditionally. + * If the pins are disabled, we'll ignore any access later. + */ + sc->sc_gc.gp_cookie = sc; + sc->sc_gc.gp_pin_read = nct_gpio_read; + sc->sc_gc.gp_pin_write = nct_gpio_write; + sc->sc_gc.gp_pin_ctl = nct_gpio_ctl; + + gba.gba_gc = &sc->sc_gc; + gba.gba_pins = sc->sc_pins; + gba.gba_npins = NCT_NUM_PINS; + + (void)config_found(sc->sc_dev, &gba, gpiobus_print); +} + +/* + * Detach device instance. + */ +static int +nct_detach(device_t self, int flags) +{ + struct nct_softc *sc = device_private(self); + + bus_space_unmap(sc->sc_iot, sc->sc_ioh, NCT_IOSIZE); + mutex_destroy(&sc->sc_lock); + return 0; +} + +/* + * Read byte from specified register. + */ +static u_int8_t +nct_rd(struct nct_softc *sc, int reg) +{ + int dev; + + KASSERT(mutex_owned(&sc->sc_lock)); + + dev = reg >> 8; + reg &= 0xff; + + if (dev != sc->sc_curdev && dev != 0x00) { + sc->sc_curdev = dev; + sc->sc_curreg = reg; + nct_outb(sc, NCT_PORT_SELECT, GD_DEVSEL); + nct_outb(sc, NCT_PORT_DATA, dev); + nct_outb(sc, NCT_PORT_SELECT, reg); + return nct_inb(sc, NCT_PORT_DATA); + } else if (reg != sc->sc_curreg) { + sc->sc_curreg = reg; + nct_outb(sc, NCT_PORT_SELECT, reg); + return nct_inb(sc, NCT_PORT_DATA); + } else { + return nct_inb(sc, NCT_PORT_DATA); + } +} + +/* + * Write byte to specified register. + */ +static void +nct_wr(struct nct_softc *sc, int reg, u_int8_t data) +{ + int dev; + + KASSERT(mutex_owned(&sc->sc_lock)); + + dev = reg >> 8; + reg &= 0xff; + + if (dev != sc->sc_curdev && dev != 0x00) { + sc->sc_curdev = dev; + sc->sc_curreg = reg; + nct_outb(sc, NCT_PORT_SELECT, GD_DEVSEL); + nct_outb(sc, NCT_PORT_DATA, dev); + nct_outb(sc, NCT_PORT_SELECT, reg); + nct_outb(sc, NCT_PORT_DATA, data); + } else if (reg != sc->sc_curreg) { + sc->sc_curreg = reg; + nct_outb(sc, NCT_PORT_SELECT, reg); + nct_outb(sc, NCT_PORT_DATA, data); + } else { + nct_outb(sc, NCT_PORT_DATA, data); + } +} + +/* + * Given pin number, return bank and pin mask. This alters no state and so + * can safely be called without the mutex held. + */ +static struct nct_bank * +nct_sel(struct nct_softc *sc, int pin, u_int8_t *mask) +{ + struct nct_bank *nb; + + KASSERT(pin >= 0 && pin < NCT_NUM_PINS); + nb = &sc->sc_bank[pin >> 3]; + KASSERT(pin >= nb->nb_firstpin); + KASSERT((pin & 7) < nb->nb_numpins); + *mask = (u_int8_t)(1 << (pin & 7)) & nb->nb_enabled; + return nb; +} + +/* + * GPIO hook: read pin. + */ +static int +nct_gpio_read(void *arg, int pin) +{ + struct nct_softc *sc = arg; + struct nct_bank *nb; + u_int8_t data, mask; + int rv = GPIO_PIN_LOW; + + nb = nct_sel(sc, pin, &mask); + if (__predict_true(mask != 0)) { + mutex_spin_enter(&sc->sc_lock); + data = nct_rd(sc, nb->nb_reg_data); + if ((data & mask) != 0) { + rv = GPIO_PIN_HIGH; + } + mutex_spin_exit(&sc->sc_lock); + } + return rv; +} + +/* + * GPIO hook: write pin. + */ +static void +nct_gpio_write(void *arg, int pin, int val) +{ + struct nct_softc *sc = arg; + struct nct_bank *nb; + u_int8_t data, mask; + + nb = nct_sel(sc, pin, &mask); + if (__predict_true(mask != 0)) { + mutex_spin_enter(&sc->sc_lock); + data = nct_rd(sc, nb->nb_reg_data); + if (val == GPIO_PIN_LOW) { + data &= ~mask; + } else if (val == GPIO_PIN_HIGH) { + data |= mask; + } + nct_wr(sc, nb->nb_reg_data, data); + mutex_spin_exit(&sc->sc_lock); + } +} + +/* + * GPIO hook: change pin parameters. + */ +static void +nct_gpio_ctl(void *arg, int pin, int flg) +{ + struct nct_softc *sc = arg; + struct nct_bank *nb; + u_int8_t data, mask; + + nb = nct_sel(sc, pin, &mask); + if (__predict_false(mask == 0)) { + return; + } + + /* + * Set input direction early to avoid perturbation. + */ + mutex_spin_enter(&sc->sc_lock); + data = nb->nb_val_dir; + if ((flg & (GPIO_PIN_INPUT | GPIO_PIN_TRISTATE)) != 0) { + data |= mask; + } + if (data != nb->nb_val_dir) { + nct_wr(sc, nb->nb_reg_dir, data); + nb->nb_val_dir = data; + } + + /* + * Set inversion. + */ + data = nb->nb_val_inv; + if ((flg & (GPIO_PIN_OUTPUT | GPIO_PIN_INVOUT)) == + (GPIO_PIN_OUTPUT | GPIO_PIN_INVOUT) || + (flg & (GPIO_PIN_INPUT | GPIO_PIN_INVIN)) == + (GPIO_PIN_INPUT | GPIO_PIN_INVIN)) { + data |= mask; + } else { + data &= ~mask; + } + if (data != nb->nb_val_inv) { + nct_wr(sc, nb->nb_reg_inv, data); + nb->nb_val_inv = data; + } + + /* + * Set drain mode. + */ + data = nb->nb_val_mode; + if ((flg & GPIO_PIN_PUSHPULL) != 0) { + data |= mask; + } else /* GPIO_PIN_OPENDRAIN */ { + data &= ~mask; + } + if (data != nb->nb_val_mode) { + nct_wr(sc, nb->nb_reg_mode, data); + nb->nb_val_mode = data; + } + + /* + * Set output direction late to avoid perturbation. + */ + data = nb->nb_val_dir; + if ((flg & (GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE)) == GPIO_PIN_OUTPUT) { + data &= ~mask; + } + if (data != nb->nb_val_dir) { + nct_wr(sc, nb->nb_reg_dir, data); + nb->nb_val_dir = data; + } + mutex_spin_exit(&sc->sc_lock); +}