1 /* $NetBSD: rtcsram.c,v 1.2 2026/02/03 11:47:18 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2024-2026 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: rtcsram.c,v 1.2 2026/02/03 11:47:18 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 <dev/clock_subr.h> 37 38 #include <lib/libkern/libkern.h> 39 40 #include <machine/wii.h> 41 #include <machine/wiiu.h> 42 43 #include "exi.h" 44 45 #define WII_RTCSRAM_FREQ EXI_FREQ_8MHZ 46 47 #define RTC_BASE 0x20000000 48 #define SRAM_BASE 0x20000100 49 #define SRAM_BIAS 0x20000400 50 51 #define WRITE_OFFSET 0x80000000 52 53 struct rtcsram_sram { 54 uint16_t checksum[2]; 55 uint32_t ead[2]; 56 int32_t counter_bias; 57 int8_t display_offset_h; 58 uint8_t ntd; 59 uint8_t language; 60 uint8_t flags; 61 uint16_t flash_id[12]; 62 uint32_t wireless_keyboard_id; 63 uint32_t wireless_pad_id[2]; 64 uint8_t last_dvd_errorcode; 65 uint8_t padding1; 66 uint16_t flash_id_checksum[2]; 67 uint16_t padding2; 68 } __aligned(32); 69 CTASSERT(sizeof(struct rtcsram_sram) == 64); 70 71 struct rtcsram_softc { 72 device_t sc_dev; 73 struct todr_chip_handle sc_todr; 74 75 uint8_t sc_chan; 76 uint8_t sc_device; 77 78 struct rtcsram_sram sc_sram; 79 }; 80 81 static int rtcsram_match(device_t, cfdata_t, void *); 82 static void rtcsram_attach(device_t, device_t, void *); 83 84 static uint32_t rtcsram_read_4(struct rtcsram_softc *, uint32_t); 85 static void rtcsram_write_4(struct rtcsram_softc *, uint32_t, uint32_t); 86 static void rtcsram_read_buf(struct rtcsram_softc *, uint32_t, void *, 87 size_t); 88 89 static int rtcsram_gettime_nobias(todr_chip_handle_t, struct timeval *); 90 static int rtcsram_settime_nobias(todr_chip_handle_t, struct timeval *); 91 static int rtcsram_gettime_bias(todr_chip_handle_t, struct timeval *); 92 static int rtcsram_settime_bias(todr_chip_handle_t, struct timeval *); 93 94 CFATTACH_DECL_NEW(rtcsram, sizeof(struct rtcsram_softc), 95 rtcsram_match, rtcsram_attach, NULL, NULL); 96 97 static int 98 rtcsram_match(device_t parent, cfdata_t cf, void *aux) 99 { 100 struct exi_attach_args * const eaa = aux; 101 102 /* RTC / SRAM is always on channel 0, device 1. */ 103 return eaa->eaa_chan == 0 && eaa->eaa_device == 1; 104 } 105 106 static void 107 rtcsram_attach(device_t parent, device_t self, void *aux) 108 { 109 struct rtcsram_softc * const sc = device_private(self); 110 struct exi_attach_args * const eaa = aux; 111 112 aprint_naive("\n"); 113 aprint_normal(": RTC/SRAM\n"); 114 115 sc->sc_dev = self; 116 sc->sc_chan = eaa->eaa_chan; 117 sc->sc_device = eaa->eaa_device; 118 119 rtcsram_read_buf(sc, SRAM_BASE, &sc->sc_sram, sizeof(sc->sc_sram)); 120 hexdump(aprint_debug, device_xname(self), &sc->sc_sram, 121 sizeof(sc->sc_sram)); 122 123 sc->sc_todr.todr_dev = self; 124 if (wiiu_plat) { 125 sc->sc_todr.todr_gettime = rtcsram_gettime_bias; 126 sc->sc_todr.todr_settime = rtcsram_settime_bias; 127 } else { 128 sc->sc_todr.todr_gettime = rtcsram_gettime_nobias; 129 sc->sc_todr.todr_settime = rtcsram_settime_nobias; 130 } 131 todr_attach(&sc->sc_todr); 132 } 133 134 static uint32_t 135 rtcsram_read_4(struct rtcsram_softc *sc, uint32_t offset) 136 { 137 uint32_t val; 138 139 exi_select(sc->sc_chan, sc->sc_device, WII_RTCSRAM_FREQ); 140 exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset)); 141 exi_recv_imm(sc->sc_chan, sc->sc_device, &val, sizeof(val)); 142 exi_unselect(sc->sc_chan); 143 144 return val; 145 } 146 147 static void 148 rtcsram_write_4(struct rtcsram_softc *sc, uint32_t offset, uint32_t val) 149 { 150 offset |= WRITE_OFFSET; 151 152 exi_select(sc->sc_chan, sc->sc_device, WII_RTCSRAM_FREQ); 153 exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset)); 154 exi_send_imm(sc->sc_chan, sc->sc_device, &val, sizeof(val)); 155 exi_unselect(sc->sc_chan); 156 } 157 158 static void 159 rtcsram_read_buf(struct rtcsram_softc *sc, uint32_t offset, void *data, 160 size_t datalen) 161 { 162 exi_select(sc->sc_chan, sc->sc_device, WII_RTCSRAM_FREQ); 163 exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset)); 164 exi_recv_dma(sc->sc_chan, sc->sc_device, data, datalen); 165 exi_unselect(sc->sc_chan); 166 } 167 168 static int 169 rtcsram_gettime_nobias(todr_chip_handle_t ch, struct timeval *tv) 170 { 171 struct rtcsram_softc * const sc = device_private(ch->todr_dev); 172 uint32_t val; 173 174 val = rtcsram_read_4(sc, RTC_BASE); 175 tv->tv_sec = (uint64_t)val; 176 tv->tv_usec = 0; 177 178 return 0; 179 } 180 181 static int 182 rtcsram_settime_nobias(todr_chip_handle_t ch, struct timeval *tv) 183 { 184 struct rtcsram_softc * const sc = device_private(ch->todr_dev); 185 186 rtcsram_write_4(sc, RTC_BASE, tv->tv_sec); 187 188 return 0; 189 } 190 191 static int 192 rtcsram_gettime_bias(todr_chip_handle_t ch, struct timeval *tv) 193 { 194 struct rtcsram_softc * const sc = device_private(ch->todr_dev); 195 uint32_t val; 196 197 val = rtcsram_read_4(sc, RTC_BASE); 198 tv->tv_sec = (uint64_t)val + sc->sc_sram.counter_bias; 199 tv->tv_usec = 0; 200 201 return 0; 202 } 203 204 static int 205 rtcsram_settime_bias(todr_chip_handle_t ch, struct timeval *tv) 206 { 207 struct rtcsram_softc * const sc = device_private(ch->todr_dev); 208 209 sc->sc_sram.counter_bias = tv->tv_sec - rtcsram_read_4(sc, RTC_BASE); 210 rtcsram_write_4(sc, SRAM_BIAS, sc->sc_sram.counter_bias); 211 212 return 0; 213 } 214