Page MenuHomeFreeBSD

D52246.id161296.diff
No OneTemporary

D52246.id161296.diff

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] = {0};
+
+ struct iic_msg msgs[2] = {
+ { 0, IIC_M_WR, 1, &reg },
+ { 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,187 @@
+/*-
+ * 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

Mime Type
text/plain
Expires
Wed, Nov 26, 6:01 PM (15 h, 8 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
26221441
Default Alt Text
D52246.id161296.diff (24 KB)

Event Timeline