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