1/* $NetBSD: gecko.c,v 1.1 2025/11/15 17:59:24 jmcneill Exp $ */ 2 3/*- 4 * Copyright (c) 2025 Jared McNeill <jmcneill@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 2025/11/15 17:59:24 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 69static int usbgecko_cngetc(dev_t); 70static void usbgecko_cnputc(dev_t, int); 71static void usbgecko_cnpollc(dev_t, int); 72 73static struct cnm_state usbgecko_cnmstate; 74static 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}; 81static uint32_t usbgecko_chan; 82static bool usbgecko_found; 83 84struct usbgecko_softc { 85 device_t sc_dev; 86 uint8_t sc_chan; 87 uint8_t sc_device; 88}; 89 90static struct usbgecko_softc *usbgecko_sc; 91 92static int usbgecko_match(device_t, cfdata_t, void *); 93static void usbgecko_attach(device_t, device_t, void *); 94 95CFATTACH_DECL_NEW(gecko, sizeof(struct usbgecko_softc), 96 usbgecko_match, usbgecko_attach, NULL, NULL); 97 98static void 99usbgecko_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 110static uint16_t 111usbgecko_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 134static uint32_t 135usbgecko_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 154static int 155usbgecko_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 163static void 164usbgecko_cnputc(dev_t dev, int c) 165{ 166 c &= 0xff; 167 usbgecko_command(USBGECKO_CMD_SEND_BYTE | (c << 4)); 168} 169 170static void 171usbgecko_cnpollc(dev_t dev, int on) 172{ 173} 174 175static bool 176usbgecko_detect(void) 177{ 178 return usbgecko_id() == 0 && 179 usbgecko_command(USBGECKO_CMD_ID) == 0x0470; 180} 181 182void 183usbgecko_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 208static int 209usbgecko_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 219static void 220usbgecko_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