Module Name:    src
Committed By:   jmcneill
Date:           Thu Jan 25 11:47:53 UTC 2024

Modified Files:
        src/sys/arch/evbppc/conf: WII files.wii
        src/sys/arch/evbppc/include: wii.h
        src/sys/arch/evbppc/wii: mainbus.c
Added Files:
        src/sys/arch/evbppc/wii/dev: exi.c exi.h rtcsram.c

Log Message:
wii: Add External interface bus and RTC support


To generate a diff of this commit:
cvs rdiff -u -r1.3 -r1.4 src/sys/arch/evbppc/conf/WII \
    src/sys/arch/evbppc/conf/files.wii
cvs rdiff -u -r1.6 -r1.7 src/sys/arch/evbppc/include/wii.h
cvs rdiff -u -r1.2 -r1.3 src/sys/arch/evbppc/wii/mainbus.c
cvs rdiff -u -r0 -r1.1 src/sys/arch/evbppc/wii/dev/exi.c \
    src/sys/arch/evbppc/wii/dev/exi.h src/sys/arch/evbppc/wii/dev/rtcsram.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/evbppc/conf/WII
diff -u src/sys/arch/evbppc/conf/WII:1.3 src/sys/arch/evbppc/conf/WII:1.4
--- src/sys/arch/evbppc/conf/WII:1.3	Tue Jan 23 21:48:12 2024
+++ src/sys/arch/evbppc/conf/WII	Thu Jan 25 11:47:53 2024
@@ -1,4 +1,4 @@
-#	$NetBSD: WII,v 1.3 2024/01/23 21:48:12 jmcneill Exp $
+#	$NetBSD: WII,v 1.4 2024/01/25 11:47:53 jmcneill Exp $
 #
 #	Nintendo Wii
 #
@@ -128,6 +128,8 @@ options 	WSDISPLAY_DEFAULTSCREENS=4
 options 	WSDISPLAY_SCROLLSUPPORT
 
 hollywood0 	at mainbus0 irq 14
+exi0		at mainbus0 addr 0x0d006800 irq 4	# External interface
+rtcsram0	at exi0					# RTC/SRAM chip
 bwai0		at mainbus0 addr 0x0d006c00 irq 5	# Audio interface
 bwdsp0		at mainbus0 addr 0x0c005000 irq 6	# DSP
 
Index: src/sys/arch/evbppc/conf/files.wii
diff -u src/sys/arch/evbppc/conf/files.wii:1.3 src/sys/arch/evbppc/conf/files.wii:1.4
--- src/sys/arch/evbppc/conf/files.wii:1.3	Tue Jan 23 21:48:12 2024
+++ src/sys/arch/evbppc/conf/files.wii	Thu Jan 25 11:47:53 2024
@@ -1,4 +1,4 @@
-#	$NetBSD: files.wii,v 1.3 2024/01/23 21:48:12 jmcneill Exp $
+#	$NetBSD: files.wii,v 1.4 2024/01/25 11:47:53 jmcneill Exp $
 #
 #
 maxpartitions 16
@@ -47,6 +47,15 @@ device	bwdsp: audiobus
 attach	bwdsp at mainbus
 file	arch/evbppc/wii/dev/bwdsp.c		bwdsp
 
+define	exi { }
+device	exi: exi
+attach	exi at mainbus
+file	arch/evbppc/wii/dev/exi.c		exi
+
+device	rtcsram
+attach	rtcsram at exi
+file	arch/evbppc/wii/dev/rtcsram.c		rtcsram
+
 define	hollywood { [addr=-1], [irq=-1] }
 device	hollywood: hollywood
 attach	hollywood at mainbus

Index: src/sys/arch/evbppc/include/wii.h
diff -u src/sys/arch/evbppc/include/wii.h:1.6 src/sys/arch/evbppc/include/wii.h:1.7
--- src/sys/arch/evbppc/include/wii.h:1.6	Wed Jan 24 21:53:34 2024
+++ src/sys/arch/evbppc/include/wii.h	Thu Jan 25 11:47:53 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: wii.h,v 1.6 2024/01/24 21:53:34 jmcneill Exp $ */
+/* $NetBSD: wii.h,v 1.7 2024/01/25 11:47:53 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2024 Jared McNeill <jmcne...@invisible.ca>
@@ -57,6 +57,9 @@
 #define DSP_BASE			0x0c005000
 #define DSP_SIZE			0x00000200
 
+#define EXI_BASE			0x0d006800
+#define EXI_SIZE			0x00000080
+
 #define AI_BASE				0x0d006c00
 #define AI_SIZE				0x00000020
 
@@ -96,6 +99,7 @@
 #define PI_INTMR			(PI_BASE + 0x04)
 
 /* Processor IRQs */
+#define PI_IRQ_EXI			4
 #define PI_IRQ_AI			5
 #define PI_IRQ_DSP			6
 #define PI_IRQ_HOLLYWOOD		14

Index: src/sys/arch/evbppc/wii/mainbus.c
diff -u src/sys/arch/evbppc/wii/mainbus.c:1.2 src/sys/arch/evbppc/wii/mainbus.c:1.3
--- src/sys/arch/evbppc/wii/mainbus.c:1.2	Mon Jan 22 21:28:15 2024
+++ src/sys/arch/evbppc/wii/mainbus.c	Thu Jan 25 11:47:53 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: mainbus.c,v 1.2 2024/01/22 21:28:15 jmcneill Exp $ */
+/* $NetBSD: mainbus.c,v 1.3 2024/01/25 11:47:53 jmcneill Exp $ */
 
 /*
  * Copyright (c) 2002, 2024 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: mainbus.c,v 1.2 2024/01/22 21:28:15 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: mainbus.c,v 1.3 2024/01/25 11:47:53 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/device.h>
@@ -102,6 +102,11 @@ mainbus_attach(device_t parent, device_t
 	maa.maa_irq = MAINBUSCF_IRQ_DEFAULT;
 	config_found(self, &maa, mainbus_print, CFARGS_NONE);
 
+	maa.maa_name = "exi";
+	maa.maa_addr = EXI_BASE;
+	maa.maa_irq = PI_IRQ_EXI;
+	config_found(self, &maa, mainbus_print, CFARGS_NONE);
+
 	maa.maa_name = "hollywood";
 	maa.maa_addr = MAINBUSCF_ADDR_DEFAULT;
 	maa.maa_irq = PI_IRQ_HOLLYWOOD;

Added files:

Index: src/sys/arch/evbppc/wii/dev/exi.c
diff -u /dev/null src/sys/arch/evbppc/wii/dev/exi.c:1.1
--- /dev/null	Thu Jan 25 11:47:53 2024
+++ src/sys/arch/evbppc/wii/dev/exi.c	Thu Jan 25 11:47:53 2024
@@ -0,0 +1,348 @@
+/* $NetBSD: exi.c,v 1.1 2024/01/25 11:47:53 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2024 Jared McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: exi.c,v 1.1 2024/01/25 11:47:53 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/systm.h>
+#include <sys/bitops.h>
+#include <sys/mutex.h>
+#include <uvm/uvm_extern.h>
+
+#include <machine/wii.h>
+#include <machine/pio.h>
+
+#include "locators.h"
+#include "mainbus.h"
+#include "exi.h"
+
+#define	EXI_NUM_CHAN		3
+#define	EXI_NUM_DEV		3
+
+/* This is an arbitrary limit. The real limit is probably much higher. */
+#define	EXI_MAX_DMA		4096
+
+#define	EXI_CSR(n)		(0x00 + (n) * 0x14)
+#define	 EXI_CSR_CS		__BITS(9,7)
+#define	EXI_MAR(n)		(0x04 + (n) * 0x14)
+#define	EXI_LENGTH(n)		(0x08 + (n) * 0x14)
+#define	EXI_CR(n)		(0x0c + (n) * 0x14)
+#define	 EXI_CR_TLEN		__BITS(5,4)
+#define  EXI_CR_RW		__BITS(3,2)
+#define  EXI_CR_RW_READ		__SHIFTIN(0, EXI_CR_RW)
+#define  EXI_CR_RW_WRITE	__SHIFTIN(1, EXI_CR_RW)
+#define	 EXI_CR_DMA		__BIT(1)
+#define  EXI_CR_TSTART		__BIT(0)
+#define	EXI_DATA(n)		(0x10 + (n) * 0x14)
+
+#define	ASSERT_CHAN_VALID(chan)	KASSERT((chan) >= 0 && (chan) < EXI_NUM_CHAN)
+#define	ASSERT_DEV_VALID(dev)	KASSERT((dev) >= 0 && (dev) < EXI_NUM_DEV)
+#define ASSERT_LEN_VALID(len)	KASSERT((len) == 1 || (len) == 2 || (len) == 4)
+
+struct exi_channel {
+	kmutex_t		ch_lock;
+
+	bus_dmamap_t		ch_dmamap;
+
+	device_t		ch_child[EXI_NUM_DEV];
+};
+
+struct exi_softc {
+	device_t		sc_dev;
+	bus_space_tag_t		sc_bst;
+	bus_space_handle_t	sc_bsh;
+	bus_dma_tag_t		sc_dmat;
+
+	struct exi_channel	sc_chan[EXI_NUM_CHAN];
+};
+
+static struct exi_softc *exi_softc;
+
+#define RD4(sc, reg)							\
+	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define WR4(sc, reg, val)						\
+	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+
+static int	exi_match(device_t, cfdata_t, void *);
+static void	exi_attach(device_t, device_t, void *);
+
+static int	exi_rescan(device_t, const char *, const int *);
+static int	exi_print(void *, const char *);
+
+CFATTACH_DECL_NEW(exi, sizeof(struct exi_softc),
+	exi_match, exi_attach, NULL, NULL);
+
+static int
+exi_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct mainbus_attach_args *maa = aux;
+
+	return strcmp(maa->maa_name, "exi") == 0;
+}
+
+static void
+exi_attach(device_t parent, device_t self, void *aux)
+{
+	struct mainbus_attach_args * const maa = aux;
+	struct exi_softc * const sc = device_private(self);
+	uint8_t chan;
+	int error;
+
+	KASSERT(device_unit(self) == 0);
+
+	aprint_naive("\n");
+	aprint_normal(": External Interface\n");
+
+	exi_softc = sc;
+	sc->sc_dev = self;
+	sc->sc_bst = maa->maa_bst;
+	if (bus_space_map(sc->sc_bst, maa->maa_addr, EXI_SIZE, 0,
+	    &sc->sc_bsh) != 0) {
+		aprint_error_dev(self, "couldn't map registers\n");
+		return;
+	}
+	sc->sc_dmat = maa->maa_dmat;
+	for (chan = 0; chan < EXI_NUM_CHAN; chan++) {
+		mutex_init(&sc->sc_chan[chan].ch_lock, MUTEX_DEFAULT, IPL_VM);
+		error = bus_dmamap_create(exi_softc->sc_dmat, EXI_MAX_DMA, 1,
+		    EXI_MAX_DMA, 0, BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
+		    &sc->sc_chan[chan].ch_dmamap);
+		if (error != 0) {
+			aprint_error_dev(self, "couldn't create dmamap: %d\n",
+			    error);
+			return;
+		}
+	}
+
+	exi_rescan(self, NULL, NULL);
+}
+
+static int
+exi_rescan(device_t self, const char *ifattr, const int *locs)
+{
+	struct exi_softc * const sc = device_private(self);
+	uint8_t chan, dev;
+
+	for (chan = 0; chan < EXI_NUM_CHAN; chan++) {
+		struct exi_channel *ch = &sc->sc_chan[chan];
+		for (dev = 0; dev < EXI_NUM_DEV; dev++) {
+			struct exi_attach_args eaa = {};
+			uint16_t command = 0x0000; /* ID command */
+			uint32_t id = 0;
+
+			if (ch->ch_child[dev] != NULL) {
+				continue;
+			}
+
+			exi_select(chan, dev);
+			exi_send_imm(chan, dev, &command, sizeof(command));
+			exi_recv_imm(chan, dev, &id, sizeof(id));
+			exi_unselect(chan);
+
+			if (id == 0 || id == 0xffffffff) {
+				continue;
+			}
+
+			eaa.eaa_id = id;
+			eaa.eaa_chan = chan;
+			eaa.eaa_device = dev;
+
+			ch->ch_child[dev] = config_found(self, &eaa, exi_print,
+			    CFARGS(.submatch = config_stdsubmatch,
+				   .locators = locs));
+		}
+	}
+
+	return 0;
+}
+
+static int
+exi_print(void *aux, const char *pnp)
+{
+	struct exi_attach_args *eaa = aux;
+
+	if (pnp != NULL) {
+		aprint_normal("EXI device 0x%08x at %s", eaa->eaa_id, pnp);
+	}
+
+	aprint_normal(" addr %u-%u", eaa->eaa_chan, eaa->eaa_device);
+
+	return UNCONF;
+}
+
+void
+exi_select(uint8_t chan, uint8_t dev)
+{
+	struct exi_channel *ch;
+	uint32_t val;
+
+	ASSERT_CHAN_VALID(chan);
+	ASSERT_DEV_VALID(dev);
+
+	ch = &exi_softc->sc_chan[chan];
+	mutex_enter(&ch->ch_lock);
+
+	val = RD4(exi_softc, EXI_CSR(chan));
+	val &= ~EXI_CSR_CS;
+	val |= __SHIFTIN(__BIT(dev), EXI_CSR_CS);
+	WR4(exi_softc, EXI_CSR(chan), val);
+}
+
+void
+exi_unselect(uint8_t chan)
+{
+	struct exi_channel *ch;
+	uint32_t val;
+
+	ASSERT_CHAN_VALID(chan);
+
+	ch = &exi_softc->sc_chan[chan];
+
+	val = RD4(exi_softc, EXI_CSR(chan));
+	val &= ~EXI_CSR_CS;
+	WR4(exi_softc, EXI_CSR(chan), val);
+
+	mutex_exit(&ch->ch_lock);
+}
+
+static void
+exi_wait(uint8_t chan)
+{
+	uint32_t val;
+
+	ASSERT_CHAN_VALID(chan);
+
+	do {
+		val = RD4(exi_softc, EXI_CR(chan));
+	} while ((val & EXI_CR_TSTART) != 0);
+}
+
+void
+exi_send_imm(uint8_t chan, uint8_t dev, const void *data, size_t datalen)
+{
+	struct exi_channel *ch;
+	uint32_t val = 0;
+
+	ASSERT_CHAN_VALID(chan);
+	ASSERT_DEV_VALID(dev);
+	ASSERT_LEN_VALID(datalen);
+
+	ch = &exi_softc->sc_chan[chan];
+	KASSERT(mutex_owned(&ch->ch_lock));
+
+	switch (datalen) {
+	case 1:
+		val = *(const uint8_t *)data << 24;
+		break;
+	case 2:
+		val = *(const uint16_t *)data << 16;
+		break;
+	case 4:
+		val = *(const uint32_t *)data;
+		break;
+	}
+
+	WR4(exi_softc, EXI_DATA(chan), val);
+	WR4(exi_softc, EXI_CR(chan),
+	    EXI_CR_TSTART | EXI_CR_RW_WRITE |
+	    __SHIFTIN(datalen - 1, EXI_CR_TLEN));
+	exi_wait(chan);
+}
+
+void
+exi_recv_imm(uint8_t chan, uint8_t dev, void *data, size_t datalen)
+{
+	struct exi_channel *ch;
+	uint32_t val;
+
+	ASSERT_CHAN_VALID(chan);
+	ASSERT_DEV_VALID(dev);
+	ASSERT_LEN_VALID(datalen);
+
+	ch = &exi_softc->sc_chan[chan];
+	KASSERT(mutex_owned(&ch->ch_lock));
+
+	WR4(exi_softc, EXI_CR(chan),
+	    EXI_CR_TSTART | EXI_CR_RW_READ |
+	    __SHIFTIN(datalen - 1, EXI_CR_TLEN));
+	exi_wait(chan);
+	val = RD4(exi_softc, EXI_DATA(chan));
+
+	switch (datalen) {
+	case 1:
+		*(uint8_t *)data = val >> 24;
+		break;
+	case 2:
+		*(uint16_t *)data = val >> 16;
+		break;
+	case 4:
+		*(uint32_t *)data = val;
+		break;
+	}
+}
+
+void
+exi_recv_dma(uint8_t chan, uint8_t dev, void *data, size_t datalen)
+{
+	struct exi_channel *ch;
+	int error;
+
+	ASSERT_CHAN_VALID(chan);
+	ASSERT_DEV_VALID(dev);
+	KASSERT((datalen & 0x1f) == 0);
+
+	ch = &exi_softc->sc_chan[chan];
+	KASSERT(mutex_owned(&ch->ch_lock));
+
+	error = bus_dmamap_load(exi_softc->sc_dmat, ch->ch_dmamap,
+	    data, datalen, NULL, BUS_DMA_WAITOK);
+	if (error != 0) {
+		device_printf(exi_softc->sc_dev, "can't load DMA handle: %d\n",
+		    error);
+		return;
+	}
+
+	KASSERT((ch->ch_dmamap->dm_segs[0].ds_addr & 0x1f) == 0);
+
+	bus_dmamap_sync(exi_softc->sc_dmat, ch->ch_dmamap, 0, datalen,
+	    BUS_DMASYNC_PREREAD);
+
+	WR4(exi_softc, EXI_MAR(chan), ch->ch_dmamap->dm_segs[0].ds_addr);
+	WR4(exi_softc, EXI_LENGTH(chan), datalen);
+	WR4(exi_softc, EXI_CR(chan),
+	    EXI_CR_TSTART | EXI_CR_RW_READ | EXI_CR_DMA);
+	exi_wait(chan);
+
+	bus_dmamap_sync(exi_softc->sc_dmat, ch->ch_dmamap, 0, datalen,
+	    BUS_DMASYNC_POSTREAD);
+
+	bus_dmamap_unload(exi_softc->sc_dmat, ch->ch_dmamap);
+}
Index: src/sys/arch/evbppc/wii/dev/exi.h
diff -u /dev/null src/sys/arch/evbppc/wii/dev/exi.h:1.1
--- /dev/null	Thu Jan 25 11:47:53 2024
+++ src/sys/arch/evbppc/wii/dev/exi.h	Thu Jan 25 11:47:53 2024
@@ -0,0 +1,44 @@
+/* $NetBSD: exi.h,v 1.1 2024/01/25 11:47:53 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2024 Jared McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _WII_DEV_EXI_H_
+#define _WII_DEV_EXI_H_
+
+struct exi_attach_args {
+	uint32_t	eaa_id;
+	uint8_t		eaa_chan;
+	uint8_t		eaa_device;
+};
+
+void exi_select(uint8_t, uint8_t);
+void exi_unselect(uint8_t);
+void exi_send_imm(uint8_t, uint8_t, const void *, size_t);
+void exi_recv_imm(uint8_t, uint8_t, void *, size_t);
+void exi_recv_dma(uint8_t, uint8_t, void *, size_t);
+
+#endif /* _WII_DEV_EXI_H_ */
Index: src/sys/arch/evbppc/wii/dev/rtcsram.c
diff -u /dev/null src/sys/arch/evbppc/wii/dev/rtcsram.c:1.1
--- /dev/null	Thu Jan 25 11:47:53 2024
+++ src/sys/arch/evbppc/wii/dev/rtcsram.c	Thu Jan 25 11:47:53 2024
@@ -0,0 +1,177 @@
+/* $NetBSD: rtcsram.c,v 1.1 2024/01/25 11:47:53 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2024 Jared McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: rtcsram.c,v 1.1 2024/01/25 11:47:53 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/systm.h>
+#include <dev/clock_subr.h>
+
+#include <lib/libkern/libkern.h>
+
+#include "exi.h"
+
+#define	WII_RTCSRAM_ID		0xfffff308
+
+#define	RTC_BASE		0x20000000
+#define	SRAM_BASE		0x20000100
+
+#define	WRITE_OFFSET		0x80000000
+
+struct rtcsram_sram {
+	uint16_t	checksum[2];
+	uint16_t	ead[2];
+	int32_t		counter_bias;
+	int8_t		display_offset_h;
+	uint8_t		ntd;
+	uint8_t		language;
+	uint8_t		flags;
+	uint16_t	flash_id[12];
+	uint32_t	wireless_keyboard_id;
+	uint32_t	wireless_pad_id[2];
+	uint8_t		last_dvd_errorcode;
+	uint8_t		padding1;
+	uint16_t	flash_id_checksum[2];
+	uint16_t	padding2;
+} __aligned(32);
+CTASSERT(sizeof(struct rtcsram_sram) == 64);
+
+struct rtcsram_softc {
+	struct todr_chip_handle	sc_todr;
+
+	uint8_t			sc_chan;
+	uint8_t			sc_device;
+
+	struct rtcsram_sram	sc_sram;
+};
+
+static int	rtcsram_match(device_t, cfdata_t, void *);
+static void	rtcsram_attach(device_t, device_t, void *);
+
+static uint32_t	rtcsram_read_4(struct rtcsram_softc *, uint32_t);
+static void	rtcsram_write_4(struct rtcsram_softc *, uint32_t, uint32_t);
+static void	rtcsram_read_buf(struct rtcsram_softc *, uint32_t, void *,
+				 size_t);
+
+static int	rtcsram_gettime(todr_chip_handle_t, struct timeval *);
+static int	rtcsram_settime(todr_chip_handle_t, struct timeval *);
+
+CFATTACH_DECL_NEW(rtcsram, sizeof(struct rtcsram_softc),
+	rtcsram_match, rtcsram_attach, NULL, NULL);
+
+static int
+rtcsram_match(device_t parent, cfdata_t cf, void *aux)
+{
+	struct exi_attach_args * const eaa = aux;
+
+	return eaa->eaa_id == WII_RTCSRAM_ID;
+}
+
+static void
+rtcsram_attach(device_t parent, device_t self, void *aux)
+{
+	struct rtcsram_softc * const sc = device_private(self);
+	struct exi_attach_args * const eaa = aux;
+
+	aprint_naive("\n");
+	aprint_normal(": RTC/SRAM\n");
+
+	sc->sc_chan = eaa->eaa_chan;
+	sc->sc_device = eaa->eaa_device;
+
+	/* Read RTC counter bias from SRAM. */
+	rtcsram_read_buf(sc, SRAM_BASE, &sc->sc_sram, sizeof(sc->sc_sram));
+	aprint_debug_dev(self, "counter bias %d\n", sc->sc_sram.counter_bias);
+	hexdump(aprint_debug, device_xname(self), &sc->sc_sram,
+	    sizeof(sc->sc_sram));
+
+	sc->sc_todr.cookie = sc;
+	sc->sc_todr.todr_gettime = rtcsram_gettime;
+	sc->sc_todr.todr_settime = rtcsram_settime;
+	todr_attach(&sc->sc_todr);
+}
+
+static uint32_t
+rtcsram_read_4(struct rtcsram_softc *sc, uint32_t offset)
+{
+	uint32_t val;
+
+	exi_select(sc->sc_chan, sc->sc_device);
+	exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset));
+	exi_recv_imm(sc->sc_chan, sc->sc_device, &val, sizeof(val));
+	exi_unselect(sc->sc_chan);
+
+	return val;
+}
+
+static void
+rtcsram_write_4(struct rtcsram_softc *sc, uint32_t offset, uint32_t val)
+{
+	offset |= WRITE_OFFSET;
+
+	exi_select(sc->sc_chan, sc->sc_device);
+	exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset));
+	exi_send_imm(sc->sc_chan, sc->sc_device, &val, sizeof(val));
+	exi_unselect(sc->sc_chan);
+}
+
+static void
+rtcsram_read_buf(struct rtcsram_softc *sc, uint32_t offset, void *data,
+    size_t datalen)
+{
+	exi_select(sc->sc_chan, sc->sc_device);
+	exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset));
+	exi_recv_dma(sc->sc_chan, sc->sc_device, data, datalen);
+	exi_unselect(sc->sc_chan);
+}
+
+static int
+rtcsram_gettime(todr_chip_handle_t ch, struct timeval *tv)
+{
+	struct rtcsram_softc * const sc = ch->cookie;
+	uint32_t val;
+
+	val = rtcsram_read_4(sc, RTC_BASE);
+	tv->tv_sec = (uint64_t)val + sc->sc_sram.counter_bias;
+	tv->tv_usec = 0;
+
+	return 0;
+}
+
+static int
+rtcsram_settime(todr_chip_handle_t ch, struct timeval *tv)
+{
+	struct rtcsram_softc * const sc = ch->cookie;
+
+	rtcsram_write_4(sc, RTC_BASE, tv->tv_sec - sc->sc_sram.counter_bias);
+
+	return 0;
+}

Reply via email to