Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F137780939
D52246.id161274.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
24 KB
Referenced Files
None
Subscribers
None
D52246.id161274.diff
View Options
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -321,6 +321,7 @@
mac_test.4 \
malo.4 \
max44009.4 \
+ mcp23xxx.4 \
md.4 \
mdio.4 \
me.4 \
diff --git a/share/man/man4/mcp23xxx.4 b/share/man/man4/mcp23xxx.4
new file mode 100644
--- /dev/null
+++ b/share/man/man4/mcp23xxx.4
@@ -0,0 +1,89 @@
+.Dd August 25, 2025
+.Dt MCP23XXX 4
+.Sh NAME
+.Nm mcp23xxx
+.Nd driver for mcp23008/mcp23017/mcp23s08/mcp23s17
+IO expanders
+.Sh SYNOPSIS
+.Cd "device mcp23017"
+.Cd "device mcp23s17"
+.Cd "device gpio"
+.Cd "device spibus"
+.Cd "device iicbus"
+.Pp
+In
+.Xr rc.conf 5 :
+.Cd mcp23017_load="YES"
+.Cd mcp23s17_load="YES"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides
+.Xr gpiobus 4
+control over the expander.
+Two variants of the driver exist, mcp23s17 for SPI, and mcp23017 for I2C.
+The driver supports the following GPIO flags:
+.Pp
+.Bl -tag -width "GPIO_PIN_PULLDOWN" -compact
+.It Dv GPIO_PIN_OUTPUT
+.It Dv GPIO_PIN_INPUT
+.It Dv GPIO_PIN_INVIN
+.It Dv GPIO_PIN_PULLUP
+.El
+.Pp
+Interrupts are not supported.
+.Sh HARDWARE
+The following properties are required for nodes that describe
+.Nm
+devices:
+.Bl -tag -width "compatible" -compact
+.It Va compatible
+Must be one of:
+.Bl -tag -compact -width "microchip,mcp23008"
+.It microchip,mcp23008
+.It microchip,mcp23017
+.It microchip,mcp23s08
+.It microchip,mcp23s17
+.El
+.It Va reg
+For I2C devices, this specifies the I2C address.
+For SPI devices, this specifies the chip select number.
+.El
+.Pp
+The following properties are optional:
+.Bl -tag -width "compatible" -compact
+.It Va spi-addr
+Used to set the address bits according to
+the state of the hardware address pins (A0–A2) (SPI variants only).
+For example, if A0 is tied high, this value should include
+0x1.
+.El
+.Sh EXAMPLES
+The following example shows an I2C variant (MCP23017)
+attached to an I2C bus:
+.Bd -literal
+mcp23017@20 {
+ compatible = "microchip,mcp23017";
+ reg = <0x20>;
+};
+.Ed
+.Pp
+The following example shows an SPI variant (MCP23S17)
+attached to an SPI bus, with address pin A0 pulled high:
+.Bd -literal
+mcp23s17@0 {
+ compatible = "microchip,mcp23s17";
+ reg = <0>;
+ spi-addr = <0x1>;
+};
+.Ed
+.Sh HISTORY
+The
+.Nm
+device driver first appeared in
+.Fx 15.0 .
+.Sh AUTHORS
+The
+.Nm
+device driver and manpage was written by
+.An Evgenii Ivanov Aq Mt devivanov@proton.me .
diff --git a/sys/conf/files b/sys/conf/files
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1733,6 +1733,9 @@
dev/gpio/gpiobus_if.m optional gpio
dev/gpio/gpiopps.c optional gpiopps fdt
dev/gpio/ofw_gpiobus.c optional fdt gpio
+dev/gpio/mcp23xxx.c optional mcp23017 | mcp23s17
+dev/gpio/mcp23s17.c optional mcp23s17 fdt gpio spibus
+dev/gpio/mcp23017.c optional mcp23017 fdt gpio iicbus
dev/hid/bcm5974.c optional bcm5974
dev/hid/hconf.c optional hconf
dev/hid/hcons.c optional hcons
diff --git a/sys/dev/gpio/mcp23017.c b/sys/dev/gpio/mcp23017.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/gpio/mcp23017.c
@@ -0,0 +1,174 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Evgenii Ivanov <devivanov@proton.me>
+ *
+ * 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 AUTHORS 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 AUTHORS 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/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/openfirm.h>
+
+#include <dev/iicbus/iicbus.h>
+#include <dev/iicbus/iiconf.h>
+
+#include "gpio_if.h"
+#include "mcp23xxx.h"
+
+static struct ofw_compat_data compat_data[] = {
+ { "microchip,mcp23017", MCP23017 },
+ { "microchip,mcp23008", MCP23008 },
+ { NULL, 0 }
+};
+
+static int
+mcp23017_read(struct mcp23xxx_softc *sc, uint8_t reg, uint16_t *val)
+{
+ int error;
+ uint8_t buf[2];
+
+ struct iic_msg msgs[2] = {
+ { 0, IIC_M_WR, 1, ® },
+ { 0, IIC_M_RD, 1, buf },
+ };
+
+ msgs[0].slave = sc->addr;
+ msgs[1].slave = sc->addr;
+
+ if (sc->npin == 16)
+ msgs[1].len = 2;
+
+ error = iicbus_transfer_excl(sc->dev, msgs, 2, IIC_WAIT);
+ *val = (uint16_t)buf[1] << 8 | buf[0];
+ return (iic2errno(error));
+}
+
+static int
+mcp23017_write(struct mcp23xxx_softc *sc, uint8_t reg, uint16_t val)
+{
+ int error;
+ struct iic_msg msg;
+ uint8_t buf[] = { reg, val, val >> 8 };
+
+ msg.slave = sc->addr;
+ msg.flags = IIC_M_WR;
+ msg.buf = buf;
+ msg.len = sc->npin == 16 ? 3 : 2;
+
+ error = iicbus_transfer_excl(sc->dev, &msg, 1, IIC_WAIT);
+ return (iic2errno(error));
+}
+
+static int
+mcp23017_probe(device_t dev)
+{
+ int chip;
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ chip = (int)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ if (chip == 0)
+ return (ENXIO);
+
+ if (chip == MCP23017)
+ device_set_desc(dev, "MCP23017 GPIO controller");
+ else
+ device_set_desc(dev, "MCP23008 GPIO controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mcp23017_attach(device_t dev)
+{
+ int chip;
+ struct mcp23xxx_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->addr = iicbus_get_addr(dev);
+
+ chip = (int)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ sc->dev = dev;
+ sc->write = mcp23017_write;
+ sc->read = mcp23017_read;
+ sc->npin = chip == MCP23017 ? 16 : 8;
+
+ return (mcp23xxx_attach(sc));
+}
+
+static int
+mcp23017_detach(device_t dev)
+{
+ struct mcp23xxx_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (mcp23xxx_detach(sc));
+}
+
+static phandle_t
+mcp23017_gpio_get_node(device_t bus, device_t dev)
+{
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t mcp23017_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mcp23017_probe),
+ DEVMETHOD(device_attach, mcp23017_attach),
+ DEVMETHOD(device_detach, mcp23017_detach),
+
+ /* GPIO protocol */
+ DEVMETHOD(gpio_get_bus, mcp23xxx_get_bus),
+ DEVMETHOD(gpio_pin_max, mcp23xxx_pin_max),
+ DEVMETHOD(gpio_pin_getcaps, mcp23xxx_pin_getcaps),
+ DEVMETHOD(gpio_pin_getflags, mcp23xxx_pin_getflags),
+ DEVMETHOD(gpio_pin_setflags, mcp23xxx_pin_setflags),
+ DEVMETHOD(gpio_pin_getname, mcp23xxx_pin_getname),
+ DEVMETHOD(gpio_pin_get, mcp23xxx_pin_get),
+ DEVMETHOD(gpio_pin_set, mcp23xxx_pin_set),
+ DEVMETHOD(gpio_pin_toggle, mcp23xxx_pin_toggle),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, mcp23017_gpio_get_node),
+
+ DEVMETHOD_END
+};
+
+static driver_t mcp23017_driver = {
+ "gpio",
+ mcp23017_methods,
+ sizeof(struct mcp23xxx_softc)
+};
+
+DRIVER_MODULE(mcp23017, iicbus, mcp23017_driver, 0, 0);
+MODULE_DEPEND(mcp23017, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
+MODULE_DEPEND(mcp23017, gpiobus, 1, 1, 1);
+MODULE_VERSION(mcp23017, 1);
+IICBUS_FDT_PNP_INFO(compat_data);
diff --git a/sys/dev/gpio/mcp23s17.c b/sys/dev/gpio/mcp23s17.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/gpio/mcp23s17.c
@@ -0,0 +1,188 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Evgenii Ivanov <devivanov@proton.me>
+ *
+ * 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 AUTHORS 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 AUTHORS 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/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+
+#include "gpio_if.h"
+#include "mcp23xxx.h"
+#include "spibus_if.h"
+
+#define MCP_WRITE 0
+#define MCP_READ 1
+
+static struct ofw_compat_data compat_data[] = {
+ { "microchip,mcp23s17", MCP23S17 },
+ { "microchip,mcp23s08", MCP23S08 },
+ { NULL, 0 }
+};
+
+static int
+mcp23s17_read(struct mcp23xxx_softc *sc, uint8_t reg, uint16_t *val)
+{
+ int error;
+ struct spi_command spi_command = SPI_COMMAND_INITIALIZER;
+ uint8_t buf[] = {
+ sc->addr | MCP_READ, /* address, r/w */
+ reg
+ };
+
+ spi_command.tx_cmd = spi_command.rx_cmd = buf;
+ spi_command.tx_cmd_sz = spi_command.rx_cmd_sz = sizeof(buf);
+ spi_command.tx_data = spi_command.rx_data = buf;
+ if (sc->npin == 16)
+ spi_command.tx_data_sz = spi_command.rx_data_sz = 2;
+ else
+ spi_command.tx_data_sz = spi_command.rx_data_sz = 1;
+
+ error = SPIBUS_TRANSFER(sc->parent, sc->dev, &spi_command);
+ *val = (uint16_t)buf[1] << 8 | buf[0];
+ return (error);
+}
+
+static int
+mcp23s17_write(struct mcp23xxx_softc *sc, uint8_t reg, uint16_t val)
+{
+ struct spi_command spi_command = SPI_COMMAND_INITIALIZER;
+ uint8_t buf[] = {
+ sc->addr | MCP_WRITE, /* address, r/w */
+ reg,
+ val,
+ val >> 8,
+ };
+
+ spi_command.tx_cmd = spi_command.rx_cmd = buf;
+ if (sc->npin == 16)
+ spi_command.tx_cmd_sz = spi_command.rx_cmd_sz = 4;
+ else
+ spi_command.tx_cmd_sz = spi_command.rx_cmd_sz = 3;
+
+ return (SPIBUS_TRANSFER(sc->parent, sc->dev, &spi_command));
+}
+
+static int
+mcp23s17_probe(device_t dev)
+{
+ int chip;
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ chip = (int)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ if (chip == 0)
+ return (ENXIO);
+
+ if (chip == MCP23S17)
+ device_set_desc(dev, "MCP23S17 GPIO controller");
+ else
+ device_set_desc(dev, "MCP23S08 GPIO controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+mcp23s17_attach(device_t dev)
+{
+ int chip;
+ pcell_t addr;
+ struct mcp23xxx_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->parent = device_get_parent(dev);
+
+ chip = (int)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ sc->addr = 0x20;
+ sc->dev = dev;
+ sc->write = mcp23s17_write;
+ sc->read = mcp23s17_read;
+ sc->npin = chip == MCP23S17 ? 16 : 8;
+
+ if (OF_getencprop(ofw_bus_get_node(dev), "spi-addr", &addr,
+ sizeof(addr)) != 0)
+ sc->addr |= addr;
+
+ sc->addr <<= 1;
+
+ return (mcp23xxx_attach(sc));
+}
+
+static int
+mcp23s17_detach(device_t dev)
+{
+ struct mcp23xxx_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (mcp23xxx_detach(sc));
+}
+
+static phandle_t
+mcp23s17_gpio_get_node(device_t bus, device_t dev)
+{
+ return (ofw_bus_get_node(bus));
+}
+
+static device_method_t mcp23s17_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, mcp23s17_probe),
+ DEVMETHOD(device_attach, mcp23s17_attach),
+ DEVMETHOD(device_detach, mcp23s17_detach),
+
+ /* GPIO protocol */
+ DEVMETHOD(gpio_get_bus, mcp23xxx_get_bus),
+ DEVMETHOD(gpio_pin_max, mcp23xxx_pin_max),
+ DEVMETHOD(gpio_pin_getcaps, mcp23xxx_pin_getcaps),
+ DEVMETHOD(gpio_pin_getflags, mcp23xxx_pin_getflags),
+ DEVMETHOD(gpio_pin_setflags, mcp23xxx_pin_setflags),
+ DEVMETHOD(gpio_pin_getname, mcp23xxx_pin_getname),
+ DEVMETHOD(gpio_pin_get, mcp23xxx_pin_get),
+ DEVMETHOD(gpio_pin_set, mcp23xxx_pin_set),
+ DEVMETHOD(gpio_pin_toggle, mcp23xxx_pin_toggle),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, mcp23s17_gpio_get_node),
+
+ DEVMETHOD_END
+};
+
+static driver_t mcp23s17_driver = {
+ "gpio",
+ mcp23s17_methods,
+ sizeof(struct mcp23xxx_softc)
+};
+
+DRIVER_MODULE(mcp23s17, spibus, mcp23s17_driver, 0, 0);
+MODULE_DEPEND(mcp23s17, spibus, 1, 1, 1);
+MODULE_DEPEND(mcp23s17, gpiobus, 1, 1, 1);
diff --git a/sys/dev/gpio/mcp23xxx.h b/sys/dev/gpio/mcp23xxx.h
new file mode 100644
--- /dev/null
+++ b/sys/dev/gpio/mcp23xxx.h
@@ -0,0 +1,69 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Evgenii Ivanov <devivanov@proton.me>
+ *
+ * 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 AUTHORS 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 AUTHORS 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 __MCP23XXX_H__
+#define __MCP23XXX_H__
+
+enum {
+ MCP23017 = 1,
+ MCP23S17,
+ MCP23008,
+ MCP23S08,
+};
+
+struct mcp23xxx_softc {
+ device_t dev;
+ device_t busdev;
+ struct sx lock;
+ uint8_t npin;
+
+ int (*write)(struct mcp23xxx_softc *, uint8_t, uint16_t);
+ int (*read)(struct mcp23xxx_softc *, uint8_t, uint16_t *);
+
+ uint16_t gpio;
+ uint16_t dir;
+ uint16_t ipol;
+ uint16_t gppu;
+
+ uint8_t addr;
+ device_t parent; /* only used in spi */
+};
+
+int mcp23xxx_pin_get(device_t dev, uint32_t pin, unsigned int *on);
+int mcp23xxx_pin_set(device_t dev, uint32_t pin, unsigned int on);
+int mcp23xxx_pin_toggle(device_t dev, uint32_t pin);
+int mcp23xxx_pin_getflags(device_t dev, uint32_t pin, uint32_t *pflags);
+int mcp23xxx_pin_setflags(device_t dev, uint32_t pin, uint32_t flags);
+int mcp23xxx_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps);
+int mcp23xxx_pin_getname(device_t dev, uint32_t pin, char *name);
+int mcp23xxx_pin_max(device_t dev, int *maxpin);
+device_t mcp23xxx_get_bus(device_t dev);
+
+int mcp23xxx_attach(struct mcp23xxx_softc *sc);
+int mcp23xxx_detach(struct mcp23xxx_softc *sc);
+
+#endif
diff --git a/sys/dev/gpio/mcp23xxx.c b/sys/dev/gpio/mcp23xxx.c
new file mode 100644
--- /dev/null
+++ b/sys/dev/gpio/mcp23xxx.c
@@ -0,0 +1,337 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 Evgenii Ivanov <devivanov@proton.me>
+ *
+ * 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 AUTHORS 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 AUTHORS 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/param.h>
+#include <sys/bus.h>
+#include <sys/gpio.h>
+#include <sys/lock.h>
+#include <sys/sx.h>
+
+#include <dev/gpio/gpiobusvar.h>
+
+#include "mcp23xxx.h"
+
+#define PIN_CAPS \
+ (GPIO_PIN_OUTPUT | GPIO_PIN_INPUT | GPIO_PIN_INVIN | GPIO_PIN_PULLUP)
+
+#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
+#define MAXREG 0x0B
+
+#define MCP23XXX_IODIR_IN 0xFFFF
+
+struct regdefault {
+ uint32_t reg;
+ uint32_t defval;
+};
+
+static const struct regdefault mcp_regdefault[] = {
+ { REG_IODIR, 0xFFFF },
+ { REG_IPOL, 0x0000 },
+ { REG_GPINTEN, 0x0000 },
+ { REG_DEFVAL, 0x0000 },
+ { REG_INTCON, 0x0000 },
+ { REG_IOCON, 0x0000 },
+ { REG_GPPU, 0x0000 },
+ { REG_INTF, 0x0000 },
+ { REG_INTCAP, 0x0000 },
+ { REG_GPIO, 0x0000 },
+ { REG_OLAT, 0x0000 },
+};
+
+static int
+mcp_write(struct mcp23xxx_softc *sc, uint8_t reg, uint16_t pin)
+{
+ int error;
+
+ if (sc->npin == 16)
+ reg = reg << 1;
+
+ error = sc->write(sc, reg, pin);
+ if (error) {
+ device_printf(sc->dev,
+ "failed to write to register %02x. error: %d\n",
+ reg, error);
+ }
+
+ return (error);
+}
+
+static int
+mcp_read(struct mcp23xxx_softc *sc, uint8_t reg, uint16_t *pin)
+{
+ int error;
+
+ if (sc->npin == 16)
+ reg = reg << 1;
+
+ error = sc->read(sc, reg, pin);
+ if (error) {
+ device_printf(sc->dev,
+ "failed to read from register %02x. error: %d\n",
+ reg, error);
+ }
+
+ return (error);
+}
+
+int
+mcp23xxx_pin_get(device_t dev, uint32_t pin, unsigned int *on)
+{
+ int error;
+ struct mcp23xxx_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->npin)
+ return (EINVAL);
+
+ error = 0;
+
+ /* convert pin number to bitmask */
+ pin = 1 << pin;
+
+ sx_xlock(&sc->lock);
+ if ((sc->dir & pin) & MCP23XXX_IODIR_IN)
+ error = mcp_read(sc, REG_GPIO, &sc->gpio);
+
+ *on = (sc->gpio & pin) ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
+ sx_unlock(&sc->lock);
+ return (error);
+}
+
+int
+mcp23xxx_pin_set(device_t dev, uint32_t pin, unsigned int on)
+{
+ int error;
+ struct mcp23xxx_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->npin)
+ return (EINVAL);
+
+ /* convert pin number to bitmask */
+ pin = 1 << pin;
+
+ sx_xlock(&sc->lock);
+ if ((sc->dir & pin) & MCP23XXX_IODIR_IN) {
+ sx_unlock(&sc->lock);
+ return (EINVAL);
+ }
+
+ sc->gpio = (sc->gpio & ~((uint16_t)pin)) | (pin * on);
+ error = mcp_write(sc, REG_GPIO, sc->gpio);
+ sx_xunlock(&sc->lock);
+ return (error);
+}
+
+int
+mcp23xxx_pin_toggle(device_t dev, uint32_t pin)
+{
+ int error;
+ struct mcp23xxx_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->npin)
+ return (EINVAL);
+
+ /* convert pin number to bitmask */
+ pin = 1 << pin;
+
+ sx_xlock(&sc->lock);
+ if ((sc->dir & pin) & MCP23XXX_IODIR_IN) {
+ sx_unlock(&sc->lock);
+ return (EINVAL);
+ }
+
+ sc->gpio = sc->gpio ^ (pin);
+ error = mcp_write(sc, REG_GPIO, sc->gpio);
+ sx_unlock(&sc->lock);
+ return (error);
+}
+
+int
+mcp23xxx_pin_getflags(device_t dev, uint32_t pin, uint32_t *pflags)
+{
+ struct mcp23xxx_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->npin)
+ return (EINVAL);
+
+ /* convert pin number to bitmask */
+ pin = 1 << pin;
+
+ sx_xlock(&sc->lock);
+ *pflags = (sc->dir & pin) ? GPIO_PIN_INPUT : GPIO_PIN_OUTPUT;
+ *pflags |= (sc->ipol & pin) ? GPIO_PIN_INVIN : 0;
+ *pflags |= (sc->gppu & pin) ? GPIO_PIN_PULLUP : 0;
+ sx_xunlock(&sc->lock);
+
+ return (0);
+}
+
+int
+mcp23xxx_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
+{
+ int error;
+ struct mcp23xxx_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->npin)
+ return (EINVAL);
+ if ((flags & ~PIN_CAPS) != 0)
+ return (EINVAL);
+
+ error = 0;
+
+ /* convert pin number to bitmask */
+ pin = 1 << pin;
+
+ sx_xlock(&sc->lock);
+ if (flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
+ if (flags & GPIO_PIN_OUTPUT)
+ sc->dir &= ~(pin);
+ else
+ sc->dir |= pin;
+
+ error = mcp_write(sc, REG_IODIR, sc->dir);
+ if (error)
+ goto end;
+ }
+
+ if (flags & GPIO_PIN_INVIN) {
+ sc->gppu |= pin;
+ error = mcp_write(sc, REG_GPPU, sc->gppu);
+ } else if (sc->gppu & pin) {
+ sc->gppu &= ~(pin);
+ error = mcp_write(sc, REG_GPPU, sc->gppu);
+ }
+
+ if (flags & GPIO_PIN_INVIN) {
+ sc->ipol |= pin;
+ error = mcp_write(sc, REG_IPOL, sc->ipol);
+ } else if (sc->ipol & pin) {
+ sc->ipol &= ~(pin);
+ error = mcp_write(sc, REG_IPOL, sc->ipol);
+ }
+
+end:
+ sx_unlock(&sc->lock);
+ return (error);
+}
+
+int
+mcp23xxx_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+ struct mcp23xxx_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->npin)
+ return (EINVAL);
+
+ *caps = PIN_CAPS;
+ return (0);
+}
+
+int
+mcp23xxx_pin_getname(device_t dev, uint32_t pin, char *name)
+{
+ int bank;
+ struct mcp23xxx_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (pin >= sc->npin)
+ return (EINVAL);
+
+ bank = pin / 8;
+ pin -= bank * 8;
+
+ snprintf(name, GPIOMAXNAME, "P%c%d", 'A' + bank, pin);
+ return (0);
+}
+
+int
+mcp23xxx_pin_max(device_t dev, int *maxpin)
+{
+ struct mcp23xxx_softc *sc;
+
+ sc = device_get_softc(dev);
+ *maxpin = sc->npin - 1;
+ return (0);
+}
+
+device_t
+mcp23xxx_get_bus(device_t dev)
+{
+ struct mcp23xxx_softc *sc;
+
+ sc = device_get_softc(dev);
+ return (sc->busdev);
+}
+
+int
+mcp23xxx_attach(struct mcp23xxx_softc *sc)
+{
+ int i, error;
+
+ for (i = 0; i < MAXREG; i++) {
+ error = mcp_write(sc, mcp_regdefault[i].reg,
+ mcp_regdefault[i].defval);
+ if (error)
+ return (error);
+ }
+
+ sc->busdev = gpiobus_add_bus(sc->dev);
+ if (sc->busdev == NULL)
+ return (ENXIO);
+
+ sc->gpio = mcp_regdefault[REG_GPIO].defval;
+ sc->dir = mcp_regdefault[REG_IODIR].defval;
+ sc->ipol = mcp_regdefault[REG_IPOL].defval;
+ sc->gppu = mcp_regdefault[REG_GPPU].defval;
+
+ sx_init(&sc->lock, "mcp23xxx lock");
+ bus_attach_children(sc->dev);
+ return (0);
+}
+
+int
+mcp23xxx_detach(struct mcp23xxx_softc *sc)
+{
+ gpiobus_detach_bus(sc->dev);
+ sx_destroy(&sc->lock);
+ return (0);
+}
diff --git a/sys/modules/i2c/Makefile b/sys/modules/i2c/Makefile
--- a/sys/modules/i2c/Makefile
+++ b/sys/modules/i2c/Makefile
@@ -31,7 +31,8 @@
rv3032 \
rx8803 \
tca64xx \
- tmp461
+ tmp461 \
+ mcp23017
.endif
.if ${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "amd64" || \
diff --git a/sys/modules/i2c/mcp23017/Makefile b/sys/modules/i2c/mcp23017/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/i2c/mcp23017/Makefile
@@ -0,0 +1,8 @@
+.PATH: ${SRCTOP}/sys/dev/gpio/
+
+KMOD= mcp23017
+SRCS= mcp23xxx.c mcp23017.c
+SRCS+= device_if.h bus_if.h ofw_bus_if.h opt_platform.h
+SRCS+= gpio_if.h
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/spi/Makefile b/sys/modules/spi/Makefile
--- a/sys/modules/spi/Makefile
+++ b/sys/modules/spi/Makefile
@@ -3,7 +3,11 @@
at45d \
${_atopcase} \
mx25l \
- spibus \
+ spibus
+
+.if !empty(OPT_FDT)
+SUBDIR += mcp23s17
+.endif
.if ${MACHINE_CPUARCH} == "i386" || ${MACHINE_CPUARCH} == "amd64"
_atopcase=atopcase
diff --git a/sys/modules/spi/mcp23s17/Makefile b/sys/modules/spi/mcp23s17/Makefile
new file mode 100644
--- /dev/null
+++ b/sys/modules/spi/mcp23s17/Makefile
@@ -0,0 +1,8 @@
+.PATH: ${SRCTOP}/sys/dev/gpio/
+
+KMOD= mcp23s17
+SRCS= mcp23xxx.c mcp23s17.c
+SRCS+= device_if.h bus_if.h ofw_bus_if.h opt_platform.h
+SRCS+= gpio_if.h spibus_if.h
+
+.include <bsd.kmod.mk>
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Nov 26, 7:13 PM (4 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
26223202
Default Alt Text
D52246.id161274.diff (24 KB)
Attached To
Mode
D52246: gpio: Add support for mcp23008/mcp23017/mcp23s08/mcp23s17 IO expanders
Attached
Detach File
Event Timeline
Log In to Comment