Home | History | Annotate | Line # | Download | only in dev
      1 /* $NetBSD: exi.c,v 1.2 2024/02/10 11:00:15 jmcneill Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2024 Jared McNeill <jmcneill (at) 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 AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: exi.c,v 1.2 2024/02/10 11:00:15 jmcneill Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/bus.h>
     34 #include <sys/device.h>
     35 #include <sys/systm.h>
     36 #include <sys/bitops.h>
     37 #include <sys/mutex.h>
     38 #include <uvm/uvm_extern.h>
     39 
     40 #include <machine/wii.h>
     41 #include <machine/pio.h>
     42 
     43 #include "locators.h"
     44 #include "mainbus.h"
     45 #include "exi.h"
     46 
     47 #define	EXI_NUM_CHAN		3
     48 #define	EXI_NUM_DEV		3
     49 
     50 /* This is an arbitrary limit. The real limit is probably much higher. */
     51 #define	EXI_MAX_DMA		4096
     52 
     53 #define	EXI_CSR(n)		(0x00 + (n) * 0x14)
     54 #define	 EXI_CSR_CS		__BITS(9,7)
     55 #define	 EXI_CSR_CLK		__BITS(6,4)
     56 #define	EXI_MAR(n)		(0x04 + (n) * 0x14)
     57 #define	EXI_LENGTH(n)		(0x08 + (n) * 0x14)
     58 #define	EXI_CR(n)		(0x0c + (n) * 0x14)
     59 #define	 EXI_CR_TLEN		__BITS(5,4)
     60 #define  EXI_CR_RW		__BITS(3,2)
     61 #define  EXI_CR_RW_READ		__SHIFTIN(0, EXI_CR_RW)
     62 #define  EXI_CR_RW_WRITE	__SHIFTIN(1, EXI_CR_RW)
     63 #define	 EXI_CR_DMA		__BIT(1)
     64 #define  EXI_CR_TSTART		__BIT(0)
     65 #define	EXI_DATA(n)		(0x10 + (n) * 0x14)
     66 
     67 #define	ASSERT_CHAN_VALID(chan)	KASSERT((chan) >= 0 && (chan) < EXI_NUM_CHAN)
     68 #define	ASSERT_DEV_VALID(dev)	KASSERT((dev) >= 0 && (dev) < EXI_NUM_DEV)
     69 #define ASSERT_LEN_VALID(len)	KASSERT((len) == 1 || (len) == 2 || (len) == 4)
     70 
     71 struct exi_channel {
     72 	kmutex_t		ch_lock;
     73 
     74 	bus_dmamap_t		ch_dmamap;
     75 
     76 	device_t		ch_child[EXI_NUM_DEV];
     77 };
     78 
     79 struct exi_softc {
     80 	device_t		sc_dev;
     81 	bus_space_tag_t		sc_bst;
     82 	bus_space_handle_t	sc_bsh;
     83 	bus_dma_tag_t		sc_dmat;
     84 
     85 	struct exi_channel	sc_chan[EXI_NUM_CHAN];
     86 };
     87 
     88 static struct exi_softc *exi_softc;
     89 
     90 #define RD4(sc, reg)							\
     91 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
     92 #define WR4(sc, reg, val)						\
     93 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
     94 
     95 static int	exi_match(device_t, cfdata_t, void *);
     96 static void	exi_attach(device_t, device_t, void *);
     97 
     98 static int	exi_rescan(device_t, const char *, const int *);
     99 static int	exi_print(void *, const char *);
    100 
    101 CFATTACH_DECL_NEW(exi, sizeof(struct exi_softc),
    102 	exi_match, exi_attach, NULL, NULL);
    103 
    104 static int
    105 exi_match(device_t parent, cfdata_t cf, void *aux)
    106 {
    107 	struct mainbus_attach_args *maa = aux;
    108 
    109 	return strcmp(maa->maa_name, "exi") == 0;
    110 }
    111 
    112 static void
    113 exi_attach(device_t parent, device_t self, void *aux)
    114 {
    115 	struct mainbus_attach_args * const maa = aux;
    116 	struct exi_softc * const sc = device_private(self);
    117 	uint8_t chan;
    118 	int error;
    119 
    120 	KASSERT(device_unit(self) == 0);
    121 
    122 	aprint_naive("\n");
    123 	aprint_normal(": External Interface\n");
    124 
    125 	exi_softc = sc;
    126 	sc->sc_dev = self;
    127 	sc->sc_bst = maa->maa_bst;
    128 	if (bus_space_map(sc->sc_bst, maa->maa_addr, EXI_SIZE, 0,
    129 	    &sc->sc_bsh) != 0) {
    130 		aprint_error_dev(self, "couldn't map registers\n");
    131 		return;
    132 	}
    133 	sc->sc_dmat = maa->maa_dmat;
    134 	for (chan = 0; chan < EXI_NUM_CHAN; chan++) {
    135 		mutex_init(&sc->sc_chan[chan].ch_lock, MUTEX_DEFAULT, IPL_VM);
    136 		error = bus_dmamap_create(exi_softc->sc_dmat, EXI_MAX_DMA, 1,
    137 		    EXI_MAX_DMA, 0, BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
    138 		    &sc->sc_chan[chan].ch_dmamap);
    139 		if (error != 0) {
    140 			aprint_error_dev(self, "couldn't create dmamap: %d\n",
    141 			    error);
    142 			return;
    143 		}
    144 	}
    145 
    146 	exi_rescan(self, NULL, NULL);
    147 }
    148 
    149 static int
    150 exi_rescan(device_t self, const char *ifattr, const int *locs)
    151 {
    152 	struct exi_softc * const sc = device_private(self);
    153 	uint8_t chan, dev;
    154 
    155 	for (chan = 0; chan < EXI_NUM_CHAN; chan++) {
    156 		struct exi_channel *ch = &sc->sc_chan[chan];
    157 		for (dev = 0; dev < EXI_NUM_DEV; dev++) {
    158 			struct exi_attach_args eaa = {};
    159 			uint16_t command = 0x0000; /* ID command */
    160 			uint32_t id = 0;
    161 
    162 			if (ch->ch_child[dev] != NULL) {
    163 				continue;
    164 			}
    165 
    166 			exi_select(chan, dev, EXI_FREQ_8MHZ);
    167 			exi_send_imm(chan, dev, &command, sizeof(command));
    168 			exi_recv_imm(chan, dev, &id, sizeof(id));
    169 			exi_unselect(chan);
    170 
    171 			if (id == 0 || id == 0xffffffff) {
    172 				continue;
    173 			}
    174 
    175 			eaa.eaa_id = id;
    176 			eaa.eaa_chan = chan;
    177 			eaa.eaa_device = dev;
    178 
    179 			ch->ch_child[dev] = config_found(self, &eaa, exi_print,
    180 			    CFARGS(.submatch = config_stdsubmatch,
    181 				   .locators = locs));
    182 		}
    183 	}
    184 
    185 	return 0;
    186 }
    187 
    188 static int
    189 exi_print(void *aux, const char *pnp)
    190 {
    191 	struct exi_attach_args *eaa = aux;
    192 
    193 	if (pnp != NULL) {
    194 		aprint_normal("EXI device 0x%08x at %s", eaa->eaa_id, pnp);
    195 	}
    196 
    197 	aprint_normal(" addr %u-%u", eaa->eaa_chan, eaa->eaa_device);
    198 
    199 	return UNCONF;
    200 }
    201 
    202 void
    203 exi_select(uint8_t chan, uint8_t dev, exi_freq_t freq)
    204 {
    205 	struct exi_channel *ch;
    206 	uint32_t val;
    207 
    208 	ASSERT_CHAN_VALID(chan);
    209 	ASSERT_DEV_VALID(dev);
    210 
    211 	ch = &exi_softc->sc_chan[chan];
    212 	mutex_enter(&ch->ch_lock);
    213 
    214 	val = RD4(exi_softc, EXI_CSR(chan));
    215 	val &= ~EXI_CSR_CS;
    216 	val |= __SHIFTIN(__BIT(dev), EXI_CSR_CS);
    217 	val &= ~EXI_CSR_CLK;
    218 	val |= __SHIFTIN(freq, EXI_CSR_CLK);
    219 	WR4(exi_softc, EXI_CSR(chan), val);
    220 }
    221 
    222 void
    223 exi_unselect(uint8_t chan)
    224 {
    225 	struct exi_channel *ch;
    226 	uint32_t val;
    227 
    228 	ASSERT_CHAN_VALID(chan);
    229 
    230 	ch = &exi_softc->sc_chan[chan];
    231 
    232 	val = RD4(exi_softc, EXI_CSR(chan));
    233 	val &= ~EXI_CSR_CS;
    234 	WR4(exi_softc, EXI_CSR(chan), val);
    235 
    236 	mutex_exit(&ch->ch_lock);
    237 }
    238 
    239 static void
    240 exi_wait(uint8_t chan)
    241 {
    242 	uint32_t val;
    243 
    244 	ASSERT_CHAN_VALID(chan);
    245 
    246 	do {
    247 		val = RD4(exi_softc, EXI_CR(chan));
    248 	} while ((val & EXI_CR_TSTART) != 0);
    249 }
    250 
    251 void
    252 exi_send_imm(uint8_t chan, uint8_t dev, const void *data, size_t datalen)
    253 {
    254 	struct exi_channel *ch;
    255 	uint32_t val = 0;
    256 
    257 	ASSERT_CHAN_VALID(chan);
    258 	ASSERT_DEV_VALID(dev);
    259 	ASSERT_LEN_VALID(datalen);
    260 
    261 	ch = &exi_softc->sc_chan[chan];
    262 	KASSERT(mutex_owned(&ch->ch_lock));
    263 
    264 	switch (datalen) {
    265 	case 1:
    266 		val = *(const uint8_t *)data << 24;
    267 		break;
    268 	case 2:
    269 		val = *(const uint16_t *)data << 16;
    270 		break;
    271 	case 4:
    272 		val = *(const uint32_t *)data;
    273 		break;
    274 	}
    275 
    276 	WR4(exi_softc, EXI_DATA(chan), val);
    277 	WR4(exi_softc, EXI_CR(chan),
    278 	    EXI_CR_TSTART | EXI_CR_RW_WRITE |
    279 	    __SHIFTIN(datalen - 1, EXI_CR_TLEN));
    280 	exi_wait(chan);
    281 }
    282 
    283 void
    284 exi_recv_imm(uint8_t chan, uint8_t dev, void *data, size_t datalen)
    285 {
    286 	struct exi_channel *ch;
    287 	uint32_t val;
    288 
    289 	ASSERT_CHAN_VALID(chan);
    290 	ASSERT_DEV_VALID(dev);
    291 	ASSERT_LEN_VALID(datalen);
    292 
    293 	ch = &exi_softc->sc_chan[chan];
    294 	KASSERT(mutex_owned(&ch->ch_lock));
    295 
    296 	WR4(exi_softc, EXI_CR(chan),
    297 	    EXI_CR_TSTART | EXI_CR_RW_READ |
    298 	    __SHIFTIN(datalen - 1, EXI_CR_TLEN));
    299 	exi_wait(chan);
    300 	val = RD4(exi_softc, EXI_DATA(chan));
    301 
    302 	switch (datalen) {
    303 	case 1:
    304 		*(uint8_t *)data = val >> 24;
    305 		break;
    306 	case 2:
    307 		*(uint16_t *)data = val >> 16;
    308 		break;
    309 	case 4:
    310 		*(uint32_t *)data = val;
    311 		break;
    312 	}
    313 }
    314 
    315 void
    316 exi_recv_dma(uint8_t chan, uint8_t dev, void *data, size_t datalen)
    317 {
    318 	struct exi_channel *ch;
    319 	int error;
    320 
    321 	ASSERT_CHAN_VALID(chan);
    322 	ASSERT_DEV_VALID(dev);
    323 	KASSERT((datalen & 0x1f) == 0);
    324 
    325 	ch = &exi_softc->sc_chan[chan];
    326 	KASSERT(mutex_owned(&ch->ch_lock));
    327 
    328 	error = bus_dmamap_load(exi_softc->sc_dmat, ch->ch_dmamap,
    329 	    data, datalen, NULL, BUS_DMA_WAITOK);
    330 	if (error != 0) {
    331 		device_printf(exi_softc->sc_dev, "can't load DMA handle: %d\n",
    332 		    error);
    333 		return;
    334 	}
    335 
    336 	KASSERT((ch->ch_dmamap->dm_segs[0].ds_addr & 0x1f) == 0);
    337 
    338 	bus_dmamap_sync(exi_softc->sc_dmat, ch->ch_dmamap, 0, datalen,
    339 	    BUS_DMASYNC_PREREAD);
    340 
    341 	WR4(exi_softc, EXI_MAR(chan), ch->ch_dmamap->dm_segs[0].ds_addr);
    342 	WR4(exi_softc, EXI_LENGTH(chan), datalen);
    343 	WR4(exi_softc, EXI_CR(chan),
    344 	    EXI_CR_TSTART | EXI_CR_RW_READ | EXI_CR_DMA);
    345 	exi_wait(chan);
    346 
    347 	bus_dmamap_sync(exi_softc->sc_dmat, ch->ch_dmamap, 0, datalen,
    348 	    BUS_DMASYNC_POSTREAD);
    349 
    350 	bus_dmamap_unload(exi_softc->sc_dmat, ch->ch_dmamap);
    351 }
    352