Home | History | Annotate | Line # | Download | only in dev
      1 /*	$NetBSD: octeon_gpio.c,v 1.1 2026/07/07 08:14:15 kbowling Exp $	*/
      2 /*	$OpenBSD: octgpio.c,v 1.2 2019/09/29 04:28:52 visa Exp $	*/
      3 
      4 /*
      5  * Copyright (c) 2019 Visa Hankala
      6  * Copyright (c) 2026 Kevin Bowling <kevin.bowling (at) kev009.com>
      7  *
      8  * Permission to use, copy, modify, and/or distribute this software for any
      9  * purpose with or without fee is hereby granted, provided that the above
     10  * copyright notice and this permission notice appear in all copies.
     11  *
     12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     19  */
     20 
     21 /* Driver for OCTEON GPIO controller. */
     22 
     23 #include <sys/cdefs.h>
     24 __KERNEL_RCSID(0, "$NetBSD: octeon_gpio.c,v 1.1 2026/07/07 08:14:15 kbowling Exp $");
     25 
     26 #include <sys/param.h>
     27 #include <sys/systm.h>
     28 #include <sys/bus.h>
     29 #include <sys/device.h>
     30 #include <sys/endian.h>
     31 #include <sys/gpio.h>
     32 #include <sys/kmem.h>
     33 #include <sys/mutex.h>
     34 
     35 #include <dev/fdt/fdtvar.h>
     36 
     37 #include <mips/cpuregs.h>
     38 #include <mips/locore.h>
     39 
     40 #include <mips/cavium/dev/octeon_gpioreg.h>
     41 
     42 struct octgpio_softc {
     43 	device_t		 sc_dev;
     44 	bus_space_tag_t		 sc_iot;
     45 	bus_space_handle_t	 sc_ioh;
     46 	kmutex_t		 sc_lock;
     47 	uint32_t		 sc_npins;
     48 	uint32_t		 sc_xbit;
     49 	bool			 sc_out_sel;	/* BIT_CFG has OUTPUT_SEL */
     50 };
     51 
     52 struct octgpio_pin {
     53 	struct octgpio_softc	*pin_sc;
     54 	uint32_t		 pin_no;
     55 	bool			 pin_actlo;
     56 };
     57 
     58 #define GPIO_RD_8(sc, reg) \
     59 	bus_space_read_8((sc)->sc_iot, (sc)->sc_ioh, (reg))
     60 #define GPIO_WR_8(sc, reg, val) \
     61 	bus_space_write_8((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
     62 
     63 static int	octgpio_match(device_t, cfdata_t, void *);
     64 static void	octgpio_attach(device_t, device_t, void *);
     65 
     66 static void	*octgpio_acquire(device_t, const void *, size_t, int);
     67 static void	 octgpio_release(device_t, void *);
     68 static int	 octgpio_read(device_t, void *, bool);
     69 static void	 octgpio_write(device_t, void *, int, bool);
     70 
     71 static void	 octgpio_pin_ctl(struct octgpio_softc *, uint32_t, int);
     72 
     73 CFATTACH_DECL_NEW(octgpio, sizeof(struct octgpio_softc),
     74     octgpio_match, octgpio_attach, NULL, NULL);
     75 
     76 static const struct device_compatible_entry compat_data[] = {
     77 	{ .compat = "cavium,octeon-3860-gpio" },
     78 	{ .compat = "cavium,octeon-7890-gpio" },
     79 	DEVICE_COMPAT_EOL
     80 };
     81 
     82 static const struct fdtbus_gpio_controller_func octgpio_funcs = {
     83 	.acquire = octgpio_acquire,
     84 	.release = octgpio_release,
     85 	.read = octgpio_read,
     86 	.write = octgpio_write,
     87 };
     88 
     89 static int
     90 octgpio_match(device_t parent, cfdata_t cf, void *aux)
     91 {
     92 	struct fdt_attach_args * const faa = aux;
     93 
     94 	return of_compatible_match(faa->faa_phandle, compat_data);
     95 }
     96 
     97 static void
     98 octgpio_attach(device_t parent, device_t self, void *aux)
     99 {
    100 	struct fdt_attach_args * const faa = aux;
    101 	struct octgpio_softc *sc = device_private(self);
    102 	const int phandle = faa->faa_phandle;
    103 	bus_addr_t addr;
    104 	bus_size_t size;
    105 
    106 	sc->sc_dev = self;
    107 	sc->sc_iot = faa->faa_bst;
    108 
    109 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
    110 		aprint_error(": could not get registers\n");
    111 		return;
    112 	}
    113 	/*
    114 	 * Device trees give a 0x100 window but the extended pin
    115 	 * configuration registers (GPIO_XBIT_CFG) live at +0x100.
    116 	 */
    117 	if (size < GPIO_SIZE)
    118 		size = GPIO_SIZE;
    119 	if (bus_space_map(sc->sc_iot, addr, size, 0, &sc->sc_ioh) != 0) {
    120 		aprint_error(": could not map registers\n");
    121 		return;
    122 	}
    123 
    124 	/*
    125 	 * Set the pin count and the register layout per the SDK CSR
    126 	 * definitions (cvmx-gpio-defs.h).  Pins below sc_xbit are
    127 	 * configured through GPIO_BIT_CFG, the rest through
    128 	 * GPIO_XBIT_CFG.  On CN73XX and CN78XX all pins use the
    129 	 * GPIO_XBIT_CFG range.  The BIT_CFG OUTPUT_SEL output mux
    130 	 * exists on CN70XX and newer only; on CN61XX-class chips the
    131 	 * same bits belong to SYNCE_SEL and must not be touched.
    132 	 */
    133 	switch (MIPS_PRID_IMPL(mips_options.mips_cpu_id)) {
    134 	case MIPS_CN30XX:
    135 	case MIPS_CN31XX:
    136 	case MIPS_CN50XX:
    137 		sc->sc_npins = 24;
    138 		sc->sc_xbit = 16;
    139 		break;
    140 	case MIPS_CN38XX:
    141 	case MIPS_CN52XX:
    142 	case MIPS_CN56XX:
    143 	case MIPS_CN58XX:
    144 	case MIPS_CN63XX:
    145 	case MIPS_CN68XX:
    146 		sc->sc_npins = 16;
    147 		sc->sc_xbit = 16;
    148 		break;
    149 	case MIPS_CN61XX:
    150 	case MIPS_CN66XX:
    151 	case MIPS_CNF71XX:
    152 		sc->sc_npins = 20;
    153 		sc->sc_xbit = 16;
    154 		break;
    155 	case MIPS_CN70XX:
    156 		sc->sc_npins = 20;
    157 		sc->sc_xbit = 16;
    158 		sc->sc_out_sel = true;
    159 		break;
    160 	case MIPS_CN73XX:
    161 	case MIPS_CNF75XX:
    162 		sc->sc_npins = 32;
    163 		sc->sc_xbit = 0;
    164 		sc->sc_out_sel = true;
    165 		break;
    166 	case MIPS_CN78XX:
    167 		sc->sc_npins = 20;
    168 		sc->sc_xbit = 0;
    169 		sc->sc_out_sel = true;
    170 		break;
    171 	default:
    172 		/* Be conservative about unknown models. */
    173 		sc->sc_npins = 16;
    174 		sc->sc_xbit = 16;
    175 		break;
    176 	}
    177 
    178 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
    179 
    180 	aprint_naive("\n");
    181 	aprint_normal(": GPIO controller, %u pins\n", sc->sc_npins);
    182 
    183 	fdtbus_register_gpio_controller(self, phandle, &octgpio_funcs);
    184 }
    185 
    186 static bus_size_t
    187 octgpio_cfg_reg(struct octgpio_softc *sc, uint32_t pin)
    188 {
    189 
    190 	if (pin >= sc->sc_xbit)
    191 		return GPIO_XBIT_CFG16_OFFSET + (pin - sc->sc_xbit) * 8;
    192 	return GPIO_BIT_CFG0_OFFSET + pin * 8;
    193 }
    194 
    195 static void
    196 octgpio_pin_ctl(struct octgpio_softc *sc, uint32_t pin, int flags)
    197 {
    198 	uint64_t value;
    199 	bus_size_t reg;
    200 
    201 	KASSERT(pin < sc->sc_npins);
    202 	reg = octgpio_cfg_reg(sc, pin);
    203 
    204 	mutex_enter(&sc->sc_lock);
    205 	value = GPIO_RD_8(sc, reg);
    206 	if (ISSET(flags, GPIO_PIN_OUTPUT)) {
    207 		value |= GPIO_BIT_CFG_TX_OE;
    208 		/* Select normal GPIO output where the mux exists. */
    209 		if (sc->sc_out_sel)
    210 			value &= ~GPIO_BIT_CFG_OUT_SEL;
    211 	} else {
    212 		value &= ~(GPIO_BIT_CFG_TX_OE | GPIO_BIT_CFG_RX_XOR);
    213 	}
    214 	/*
    215 	 * Interrupts are not supported; keep INT_EN clear (on models
    216 	 * whose XBIT_CFG lacks INT_EN the bit reads as zero anyway).
    217 	 */
    218 	value &= ~GPIO_BIT_CFG_INT_EN;
    219 	GPIO_WR_8(sc, reg, value);
    220 	mutex_exit(&sc->sc_lock);
    221 }
    222 
    223 static void *
    224 octgpio_acquire(device_t dev, const void *data, size_t len, int flags)
    225 {
    226 	struct octgpio_softc *sc = device_private(dev);
    227 	const u_int *gpio = data;
    228 	struct octgpio_pin *pin;
    229 	u_int pinno;
    230 	bool actlo;
    231 
    232 	/* #gpio-cells = <2>: controller phandle, pin, flags. */
    233 	if (len != 12)
    234 		return NULL;
    235 
    236 	pinno = be32toh(gpio[1]);
    237 	actlo = (be32toh(gpio[2]) & 1) != 0;
    238 	if (pinno >= sc->sc_npins)
    239 		return NULL;
    240 
    241 	octgpio_pin_ctl(sc, pinno, flags);
    242 
    243 	pin = kmem_zalloc(sizeof(*pin), KM_SLEEP);
    244 	pin->pin_sc = sc;
    245 	pin->pin_no = pinno;
    246 	pin->pin_actlo = actlo;
    247 
    248 	return pin;
    249 }
    250 
    251 static void
    252 octgpio_release(device_t dev, void *priv)
    253 {
    254 	struct octgpio_softc *sc = device_private(dev);
    255 	struct octgpio_pin *pin = priv;
    256 
    257 	octgpio_pin_ctl(sc, pin->pin_no, GPIO_PIN_INPUT);
    258 	kmem_free(pin, sizeof(*pin));
    259 }
    260 
    261 static int
    262 octgpio_read(device_t dev, void *priv, bool raw)
    263 {
    264 	struct octgpio_pin *pin = priv;
    265 	struct octgpio_softc *sc = pin->pin_sc;
    266 	int val;
    267 
    268 	val = (GPIO_RD_8(sc, GPIO_RX_DAT_OFFSET) >> pin->pin_no) & 1;
    269 	if (!raw && pin->pin_actlo)
    270 		val = !val;
    271 
    272 	return val;
    273 }
    274 
    275 static void
    276 octgpio_write(device_t dev, void *priv, int val, bool raw)
    277 {
    278 	struct octgpio_pin *pin = priv;
    279 	struct octgpio_softc *sc = pin->pin_sc;
    280 
    281 	if (!raw && pin->pin_actlo)
    282 		val = !val;
    283 
    284 	if (val)
    285 		GPIO_WR_8(sc, GPIO_TX_SET_OFFSET, 1ull << pin->pin_no);
    286 	else
    287 		GPIO_WR_8(sc, GPIO_TX_CLR_OFFSET, 1ull << pin->pin_no);
    288 }
    289