Module Name:    src
Committed By:   thorpej
Date:           Mon Jan 17 16:31:24 UTC 2022

Modified Files:
        src/distrib/sets/lists/man: mi
        src/share/man/man4: Makefile
        src/sys/arch/evbarm/conf: RPI
        src/sys/dev/i2c: files.i2c
        src/sys/dev/spi: files.spi
Added Files:
        src/share/man/man4: mcpgpio.4
        src/sys/dev/i2c: mcp23xxxgpio_i2c.c
        src/sys/dev/ic: mcp23xxxgpio.c mcp23xxxgpioreg.h mcp23xxxgpiovar.h
        src/sys/dev/spi: mcp23xxxgpio_spi.c
Removed Files:
        src/share/man/man4: mcp23s17gpio.4
        src/sys/dev/spi: mcp23s17.c mcp23s17.h

Log Message:
Re-factor and overhaul the "mcp23s17gpio" driver as "mcpgpio", and
add support for 8-bit and I2C variants of the chip:
- MCP23008 / MCP23S08: 8-bit (I2C / SPI)
- MCP23017 / MCP23S17: 16-bit (I2C / SPI)
- MCP23018 / MCP23S18: 16-bit (I2C / SPI), open-drain outputs

The MCP23x17 and MCP23x18 are essentially identical, software-wise; we
merely report different GPIO pin capabilities (no push-pull output for
MCP23x18).  Also, remove the tri-state capability that was previously
advertised by the old version of this driver; these chips have no way
to put the pin into a HI-Z mode.

All 3 I2C versions are supported, but the SPI front-end still only
supports the MCP23S17 for now (SPI autoconfiguration needs an overhaul).

mcp23s17gpio(4) remains present as a link to the new mcpgpio(4) man page.

XXX Still to-do: FDT integration, interrupt suppoort.


To generate a diff of this commit:
cvs rdiff -u -r1.1731 -r1.1732 src/distrib/sets/lists/man/mi
cvs rdiff -u -r1.721 -r1.722 src/share/man/man4/Makefile
cvs rdiff -u -r1.2 -r0 src/share/man/man4/mcp23s17gpio.4
cvs rdiff -u -r0 -r1.1 src/share/man/man4/mcpgpio.4
cvs rdiff -u -r1.94 -r1.95 src/sys/arch/evbarm/conf/RPI
cvs rdiff -u -r1.121 -r1.122 src/sys/dev/i2c/files.i2c
cvs rdiff -u -r0 -r1.1 src/sys/dev/i2c/mcp23xxxgpio_i2c.c
cvs rdiff -u -r0 -r1.1 src/sys/dev/ic/mcp23xxxgpio.c \
    src/sys/dev/ic/mcp23xxxgpioreg.h src/sys/dev/ic/mcp23xxxgpiovar.h
cvs rdiff -u -r1.8 -r1.9 src/sys/dev/spi/files.spi
cvs rdiff -u -r1.4 -r0 src/sys/dev/spi/mcp23s17.c
cvs rdiff -u -r1.1 -r0 src/sys/dev/spi/mcp23s17.h
cvs rdiff -u -r0 -r1.1 src/sys/dev/spi/mcp23xxxgpio_spi.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.1731 src/distrib/sets/lists/man/mi:1.1732
--- src/distrib/sets/lists/man/mi:1.1731	Thu Jan  6 21:55:24 2022
+++ src/distrib/sets/lists/man/mi	Mon Jan 17 16:31:23 2022
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1731 2022/01/06 21:55:24 nia Exp $
+# $NetBSD: mi,v 1.1732 2022/01/17 16:31:23 thorpej Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -1471,6 +1471,7 @@
 ./usr/share/man/cat4/mcp3kadc.0			man-sys-catman		.cat
 ./usr/share/man/cat4/mcp48x1dac.0		man-sys-catman		.cat
 ./usr/share/man/cat4/mcp980x.0			man-sys-catman		.cat
+./usr/share/man/cat4/mcpgpio.0			man-sys-catman		.cat
 ./usr/share/man/cat4/mcx.0			man-sys-catman		.cat
 ./usr/share/man/cat4/md.0			man-sys-catman		.cat
 ./usr/share/man/cat4/mfb.0			man-sys-catman		.cat
@@ -4679,6 +4680,7 @@
 ./usr/share/man/html4/mcp3kadc.html		man-sys-htmlman		html
 ./usr/share/man/html4/mcp48x1dac.html		man-sys-htmlman		html
 ./usr/share/man/html4/mcp980x.html		man-sys-htmlman		html
+./usr/share/man/html4/mcpgpio.html		man-sys-htmlman		html
 ./usr/share/man/html4/mcx.html			man-sys-htmlman		html
 ./usr/share/man/html4/md.html			man-sys-htmlman		html
 ./usr/share/man/html4/mfb.html			man-sys-htmlman		html
@@ -7740,6 +7742,7 @@
 ./usr/share/man/man4/mcp3kadc.4			man-sys-man		.man
 ./usr/share/man/man4/mcp48x1dac.4		man-sys-man		.man
 ./usr/share/man/man4/mcp980x.4			man-sys-man		.man
+./usr/share/man/man4/mcpgpio.4			man-sys-man		.man
 ./usr/share/man/man4/mcx.4			man-sys-man		.man
 ./usr/share/man/man4/md.4			man-sys-man		.man
 ./usr/share/man/man4/mfb.4			man-sys-man		.man

Index: src/share/man/man4/Makefile
diff -u src/share/man/man4/Makefile:1.721 src/share/man/man4/Makefile:1.722
--- src/share/man/man4/Makefile:1.721	Thu Jan  6 21:55:23 2022
+++ src/share/man/man4/Makefile	Mon Jan 17 16:31:23 2022
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.721 2022/01/06 21:55:23 nia Exp $
+#	$NetBSD: Makefile,v 1.722 2022/01/17 16:31:23 thorpej Exp $
 #	@(#)Makefile	8.1 (Berkeley) 6/18/93
 
 MAN=	aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \
@@ -137,7 +137,9 @@ MAN+=	dbcool.4 g760a.4 lmenv.4 lmtemp.4 
 	smscmon.4 spdmem.4 tps65217pmic.4
 
 # machine-independent SPI devices
-MAN +=	m25p.4 mcp23s17gpio.4 mcp3kadc.4 mcp48x1dac.4 tm121temp.4
+MAN +=	m25p.4 mcpgpio.4 mcp3kadc.4 mcp48x1dac.4 tm121temp.4
+
+MLINKS+=mcpgpio.4 mcp23s17gpio.4
 
 # machine-independent SD/MMC devices
 MAN +=	sbt.4 sdhc.4 sdmmc.4

Index: src/sys/arch/evbarm/conf/RPI
diff -u src/sys/arch/evbarm/conf/RPI:1.94 src/sys/arch/evbarm/conf/RPI:1.95
--- src/sys/arch/evbarm/conf/RPI:1.94	Mon Nov 23 06:24:35 2020
+++ src/sys/arch/evbarm/conf/RPI	Mon Jan 17 16:31:24 2022
@@ -1,5 +1,5 @@
 #
-#	$NetBSD: RPI,v 1.94 2020/11/23 06:24:35 rin Exp $
+#	$NetBSD: RPI,v 1.95 2022/01/17 16:31:24 thorpej Exp $
 #
 #	RPi -- Raspberry Pi
 #
@@ -178,12 +178,15 @@ spi* at spibus?
 #mcp3kadc0 at spi? slave 0 flags 0
 
 # PIFace or other boards using that chip (needs gpio)
-#mcp23s17gpio0 at spi? slave 0 flags 0
-#mcp23s17gpio1 at spi? slave 0 flags 1
-#mcp23s17gpio2 at spi? slave 0 flags 2
-#mcp23s17gpio3 at spi? slave 0 flags 3
+#mcpgpio0 at spi? slave 0 flags 0
+#mcpgpio1 at spi? slave 0 flags 1
+#mcpgpio2 at spi? slave 0 flags 2
+#mcpgpio3 at spi? slave 0 flags 3
 
-# gpio support (e. g. mcp23s17gpio, bcmgpio)
+# MCP230xx GPIO on I2C.
+mcpgpio* at iic? addr ?
+
+# gpio support (e. g. mcpgpio, bcmgpio)
 gpio*		at gpiobus?
 
 # various options for wscons - we try to look as much like a standard

Index: src/sys/dev/i2c/files.i2c
diff -u src/sys/dev/i2c/files.i2c:1.121 src/sys/dev/i2c/files.i2c:1.122
--- src/sys/dev/i2c/files.i2c:1.121	Mon Dec 27 23:04:20 2021
+++ src/sys/dev/i2c/files.i2c	Mon Jan 17 16:31:23 2022
@@ -1,4 +1,4 @@
-#	$NetBSD: files.i2c,v 1.121 2021/12/27 23:04:20 andvar Exp $
+#	$NetBSD: files.i2c,v 1.122 2022/01/17 16:31:23 thorpej Exp $
 
 obsolete defflag	opt_i2cbus.h		I2C_SCAN
 define	i2cbus { }
@@ -204,6 +204,10 @@ attach	tps65217pmic at iic
 attach	tps65217reg at tps65217pmic
 file	dev/i2c/tps65217pmic.c 		tps65217pmic	needs-flag
 
+# Microchip MCP23008 / MCP23017 I/O Expander
+attach	mcpgpio at iic with mcpgpio_i2c
+file	dev/i2c/mcp23xxxgpio_i2c.c	mcpgpio_i2c
+
 # Microchip MCP980x
 device	mcp980x: sysmon_envsys
 attach	mcp980x at iic

Index: src/sys/dev/spi/files.spi
diff -u src/sys/dev/spi/files.spi:1.8 src/sys/dev/spi/files.spi:1.9
--- src/sys/dev/spi/files.spi:1.8	Tue Dec  7 17:39:54 2021
+++ src/sys/dev/spi/files.spi	Mon Jan 17 16:31:23 2022
@@ -1,4 +1,4 @@
-#	$NetBSD: files.spi,v 1.8 2021/12/07 17:39:54 brad Exp $
+#	$NetBSD: files.spi,v 1.9 2022/01/17 16:31:23 thorpej Exp $
 
 define	spibus { }
 
@@ -32,9 +32,8 @@ attach	mcp48x1dac at spi
 file	dev/spi/mcp48x1.c		mcp48x1dac	
 
 # MCP23S17 16-bit GPIO
-device	mcp23s17gpio: gpiobus
-attach	mcp23s17gpio at spi
-file	dev/spi/mcp23s17.c		mcp23s17gpio
+attach	mcpgpio at spi with mcpgpio_spi
+file	dev/spi/mcp23xxxgpio_spi.c	mcpgpio_spi
 
 # Solomon Systech SSD13xx PLED/OLED display
 attach	ssdfb at spi with ssdfb_spi
@@ -47,4 +46,4 @@ file	dev/spi/mcp3k.c			mcp3kadc
 
 # Sparkfun Serial motor controller
 attach  scmd at spi with scmdspi
-file	dev/spi/scmdspi.c			scmdspi
+file	dev/spi/scmdspi.c		scmdspi

Added files:

Index: src/share/man/man4/mcpgpio.4
diff -u /dev/null src/share/man/man4/mcpgpio.4:1.1
--- /dev/null	Mon Jan 17 16:31:24 2022
+++ src/share/man/man4/mcpgpio.4	Mon Jan 17 16:31:23 2022
@@ -0,0 +1,108 @@
+.\"	$NetBSD: mcpgpio.4,v 1.1 2022/01/17 16:31:23 thorpej Exp $
+.\"
+.\"Copyright (c) 2014 Frank Kardel
+.\"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.
+.\"
+.\"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 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 January 10, 2022
+.Dt MCPGPIO 4
+.Os
+.Sh NAME
+.Nm mcpgpio
+.Nd Driver for Microchip I/O Expanders on I2C and SPI bus
+.Sh SYNOPSIS
+.Ss I2C
+.Cd "mcpgpio* at iic? addr ?"
+.Cd "gpio* at gpiobus?"
+.Ss SPI
+.Cd "mcpgpio0 at spi? slave 0 flags 0"
+.Cd "mcpgpio1 at spi? slave 0 flags 1"
+.Cd "mcpgpio2 at spi? slave 0 flags 2"
+.Cd "mcpgpio3 at spi? slave 0 flags 3"
+.Cd "gpio* at gpiobus?"
+.Sh DESCRIPTION
+The
+.Nm
+driver supports the following Microchip I/O Expanders:
+.Bl -tag -width "mcp23x08"
+.It MCP23008
+8-bit I/O expander, I2C interface
+.It MCP23S08
+8-bit I/O expander, SPI interface
+.It MCP23017
+16-bit I/O expander, I2C interface
+.It MCP23S17
+16-bit I/O expander, SPI interface
+.It MCP23018
+16-bit I/O expander, open-drain outputs, I2C interface
+.It MCP23S18
+16-bit I/O expander, open-drain outputs, SPI interface
+.El
+.Pp
+Access to the pins is provided by the
+.Xr gpio 4
+interface.
+.Pp
+The SPI version of these devices support multiple chips per chip select
+signal.
+On the MCP23S08 and MCP23S17, this is achieved by tying the address select
+pins to VDD or GND to select an address
+.Pq 0-3 on MCP23S08 or 0-7 on MCP23S17 .
+The MCP23S18 has a similar capability, but uses an analog voltage input
+on a single address select pin, along with an internal voltage divider
+ladder and a series of comparators to generate the 3 address bits; see
+the data sheet for details.
+The
+.Ar flags
+argument in the configuration directive for SPI attachments selects the
+hardware address of the chip instance for that driver instance.
+.Sh SEE ALSO
+.Xr gpio 4 ,
+.Xr iic 4 ,
+.Xr intro 4 ,
+.Xr spi 4
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Nx 7.0 .
+It was overhauled in
+.Nx 10.0
+to support additional chip types and to add the I2C attachment.
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Frank Kardel Aq Mt kar...@netbsd.org
+and
+.An Jason R. Thorpe Aq Mt thor...@netbsd.org .
+.Sh BUGS
+SPI instances of the
+.Nm
+driver do not utilize the Device Tree bindings for this device.
+.Pp
+The
+.Nm
+driver does not currently act as a GPIO provider for the platform
+device tree.

Index: src/sys/dev/i2c/mcp23xxxgpio_i2c.c
diff -u /dev/null src/sys/dev/i2c/mcp23xxxgpio_i2c.c:1.1
--- /dev/null	Mon Jan 17 16:31:24 2022
+++ src/sys/dev/i2c/mcp23xxxgpio_i2c.c	Mon Jan 17 16:31:23 2022
@@ -0,0 +1,176 @@
+/*      $NetBSD: mcp23xxxgpio_i2c.c,v 1.1 2022/01/17 16:31:23 thorpej Exp $	*/
+
+/*-
+ * Copyright (c) 2022 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: mcp23xxxgpio_i2c.c,v 1.1 2022/01/17 16:31:23 thorpej Exp $");
+
+/* 
+ * Driver for Microchip serial I/O expanders:
+ *
+ *	MCP23008	8-bit, I2C interface
+ *	MCP23017	16-bit, I2C interface
+ *	MCP23018	16-bit (open-drain outputs), I2C interface
+ *
+ * Data sheet:
+ *
+ *	https://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf
+ */
+
+#include <sys/types.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+
+#include <dev/ic/mcp23xxxgpioreg.h>
+#include <dev/ic/mcp23xxxgpiovar.h>
+
+#include <dev/i2c/i2cvar.h>
+
+struct mcpgpio_i2c_softc {
+	struct mcpgpio_softc	sc_mcpgpio;
+
+	i2c_tag_t		sc_i2c;
+	i2c_addr_t		sc_addr;
+};
+
+#define	MCPGPIO_TO_I2C(sc)					\
+	container_of((sc), struct mcpgpio_i2c_softc, sc_mcpgpio)
+
+static const struct mcpgpio_variant mcp23008 = {
+	.name = "MCP23008",
+	.type = MCPGPIO_TYPE_23x08,
+};
+
+static const struct mcpgpio_variant mcp23017 = {
+	.name = "MCP23017",
+	.type = MCPGPIO_TYPE_23x17,
+};
+
+static const struct mcpgpio_variant mcp23018 = {
+	.name = "MCP23018",
+	.type = MCPGPIO_TYPE_23x18,
+};
+
+static const struct device_compatible_entry compat_data[] = {
+	{ .compat = "microchip,mcp23008",	.data = &mcp23008 },
+	{ .compat = "microchip,mcp23017",	.data = &mcp23017 },
+	{ .compat = "microchip,mcp23018",	.data = &mcp23018 },
+	DEVICE_COMPAT_EOL
+};
+
+static int
+mcpgpio_i2c_lock(struct mcpgpio_softc *sc)
+{
+	struct mcpgpio_i2c_softc *isc = MCPGPIO_TO_I2C(sc);
+
+	return iic_acquire_bus(isc->sc_i2c, 0);
+}
+
+static void
+mcpgpio_i2c_unlock(struct mcpgpio_softc *sc)
+{
+	struct mcpgpio_i2c_softc *isc = MCPGPIO_TO_I2C(sc);
+
+	iic_release_bus(isc->sc_i2c, 0);
+}
+
+static int
+mcpgpio_i2c_read(struct mcpgpio_softc *sc, unsigned int bank,
+    uint8_t reg, uint8_t *valp)
+{
+	struct mcpgpio_i2c_softc *isc = MCPGPIO_TO_I2C(sc);
+
+	/* Only one chip per hardware address in i2c. */
+	KASSERT((bank & ~1U) == 0);
+
+	return iic_exec(isc->sc_i2c, I2C_OP_READ_WITH_STOP, isc->sc_addr,
+	    &reg, 1, valp, 1, 0);
+}
+
+static int
+mcpgpio_i2c_write(struct mcpgpio_softc *sc, unsigned int bank,
+    uint8_t reg, uint8_t val)
+{
+	struct mcpgpio_i2c_softc *isc = MCPGPIO_TO_I2C(sc);
+
+	/* Only one chip per hardware address in i2c. */
+	KASSERT((bank & ~1U) == 0);
+
+	return iic_exec(isc->sc_i2c, I2C_OP_WRITE_WITH_STOP, isc->sc_addr,
+	    &reg, 1, &val, 1, 0);
+}
+
+static const struct mcpgpio_accessops mcpgpio_i2c_accessops = {
+	.lock	=	mcpgpio_i2c_lock,
+	.unlock	=	mcpgpio_i2c_unlock,
+	.read	=	mcpgpio_i2c_read,
+	.write	=	mcpgpio_i2c_write,
+};
+
+static int
+mcpgpio_i2c_match(device_t parent, cfdata_t match, void *aux)
+{
+	struct i2c_attach_args *ia = aux;
+	int match_result;
+
+	if (iic_use_direct_match(ia, match, compat_data, &match_result)) {
+		return match_result;
+	}
+	return 0;
+}
+
+static void
+mcpgpio_i2c_attach(device_t parent, device_t self, void *aux)
+{
+	struct mcpgpio_i2c_softc *isc = device_private(self);
+	struct mcpgpio_softc *sc = &isc->sc_mcpgpio;
+	struct i2c_attach_args *ia = aux;
+	const struct device_compatible_entry *dce;
+
+	isc->sc_i2c = ia->ia_tag;
+	isc->sc_addr = ia->ia_addr;
+
+	dce = iic_compatible_lookup(ia, compat_data);
+	KASSERT(dce != NULL);
+
+	sc->sc_dev = self;
+	sc->sc_variant = dce->data;
+	sc->sc_iocon = IOCON_SEQOP;
+	sc->sc_phandle = ia->ia_cookie;
+	sc->sc_accessops = &mcpgpio_i2c_accessops;
+
+	aprint_naive("\n");	
+	aprint_normal(": %s I/O Expander\n", sc->sc_variant->name);
+
+	mcpgpio_attach(sc);
+}
+
+CFATTACH_DECL_NEW(mcpgpio_i2c, sizeof(struct mcpgpio_i2c_softc),
+    mcpgpio_i2c_match, mcpgpio_i2c_attach, NULL, NULL);

Index: src/sys/dev/ic/mcp23xxxgpio.c
diff -u /dev/null src/sys/dev/ic/mcp23xxxgpio.c:1.1
--- /dev/null	Mon Jan 17 16:31:24 2022
+++ src/sys/dev/ic/mcp23xxxgpio.c	Mon Jan 17 16:31:23 2022
@@ -0,0 +1,355 @@
+/*      $NetBSD: mcp23xxxgpio.c,v 1.1 2022/01/17 16:31:23 thorpej Exp $	*/
+
+/*-
+ * Copyright (c) 2014, 2022 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Frank Kardel, and by Jason R. Thorpe.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: mcp23xxxgpio.c,v 1.1 2022/01/17 16:31:23 thorpej Exp $");
+
+/* 
+ * Driver for Microchip serial I/O expansers:
+ *
+ *	MCP23008	8-bit, I2C interface
+ *	MCP23S08	8-bit, SPI interface
+ *	MCP23017	16-bit, I2C interface
+ *	MCP23S17	16-bit, SPI interface
+ *	MCP23018	16-bit (open-drain outputs), I2C interface
+ *	MCP23S18	16-bit (open-drain outputs), SPI interface
+ *
+ * Data sheet:
+ *
+ *	https://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf
+ */
+
+#include "gpio.h"
+
+#include <sys/types.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/kmem.h>
+
+#include <dev/ic/mcp23xxxgpioreg.h>
+#include <dev/ic/mcp23xxxgpiovar.h>
+
+#define	PIN_BANK(p)	((p) / MCPGPIO_PINS_PER_BANK)
+#define	PIN_EPIN(p)	((p) % MCPGPIO_PINS_PER_BANK)
+
+static uint8_t
+mcpgpio_regaddr(struct mcpgpio_softc *sc, uint8_t bank, uint8_t reg)
+{
+
+	if (sc->sc_variant->type == MCPGPIO_TYPE_23x08) {
+		return reg;
+	}
+	if (sc->sc_iocon & IOCON_BANK) {
+		return REGADDR_BANK1(bank & 1, reg);
+	}
+	return REGADDR_BANK0(bank & 1, reg);
+}
+
+static const char *
+mcpgpio_regname(uint8_t reg)
+{
+	static const char * const regnames[] = {
+		[REG_IODIR]	=	"IODIR",
+		[REG_IPOL]	=	"IPOL",
+		[REG_GPINTEN]	=	"GPINTEN",
+		[REG_DEFVAL]	=	"DEFVAL",
+		[REG_INTCON]	=	"INTCON",
+		[REG_IOCON]	=	"IOCON",
+		[REG_GPPU]	=	"GPPU",
+		[REG_INTF]	=	"INTF",
+		[REG_INTCAP]	=	"INTCAP",
+		[REG_GPIO]	=	"GPIO",
+		[REG_OLAT]	=	"OLAT",
+	};
+	KASSERT(reg <= REG_OLAT);
+	return regnames[reg];
+}
+
+static const char *
+mcpgpio_bankname(struct mcpgpio_softc *sc, uint8_t bank)
+{
+	static const char * const banknames[] = { "A", "B" };
+
+	if (sc->sc_variant->type == MCPGPIO_TYPE_23x08) {
+		return "";
+	}
+	return banknames[bank & 1];
+}
+
+static int
+mcpgpio__lock(struct mcpgpio_softc *sc, const char *fn)
+{
+	int error;
+
+	error = sc->sc_accessops->lock(sc);
+	if (__predict_false(error != 0)) {
+		aprint_error_dev(sc->sc_dev,
+		    "%s: unable to lock device, error=%d\n", fn, error);
+	}
+	return error;
+}
+
+#define	mcpgpio_lock(sc)						\
+	mcpgpio__lock((sc), __func__)
+
+static void
+mcpgpio_unlock(struct mcpgpio_softc *sc)
+{
+	sc->sc_accessops->unlock(sc);
+}
+
+static int
+mcpgpio__read(struct mcpgpio_softc *sc, const char *fn,
+    uint8_t bank, uint8_t reg, uint8_t *valp)
+{
+	int error;
+	uint8_t regaddr = mcpgpio_regaddr(sc, bank, reg);
+
+	error = sc->sc_accessops->read(sc, bank, regaddr, valp);
+	if (__predict_false(error != 0)) {
+		aprint_error_dev(sc->sc_dev,
+		    "%s: unable to read %s%s[0x%02x], error=%d\n", fn,
+		    mcpgpio_regname(reg), mcpgpio_bankname(sc, bank),
+		    regaddr, error);
+	}
+	return error;
+}
+
+#define	mcpgpio_read(sc, b, r, v)					\
+	mcpgpio__read((sc), __func__, (b), (r), (v))
+
+static int
+mcpgpio__write(struct mcpgpio_softc *sc, const char *fn,
+    uint8_t bank, uint8_t reg, uint8_t val)
+{
+	int error;
+	uint8_t regaddr = mcpgpio_regaddr(sc, bank, reg);
+
+	error = sc->sc_accessops->write(sc, bank, regaddr, val);
+	if (__predict_false(error != 0)) {
+		aprint_error_dev(sc->sc_dev,
+		    "%s: unable to write %s%s[0x%02x], error=%d\n", fn,
+		    mcpgpio_regname(reg), mcpgpio_bankname(sc, bank),
+		    regaddr, error);
+	}
+	return error;
+}
+
+#define	mcpgpio_write(sc, b, r, v)					\
+	mcpgpio__write((sc), __func__, (b), (r), (v))
+
+#if NGPIO > 0
+/* GPIO support functions */
+static int
+mcpgpio_gpio_pin_read(void *arg, int pin)
+{
+	struct mcpgpio_softc *sc = arg;
+	uint8_t data;
+	int val;
+	int error;
+
+	KASSERT(pin >= 0 && pin < sc->sc_npins);
+
+	const uint8_t bank = PIN_BANK(pin);
+	const uint8_t epin = PIN_EPIN(pin);
+
+	error = mcpgpio_lock(sc);
+	if (__predict_false(error != 0)) {
+		return GPIO_PIN_LOW;
+	}
+	error = mcpgpio_read(sc, bank, REG_GPIO, &data);
+	if (error) {
+		data = 0;
+	}
+	mcpgpio_unlock(sc);
+
+	val = data & __BIT(epin) ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
+
+	return val;
+}
+
+static void
+mcpgpio_gpio_pin_write(void *arg, int pin, int value)
+{
+	struct mcpgpio_softc *sc = arg;
+	uint8_t data;
+	int error;
+
+	KASSERT(pin >= 0 && pin < sc->sc_npins);
+
+	const uint8_t bank = PIN_BANK(pin);
+	const uint8_t epin = PIN_EPIN(pin);
+
+	error = mcpgpio_lock(sc);
+	if (__predict_false(error != 0)) {
+		return;
+	}
+
+	error = mcpgpio_read(sc, bank, REG_OLAT, &data);
+	if (__predict_true(error == 0)) {
+		if (value == GPIO_PIN_HIGH) {
+			data |= __BIT(epin);
+		} else {
+			data &= ~__BIT(epin);
+		}
+		(void) mcpgpio_write(sc, bank, REG_OLAT, data);
+	}
+
+	mcpgpio_unlock(sc);
+}
+
+static void
+mcpgpio_gpio_pin_ctl(void *arg, int pin, int flags)
+{
+	struct mcpgpio_softc *sc = arg;
+	uint8_t iodir, ipol, gppu;
+	int error;
+
+	KASSERT(pin >= 0 && pin < sc->sc_npins);
+
+	const uint8_t bank = PIN_BANK(pin);
+	const uint8_t epin = PIN_EPIN(pin);
+	const uint8_t bit = __BIT(epin);
+
+	error = mcpgpio_lock(sc);
+	if (__predict_false(error != 0)) {
+		return;
+	}
+
+	if ((error = mcpgpio_read(sc, bank, REG_IODIR, &iodir)) != 0 ||
+	    (error = mcpgpio_read(sc, bank, REG_IPOL, &ipol)) != 0 ||
+	    (error = mcpgpio_read(sc, bank, REG_GPPU, &gppu)) != 0) {
+		return;
+	}
+
+	if (flags & (GPIO_PIN_OUTPUT|GPIO_PIN_INPUT)) {
+		if ((flags & GPIO_PIN_INPUT) || !(flags & GPIO_PIN_OUTPUT)) {
+			/* for safety INPUT will override output */
+			iodir |= bit;
+		} else {
+			iodir &= ~bit;
+		}
+	}
+
+	if (flags & GPIO_PIN_INVIN) {
+		ipol |= bit;
+	} else {
+		ipol &= ~bit;
+	}
+
+	if (flags & GPIO_PIN_PULLUP) {
+		gppu |= bit;
+	} else {
+		gppu &= ~bit;
+	}
+
+	(void) mcpgpio_write(sc, bank, REG_IODIR, iodir);
+	(void) mcpgpio_write(sc, bank, REG_IPOL, ipol);
+	(void) mcpgpio_write(sc, bank, REG_GPPU, gppu);
+
+	mcpgpio_unlock(sc);
+} 
+#endif /* NGPIO > 0 */
+
+void
+mcpgpio_attach(struct mcpgpio_softc *sc)
+{
+	int error;
+
+	KASSERT(sc->sc_variant != NULL);
+
+	/*
+	 * The SPI front-end provides the logical pin count to
+	 * deal with muliple chips on one chip select.
+	 */
+	if (sc->sc_npins == 0) {
+		sc->sc_npins = sc->sc_variant->type == MCPGPIO_TYPE_23x08
+		? MCP23x08_GPIO_NPINS : MCP23x17_GPIO_NPINS;
+	}
+	sc->sc_gpio_pins =
+	    kmem_zalloc(sc->sc_npins * sizeof(*sc->sc_gpio_pins), KM_SLEEP);
+
+	/*
+	 * Perform the basic setup of the device.  We program the IOCON
+	 * register once for each bank, even though the data sheet is
+	 * not clear that this is strictly necessary.
+	 */
+	if (mcpgpio_lock(sc) != 0) {
+		return;
+	}
+	error = mcpgpio_write(sc, 0, REG_IOCON, sc->sc_iocon);
+	if (error == 0 && sc->sc_variant->type != MCPGPIO_TYPE_23x08) {
+		error = mcpgpio_write(sc, 1, REG_IOCON, sc->sc_iocon);
+	}
+	mcpgpio_unlock(sc);
+	if (error) {
+		return;
+	}
+
+	/* XXX FDT glue. */
+
+#if NGPIO > 0
+	struct gpiobus_attach_args gba;
+	int pin_output_caps;
+	int i;
+
+	pin_output_caps = sc->sc_variant->type == MCPGPIO_TYPE_23x18
+	    ? GPIO_PIN_OPENDRAIN : GPIO_PIN_PUSHPULL;
+
+	for (i = 0; i < sc->sc_npins; i++) {
+		sc->sc_gpio_pins[i].pin_num = i;
+		sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT |
+			GPIO_PIN_OUTPUT |
+			pin_output_caps |
+			GPIO_PIN_PULLUP |
+			GPIO_PIN_INVIN;
+
+		/* read initial state */
+		sc->sc_gpio_pins[i].pin_state =
+			mcpgpio_gpio_pin_read(sc, i);
+	}
+
+	/* create controller tag */
+	sc->sc_gpio_gc.gp_cookie = sc;
+	sc->sc_gpio_gc.gp_pin_read = mcpgpio_gpio_pin_read;
+	sc->sc_gpio_gc.gp_pin_write = mcpgpio_gpio_pin_write;
+	sc->sc_gpio_gc.gp_pin_ctl = mcpgpio_gpio_pin_ctl;
+
+	gba.gba_gc = &sc->sc_gpio_gc;
+	gba.gba_pins = sc->sc_gpio_pins;
+	gba.gba_npins = sc->sc_npins;
+
+	config_found(sc->sc_dev, &gba, gpiobus_print, CFARGS_NONE);
+#else
+	aprint_normal_dev(sc->sc_dev, "no GPIO configured in kernel");
+#endif
+}
Index: src/sys/dev/ic/mcp23xxxgpioreg.h
diff -u /dev/null src/sys/dev/ic/mcp23xxxgpioreg.h:1.1
--- /dev/null	Mon Jan 17 16:31:24 2022
+++ src/sys/dev/ic/mcp23xxxgpioreg.h	Mon Jan 17 16:31:23 2022
@@ -0,0 +1,118 @@
+/*      $NetBSD: mcp23xxxgpioreg.h,v 1.1 2022/01/17 16:31:23 thorpej Exp $	*/
+
+/*-
+ * Copyright (c) 2014, 2022 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Frank Kardel, and by Jason R. Thorpe.
+ *
+ * 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.
+ */
+
+#ifndef _DEV_IC_MCP23xxxGPIOREG_H_
+#define _DEV_IC_MCP23xxxGPIOREG_H_
+
+/*
+ * Microchip serial I/O expanders:
+ *
+ *	MCP23008	8-bit, I2C interface
+ *	MCP23S08	8-bit, SPI interface
+ *	MCP23017	16-bit, I2C interface
+ *	MCP23S17	16-bit, SPI interface
+ *	MCP23018	16-bit (open-drain outputs), I2C interface
+ *	MCP23S18	16-bit (open-drain outputs), SPI interface
+ *
+ * Data sheet:
+ *
+ *	https://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf
+ */
+
+/* resources */
+#define	MCPGPIO_PINS_PER_BANK	8
+
+#define	MCP23x08_GPIO_NBANKS	1
+#define	MCP23x08_GPIO_NPINS	(MCPGPIO_PINS_PER_BANK * MCP23x08_GPIO_NBANKS)
+
+#define	MCP23x17_GPIO_NBANKS	2
+#define MCP23x17_GPIO_NPINS	(MCPGPIO_PINS_PER_BANK * MCP23x17_GPIO_NBANKS)
+
+/*
+ * The MCP23x17 has two addressing schemes, depending on the setting
+ * of IOCON.BANK:
+ *
+ * IOCON.BANK=1		IOCON.BANK=0		Register
+ * -----------------------------------------------------
+ *	0x00			0x00		IODIRA
+ *	0x10			0x01		IODIRB
+ *	0x01			0x02		IPOLA
+ *	0x11			0x03		IPOLB
+ *	0x02			0x04		GPINTENA
+ *	0x12			0x05		GPINTENB
+ *	0x03			0x06		DEFVALA
+ *	0x13			0x07		DEFVALB
+ *	0x04			0x08		INTCONA
+ *	0x14			0x09		INTCONB
+ *	0x05			0x0a		IOCON
+ *	0x15			0x0b		IOCON (yes, it's an alias)
+ *	0x06			0x0c		GPPUA
+ *	0x16			0x0d		GPPUB
+ *	0x07			0x0e		INTFA
+ *	0x17			0x0f		INTFB
+ *	0x08			0x10		INTCAPA
+ *	0x18			0x11		INTCAPB
+ *	0x09			0x12		GPIOA
+ *	0x19			0x13		GPIOB
+ *	0x0a			0x14		OLATA
+ *	0x1a			0x15		OLATB
+ *
+ * The MCP23x08, of course, only has a single bank of 8 GPIOs, and it
+ * has an addressing schme that operates like IOCON.BANK=1
+ */
+#define	REG_IODIR		0x00
+#define	REG_IPOL		0x01
+#define	REG_GPINTEN		0x02
+#define	REG_DEFVAL		0x03
+#define	REG_INTCON		0x04
+#define	REG_IOCON		0x05
+#define	REG_GPPU		0x06
+#define	REG_INTF		0x07
+#define	REG_INTCAP		0x08
+#define	REG_GPIO		0x09
+#define	REG_OLAT		0x0a
+
+/* IOCON.BANK=1 */
+#define	REGADDR_BANK1(bank, reg)	(((bank) << 4) | (reg))
+
+/* IOCON.BANK=0 */
+#define	REGADDR_BANK0(bank, reg)	(((reg) << 1) | (bank))
+
+/* bits */
+#define IOCON_BANK	__BIT(7) /* select address layout (23x1x only) */
+#define IOCON_MIRROR	__BIT(6) /* mirror INTA/INTB outputs (23x1x only) */
+#define IOCON_SEQOP	__BIT(5) /* sequential address operation */
+#define IOCON_DISLW	__BIT(4) /* slew rate SDA output */
+#define IOCON_HAEN	__BIT(3) /* hardware address enable bit (SPI only) */
+#define IOCON_ODR	__BIT(2) /* configure INT pin as open drain */
+#define IOCON_INTPOL	__BIT(1) /* INT pin polarity (unless ODR is set) */
+
+#endif /* _DEV_IC_MCP23xxxGPIOREG_H_ */
Index: src/sys/dev/ic/mcp23xxxgpiovar.h
diff -u /dev/null src/sys/dev/ic/mcp23xxxgpiovar.h:1.1
--- /dev/null	Mon Jan 17 16:31:24 2022
+++ src/sys/dev/ic/mcp23xxxgpiovar.h	Mon Jan 17 16:31:23 2022
@@ -0,0 +1,90 @@
+/*      $NetBSD: mcp23xxxgpiovar.h,v 1.1 2022/01/17 16:31:23 thorpej Exp $	*/
+
+/*-
+ * Copyright (c) 2014, 2022 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Frank Kardel, and by Jason R. Thorpe.
+ *
+ * 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.
+ */
+
+#ifndef _DEV_IC_MCP23xxxGPIOVAR_H_
+#define	_DEV_IC_MCP23xxxGPIOVAR_H_
+
+/* 
+ * Driver for Microchip serial I/O expansers:
+ *
+ *	MCP23008	8-bit, I2C interface
+ *	MCP23S08	8-bit, SPI interface
+ *	MCP23017	16-bit, I2C interface
+ *	MCP23S17	16-bit, SPI interface
+ *	MCP23018	16-bit (open-drain outputs), I2C interface
+ *	MCP23S18	16-bit (open-drain outputs), SPI interface
+ *
+ * Data sheet:
+ *
+ *	https://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf
+ */
+
+#include <sys/gpio.h>
+#include <dev/gpio/gpiovar.h>
+
+struct mcpgpio_softc;
+
+typedef enum {
+	MCPGPIO_TYPE_23x08	= 0,
+	MCPGPIO_TYPE_23x17	= 1,
+	MCPGPIO_TYPE_23x18	= 2,
+} mcpgpio_type;
+
+struct mcpgpio_variant {
+	const char		*name;
+	mcpgpio_type		type;
+};
+
+struct mcpgpio_accessops {
+	int			(*lock)(struct mcpgpio_softc *);
+	void			(*unlock)(struct mcpgpio_softc *);
+	int			(*read)(struct mcpgpio_softc *,
+				    unsigned int, uint8_t, uint8_t *);
+	int			(*write)(struct mcpgpio_softc *,
+				    unsigned int, uint8_t, uint8_t);
+};
+
+struct mcpgpio_softc {
+	device_t                sc_dev;
+	const struct mcpgpio_variant *sc_variant;
+	struct gpio_chipset_tag	sc_gpio_gc;
+	gpio_pin_t		*sc_gpio_pins;
+	unsigned int		sc_npins;
+	int			sc_phandle;
+	uint8_t			sc_iocon; /* I/O configuration */
+
+	/* Bus-specific access functions. */
+	const struct mcpgpio_accessops *sc_accessops;
+};
+
+void	mcpgpio_attach(struct mcpgpio_softc *);
+
+#endif /* _DEV_IC_MCP23xxxGPIOVAR_H_ */

Index: src/sys/dev/spi/mcp23xxxgpio_spi.c
diff -u /dev/null src/sys/dev/spi/mcp23xxxgpio_spi.c:1.1
--- /dev/null	Mon Jan 17 16:31:24 2022
+++ src/sys/dev/spi/mcp23xxxgpio_spi.c	Mon Jan 17 16:31:23 2022
@@ -0,0 +1,298 @@
+/*      $NetBSD: mcp23xxxgpio_spi.c,v 1.1 2022/01/17 16:31:23 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 2014, 2022 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Frank Kardel, and by Jason R. Thorpe.
+ *
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: mcp23xxxgpio_spi.c,v 1.1 2022/01/17 16:31:23 thorpej Exp $");
+
+/* 
+ * Driver for Microchip serial I/O expanders:
+ *
+ *	MCP23S08	8-bit, SPI interface
+ *	MCP23S17	16-bit, SPI interface
+ *	MCP23S18	16-bit (open-drain outputs), SPI interface
+ *
+ * Data sheet:
+ *
+ *	https://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf
+ */
+
+#include <sys/types.h>
+#include <sys/bitops.h>
+#include <sys/device.h>
+#include <sys/kernel.h>
+#include <sys/mutex.h>
+
+#include <dev/ic/mcp23xxxgpioreg.h>
+#include <dev/ic/mcp23xxxgpiovar.h>
+
+#include <dev/spi/spivar.h>
+
+/*
+ * Multi-chip-on-select configurations appear to the upper layers like
+ * additional GPIO banks; mixing different chip types on the same chip
+ * select is not allowed.
+ *
+ * Some chips have 2 banks per chip, and we have up to 8 chips per chip
+ * select, it's a total of 16 banks per chip select / driver instance.
+ */
+#define	MCPGPIO_SPI_MAXBANKS	16
+
+struct mcpgpio_spi_softc {
+	struct mcpgpio_softc	sc_mcpgpio;
+
+	kmutex_t		sc_mutex;
+	struct spi_handle      *sc_sh; 
+	uint8_t			sc_ha[MCPGPIO_SPI_MAXBANKS];
+};
+
+/*
+ * SPI-specific commands (the serial interface on the I2C flavor of
+ * the chip uses the I2C protocol to infer this information).  Careful
+ * readers will note that this ends up being exactly the same bits
+ * on the serial interface that the I2C flavor of the chip uses.
+ *
+ * The SPI version can have up to 4 (or 8) chips per chip-select, demuxed
+ * using the hardware address (selected by tying the 2 or 3 HA pins high/low
+ * as desired).
+ */
+#define	OP_READ(ha)	(0x41 | ((ha) << 1))
+#define	OP_WRITE(ha)	(0x40 | ((ha) << 1))
+
+#define	MCPGPIO_TO_SPI(sc)					\
+	container_of((sc), struct mcpgpio_spi_softc, sc_mcpgpio)
+
+#if 0
+static const struct mcpgpio_variant mcp23s08 = {
+	.name = "MCP23S08",
+	.type = MCPGPIO_TYPE_23x08,
+};
+#endif
+
+static const struct mcpgpio_variant mcp23s17 = {
+	.name = "MCP23S17",
+	.type = MCPGPIO_TYPE_23x17,
+};
+
+#if 0
+static const struct mcpgpio_variant mcp23s18 = {
+	.name = "MCP23S18",
+	.type = MCPGPIO_TYPE_23x18,
+};
+#endif
+
+#if 0
+static const struct device_compatible_entry compat_data[] = {
+	{ .compat = "microchip,mcp23s08",	.data = &mcp23s08 },
+	{ .compat = "microchip,mcp23s17",	.data = &mcp23s17 },
+	{ .compat = "microchip,mcp23s18",	.data = &mcp23s18 },
+	DEVICE_COMPAT_EOL
+};
+#endif
+
+static int
+mcpgpio_spi_lock(struct mcpgpio_softc *sc)
+{
+	struct mcpgpio_spi_softc *ssc = MCPGPIO_TO_SPI(sc);
+
+	mutex_enter(&ssc->sc_mutex);
+	return 0;
+}
+
+static void
+mcpgpio_spi_unlock(struct mcpgpio_softc *sc)
+{
+	struct mcpgpio_spi_softc *ssc = MCPGPIO_TO_SPI(sc);
+
+	mutex_exit(&ssc->sc_mutex);
+}
+
+static int
+mcpgpio_spi_read(struct mcpgpio_softc *sc, unsigned int bank,
+    uint8_t reg, uint8_t *valp)
+{
+	struct mcpgpio_spi_softc *ssc = MCPGPIO_TO_SPI(sc);
+	uint8_t buf[2];
+
+	KASSERT(bank < (sc->sc_npins >> 3));
+
+	buf[0] = OP_READ(ssc->sc_ha[bank]);
+	buf[1] = reg;
+
+	return spi_send_recv(ssc->sc_sh, 2, buf, 1, valp);
+}
+
+static int
+mcpgpio_spi_write(struct mcpgpio_softc *sc, unsigned int bank,
+    uint8_t reg, uint8_t val)
+{
+	struct mcpgpio_spi_softc *ssc = MCPGPIO_TO_SPI(sc);
+	uint8_t buf[3];
+
+	KASSERT(bank < (sc->sc_npins >> 3));
+
+	buf[0] = OP_WRITE(ssc->sc_ha[bank]);
+	buf[1] = reg;
+	buf[2] = val;
+
+	return spi_send(ssc->sc_sh, 3, buf);
+}
+
+static const struct mcpgpio_accessops mcpgpio_spi_accessops = {
+	.lock	=	mcpgpio_spi_lock,
+	.unlock	=	mcpgpio_spi_unlock,
+	.read	=	mcpgpio_spi_read,
+	.write	=	mcpgpio_spi_write,
+};
+
+static int
+mcpgpio_spi_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct spi_attach_args *sa = aux;
+
+	/* MCP23S17 has no way to detect it! */
+
+	/* run at 10MHz */
+	if (spi_configure(sa->sa_handle, SPI_MODE_0, 10000000))
+		return 0;
+
+	return 1;
+}
+
+static void
+mcpgpio_spi_attach(device_t parent, device_t self, void *aux)
+{
+	struct mcpgpio_spi_softc *ssc = device_private(self);
+	struct mcpgpio_softc *sc = &ssc->sc_mcpgpio;
+	struct spi_attach_args *sa = aux;
+	uint32_t spi_present_mask;
+	int bank, nchips, error, ha;
+
+	mutex_init(&ssc->sc_mutex, MUTEX_DEFAULT, IPL_NONE);
+	ssc->sc_sh = sa->sa_handle;
+
+	sc->sc_dev = self;
+	sc->sc_variant = &mcp23s17;		/* XXX */
+	sc->sc_iocon = IOCON_HAEN | IOCON_SEQOP;
+	sc->sc_npins = MCP23x17_GPIO_NPINS;
+	sc->sc_phandle = -1;
+	sc->sc_accessops = &mcpgpio_spi_accessops;
+
+	aprint_naive("\n");	
+	aprint_normal(": %s I/O Expander\n", sc->sc_variant->name);
+
+	/*
+	 * Before we decode the topology information, ensure each
+	 * chip has IOCON.HAEN set so that it will actually decode
+	 * the address bits.
+	 *
+	 * XXX Going on blind faith that IOCON.BANK is already 0.
+	 */
+	if (sc->sc_variant->type == MCPGPIO_TYPE_23x08) {
+		error = mcpgpio_spi_write(sc, 0, REG_IOCON, sc->sc_iocon);
+	} else {
+		error = mcpgpio_spi_write(sc, 0, REGADDR_BANK0(0, REG_IOCON),
+		    sc->sc_iocon);
+		if (error == 0) {
+			error = mcpgpio_spi_write(sc, 1,
+			    REGADDR_BANK0(1, REG_IOCON), sc->sc_iocon);
+		}
+	}
+	if (error) {
+		aprint_error_dev(self,
+		    "unable to initialize IOCON, error=%d\n", error);
+		return;
+	}
+
+#if 0
+	/*
+	 * The number of devices sharing this chip select, along
+	 * with their assigned addresses, is encoded in the
+	 * "microchip,spi-present-mask" property.  Note that this
+	 * device tree binding means that we will just have a
+	 * single driver instance for however many chips are on
+	 * this chip select.  We treat them logically as banks.
+	 */
+	if (of_getprop_uint32(phandle, "microchip,spi-present-mask",
+			      &spi_present_mask) != 0 ||
+	    of_getprop_uint32(phandle, "mcp,spi-present-mask",
+			      &spi_present_mask) != 0) {
+		aprint_error_dev(self,
+		    "missing \"microchip,spi-present-mask\" property\n");
+		return false;
+	}
+#else
+	/*
+	 * XXX Until we support decoding the DT properties that
+	 * XXX give us the topology information.
+	 */
+	spi_present_mask = __BIT(device_cfdata(self)->cf_flags & 0x7);
+#endif
+
+	/*
+	 * The 23S08 has 2 address pins (4 devices per chip select),
+	 * and the others have 3 (8 devices per chip select).
+	 */
+	if (spi_present_mask == 0 ||
+	    (sc->sc_variant->type == MCPGPIO_TYPE_23x08 &&
+	     spi_present_mask >= __BIT(4)) ||
+	    (sc->sc_variant->type != MCPGPIO_TYPE_23x08 &&
+	     spi_present_mask >= __BIT(8))) {
+		aprint_error_dev(self,
+		    "invalid \"microchip,spi-present-mask\" value: 0x%08x\n",
+		    spi_present_mask);
+		return;
+	}
+	nchips = popcount32(spi_present_mask);
+	sc->sc_npins = nchips *
+	    (sc->sc_variant->type == MCPGPIO_TYPE_23x08 ? MCP23x08_GPIO_NPINS
+							: MCP23x17_GPIO_NPINS);
+
+	/* Record the hardware addresses for each logical bank of 8 pins. */
+	for (bank = 0; spi_present_mask != 0; spi_present_mask &= ~__BIT(ha)) {
+		int ha_first, ha_last;
+
+		ha = ffs32(spi_present_mask) - 1;
+		ha_first = bank * MCPGPIO_PINS_PER_BANK;
+		ssc->sc_ha[bank++] = ha;
+		if (sc->sc_variant->type != MCPGPIO_TYPE_23x08) {
+			ssc->sc_ha[bank++] = ha;
+		}
+		ha_last = (bank * MCPGPIO_PINS_PER_BANK) - 1;
+		aprint_verbose_dev(self, "pins %d..%d at HA %d\n",
+		    ha_first, ha_last, ha);
+	}
+	KASSERT((bank * MCPGPIO_PINS_PER_BANK) == sc->sc_npins);
+
+	mcpgpio_attach(sc);
+}
+
+CFATTACH_DECL_NEW(mcpgpio_spi, sizeof(struct mcpgpio_spi_softc),
+    mcpgpio_spi_match, mcpgpio_spi_attach, NULL, NULL);

Reply via email to