11.1Sjmcneill/* $NetBSD: rtcsram.c,v 1.1 2026/01/09 22:54:30 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: rtcsram.c,v 1.1 2026/01/09 22:54:30 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 <dev/clock_subr.h>
371.1Sjmcneill
381.1Sjmcneill#include <lib/libkern/libkern.h>
391.1Sjmcneill
401.1Sjmcneill#include "exi.h"
411.1Sjmcneill
421.1Sjmcneill#define	WII_RTCSRAM_ID		0xfffff308
431.1Sjmcneill#define	WII_RTCSRAM_FREQ	EXI_FREQ_8MHZ
441.1Sjmcneill
451.1Sjmcneill#define	RTC_BASE		0x20000000
461.1Sjmcneill#define	SRAM_BASE		0x20000100
471.1Sjmcneill
481.1Sjmcneill#define	WRITE_OFFSET		0x80000000
491.1Sjmcneill
501.1Sjmcneillstruct rtcsram_sram {
511.1Sjmcneill	uint16_t	checksum[2];
521.1Sjmcneill	uint32_t	ead[2];
531.1Sjmcneill	int32_t		counter_bias;
541.1Sjmcneill	int8_t		display_offset_h;
551.1Sjmcneill	uint8_t		ntd;
561.1Sjmcneill	uint8_t		language;
571.1Sjmcneill	uint8_t		flags;
581.1Sjmcneill	uint16_t	flash_id[12];
591.1Sjmcneill	uint32_t	wireless_keyboard_id;
601.1Sjmcneill	uint32_t	wireless_pad_id[2];
611.1Sjmcneill	uint8_t		last_dvd_errorcode;
621.1Sjmcneill	uint8_t		padding1;
631.1Sjmcneill	uint16_t	flash_id_checksum[2];
641.1Sjmcneill	uint16_t	padding2;
651.1Sjmcneill} __aligned(32);
661.1SjmcneillCTASSERT(sizeof(struct rtcsram_sram) == 64);
671.1Sjmcneill
681.1Sjmcneillstruct rtcsram_softc {
691.1Sjmcneill	device_t		sc_dev;
701.1Sjmcneill	struct todr_chip_handle	sc_todr;
711.1Sjmcneill
721.1Sjmcneill	uint8_t			sc_chan;
731.1Sjmcneill	uint8_t			sc_device;
741.1Sjmcneill
751.1Sjmcneill	struct rtcsram_sram	sc_sram;
761.1Sjmcneill};
771.1Sjmcneill
781.1Sjmcneillstatic int	rtcsram_match(device_t, cfdata_t, void *);
791.1Sjmcneillstatic void	rtcsram_attach(device_t, device_t, void *);
801.1Sjmcneill
811.1Sjmcneillstatic uint32_t	rtcsram_read_4(struct rtcsram_softc *, uint32_t);
821.1Sjmcneillstatic void	rtcsram_write_4(struct rtcsram_softc *, uint32_t, uint32_t);
831.1Sjmcneillstatic void	rtcsram_read_buf(struct rtcsram_softc *, uint32_t, void *,
841.1Sjmcneill				 size_t);
851.1Sjmcneill
861.1Sjmcneillstatic int	rtcsram_gettime(todr_chip_handle_t, struct timeval *);
871.1Sjmcneillstatic int	rtcsram_settime(todr_chip_handle_t, struct timeval *);
881.1Sjmcneill
891.1SjmcneillCFATTACH_DECL_NEW(rtcsram, sizeof(struct rtcsram_softc),
901.1Sjmcneill	rtcsram_match, rtcsram_attach, NULL, NULL);
911.1Sjmcneill
921.1Sjmcneillstatic int
931.1Sjmcneillrtcsram_match(device_t parent, cfdata_t cf, void *aux)
941.1Sjmcneill{
951.1Sjmcneill	struct exi_attach_args * const eaa = aux;
961.1Sjmcneill
971.1Sjmcneill	return eaa->eaa_id == WII_RTCSRAM_ID;
981.1Sjmcneill}
991.1Sjmcneill
1001.1Sjmcneillstatic void
1011.1Sjmcneillrtcsram_attach(device_t parent, device_t self, void *aux)
1021.1Sjmcneill{
1031.1Sjmcneill	struct rtcsram_softc * const sc = device_private(self);
1041.1Sjmcneill	struct exi_attach_args * const eaa = aux;
1051.1Sjmcneill
1061.1Sjmcneill	aprint_naive("\n");
1071.1Sjmcneill	aprint_normal(": RTC/SRAM\n");
1081.1Sjmcneill
1091.1Sjmcneill	sc->sc_dev = self;
1101.1Sjmcneill	sc->sc_chan = eaa->eaa_chan;
1111.1Sjmcneill	sc->sc_device = eaa->eaa_device;
1121.1Sjmcneill
1131.1Sjmcneill	rtcsram_read_buf(sc, SRAM_BASE, &sc->sc_sram, sizeof(sc->sc_sram));
1141.1Sjmcneill	hexdump(aprint_debug, device_xname(self), &sc->sc_sram,
1151.1Sjmcneill	    sizeof(sc->sc_sram));
1161.1Sjmcneill
1171.1Sjmcneill	sc->sc_todr.todr_dev = self;
1181.1Sjmcneill	sc->sc_todr.todr_gettime = rtcsram_gettime;
1191.1Sjmcneill	sc->sc_todr.todr_settime = rtcsram_settime;
1201.1Sjmcneill	todr_attach(&sc->sc_todr);
1211.1Sjmcneill}
1221.1Sjmcneill
1231.1Sjmcneillstatic uint32_t
1241.1Sjmcneillrtcsram_read_4(struct rtcsram_softc *sc, uint32_t offset)
1251.1Sjmcneill{
1261.1Sjmcneill	uint32_t val;
1271.1Sjmcneill
1281.1Sjmcneill	exi_select(sc->sc_chan, sc->sc_device, WII_RTCSRAM_FREQ);
1291.1Sjmcneill	exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset));
1301.1Sjmcneill	exi_recv_imm(sc->sc_chan, sc->sc_device, &val, sizeof(val));
1311.1Sjmcneill	exi_unselect(sc->sc_chan);
1321.1Sjmcneill
1331.1Sjmcneill	return val;
1341.1Sjmcneill}
1351.1Sjmcneill
1361.1Sjmcneillstatic void
1371.1Sjmcneillrtcsram_write_4(struct rtcsram_softc *sc, uint32_t offset, uint32_t val)
1381.1Sjmcneill{
1391.1Sjmcneill	offset |= WRITE_OFFSET;
1401.1Sjmcneill
1411.1Sjmcneill	exi_select(sc->sc_chan, sc->sc_device, WII_RTCSRAM_FREQ);
1421.1Sjmcneill	exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset));
1431.1Sjmcneill	exi_send_imm(sc->sc_chan, sc->sc_device, &val, sizeof(val));
1441.1Sjmcneill	exi_unselect(sc->sc_chan);
1451.1Sjmcneill}
1461.1Sjmcneill
1471.1Sjmcneillstatic void
1481.1Sjmcneillrtcsram_read_buf(struct rtcsram_softc *sc, uint32_t offset, void *data,
1491.1Sjmcneill    size_t datalen)
1501.1Sjmcneill{
1511.1Sjmcneill	exi_select(sc->sc_chan, sc->sc_device, WII_RTCSRAM_FREQ);
1521.1Sjmcneill	exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset));
1531.1Sjmcneill	exi_recv_dma(sc->sc_chan, sc->sc_device, data, datalen);
1541.1Sjmcneill	exi_unselect(sc->sc_chan);
1551.1Sjmcneill}
1561.1Sjmcneill
1571.1Sjmcneillstatic int
1581.1Sjmcneillrtcsram_gettime(todr_chip_handle_t ch, struct timeval *tv)
1591.1Sjmcneill{
1601.1Sjmcneill	struct rtcsram_softc * const sc = device_private(ch->todr_dev);
1611.1Sjmcneill	uint32_t val;
1621.1Sjmcneill
1631.1Sjmcneill	val = rtcsram_read_4(sc, RTC_BASE);
1641.1Sjmcneill	tv->tv_sec = (uint64_t)val;
1651.1Sjmcneill	tv->tv_usec = 0;
1661.1Sjmcneill
1671.1Sjmcneill	return 0;
1681.1Sjmcneill}
1691.1Sjmcneill
1701.1Sjmcneillstatic int
1711.1Sjmcneillrtcsram_settime(todr_chip_handle_t ch, struct timeval *tv)
1721.1Sjmcneill{
1731.1Sjmcneill	struct rtcsram_softc * const sc = device_private(ch->todr_dev);
1741.1Sjmcneill
1751.1Sjmcneill	rtcsram_write_4(sc, RTC_BASE, tv->tv_sec);
1761.1Sjmcneill
1771.1Sjmcneill	return 0;
1781.1Sjmcneill}
179