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