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

Reply via email to