11.1Sjmcneill/* $NetBSD: gecko.c,v 1.1 2025/11/15 17:59:24 jmcneill Exp $ */ 21.1Sjmcneill 31.1Sjmcneill/*- 41.1Sjmcneill * Copyright (c) 2025 Jared McNeill <jmcneill@invisible.ca> 51.1Sjmcneill * All rights reserved. 61.1Sjmcneill * 71.1Sjmcneill * Redistribution and use in source and binary forms, with or without 81.1Sjmcneill * modification, are permitted provided that the following conditions 91.1Sjmcneill * are met: 101.1Sjmcneill * 1. Redistributions of source code must retain the above copyright 111.1Sjmcneill * notice, this list of conditions and the following disclaimer. 121.1Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright 131.1Sjmcneill * notice, this list of conditions and the following disclaimer in the 141.1Sjmcneill * documentation and/or other materials provided with the distribution. 151.1Sjmcneill * 161.1Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 171.1Sjmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 181.1Sjmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 191.1Sjmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 201.1Sjmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 211.1Sjmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 221.1Sjmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 231.1Sjmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 241.1Sjmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 251.1Sjmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 261.1Sjmcneill * POSSIBILITY OF SUCH DAMAGE. 271.1Sjmcneill */ 281.1Sjmcneill 291.1Sjmcneill/* 301.1Sjmcneill * Serial console support over USB Gecko. 311.1Sjmcneill */ 321.1Sjmcneill 331.1Sjmcneill#include <sys/cdefs.h> 341.1Sjmcneill 351.1Sjmcneill__KERNEL_RCSID(0, "$NetBSD: gecko.c,v 1.1 2025/11/15 17:59:24 jmcneill Exp $"); 361.1Sjmcneill 371.1Sjmcneill#include <sys/param.h> 381.1Sjmcneill#include <sys/systm.h> 391.1Sjmcneill#include <sys/bus.h> 401.1Sjmcneill#include <sys/bitops.h> 411.1Sjmcneill#include <sys/conf.h> 421.1Sjmcneill#include <sys/device.h> 431.1Sjmcneill#include <machine/pio.h> 441.1Sjmcneill#include <machine/wii.h> 451.1Sjmcneill 461.1Sjmcneill#include <dev/cons.h> 471.1Sjmcneill 481.1Sjmcneill#include "exi.h" 491.1Sjmcneill#include "exireg.h" 501.1Sjmcneill#include "gecko.h" 511.1Sjmcneill 521.1Sjmcneill#define USBGECKO_CSR \ 531.1Sjmcneill (__SHIFTIN(1, EXI_CSR_CS) | \ 541.1Sjmcneill __SHIFTIN(EXI_FREQ_32MHZ, EXI_CSR_CLK)) 551.1Sjmcneill#define USBGECKO_CR(len, rdwr) \ 561.1Sjmcneill (__SHIFTIN((len) - 1, EXI_CR_TLEN) | \ 571.1Sjmcneill (rdwr) | EXI_CR_TSTART) 581.1Sjmcneill 591.1Sjmcneill#define USBGECKO_CMD_LED_OFF 0x7000 601.1Sjmcneill#define USBGECKO_CMD_LED_ON 0x8000 611.1Sjmcneill#define USBGECKO_CMD_ID 0x9000 621.1Sjmcneill#define USBGECKO_CMD_RECV_BYTE 0xa000 631.1Sjmcneill#define USBGECKO_CMD_SEND_BYTE 0xb000 641.1Sjmcneill 651.1Sjmcneill 661.1Sjmcneill#define RD4(reg) in32(EXI_BASE + (reg)) 671.1Sjmcneill#define WR4(reg, val) out32(EXI_BASE + (reg), val) 681.1Sjmcneill 691.1Sjmcneillstatic int usbgecko_cngetc(dev_t); 701.1Sjmcneillstatic void usbgecko_cnputc(dev_t, int); 711.1Sjmcneillstatic void usbgecko_cnpollc(dev_t, int); 721.1Sjmcneill 731.1Sjmcneillstatic struct cnm_state usbgecko_cnmstate; 741.1Sjmcneillstatic struct consdev usbgecko_consdev = { 751.1Sjmcneill .cn_getc = usbgecko_cngetc, 761.1Sjmcneill .cn_putc = usbgecko_cnputc, 771.1Sjmcneill .cn_pollc = usbgecko_cnpollc, 781.1Sjmcneill .cn_dev = NODEV, 791.1Sjmcneill .cn_pri = CN_NORMAL, 801.1Sjmcneill}; 811.1Sjmcneillstatic uint32_t usbgecko_chan; 821.1Sjmcneillstatic bool usbgecko_found; 831.1Sjmcneill 841.1Sjmcneillstruct usbgecko_softc { 851.1Sjmcneill device_t sc_dev; 861.1Sjmcneill uint8_t sc_chan; 871.1Sjmcneill uint8_t sc_device; 881.1Sjmcneill}; 891.1Sjmcneill 901.1Sjmcneillstatic struct usbgecko_softc *usbgecko_sc; 911.1Sjmcneill 921.1Sjmcneillstatic int usbgecko_match(device_t, cfdata_t, void *); 931.1Sjmcneillstatic void usbgecko_attach(device_t, device_t, void *); 941.1Sjmcneill 951.1SjmcneillCFATTACH_DECL_NEW(gecko, sizeof(struct usbgecko_softc), 961.1Sjmcneill usbgecko_match, usbgecko_attach, NULL, NULL); 971.1Sjmcneill 981.1Sjmcneillstatic void 991.1Sjmcneillusbgecko_wait(void) 1001.1Sjmcneill{ 1011.1Sjmcneill int retry; 1021.1Sjmcneill 1031.1Sjmcneill for (retry = 0; retry < 1000; retry++) { 1041.1Sjmcneill if ((RD4(EXI_CR(usbgecko_chan)) & EXI_CR_TSTART) == 0) { 1051.1Sjmcneill return; 1061.1Sjmcneill } 1071.1Sjmcneill } 1081.1Sjmcneill} 1091.1Sjmcneill 1101.1Sjmcneillstatic uint16_t 1111.1Sjmcneillusbgecko_command(uint16_t command) 1121.1Sjmcneill{ 1131.1Sjmcneill struct usbgecko_softc *sc = usbgecko_sc; 1141.1Sjmcneill uint32_t command32 = (uint32_t)command << 16; 1151.1Sjmcneill uint16_t value; 1161.1Sjmcneill 1171.1Sjmcneill if (sc != NULL) { 1181.1Sjmcneill exi_select(sc->sc_chan, sc->sc_device, EXI_FREQ_32MHZ); 1191.1Sjmcneill exi_sendrecv_imm(sc->sc_chan, sc->sc_device, &command32, 1201.1Sjmcneill &value, sizeof(value)); 1211.1Sjmcneill exi_unselect(sc->sc_chan); 1221.1Sjmcneill } else { 1231.1Sjmcneill WR4(EXI_CSR(usbgecko_chan), USBGECKO_CSR); 1241.1Sjmcneill WR4(EXI_DATA(usbgecko_chan), command32); 1251.1Sjmcneill WR4(EXI_CR(usbgecko_chan), USBGECKO_CR(2, EXI_CR_RW_READWRITE)); 1261.1Sjmcneill usbgecko_wait(); 1271.1Sjmcneill value = RD4(EXI_DATA(usbgecko_chan)) >> 16; 1281.1Sjmcneill WR4(EXI_CSR(usbgecko_chan), 0); 1291.1Sjmcneill } 1301.1Sjmcneill 1311.1Sjmcneill return value; 1321.1Sjmcneill} 1331.1Sjmcneill 1341.1Sjmcneillstatic uint32_t 1351.1Sjmcneillusbgecko_id(void) 1361.1Sjmcneill{ 1371.1Sjmcneill uint32_t value; 1381.1Sjmcneill 1391.1Sjmcneill KASSERT(usbgecko_sc == NULL); 1401.1Sjmcneill 1411.1Sjmcneill WR4(EXI_CSR(usbgecko_chan), USBGECKO_CSR); 1421.1Sjmcneill WR4(EXI_DATA(usbgecko_chan), 0); 1431.1Sjmcneill WR4(EXI_CR(usbgecko_chan), USBGECKO_CR(2, EXI_CR_RW_READWRITE)); 1441.1Sjmcneill usbgecko_wait(); 1451.1Sjmcneill WR4(EXI_CR(usbgecko_chan), USBGECKO_CR(sizeof(value), 1461.1Sjmcneill EXI_CR_RW_READWRITE)); 1471.1Sjmcneill usbgecko_wait(); 1481.1Sjmcneill value = RD4(EXI_DATA(usbgecko_chan)); 1491.1Sjmcneill WR4(EXI_CSR(usbgecko_chan), 0); 1501.1Sjmcneill 1511.1Sjmcneill return value; 1521.1Sjmcneill} 1531.1Sjmcneill 1541.1Sjmcneillstatic int 1551.1Sjmcneillusbgecko_cngetc(dev_t dev) 1561.1Sjmcneill{ 1571.1Sjmcneill uint16_t value; 1581.1Sjmcneill 1591.1Sjmcneill value = usbgecko_command(USBGECKO_CMD_RECV_BYTE); 1601.1Sjmcneill return (value & 0x0800) == 0 ? -1 : (value & 0xff); 1611.1Sjmcneill} 1621.1Sjmcneill 1631.1Sjmcneillstatic void 1641.1Sjmcneillusbgecko_cnputc(dev_t dev, int c) 1651.1Sjmcneill{ 1661.1Sjmcneill c &= 0xff; 1671.1Sjmcneill usbgecko_command(USBGECKO_CMD_SEND_BYTE | (c << 4)); 1681.1Sjmcneill} 1691.1Sjmcneill 1701.1Sjmcneillstatic void 1711.1Sjmcneillusbgecko_cnpollc(dev_t dev, int on) 1721.1Sjmcneill{ 1731.1Sjmcneill} 1741.1Sjmcneill 1751.1Sjmcneillstatic bool 1761.1Sjmcneillusbgecko_detect(void) 1771.1Sjmcneill{ 1781.1Sjmcneill return usbgecko_id() == 0 && 1791.1Sjmcneill usbgecko_command(USBGECKO_CMD_ID) == 0x0470; 1801.1Sjmcneill} 1811.1Sjmcneill 1821.1Sjmcneillvoid 1831.1Sjmcneillusbgecko_consinit(void) 1841.1Sjmcneill{ 1851.1Sjmcneill WR4(EXI_CSR(0), 0); 1861.1Sjmcneill WR4(EXI_CSR(1), 0); 1871.1Sjmcneill WR4(EXI_CSR(2), 0); 1881.1Sjmcneill 1891.1Sjmcneill WR4(EXI_CSR(0), EXI_CSR_EXTINT | EXI_CSR_EXTINTMASK); 1901.1Sjmcneill WR4(EXI_CSR(1), EXI_CSR_EXTINT | EXI_CSR_EXTINTMASK); 1911.1Sjmcneill 1921.1Sjmcneill usbgecko_chan = 0; 1931.1Sjmcneill if (!usbgecko_detect()) { 1941.1Sjmcneill usbgecko_chan = 1; 1951.1Sjmcneill if (!usbgecko_detect()) { 1961.1Sjmcneill return; 1971.1Sjmcneill } 1981.1Sjmcneill } 1991.1Sjmcneill 2001.1Sjmcneill cn_tab = &usbgecko_consdev; 2011.1Sjmcneill cn_init_magic(&usbgecko_cnmstate); 2021.1Sjmcneill cn_set_magic("\047\001"); 2031.1Sjmcneill 2041.1Sjmcneill usbgecko_command(USBGECKO_CMD_LED_ON); 2051.1Sjmcneill usbgecko_found = true; 2061.1Sjmcneill} 2071.1Sjmcneill 2081.1Sjmcneillstatic int 2091.1Sjmcneillusbgecko_match(device_t parent, cfdata_t cf, void *aux) 2101.1Sjmcneill{ 2111.1Sjmcneill struct exi_attach_args * const eaa = aux; 2121.1Sjmcneill 2131.1Sjmcneill return usbgecko_found && 2141.1Sjmcneill eaa->eaa_id == 0 && 2151.1Sjmcneill eaa->eaa_chan == usbgecko_chan && 2161.1Sjmcneill eaa->eaa_device == 0; 2171.1Sjmcneill} 2181.1Sjmcneill 2191.1Sjmcneillstatic void 2201.1Sjmcneillusbgecko_attach(device_t parent, device_t self, void *aux) 2211.1Sjmcneill{ 2221.1Sjmcneill struct usbgecko_softc *sc = device_private(self); 2231.1Sjmcneill struct exi_attach_args * const eaa = aux; 2241.1Sjmcneill 2251.1Sjmcneill aprint_naive("\n"); 2261.1Sjmcneill aprint_normal(": USB Gecko\n"); 2271.1Sjmcneill 2281.1Sjmcneill sc->sc_dev = self; 2291.1Sjmcneill sc->sc_chan = eaa->eaa_chan; 2301.1Sjmcneill sc->sc_device = eaa->eaa_device; 2311.1Sjmcneill 2321.1Sjmcneill usbgecko_sc = sc; 2331.1Sjmcneill} 234