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 [email protected]
+and
+.An Jason R. Thorpe Aq Mt [email protected] .
+.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,
+ ®, 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,
+ ®, 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);