1*6690c9f2Sthorpej /* $NetBSD: mcp23xxxgpio.c,v 1.2 2022/01/17 19:38:14 thorpej Exp $ */
25eb438adSthorpej
35eb438adSthorpej /*-
45eb438adSthorpej * Copyright (c) 2014, 2022 The NetBSD Foundation, Inc.
55eb438adSthorpej * All rights reserved.
65eb438adSthorpej *
75eb438adSthorpej * This code is derived from software contributed to The NetBSD Foundation
85eb438adSthorpej * by Frank Kardel, and by Jason R. Thorpe.
95eb438adSthorpej *
105eb438adSthorpej * Redistribution and use in source and binary forms, with or without
115eb438adSthorpej * modification, are permitted provided that the following conditions
125eb438adSthorpej * are met:
135eb438adSthorpej * 1. Redistributions of source code must retain the above copyright
145eb438adSthorpej * notice, this list of conditions and the following disclaimer.
155eb438adSthorpej * 2. Redistributions in binary form must reproduce the above copyright
165eb438adSthorpej * notice, this list of conditions and the following disclaimer in the
175eb438adSthorpej * documentation and/or other materials provided with the distribution.
185eb438adSthorpej *
195eb438adSthorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
205eb438adSthorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
215eb438adSthorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
225eb438adSthorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
235eb438adSthorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
245eb438adSthorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
255eb438adSthorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
265eb438adSthorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
275eb438adSthorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
285eb438adSthorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
295eb438adSthorpej * POSSIBILITY OF SUCH DAMAGE.
305eb438adSthorpej */
315eb438adSthorpej
325eb438adSthorpej #include <sys/cdefs.h>
33*6690c9f2Sthorpej __KERNEL_RCSID(0, "$NetBSD: mcp23xxxgpio.c,v 1.2 2022/01/17 19:38:14 thorpej Exp $");
345eb438adSthorpej
355eb438adSthorpej /*
365eb438adSthorpej * Driver for Microchip serial I/O expansers:
375eb438adSthorpej *
385eb438adSthorpej * MCP23008 8-bit, I2C interface
395eb438adSthorpej * MCP23S08 8-bit, SPI interface
405eb438adSthorpej * MCP23017 16-bit, I2C interface
415eb438adSthorpej * MCP23S17 16-bit, SPI interface
425eb438adSthorpej * MCP23018 16-bit (open-drain outputs), I2C interface
435eb438adSthorpej * MCP23S18 16-bit (open-drain outputs), SPI interface
445eb438adSthorpej *
455eb438adSthorpej * Data sheet:
465eb438adSthorpej *
475eb438adSthorpej * https://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf
485eb438adSthorpej */
495eb438adSthorpej
505eb438adSthorpej #include "gpio.h"
515eb438adSthorpej
525eb438adSthorpej #include <sys/types.h>
535eb438adSthorpej #include <sys/systm.h>
545eb438adSthorpej #include <sys/device.h>
555eb438adSthorpej #include <sys/kernel.h>
565eb438adSthorpej #include <sys/kmem.h>
575eb438adSthorpej
585eb438adSthorpej #include <dev/ic/mcp23xxxgpioreg.h>
595eb438adSthorpej #include <dev/ic/mcp23xxxgpiovar.h>
605eb438adSthorpej
615eb438adSthorpej #define PIN_BANK(p) ((p) / MCPGPIO_PINS_PER_BANK)
625eb438adSthorpej #define PIN_EPIN(p) ((p) % MCPGPIO_PINS_PER_BANK)
635eb438adSthorpej
645eb438adSthorpej static uint8_t
mcpgpio_regaddr(struct mcpgpio_softc * sc,uint8_t bank,uint8_t reg)655eb438adSthorpej mcpgpio_regaddr(struct mcpgpio_softc *sc, uint8_t bank, uint8_t reg)
665eb438adSthorpej {
675eb438adSthorpej
685eb438adSthorpej if (sc->sc_variant->type == MCPGPIO_TYPE_23x08) {
695eb438adSthorpej return reg;
705eb438adSthorpej }
715eb438adSthorpej if (sc->sc_iocon & IOCON_BANK) {
725eb438adSthorpej return REGADDR_BANK1(bank & 1, reg);
735eb438adSthorpej }
745eb438adSthorpej return REGADDR_BANK0(bank & 1, reg);
755eb438adSthorpej }
765eb438adSthorpej
775eb438adSthorpej static const char *
mcpgpio_regname(uint8_t reg)785eb438adSthorpej mcpgpio_regname(uint8_t reg)
795eb438adSthorpej {
805eb438adSthorpej static const char * const regnames[] = {
815eb438adSthorpej [REG_IODIR] = "IODIR",
825eb438adSthorpej [REG_IPOL] = "IPOL",
835eb438adSthorpej [REG_GPINTEN] = "GPINTEN",
845eb438adSthorpej [REG_DEFVAL] = "DEFVAL",
855eb438adSthorpej [REG_INTCON] = "INTCON",
865eb438adSthorpej [REG_IOCON] = "IOCON",
875eb438adSthorpej [REG_GPPU] = "GPPU",
885eb438adSthorpej [REG_INTF] = "INTF",
895eb438adSthorpej [REG_INTCAP] = "INTCAP",
905eb438adSthorpej [REG_GPIO] = "GPIO",
915eb438adSthorpej [REG_OLAT] = "OLAT",
925eb438adSthorpej };
935eb438adSthorpej KASSERT(reg <= REG_OLAT);
945eb438adSthorpej return regnames[reg];
955eb438adSthorpej }
965eb438adSthorpej
975eb438adSthorpej static const char *
mcpgpio_bankname(struct mcpgpio_softc * sc,uint8_t bank)985eb438adSthorpej mcpgpio_bankname(struct mcpgpio_softc *sc, uint8_t bank)
995eb438adSthorpej {
1005eb438adSthorpej static const char * const banknames[] = { "A", "B" };
1015eb438adSthorpej
1025eb438adSthorpej if (sc->sc_variant->type == MCPGPIO_TYPE_23x08) {
1035eb438adSthorpej return "";
1045eb438adSthorpej }
1055eb438adSthorpej return banknames[bank & 1];
1065eb438adSthorpej }
1075eb438adSthorpej
1085eb438adSthorpej static int
mcpgpio__lock(struct mcpgpio_softc * sc,const char * fn)1095eb438adSthorpej mcpgpio__lock(struct mcpgpio_softc *sc, const char *fn)
1105eb438adSthorpej {
1115eb438adSthorpej int error;
1125eb438adSthorpej
1135eb438adSthorpej error = sc->sc_accessops->lock(sc);
1145eb438adSthorpej if (__predict_false(error != 0)) {
1155eb438adSthorpej aprint_error_dev(sc->sc_dev,
1165eb438adSthorpej "%s: unable to lock device, error=%d\n", fn, error);
1175eb438adSthorpej }
1185eb438adSthorpej return error;
1195eb438adSthorpej }
1205eb438adSthorpej
1215eb438adSthorpej #define mcpgpio_lock(sc) \
1225eb438adSthorpej mcpgpio__lock((sc), __func__)
1235eb438adSthorpej
1245eb438adSthorpej static void
mcpgpio_unlock(struct mcpgpio_softc * sc)1255eb438adSthorpej mcpgpio_unlock(struct mcpgpio_softc *sc)
1265eb438adSthorpej {
1275eb438adSthorpej sc->sc_accessops->unlock(sc);
1285eb438adSthorpej }
1295eb438adSthorpej
1305eb438adSthorpej static int
mcpgpio__read(struct mcpgpio_softc * sc,const char * fn,uint8_t bank,uint8_t reg,uint8_t * valp)1315eb438adSthorpej mcpgpio__read(struct mcpgpio_softc *sc, const char *fn,
1325eb438adSthorpej uint8_t bank, uint8_t reg, uint8_t *valp)
1335eb438adSthorpej {
1345eb438adSthorpej int error;
1355eb438adSthorpej uint8_t regaddr = mcpgpio_regaddr(sc, bank, reg);
1365eb438adSthorpej
1375eb438adSthorpej error = sc->sc_accessops->read(sc, bank, regaddr, valp);
1385eb438adSthorpej if (__predict_false(error != 0)) {
1395eb438adSthorpej aprint_error_dev(sc->sc_dev,
1405eb438adSthorpej "%s: unable to read %s%s[0x%02x], error=%d\n", fn,
1415eb438adSthorpej mcpgpio_regname(reg), mcpgpio_bankname(sc, bank),
1425eb438adSthorpej regaddr, error);
1435eb438adSthorpej }
1445eb438adSthorpej return error;
1455eb438adSthorpej }
1465eb438adSthorpej
1475eb438adSthorpej #define mcpgpio_read(sc, b, r, v) \
1485eb438adSthorpej mcpgpio__read((sc), __func__, (b), (r), (v))
1495eb438adSthorpej
1505eb438adSthorpej static int
mcpgpio__write(struct mcpgpio_softc * sc,const char * fn,uint8_t bank,uint8_t reg,uint8_t val)1515eb438adSthorpej mcpgpio__write(struct mcpgpio_softc *sc, const char *fn,
1525eb438adSthorpej uint8_t bank, uint8_t reg, uint8_t val)
1535eb438adSthorpej {
1545eb438adSthorpej int error;
1555eb438adSthorpej uint8_t regaddr = mcpgpio_regaddr(sc, bank, reg);
1565eb438adSthorpej
1575eb438adSthorpej error = sc->sc_accessops->write(sc, bank, regaddr, val);
1585eb438adSthorpej if (__predict_false(error != 0)) {
1595eb438adSthorpej aprint_error_dev(sc->sc_dev,
1605eb438adSthorpej "%s: unable to write %s%s[0x%02x], error=%d\n", fn,
1615eb438adSthorpej mcpgpio_regname(reg), mcpgpio_bankname(sc, bank),
1625eb438adSthorpej regaddr, error);
1635eb438adSthorpej }
1645eb438adSthorpej return error;
1655eb438adSthorpej }
1665eb438adSthorpej
1675eb438adSthorpej #define mcpgpio_write(sc, b, r, v) \
1685eb438adSthorpej mcpgpio__write((sc), __func__, (b), (r), (v))
1695eb438adSthorpej
1705eb438adSthorpej #if NGPIO > 0
1715eb438adSthorpej /* GPIO support functions */
1725eb438adSthorpej static int
mcpgpio_gpio_pin_read(void * arg,int pin)1735eb438adSthorpej mcpgpio_gpio_pin_read(void *arg, int pin)
1745eb438adSthorpej {
1755eb438adSthorpej struct mcpgpio_softc *sc = arg;
1765eb438adSthorpej uint8_t data;
1775eb438adSthorpej int val;
1785eb438adSthorpej int error;
1795eb438adSthorpej
1805eb438adSthorpej KASSERT(pin >= 0 && pin < sc->sc_npins);
1815eb438adSthorpej
1825eb438adSthorpej const uint8_t bank = PIN_BANK(pin);
1835eb438adSthorpej const uint8_t epin = PIN_EPIN(pin);
1845eb438adSthorpej
1855eb438adSthorpej error = mcpgpio_lock(sc);
1865eb438adSthorpej if (__predict_false(error != 0)) {
1875eb438adSthorpej return GPIO_PIN_LOW;
1885eb438adSthorpej }
1895eb438adSthorpej error = mcpgpio_read(sc, bank, REG_GPIO, &data);
1905eb438adSthorpej if (error) {
1915eb438adSthorpej data = 0;
1925eb438adSthorpej }
1935eb438adSthorpej mcpgpio_unlock(sc);
1945eb438adSthorpej
1955eb438adSthorpej val = data & __BIT(epin) ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
1965eb438adSthorpej
1975eb438adSthorpej return val;
1985eb438adSthorpej }
1995eb438adSthorpej
2005eb438adSthorpej static void
mcpgpio_gpio_pin_write(void * arg,int pin,int value)2015eb438adSthorpej mcpgpio_gpio_pin_write(void *arg, int pin, int value)
2025eb438adSthorpej {
2035eb438adSthorpej struct mcpgpio_softc *sc = arg;
2045eb438adSthorpej uint8_t data;
2055eb438adSthorpej int error;
2065eb438adSthorpej
2075eb438adSthorpej KASSERT(pin >= 0 && pin < sc->sc_npins);
2085eb438adSthorpej
2095eb438adSthorpej const uint8_t bank = PIN_BANK(pin);
2105eb438adSthorpej const uint8_t epin = PIN_EPIN(pin);
2115eb438adSthorpej
2125eb438adSthorpej error = mcpgpio_lock(sc);
2135eb438adSthorpej if (__predict_false(error != 0)) {
2145eb438adSthorpej return;
2155eb438adSthorpej }
2165eb438adSthorpej
2175eb438adSthorpej error = mcpgpio_read(sc, bank, REG_OLAT, &data);
2185eb438adSthorpej if (__predict_true(error == 0)) {
2195eb438adSthorpej if (value == GPIO_PIN_HIGH) {
2205eb438adSthorpej data |= __BIT(epin);
2215eb438adSthorpej } else {
2225eb438adSthorpej data &= ~__BIT(epin);
2235eb438adSthorpej }
2245eb438adSthorpej (void) mcpgpio_write(sc, bank, REG_OLAT, data);
2255eb438adSthorpej }
2265eb438adSthorpej
2275eb438adSthorpej mcpgpio_unlock(sc);
2285eb438adSthorpej }
2295eb438adSthorpej
2305eb438adSthorpej static void
mcpgpio_gpio_pin_ctl(void * arg,int pin,int flags)2315eb438adSthorpej mcpgpio_gpio_pin_ctl(void *arg, int pin, int flags)
2325eb438adSthorpej {
2335eb438adSthorpej struct mcpgpio_softc *sc = arg;
2345eb438adSthorpej uint8_t iodir, ipol, gppu;
2355eb438adSthorpej int error;
2365eb438adSthorpej
2375eb438adSthorpej KASSERT(pin >= 0 && pin < sc->sc_npins);
2385eb438adSthorpej
2395eb438adSthorpej const uint8_t bank = PIN_BANK(pin);
2405eb438adSthorpej const uint8_t epin = PIN_EPIN(pin);
2415eb438adSthorpej const uint8_t bit = __BIT(epin);
2425eb438adSthorpej
2435eb438adSthorpej error = mcpgpio_lock(sc);
2445eb438adSthorpej if (__predict_false(error != 0)) {
2455eb438adSthorpej return;
2465eb438adSthorpej }
2475eb438adSthorpej
2485eb438adSthorpej if ((error = mcpgpio_read(sc, bank, REG_IODIR, &iodir)) != 0 ||
2495eb438adSthorpej (error = mcpgpio_read(sc, bank, REG_IPOL, &ipol)) != 0 ||
2505eb438adSthorpej (error = mcpgpio_read(sc, bank, REG_GPPU, &gppu)) != 0) {
2515eb438adSthorpej return;
2525eb438adSthorpej }
2535eb438adSthorpej
2545eb438adSthorpej if (flags & (GPIO_PIN_OUTPUT|GPIO_PIN_INPUT)) {
2555eb438adSthorpej if ((flags & GPIO_PIN_INPUT) || !(flags & GPIO_PIN_OUTPUT)) {
2565eb438adSthorpej /* for safety INPUT will override output */
2575eb438adSthorpej iodir |= bit;
2585eb438adSthorpej } else {
2595eb438adSthorpej iodir &= ~bit;
2605eb438adSthorpej }
2615eb438adSthorpej }
2625eb438adSthorpej
2635eb438adSthorpej if (flags & GPIO_PIN_INVIN) {
2645eb438adSthorpej ipol |= bit;
2655eb438adSthorpej } else {
2665eb438adSthorpej ipol &= ~bit;
2675eb438adSthorpej }
2685eb438adSthorpej
2695eb438adSthorpej if (flags & GPIO_PIN_PULLUP) {
2705eb438adSthorpej gppu |= bit;
2715eb438adSthorpej } else {
2725eb438adSthorpej gppu &= ~bit;
2735eb438adSthorpej }
2745eb438adSthorpej
2755eb438adSthorpej (void) mcpgpio_write(sc, bank, REG_IODIR, iodir);
2765eb438adSthorpej (void) mcpgpio_write(sc, bank, REG_IPOL, ipol);
2775eb438adSthorpej (void) mcpgpio_write(sc, bank, REG_GPPU, gppu);
2785eb438adSthorpej
2795eb438adSthorpej mcpgpio_unlock(sc);
2805eb438adSthorpej }
2815eb438adSthorpej #endif /* NGPIO > 0 */
2825eb438adSthorpej
2835eb438adSthorpej void
mcpgpio_attach(struct mcpgpio_softc * sc)2845eb438adSthorpej mcpgpio_attach(struct mcpgpio_softc *sc)
2855eb438adSthorpej {
2865eb438adSthorpej int error;
2875eb438adSthorpej
2885eb438adSthorpej KASSERT(sc->sc_variant != NULL);
2895eb438adSthorpej
2905eb438adSthorpej /*
2915eb438adSthorpej * The SPI front-end provides the logical pin count to
2925eb438adSthorpej * deal with muliple chips on one chip select.
2935eb438adSthorpej */
2945eb438adSthorpej if (sc->sc_npins == 0) {
2955eb438adSthorpej sc->sc_npins = sc->sc_variant->type == MCPGPIO_TYPE_23x08
2965eb438adSthorpej ? MCP23x08_GPIO_NPINS : MCP23x17_GPIO_NPINS;
2975eb438adSthorpej }
2985eb438adSthorpej sc->sc_gpio_pins =
2995eb438adSthorpej kmem_zalloc(sc->sc_npins * sizeof(*sc->sc_gpio_pins), KM_SLEEP);
3005eb438adSthorpej
3015eb438adSthorpej /*
3025eb438adSthorpej * Perform the basic setup of the device. We program the IOCON
3035eb438adSthorpej * register once for each bank, even though the data sheet is
3045eb438adSthorpej * not clear that this is strictly necessary.
3055eb438adSthorpej */
3065eb438adSthorpej if (mcpgpio_lock(sc) != 0) {
3075eb438adSthorpej return;
3085eb438adSthorpej }
3095eb438adSthorpej error = mcpgpio_write(sc, 0, REG_IOCON, sc->sc_iocon);
3105eb438adSthorpej if (error == 0 && sc->sc_variant->type != MCPGPIO_TYPE_23x08) {
3115eb438adSthorpej error = mcpgpio_write(sc, 1, REG_IOCON, sc->sc_iocon);
3125eb438adSthorpej }
3135eb438adSthorpej mcpgpio_unlock(sc);
3145eb438adSthorpej if (error) {
3155eb438adSthorpej return;
3165eb438adSthorpej }
3175eb438adSthorpej
3185eb438adSthorpej /* XXX FDT glue. */
3195eb438adSthorpej
3205eb438adSthorpej #if NGPIO > 0
3215eb438adSthorpej struct gpiobus_attach_args gba;
3225eb438adSthorpej int pin_output_caps;
3235eb438adSthorpej int i;
3245eb438adSthorpej
3255eb438adSthorpej pin_output_caps = sc->sc_variant->type == MCPGPIO_TYPE_23x18
3265eb438adSthorpej ? GPIO_PIN_OPENDRAIN : GPIO_PIN_PUSHPULL;
3275eb438adSthorpej
3285eb438adSthorpej for (i = 0; i < sc->sc_npins; i++) {
3295eb438adSthorpej sc->sc_gpio_pins[i].pin_num = i;
3305eb438adSthorpej sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INPUT |
3315eb438adSthorpej GPIO_PIN_OUTPUT |
3325eb438adSthorpej pin_output_caps |
3335eb438adSthorpej GPIO_PIN_PULLUP |
3345eb438adSthorpej GPIO_PIN_INVIN;
3355eb438adSthorpej
3365eb438adSthorpej /* read initial state */
3375eb438adSthorpej sc->sc_gpio_pins[i].pin_state =
3385eb438adSthorpej mcpgpio_gpio_pin_read(sc, i);
3395eb438adSthorpej }
3405eb438adSthorpej
3415eb438adSthorpej /* create controller tag */
3425eb438adSthorpej sc->sc_gpio_gc.gp_cookie = sc;
3435eb438adSthorpej sc->sc_gpio_gc.gp_pin_read = mcpgpio_gpio_pin_read;
3445eb438adSthorpej sc->sc_gpio_gc.gp_pin_write = mcpgpio_gpio_pin_write;
3455eb438adSthorpej sc->sc_gpio_gc.gp_pin_ctl = mcpgpio_gpio_pin_ctl;
3465eb438adSthorpej
3475eb438adSthorpej gba.gba_gc = &sc->sc_gpio_gc;
3485eb438adSthorpej gba.gba_pins = sc->sc_gpio_pins;
3495eb438adSthorpej gba.gba_npins = sc->sc_npins;
3505eb438adSthorpej
351*6690c9f2Sthorpej config_found(sc->sc_dev, &gba, gpiobus_print,
352*6690c9f2Sthorpej CFARGS(.devhandle = device_handle(sc->sc_dev)));
3535eb438adSthorpej #else
3545eb438adSthorpej aprint_normal_dev(sc->sc_dev, "no GPIO configured in kernel");
3555eb438adSthorpej #endif
3565eb438adSthorpej }
357