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, + ®, 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);