Home | History | Annotate | Line # | Download | only in dev
      1 /* $NetBSD: hwgpio.c,v 1.2 2026/01/23 09:58:55 jmcneill Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2024-2026 Jared McNeill <jmcneill (at) invisible.ca>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: hwgpio.c,v 1.2 2026/01/23 09:58:55 jmcneill Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/bus.h>
     34 #include <sys/cpu.h>
     35 #include <sys/device.h>
     36 #include <sys/kmem.h>
     37 #include <sys/bitops.h>
     38 #include <sys/gpio.h>
     39 #include <sys/mutex.h>
     40 
     41 #include <dev/gpio/gpiovar.h>
     42 
     43 #include <machine/wii.h>
     44 #include <machine/wiiu.h>
     45 #include "ahb.h"
     46 
     47 #define PIN(_num, _name, _caps)		\
     48 	{ .pin_num = (_num), 		\
     49 	  .pin_caps = (_caps),		\
     50 	  .pin_defname = (_name),	\
     51 	}
     52 
     53 static gpio_pin_t hwgpio0_pins_wii[] = {
     54 	PIN( 0, "POWER",	GPIO_PIN_INPUT),
     55 	PIN( 1, "SHUTDOWN",	GPIO_PIN_OUTPUT),
     56 	PIN( 2, "FAN",		GPIO_PIN_OUTPUT),
     57 	PIN( 3, "DC_DC",	GPIO_PIN_OUTPUT),
     58 	PIN( 4, "DI_SPIN",	GPIO_PIN_OUTPUT),
     59 	PIN( 5, "SLOT_LED",	GPIO_PIN_OUTPUT),
     60 	PIN( 6, "EJECT_BTN",	GPIO_PIN_INPUT),
     61 	PIN( 7, "SLOT_IN",	GPIO_PIN_INPUT),
     62 	PIN( 8, "SENSOR_BAR",	GPIO_PIN_OUTPUT),
     63 	PIN( 9, "DO_EJECT",	GPIO_PIN_OUTPUT),
     64 	PIN(10, "EEP_CS",	GPIO_PIN_OUTPUT),
     65 	PIN(11, "EEP_CLK",	GPIO_PIN_OUTPUT),
     66 	PIN(12, "EEP_MOSI",	GPIO_PIN_OUTPUT),
     67 	PIN(13, "EEP_MISO",	GPIO_PIN_INPUT),
     68 	PIN(14, "AVE_SCL",	GPIO_PIN_OUTPUT),
     69 	PIN(15, "AVE_SDA",	GPIO_PIN_INPUT | GPIO_PIN_OUTPUT),
     70 	PIN(16, "DEBUG0",	GPIO_PIN_INPUT | GPIO_PIN_OUTPUT),
     71 	PIN(17, "DEBUG1",	GPIO_PIN_INPUT | GPIO_PIN_OUTPUT),
     72 	PIN(18, "DEBUG2",	GPIO_PIN_INPUT | GPIO_PIN_OUTPUT),
     73 	PIN(19, "DEBUG3",	GPIO_PIN_INPUT | GPIO_PIN_OUTPUT),
     74 	PIN(20, "DEBUG4",	GPIO_PIN_INPUT | GPIO_PIN_OUTPUT),
     75 	PIN(21, "DEBUG5",	GPIO_PIN_INPUT | GPIO_PIN_OUTPUT),
     76 	PIN(22, "DEBUG6",	GPIO_PIN_INPUT | GPIO_PIN_OUTPUT),
     77 	PIN(23, "DEBUG7",	GPIO_PIN_INPUT | GPIO_PIN_OUTPUT),
     78 };
     79 
     80 static gpio_pin_t hwgpio0_pins_wiiu[] = {
     81 	PIN( 0, "RtcSysInt",		GPIO_PIN_INPUT),
     82 	PIN( 1, "DwifiMode",		GPIO_PIN_OUTPUT),
     83 	PIN( 2, "FanPower",		GPIO_PIN_OUTPUT),
     84 	PIN( 3, "DcdcPowerControl",	GPIO_PIN_OUTPUT),
     85 	PIN( 5, "Esp10Workaround",	GPIO_PIN_OUTPUT),
     86 	PIN( 6, "DRCPWRREQ",		GPIO_PIN_OUTPUT),
     87 	PIN( 7, "JIG",			GPIO_PIN_INPUT),
     88 	PIN( 8, "PadPd",		GPIO_PIN_OUTPUT),
     89 	PIN(10, "EepromCs",		GPIO_PIN_OUTPUT),
     90 	PIN(11, "EepromSk",		GPIO_PIN_OUTPUT),
     91 	PIN(12, "EepromDo",		GPIO_PIN_OUTPUT),
     92 	PIN(13, "EepromDi",		GPIO_PIN_INPUT),
     93 	PIN(14, "Av0I2cClock",		GPIO_PIN_OUTPUT),
     94 	PIN(15, "Av0I2cData",		GPIO_PIN_INPUT | GPIO_PIN_OUTPUT),
     95 	PIN(24, "Av1I2cClock",		GPIO_PIN_OUTPUT),
     96 	PIN(25, "Av1I2cData",		GPIO_PIN_INPUT | GPIO_PIN_OUTPUT),
     97 	PIN(26, "MuteLamp",		GPIO_PIN_OUTPUT),
     98 	PIN(27, "BluetoothMode",	GPIO_PIN_OUTPUT),
     99 	PIN(28, "CcrhReset",		GPIO_PIN_OUTPUT),
    100 	PIN(29, "WifiMode",		GPIO_PIN_OUTPUT),
    101 	PIN(30, "Sdcc0s0Power",		GPIO_PIN_OUTPUT),
    102 };
    103 
    104 static gpio_pin_t hwgpio1_pins_wiiu[] = {
    105 	PIN( 0, "FanSpeed",		GPIO_PIN_OUTPUT),
    106 	PIN( 1, "SmcScl",		GPIO_PIN_OUTPUT),
    107 	PIN( 2, "SmcSda",		GPIO_PIN_INPUT | GPIO_PIN_OUTPUT),
    108 	PIN( 3, "DcdcPowerControl2",	GPIO_PIN_OUTPUT),
    109 	PIN( 4, "AvInt",		GPIO_PIN_INPUT),
    110 	PIN( 5, "Ccrhlo12",		GPIO_PIN_OUTPUT),
    111 	PIN( 6, "AvReset",		GPIO_PIN_OUTPUT),
    112 };
    113 
    114 #undef PIN
    115 
    116 struct hwgpio_softc {
    117 	struct gpio_chipset_tag	sc_gp;
    118 	bus_space_tag_t sc_bst;
    119 	bus_space_handle_t sc_bsh;
    120 	kmutex_t sc_lock;
    121 };
    122 
    123 #define GPIO0_ADDR		0x0d0000c0
    124 #define GPIO1_ADDR		0x0d000520
    125 #define GPIO_SIZE		0x40
    126 
    127 #define GPIOB_OUT		0x00
    128 #define GPIOB_DIR		0x04
    129 #define GPIOB_IN		0x08
    130 
    131 #define	RD4(sc, reg)		\
    132 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
    133 #define	WR4(sc, reg, val)	\
    134 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
    135 
    136 static int
    137 hwgpio_pin_read(void *priv, int pin)
    138 {
    139 	struct hwgpio_softc * const sc = priv;
    140 
    141 	return (RD4(sc, GPIOB_IN) & __BIT(pin)) != 0;
    142 }
    143 
    144 static void
    145 hwgpio_pin_write(void *priv, int pin, int value)
    146 {
    147 	struct hwgpio_softc * const sc = priv;
    148 	uint32_t out;
    149 
    150 	mutex_enter(&sc->sc_lock);
    151 	out = RD4(sc, GPIOB_OUT);
    152 	if (value) {
    153 		out |= __BIT(pin);
    154 	} else {
    155 		out &= ~__BIT(pin);
    156 	}
    157 	WR4(sc, GPIOB_OUT, out);
    158 	mutex_exit(&sc->sc_lock);
    159 }
    160 
    161 static void
    162 hwgpio_pin_ctl(void *priv, int pin, int flags)
    163 {
    164 	struct hwgpio_softc * const sc = priv;
    165 	uint32_t dir;
    166 
    167 	mutex_enter(&sc->sc_lock);
    168 	dir = RD4(sc, GPIOB_DIR);
    169 	if (flags & GPIO_PIN_OUTPUT) {
    170 		dir |= __BIT(pin);
    171 	} else {
    172 		dir &= ~__BIT(pin);
    173 	}
    174 	WR4(sc, GPIOB_DIR, dir);
    175 	mutex_exit(&sc->sc_lock);
    176 }
    177 
    178 static int
    179 hwgpio_match(device_t parent, cfdata_t cf, void *aux)
    180 {
    181 	struct ahb_attach_args * const aaa = aux;
    182 
    183 	if (wiiu_plat) {
    184 		return aaa->aaa_addr == GPIO0_ADDR ||
    185 		       aaa->aaa_addr == GPIO1_ADDR;
    186 	} else {
    187 		return aaa->aaa_addr == GPIO0_ADDR;
    188 	}
    189 }
    190 
    191 static void
    192 hwgpio_attach(device_t parent, device_t self, void *aux)
    193 {
    194 	struct hwgpio_softc * const sc = device_private(self);
    195 	struct ahb_attach_args * const aaa = aux;
    196 	struct gpio_chipset_tag *gp = &sc->sc_gp;
    197 	struct gpiobus_attach_args gba = {};
    198 	uint32_t in, out, dir;
    199 	gpio_pin_t *pins;
    200 	int error;
    201 	u_int n, npins;
    202 
    203 	sc->sc_bst = aaa->aaa_bst;
    204 	error = bus_space_map(sc->sc_bst, aaa->aaa_addr, GPIO_SIZE, 0,
    205 	    &sc->sc_bsh);
    206 	if (error != 0) {
    207 		aprint_error(": couldn't map registers (%d)\n", error);
    208 		return;
    209 	}
    210 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_HIGH);
    211 
    212 	gp->gp_cookie = sc;
    213 	gp->gp_pin_read = hwgpio_pin_read;
    214 	gp->gp_pin_write = hwgpio_pin_write;
    215 	gp->gp_pin_ctl = hwgpio_pin_ctl;
    216 
    217 	aprint_naive("\n");
    218 	aprint_normal(": GPIO\n");
    219 
    220 	if (wiiu_plat) {
    221 		if (aaa->aaa_addr == GPIO0_ADDR) {
    222 			pins = hwgpio0_pins_wiiu;
    223 			npins = __arraycount(hwgpio0_pins_wiiu);
    224 		} else {
    225 			KASSERT(aaa->aaa_addr == GPIO1_ADDR);
    226 			pins = hwgpio1_pins_wiiu;
    227 			npins = __arraycount(hwgpio1_pins_wiiu);
    228 		}
    229 	} else {
    230 		KASSERT(aaa->aaa_addr == GPIO0_ADDR);
    231 		pins = hwgpio0_pins_wii;
    232 		npins = __arraycount(hwgpio0_pins_wii);
    233 	}
    234 
    235 	in = RD4(sc, GPIOB_IN);
    236 	out = RD4(sc, GPIOB_OUT);
    237 	dir = RD4(sc, GPIOB_DIR);
    238 	for (n = 0; n < npins; n++) {
    239 		const uint32_t mask = __BIT(pins[n].pin_num);
    240 		if (dir & mask) {
    241 			pins[n].pin_state = (out & mask) != 0;
    242 		} else {
    243 			pins[n].pin_state = (in & mask) != 0;
    244 		}
    245 	}
    246 
    247 	gba.gba_gc = &sc->sc_gp;
    248 	gba.gba_pins = pins;
    249 	gba.gba_npins = npins;
    250 	config_found(self, &gba, NULL, CFARGS_NONE);
    251 }
    252 
    253 CFATTACH_DECL_NEW(hwgpio, sizeof(struct hwgpio_softc),
    254     hwgpio_match, hwgpio_attach, NULL, NULL);
    255