rtcsram.c revision 1.3 1 /* $NetBSD: rtcsram.c,v 1.3 2025/09/07 21:45:13 thorpej 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: rtcsram.c,v 1.3 2025/09/07 21:45:13 thorpej 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 device_t sc_dev;
70 struct todr_chip_handle sc_todr;
71
72 uint8_t sc_chan;
73 uint8_t sc_device;
74
75 struct rtcsram_sram sc_sram;
76 };
77
78 static int rtcsram_match(device_t, cfdata_t, void *);
79 static void rtcsram_attach(device_t, device_t, void *);
80
81 static uint32_t rtcsram_read_4(struct rtcsram_softc *, uint32_t);
82 static void rtcsram_write_4(struct rtcsram_softc *, uint32_t, uint32_t);
83 static void rtcsram_read_buf(struct rtcsram_softc *, uint32_t, void *,
84 size_t);
85
86 static int rtcsram_gettime(todr_chip_handle_t, struct timeval *);
87 static int rtcsram_settime(todr_chip_handle_t, struct timeval *);
88
89 CFATTACH_DECL_NEW(rtcsram, sizeof(struct rtcsram_softc),
90 rtcsram_match, rtcsram_attach, NULL, NULL);
91
92 static int
93 rtcsram_match(device_t parent, cfdata_t cf, void *aux)
94 {
95 struct exi_attach_args * const eaa = aux;
96
97 return eaa->eaa_id == WII_RTCSRAM_ID;
98 }
99
100 static void
101 rtcsram_attach(device_t parent, device_t self, void *aux)
102 {
103 struct rtcsram_softc * const sc = device_private(self);
104 struct exi_attach_args * const eaa = aux;
105
106 aprint_naive("\n");
107 aprint_normal(": RTC/SRAM\n");
108
109 sc->sc_dev = self;
110 sc->sc_chan = eaa->eaa_chan;
111 sc->sc_device = eaa->eaa_device;
112
113 /* Read RTC counter bias from SRAM. */
114 rtcsram_read_buf(sc, SRAM_BASE, &sc->sc_sram, sizeof(sc->sc_sram));
115 aprint_debug_dev(self, "counter bias %d\n", sc->sc_sram.counter_bias);
116 hexdump(aprint_debug, device_xname(self), &sc->sc_sram,
117 sizeof(sc->sc_sram));
118
119 sc->sc_todr.todr_dev = self;
120 sc->sc_todr.todr_gettime = rtcsram_gettime;
121 sc->sc_todr.todr_settime = rtcsram_settime;
122 todr_attach(&sc->sc_todr);
123 }
124
125 static uint32_t
126 rtcsram_read_4(struct rtcsram_softc *sc, uint32_t offset)
127 {
128 uint32_t val;
129
130 exi_select(sc->sc_chan, sc->sc_device, WII_RTCSRAM_FREQ);
131 exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset));
132 exi_recv_imm(sc->sc_chan, sc->sc_device, &val, sizeof(val));
133 exi_unselect(sc->sc_chan);
134
135 return val;
136 }
137
138 static void
139 rtcsram_write_4(struct rtcsram_softc *sc, uint32_t offset, uint32_t val)
140 {
141 offset |= WRITE_OFFSET;
142
143 exi_select(sc->sc_chan, sc->sc_device, WII_RTCSRAM_FREQ);
144 exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset));
145 exi_send_imm(sc->sc_chan, sc->sc_device, &val, sizeof(val));
146 exi_unselect(sc->sc_chan);
147 }
148
149 static void
150 rtcsram_read_buf(struct rtcsram_softc *sc, uint32_t offset, void *data,
151 size_t datalen)
152 {
153 exi_select(sc->sc_chan, sc->sc_device, WII_RTCSRAM_FREQ);
154 exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset));
155 exi_recv_dma(sc->sc_chan, sc->sc_device, data, datalen);
156 exi_unselect(sc->sc_chan);
157 }
158
159 static int
160 rtcsram_gettime(todr_chip_handle_t ch, struct timeval *tv)
161 {
162 struct rtcsram_softc * const sc = device_private(ch->todr_dev);
163 uint32_t val;
164
165 val = rtcsram_read_4(sc, RTC_BASE);
166 tv->tv_sec = (uint64_t)val + sc->sc_sram.counter_bias;
167 tv->tv_usec = 0;
168
169 return 0;
170 }
171
172 static int
173 rtcsram_settime(todr_chip_handle_t ch, struct timeval *tv)
174 {
175 struct rtcsram_softc * const sc = device_private(ch->todr_dev);
176
177 rtcsram_write_4(sc, RTC_BASE, tv->tv_sec - sc->sc_sram.counter_bias);
178
179 return 0;
180 }
181