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
rtcsram_match(device_t parent,cfdata_t cf,void * aux)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
rtcsram_attach(device_t parent,device_t self,void * aux)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
rtcsram_read_4(struct rtcsram_softc * sc,uint32_t offset)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
rtcsram_write_4(struct rtcsram_softc * sc,uint32_t offset,uint32_t val)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
rtcsram_read_buf(struct rtcsram_softc * sc,uint32_t offset,void * data,size_t datalen)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
rtcsram_gettime(todr_chip_handle_t ch,struct timeval * tv)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
rtcsram_settime(todr_chip_handle_t ch,struct timeval * tv)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