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);
+}