11.1Sjmcneill/* $NetBSD: exi.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $ */
21.1Sjmcneill
31.1Sjmcneill/*-
41.1Sjmcneill * Copyright (c) 2024 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 AUTHOR ``AS IS'' AND ANY EXPRESS OR
171.1Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
181.1Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
191.1Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
201.1Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
211.1Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
221.1Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
231.1Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
241.1Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
251.1Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
261.1Sjmcneill * SUCH DAMAGE.
271.1Sjmcneill */
281.1Sjmcneill
291.1Sjmcneill#include <sys/cdefs.h>
301.1Sjmcneill__KERNEL_RCSID(0, "$NetBSD: exi.c,v 1.1 2026/01/09 22:54:29 jmcneill Exp $");
311.1Sjmcneill
321.1Sjmcneill#include <sys/param.h>
331.1Sjmcneill#include <sys/bus.h>
341.1Sjmcneill#include <sys/device.h>
351.1Sjmcneill#include <sys/systm.h>
361.1Sjmcneill#include <sys/bitops.h>
371.1Sjmcneill#include <sys/mutex.h>
381.1Sjmcneill#include <uvm/uvm_extern.h>
391.1Sjmcneill
401.1Sjmcneill#include <machine/wii.h>
411.1Sjmcneill#include <machine/pio.h>
421.1Sjmcneill
431.1Sjmcneill#include "locators.h"
441.1Sjmcneill#include "mainbus.h"
451.1Sjmcneill#include "exi.h"
461.1Sjmcneill#include "exireg.h"
471.1Sjmcneill
481.1Sjmcneill#define	EXI_NUM_CHAN		3
491.1Sjmcneill#define	EXI_NUM_DEV		3
501.1Sjmcneill
511.1Sjmcneill/* This is an arbitrary limit. The real limit is probably much higher. */
521.1Sjmcneill#define	EXI_MAX_DMA		4096
531.1Sjmcneill
541.1Sjmcneill#define	ASSERT_CHAN_VALID(chan)	KASSERT((chan) >= 0 && (chan) < EXI_NUM_CHAN)
551.1Sjmcneill#define	ASSERT_DEV_VALID(dev)	KASSERT((dev) >= 0 && (dev) < EXI_NUM_DEV)
561.1Sjmcneill#define ASSERT_LEN_VALID(len)	KASSERT((len) == 1 || (len) == 2 || (len) == 4)
571.1Sjmcneill
581.1Sjmcneillstruct exi_channel {
591.1Sjmcneill	kmutex_t		ch_lock;
601.1Sjmcneill
611.1Sjmcneill	bus_dmamap_t		ch_dmamap;
621.1Sjmcneill
631.1Sjmcneill	device_t		ch_child[EXI_NUM_DEV];
641.1Sjmcneill};
651.1Sjmcneill
661.1Sjmcneillstruct exi_softc {
671.1Sjmcneill	device_t		sc_dev;
681.1Sjmcneill	bus_space_tag_t		sc_bst;
691.1Sjmcneill	bus_space_handle_t	sc_bsh;
701.1Sjmcneill	bus_dma_tag_t		sc_dmat;
711.1Sjmcneill
721.1Sjmcneill	struct exi_channel	sc_chan[EXI_NUM_CHAN];
731.1Sjmcneill};
741.1Sjmcneill
751.1Sjmcneillstatic struct exi_softc *exi_softc;
761.1Sjmcneill
771.1Sjmcneill#define RD4(sc, reg)							\
781.1Sjmcneill	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
791.1Sjmcneill#define WR4(sc, reg, val)						\
801.1Sjmcneill	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
811.1Sjmcneill
821.1Sjmcneillstatic int	exi_match(device_t, cfdata_t, void *);
831.1Sjmcneillstatic void	exi_attach(device_t, device_t, void *);
841.1Sjmcneill
851.1Sjmcneillstatic int	exi_rescan(device_t, const char *, const int *);
861.1Sjmcneillstatic int	exi_print(void *, const char *);
871.1Sjmcneill
881.1SjmcneillCFATTACH_DECL_NEW(exi, sizeof(struct exi_softc),
891.1Sjmcneill	exi_match, exi_attach, NULL, NULL);
901.1Sjmcneill
911.1Sjmcneillstatic int
921.1Sjmcneillexi_match(device_t parent, cfdata_t cf, void *aux)
931.1Sjmcneill{
941.1Sjmcneill	struct mainbus_attach_args *maa = aux;
951.1Sjmcneill
961.1Sjmcneill	return strcmp(maa->maa_name, "exi") == 0;
971.1Sjmcneill}
981.1Sjmcneill
991.1Sjmcneillstatic void
1001.1Sjmcneillexi_attach(device_t parent, device_t self, void *aux)
1011.1Sjmcneill{
1021.1Sjmcneill	struct mainbus_attach_args * const maa = aux;
1031.1Sjmcneill	struct exi_softc * const sc = device_private(self);
1041.1Sjmcneill	uint8_t chan;
1051.1Sjmcneill	int error;
1061.1Sjmcneill
1071.1Sjmcneill	KASSERT(device_unit(self) == 0);
1081.1Sjmcneill
1091.1Sjmcneill	aprint_naive("\n");
1101.1Sjmcneill	aprint_normal(": External Interface\n");
1111.1Sjmcneill
1121.1Sjmcneill	exi_softc = sc;
1131.1Sjmcneill	sc->sc_dev = self;
1141.1Sjmcneill	sc->sc_bst = maa->maa_bst;
1151.1Sjmcneill	if (bus_space_map(sc->sc_bst, maa->maa_addr, EXI_SIZE, 0,
1161.1Sjmcneill	    &sc->sc_bsh) != 0) {
1171.1Sjmcneill		aprint_error_dev(self, "couldn't map registers\n");
1181.1Sjmcneill		return;
1191.1Sjmcneill	}
1201.1Sjmcneill	sc->sc_dmat = maa->maa_dmat;
1211.1Sjmcneill	for (chan = 0; chan < EXI_NUM_CHAN; chan++) {
1221.1Sjmcneill		mutex_init(&sc->sc_chan[chan].ch_lock, MUTEX_DEFAULT, IPL_VM);
1231.1Sjmcneill		error = bus_dmamap_create(exi_softc->sc_dmat, EXI_MAX_DMA, 1,
1241.1Sjmcneill		    EXI_MAX_DMA, 0, BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
1251.1Sjmcneill		    &sc->sc_chan[chan].ch_dmamap);
1261.1Sjmcneill		if (error != 0) {
1271.1Sjmcneill			aprint_error_dev(self, "couldn't create dmamap: %d\n",
1281.1Sjmcneill			    error);
1291.1Sjmcneill			return;
1301.1Sjmcneill		}
1311.1Sjmcneill	}
1321.1Sjmcneill
1331.1Sjmcneill	exi_rescan(self, NULL, NULL);
1341.1Sjmcneill}
1351.1Sjmcneill
1361.1Sjmcneillstatic int
1371.1Sjmcneillexi_rescan(device_t self, const char *ifattr, const int *locs)
1381.1Sjmcneill{
1391.1Sjmcneill	struct exi_softc * const sc = device_private(self);
1401.1Sjmcneill	uint8_t chan, dev;
1411.1Sjmcneill
1421.1Sjmcneill	for (chan = 0; chan < EXI_NUM_CHAN; chan++) {
1431.1Sjmcneill		struct exi_channel *ch = &sc->sc_chan[chan];
1441.1Sjmcneill		for (dev = 0; dev < EXI_NUM_DEV; dev++) {
1451.1Sjmcneill			struct exi_attach_args eaa = {};
1461.1Sjmcneill			uint16_t command = 0x0000; /* ID command */
1471.1Sjmcneill			uint32_t id = 0;
1481.1Sjmcneill
1491.1Sjmcneill			if (ch->ch_child[dev] != NULL) {
1501.1Sjmcneill				continue;
1511.1Sjmcneill			}
1521.1Sjmcneill
1531.1Sjmcneill			exi_select(chan, dev, EXI_FREQ_8MHZ);
1541.1Sjmcneill			exi_send_imm(chan, dev, &command, sizeof(command));
1551.1Sjmcneill			exi_recv_imm(chan, dev, &id, sizeof(id));
1561.1Sjmcneill			exi_unselect(chan);
1571.1Sjmcneill
1581.1Sjmcneill			if (id == 0xffffffff) {
1591.1Sjmcneill				continue;
1601.1Sjmcneill			}
1611.1Sjmcneill
1621.1Sjmcneill			eaa.eaa_id = id;
1631.1Sjmcneill			eaa.eaa_chan = chan;
1641.1Sjmcneill			eaa.eaa_device = dev;
1651.1Sjmcneill
1661.1Sjmcneill			ch->ch_child[dev] = config_found(self, &eaa, exi_print,
1671.1Sjmcneill			    CFARGS(.submatch = config_stdsubmatch,
1681.1Sjmcneill				   .locators = locs));
1691.1Sjmcneill		}
1701.1Sjmcneill	}
1711.1Sjmcneill
1721.1Sjmcneill	return 0;
1731.1Sjmcneill}
1741.1Sjmcneill
1751.1Sjmcneillstatic int
1761.1Sjmcneillexi_print(void *aux, const char *pnp)
1771.1Sjmcneill{
1781.1Sjmcneill	struct exi_attach_args *eaa = aux;
1791.1Sjmcneill
1801.1Sjmcneill	if (pnp != NULL && eaa->eaa_id == 0) {
1811.1Sjmcneill		return QUIET;
1821.1Sjmcneill	}
1831.1Sjmcneill
1841.1Sjmcneill	if (pnp != NULL) {
1851.1Sjmcneill		aprint_normal("exi device ID 0x%08x at %s", eaa->eaa_id, pnp);
1861.1Sjmcneill	}
1871.1Sjmcneill
1881.1Sjmcneill	aprint_normal(" channel %u device %u", eaa->eaa_chan, eaa->eaa_device);
1891.1Sjmcneill
1901.1Sjmcneill	return UNCONF;
1911.1Sjmcneill}
1921.1Sjmcneill
1931.1Sjmcneillvoid
1941.1Sjmcneillexi_select(uint8_t chan, uint8_t dev, exi_freq_t freq)
1951.1Sjmcneill{
1961.1Sjmcneill	struct exi_channel *ch;
1971.1Sjmcneill	uint32_t val;
1981.1Sjmcneill
1991.1Sjmcneill	ASSERT_CHAN_VALID(chan);
2001.1Sjmcneill	ASSERT_DEV_VALID(dev);
2011.1Sjmcneill
2021.1Sjmcneill	ch = &exi_softc->sc_chan[chan];
2031.1Sjmcneill	mutex_enter(&ch->ch_lock);
2041.1Sjmcneill
2051.1Sjmcneill	val = RD4(exi_softc, EXI_CSR(chan));
2061.1Sjmcneill	val &= ~EXI_CSR_CS;
2071.1Sjmcneill	val |= __SHIFTIN(__BIT(dev), EXI_CSR_CS);
2081.1Sjmcneill	val &= ~EXI_CSR_CLK;
2091.1Sjmcneill	val |= __SHIFTIN(freq, EXI_CSR_CLK);
2101.1Sjmcneill	WR4(exi_softc, EXI_CSR(chan), val);
2111.1Sjmcneill}
2121.1Sjmcneill
2131.1Sjmcneillvoid
2141.1Sjmcneillexi_unselect(uint8_t chan)
2151.1Sjmcneill{
2161.1Sjmcneill	struct exi_channel *ch;
2171.1Sjmcneill	uint32_t val;
2181.1Sjmcneill
2191.1Sjmcneill	ASSERT_CHAN_VALID(chan);
2201.1Sjmcneill
2211.1Sjmcneill	ch = &exi_softc->sc_chan[chan];
2221.1Sjmcneill
2231.1Sjmcneill	val = RD4(exi_softc, EXI_CSR(chan));
2241.1Sjmcneill	val &= ~EXI_CSR_CS;
2251.1Sjmcneill	WR4(exi_softc, EXI_CSR(chan), val);
2261.1Sjmcneill
2271.1Sjmcneill	mutex_exit(&ch->ch_lock);
2281.1Sjmcneill}
2291.1Sjmcneill
2301.1Sjmcneillstatic void
2311.1Sjmcneillexi_wait(uint8_t chan)
2321.1Sjmcneill{
2331.1Sjmcneill	uint32_t val;
2341.1Sjmcneill
2351.1Sjmcneill	ASSERT_CHAN_VALID(chan);
2361.1Sjmcneill
2371.1Sjmcneill	do {
2381.1Sjmcneill		val = RD4(exi_softc, EXI_CR(chan));
2391.1Sjmcneill	} while ((val & EXI_CR_TSTART) != 0);
2401.1Sjmcneill}
2411.1Sjmcneill
2421.1Sjmcneillvoid
2431.1Sjmcneillexi_send_imm(uint8_t chan, uint8_t dev, const void *data, size_t datalen)
2441.1Sjmcneill{
2451.1Sjmcneill	struct exi_channel *ch;
2461.1Sjmcneill	uint32_t val = 0;
2471.1Sjmcneill
2481.1Sjmcneill	ASSERT_CHAN_VALID(chan);
2491.1Sjmcneill	ASSERT_DEV_VALID(dev);
2501.1Sjmcneill	ASSERT_LEN_VALID(datalen);
2511.1Sjmcneill
2521.1Sjmcneill	ch = &exi_softc->sc_chan[chan];
2531.1Sjmcneill	KASSERT(mutex_owned(&ch->ch_lock));
2541.1Sjmcneill
2551.1Sjmcneill	switch (datalen) {
2561.1Sjmcneill	case 1:
2571.1Sjmcneill		val = *(const uint8_t *)data << 24;
2581.1Sjmcneill		break;
2591.1Sjmcneill	case 2:
2601.1Sjmcneill		val = *(const uint16_t *)data << 16;
2611.1Sjmcneill		break;
2621.1Sjmcneill	case 4:
2631.1Sjmcneill		val = *(const uint32_t *)data;
2641.1Sjmcneill		break;
2651.1Sjmcneill	}
2661.1Sjmcneill
2671.1Sjmcneill	WR4(exi_softc, EXI_DATA(chan), val);
2681.1Sjmcneill	WR4(exi_softc, EXI_CR(chan),
2691.1Sjmcneill	    EXI_CR_TSTART | EXI_CR_RW_WRITE |
2701.1Sjmcneill	    __SHIFTIN(datalen - 1, EXI_CR_TLEN));
2711.1Sjmcneill	exi_wait(chan);
2721.1Sjmcneill}
2731.1Sjmcneill
2741.1Sjmcneillvoid
2751.1Sjmcneillexi_recv_imm(uint8_t chan, uint8_t dev, void *data, size_t datalen)
2761.1Sjmcneill{
2771.1Sjmcneill	struct exi_channel *ch;
2781.1Sjmcneill	uint32_t val;
2791.1Sjmcneill
2801.1Sjmcneill	ASSERT_CHAN_VALID(chan);
2811.1Sjmcneill	ASSERT_DEV_VALID(dev);
2821.1Sjmcneill	ASSERT_LEN_VALID(datalen);
2831.1Sjmcneill
2841.1Sjmcneill	ch = &exi_softc->sc_chan[chan];
2851.1Sjmcneill	KASSERT(mutex_owned(&ch->ch_lock));
2861.1Sjmcneill
2871.1Sjmcneill	WR4(exi_softc, EXI_CR(chan),
2881.1Sjmcneill	    EXI_CR_TSTART | EXI_CR_RW_READ |
2891.1Sjmcneill	    __SHIFTIN(datalen - 1, EXI_CR_TLEN));
2901.1Sjmcneill	exi_wait(chan);
2911.1Sjmcneill	val = RD4(exi_softc, EXI_DATA(chan));
2921.1Sjmcneill
2931.1Sjmcneill	switch (datalen) {
2941.1Sjmcneill	case 1:
2951.1Sjmcneill		*(uint8_t *)data = val >> 24;
2961.1Sjmcneill		break;
2971.1Sjmcneill	case 2:
2981.1Sjmcneill		*(uint16_t *)data = val >> 16;
2991.1Sjmcneill		break;
3001.1Sjmcneill	case 4:
3011.1Sjmcneill		*(uint32_t *)data = val;
3021.1Sjmcneill		break;
3031.1Sjmcneill	}
3041.1Sjmcneill}
3051.1Sjmcneill
3061.1Sjmcneillvoid
3071.1Sjmcneillexi_sendrecv_imm(uint8_t chan, uint8_t dev, const void *dataout, void *datain,
3081.1Sjmcneill    size_t datalen)
3091.1Sjmcneill{
3101.1Sjmcneill	struct exi_channel *ch;
3111.1Sjmcneill	uint32_t val = 0;
3121.1Sjmcneill
3131.1Sjmcneill	ASSERT_CHAN_VALID(chan);
3141.1Sjmcneill	ASSERT_DEV_VALID(dev);
3151.1Sjmcneill	ASSERT_LEN_VALID(datalen);
3161.1Sjmcneill
3171.1Sjmcneill	ch = &exi_softc->sc_chan[chan];
3181.1Sjmcneill	KASSERT(mutex_owned(&ch->ch_lock));
3191.1Sjmcneill
3201.1Sjmcneill	switch (datalen) {
3211.1Sjmcneill	case 1:
3221.1Sjmcneill		val = *(const uint8_t *)dataout << 24;
3231.1Sjmcneill		break;
3241.1Sjmcneill	case 2:
3251.1Sjmcneill		val = *(const uint16_t *)dataout << 16;
3261.1Sjmcneill		break;
3271.1Sjmcneill	case 4:
3281.1Sjmcneill		val = *(const uint32_t *)dataout;
3291.1Sjmcneill		break;
3301.1Sjmcneill	}
3311.1Sjmcneill
3321.1Sjmcneill	WR4(exi_softc, EXI_DATA(chan), val);
3331.1Sjmcneill	WR4(exi_softc, EXI_CR(chan),
3341.1Sjmcneill	    EXI_CR_TSTART | EXI_CR_RW_READWRITE |
3351.1Sjmcneill	    __SHIFTIN(datalen - 1, EXI_CR_TLEN));
3361.1Sjmcneill	exi_wait(chan);
3371.1Sjmcneill	val = RD4(exi_softc, EXI_DATA(chan));
3381.1Sjmcneill
3391.1Sjmcneill	switch (datalen) {
3401.1Sjmcneill	case 1:
3411.1Sjmcneill		*(uint8_t *)datain = val >> 24;
3421.1Sjmcneill		break;
3431.1Sjmcneill	case 2:
3441.1Sjmcneill		*(uint16_t *)datain = val >> 16;
3451.1Sjmcneill		break;
3461.1Sjmcneill	case 4:
3471.1Sjmcneill		*(uint32_t *)datain = val;
3481.1Sjmcneill		break;
3491.1Sjmcneill	}
3501.1Sjmcneill}
3511.1Sjmcneill
3521.1Sjmcneill
3531.1Sjmcneillvoid
3541.1Sjmcneillexi_recv_dma(uint8_t chan, uint8_t dev, void *data, size_t datalen)
3551.1Sjmcneill{
3561.1Sjmcneill	struct exi_channel *ch;
3571.1Sjmcneill	int error;
3581.1Sjmcneill
3591.1Sjmcneill	ASSERT_CHAN_VALID(chan);
3601.1Sjmcneill	ASSERT_DEV_VALID(dev);
3611.1Sjmcneill	KASSERT((datalen & 0x1f) == 0);
3621.1Sjmcneill
3631.1Sjmcneill	ch = &exi_softc->sc_chan[chan];
3641.1Sjmcneill	KASSERT(mutex_owned(&ch->ch_lock));
3651.1Sjmcneill
3661.1Sjmcneill	error = bus_dmamap_load(exi_softc->sc_dmat, ch->ch_dmamap,
3671.1Sjmcneill	    data, datalen, NULL, BUS_DMA_WAITOK);
3681.1Sjmcneill	if (error != 0) {
3691.1Sjmcneill		device_printf(exi_softc->sc_dev, "can't load DMA handle: %d\n",
3701.1Sjmcneill		    error);
3711.1Sjmcneill		return;
3721.1Sjmcneill	}
3731.1Sjmcneill
3741.1Sjmcneill	KASSERT((ch->ch_dmamap->dm_segs[0].ds_addr & 0x1f) == 0);
3751.1Sjmcneill
3761.1Sjmcneill	bus_dmamap_sync(exi_softc->sc_dmat, ch->ch_dmamap, 0, datalen,
3771.1Sjmcneill	    BUS_DMASYNC_PREREAD);
3781.1Sjmcneill
3791.1Sjmcneill	WR4(exi_softc, EXI_MAR(chan), ch->ch_dmamap->dm_segs[0].ds_addr);
3801.1Sjmcneill	WR4(exi_softc, EXI_LENGTH(chan), datalen);
3811.1Sjmcneill	WR4(exi_softc, EXI_CR(chan),
3821.1Sjmcneill	    EXI_CR_TSTART | EXI_CR_RW_READ | EXI_CR_DMA);
3831.1Sjmcneill	exi_wait(chan);
3841.1Sjmcneill
3851.1Sjmcneill	bus_dmamap_sync(exi_softc->sc_dmat, ch->ch_dmamap, 0, datalen,
3861.1Sjmcneill	    BUS_DMASYNC_POSTREAD);
3871.1Sjmcneill
3881.1Sjmcneill	bus_dmamap_unload(exi_softc->sc_dmat, ch->ch_dmamap);
3891.1Sjmcneill}
390