1 /* $NetBSD: gecko.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2025 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /* 30 * Serial console support over USB Gecko. 31 */ 32 33 #include <sys/cdefs.h> 34 35 __KERNEL_RCSID(0, "$NetBSD: gecko.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $"); 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/bus.h> 40 #include <sys/bitops.h> 41 #include <sys/conf.h> 42 #include <sys/device.h> 43 #include <machine/pio.h> 44 #include <machine/wii.h> 45 46 #include <dev/cons.h> 47 48 #include "exi.h" 49 #include "exireg.h" 50 #include "gecko.h" 51 52 #define USBGECKO_CSR \ 53 (__SHIFTIN(1, EXI_CSR_CS) | \ 54 __SHIFTIN(EXI_FREQ_32MHZ, EXI_CSR_CLK)) 55 #define USBGECKO_CR(len, rdwr) \ 56 (__SHIFTIN((len) - 1, EXI_CR_TLEN) | \ 57 (rdwr) | EXI_CR_TSTART) 58 59 #define USBGECKO_CMD_LED_OFF 0x7000 60 #define USBGECKO_CMD_LED_ON 0x8000 61 #define USBGECKO_CMD_ID 0x9000 62 #define USBGECKO_CMD_RECV_BYTE 0xa000 63 #define USBGECKO_CMD_SEND_BYTE 0xb000 64 65 66 #define RD4(reg) in32(EXI_BASE + (reg)) 67 #define WR4(reg, val) out32(EXI_BASE + (reg), val) 68 69 static int usbgecko_cngetc(dev_t); 70 static void usbgecko_cnputc(dev_t, int); 71 static void usbgecko_cnpollc(dev_t, int); 72 73 static struct cnm_state usbgecko_cnmstate; 74 static struct consdev usbgecko_consdev = { 75 .cn_getc = usbgecko_cngetc, 76 .cn_putc = usbgecko_cnputc, 77 .cn_pollc = usbgecko_cnpollc, 78 .cn_dev = NODEV, 79 .cn_pri = CN_NORMAL, 80 }; 81 static uint32_t usbgecko_chan; 82 static bool usbgecko_found; 83 84 struct usbgecko_softc { 85 device_t sc_dev; 86 uint8_t sc_chan; 87 uint8_t sc_device; 88 }; 89 90 static struct usbgecko_softc *usbgecko_sc; 91 92 static int usbgecko_match(device_t, cfdata_t, void *); 93 static void usbgecko_attach(device_t, device_t, void *); 94 95 CFATTACH_DECL_NEW(gecko, sizeof(struct usbgecko_softc), 96 usbgecko_match, usbgecko_attach, NULL, NULL); 97 98 static void 99 usbgecko_wait(void) 100 { 101 int retry; 102 103 for (retry = 0; retry < 1000; retry++) { 104 if ((RD4(EXI_CR(usbgecko_chan)) & EXI_CR_TSTART) == 0) { 105 return; 106 } 107 } 108 } 109 110 static uint16_t 111 usbgecko_command(uint16_t command) 112 { 113 struct usbgecko_softc *sc = usbgecko_sc; 114 uint32_t command32 = (uint32_t)command << 16; 115 uint16_t value; 116 117 if (sc != NULL) { 118 exi_select(sc->sc_chan, sc->sc_device, EXI_FREQ_32MHZ); 119 exi_sendrecv_imm(sc->sc_chan, sc->sc_device, &command32, 120 &value, sizeof(value)); 121 exi_unselect(sc->sc_chan); 122 } else { 123 WR4(EXI_CSR(usbgecko_chan), USBGECKO_CSR); 124 WR4(EXI_DATA(usbgecko_chan), command32); 125 WR4(EXI_CR(usbgecko_chan), USBGECKO_CR(2, EXI_CR_RW_READWRITE)); 126 usbgecko_wait(); 127 value = RD4(EXI_DATA(usbgecko_chan)) >> 16; 128 WR4(EXI_CSR(usbgecko_chan), 0); 129 } 130 131 return value; 132 } 133 134 static uint32_t 135 usbgecko_id(void) 136 { 137 uint32_t value; 138 139 KASSERT(usbgecko_sc == NULL); 140 141 WR4(EXI_CSR(usbgecko_chan), USBGECKO_CSR); 142 WR4(EXI_DATA(usbgecko_chan), 0); 143 WR4(EXI_CR(usbgecko_chan), USBGECKO_CR(2, EXI_CR_RW_READWRITE)); 144 usbgecko_wait(); 145 WR4(EXI_CR(usbgecko_chan), USBGECKO_CR(sizeof(value), 146 EXI_CR_RW_READWRITE)); 147 usbgecko_wait(); 148 value = RD4(EXI_DATA(usbgecko_chan)); 149 WR4(EXI_CSR(usbgecko_chan), 0); 150 151 return value; 152 } 153 154 static int 155 usbgecko_cngetc(dev_t dev) 156 { 157 uint16_t value; 158 159 value = usbgecko_command(USBGECKO_CMD_RECV_BYTE); 160 return (value & 0x0800) == 0 ? -1 : (value & 0xff); 161 } 162 163 static void 164 usbgecko_cnputc(dev_t dev, int c) 165 { 166 c &= 0xff; 167 usbgecko_command(USBGECKO_CMD_SEND_BYTE | (c << 4)); 168 } 169 170 static void 171 usbgecko_cnpollc(dev_t dev, int on) 172 { 173 } 174 175 static bool 176 usbgecko_detect(void) 177 { 178 return usbgecko_id() == 0 && 179 usbgecko_command(USBGECKO_CMD_ID) == 0x0470; 180 } 181 182 void 183 usbgecko_consinit(void) 184 { 185 WR4(EXI_CSR(0), 0); 186 WR4(EXI_CSR(1), 0); 187 WR4(EXI_CSR(2), 0); 188 189 WR4(EXI_CSR(0), EXI_CSR_EXTINT | EXI_CSR_EXTINTMASK); 190 WR4(EXI_CSR(1), EXI_CSR_EXTINT | EXI_CSR_EXTINTMASK); 191 192 usbgecko_chan = 0; 193 if (!usbgecko_detect()) { 194 usbgecko_chan = 1; 195 if (!usbgecko_detect()) { 196 return; 197 } 198 } 199 200 cn_tab = &usbgecko_consdev; 201 cn_init_magic(&usbgecko_cnmstate); 202 cn_set_magic("\047\001"); 203 204 usbgecko_command(USBGECKO_CMD_LED_ON); 205 usbgecko_found = true; 206 } 207 208 static int 209 usbgecko_match(device_t parent, cfdata_t cf, void *aux) 210 { 211 struct exi_attach_args * const eaa = aux; 212 213 return usbgecko_found && 214 eaa->eaa_id == 0 && 215 eaa->eaa_chan == usbgecko_chan && 216 eaa->eaa_device == 0; 217 } 218 219 static void 220 usbgecko_attach(device_t parent, device_t self, void *aux) 221 { 222 struct usbgecko_softc *sc = device_private(self); 223 struct exi_attach_args * const eaa = aux; 224 225 aprint_naive("\n"); 226 aprint_normal(": USB Gecko\n"); 227 228 sc->sc_dev = self; 229 sc->sc_chan = eaa->eaa_chan; 230 sc->sc_device = eaa->eaa_device; 231 232 usbgecko_sc = sc; 233 } 234