1 /* $NetBSD: rtcsram.c,v 1.2 2024/02/10 11:00:15 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2024 Jared McNeill <jmcneill@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 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 <dev/clock_subr.h> 37 38 #include <lib/libkern/libkern.h> 39 40 #include "exi.h" 41 42 #define WII_RTCSRAM_ID 0xfffff308 43 #define WII_RTCSRAM_FREQ EXI_FREQ_8MHZ 44 45 #define RTC_BASE 0x20000000 46 #define SRAM_BASE 0x20000100 47 48 #define WRITE_OFFSET 0x80000000 49 50 struct rtcsram_sram { 51 uint16_t checksum[2]; 52 uint16_t ead[2]; 53 int32_t counter_bias; 54 int8_t display_offset_h; 55 uint8_t ntd; 56 uint8_t language; 57 uint8_t flags; 58 uint16_t flash_id[12]; 59 uint32_t wireless_keyboard_id; 60 uint32_t wireless_pad_id[2]; 61 uint8_t last_dvd_errorcode; 62 uint8_t padding1; 63 uint16_t flash_id_checksum[2]; 64 uint16_t padding2; 65 } __aligned(32); 66 CTASSERT(sizeof(struct rtcsram_sram) == 64); 67 68 struct rtcsram_softc { 69 struct todr_chip_handle sc_todr; 70 71 uint8_t sc_chan; 72 uint8_t sc_device; 73 74 struct rtcsram_sram sc_sram; 75 }; 76 77 static int rtcsram_match(device_t, cfdata_t, void *); 78 static void rtcsram_attach(device_t, device_t, void *); 79 80 static uint32_t rtcsram_read_4(struct rtcsram_softc *, uint32_t); 81 static void rtcsram_write_4(struct rtcsram_softc *, uint32_t, uint32_t); 82 static void rtcsram_read_buf(struct rtcsram_softc *, uint32_t, void *, 83 size_t); 84 85 static int rtcsram_gettime(todr_chip_handle_t, struct timeval *); 86 static int rtcsram_settime(todr_chip_handle_t, struct timeval *); 87 88 CFATTACH_DECL_NEW(rtcsram, sizeof(struct rtcsram_softc), 89 rtcsram_match, rtcsram_attach, NULL, NULL); 90 91 static int 92 rtcsram_match(device_t parent, cfdata_t cf, void *aux) 93 { 94 struct exi_attach_args * const eaa = aux; 95 96 return eaa->eaa_id == WII_RTCSRAM_ID; 97 } 98 99 static void 100 rtcsram_attach(device_t parent, device_t self, void *aux) 101 { 102 struct rtcsram_softc * const sc = device_private(self); 103 struct exi_attach_args * const eaa = aux; 104 105 aprint_naive("\n"); 106 aprint_normal(": RTC/SRAM\n"); 107 108 sc->sc_chan = eaa->eaa_chan; 109 sc->sc_device = eaa->eaa_device; 110 111 /* Read RTC counter bias from SRAM. */ 112 rtcsram_read_buf(sc, SRAM_BASE, &sc->sc_sram, sizeof(sc->sc_sram)); 113 aprint_debug_dev(self, "counter bias %d\n", sc->sc_sram.counter_bias); 114 hexdump(aprint_debug, device_xname(self), &sc->sc_sram, 115 sizeof(sc->sc_sram)); 116 117 sc->sc_todr.cookie = sc; 118 sc->sc_todr.todr_gettime = rtcsram_gettime; 119 sc->sc_todr.todr_settime = rtcsram_settime; 120 todr_attach(&sc->sc_todr); 121 } 122 123 static uint32_t 124 rtcsram_read_4(struct rtcsram_softc *sc, uint32_t offset) 125 { 126 uint32_t val; 127 128 exi_select(sc->sc_chan, sc->sc_device, WII_RTCSRAM_FREQ); 129 exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset)); 130 exi_recv_imm(sc->sc_chan, sc->sc_device, &val, sizeof(val)); 131 exi_unselect(sc->sc_chan); 132 133 return val; 134 } 135 136 static void 137 rtcsram_write_4(struct rtcsram_softc *sc, uint32_t offset, uint32_t val) 138 { 139 offset |= WRITE_OFFSET; 140 141 exi_select(sc->sc_chan, sc->sc_device, WII_RTCSRAM_FREQ); 142 exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset)); 143 exi_send_imm(sc->sc_chan, sc->sc_device, &val, sizeof(val)); 144 exi_unselect(sc->sc_chan); 145 } 146 147 static void 148 rtcsram_read_buf(struct rtcsram_softc *sc, uint32_t offset, void *data, 149 size_t datalen) 150 { 151 exi_select(sc->sc_chan, sc->sc_device, WII_RTCSRAM_FREQ); 152 exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset)); 153 exi_recv_dma(sc->sc_chan, sc->sc_device, data, datalen); 154 exi_unselect(sc->sc_chan); 155 } 156 157 static int 158 rtcsram_gettime(todr_chip_handle_t ch, struct timeval *tv) 159 { 160 struct rtcsram_softc * const sc = ch->cookie; 161 uint32_t val; 162 163 val = rtcsram_read_4(sc, RTC_BASE); 164 tv->tv_sec = (uint64_t)val + sc->sc_sram.counter_bias; 165 tv->tv_usec = 0; 166 167 return 0; 168 } 169 170 static int 171 rtcsram_settime(todr_chip_handle_t ch, struct timeval *tv) 172 { 173 struct rtcsram_softc * const sc = ch->cookie; 174 175 rtcsram_write_4(sc, RTC_BASE, tv->tv_sec - sc->sc_sram.counter_bias); 176 177 return 0; 178 } 179