1*abdf7c82Sjmcneill /* $NetBSD: rtcsram.c,v 1.2 2024/02/10 11:00:15 jmcneill Exp $ */
2c0f93ec1Sjmcneill
3c0f93ec1Sjmcneill /*-
4c0f93ec1Sjmcneill * Copyright (c) 2024 Jared McNeill <jmcneill@invisible.ca>
5c0f93ec1Sjmcneill * All rights reserved.
6c0f93ec1Sjmcneill *
7c0f93ec1Sjmcneill * Redistribution and use in source and binary forms, with or without
8c0f93ec1Sjmcneill * modification, are permitted provided that the following conditions
9c0f93ec1Sjmcneill * are met:
10c0f93ec1Sjmcneill * 1. Redistributions of source code must retain the above copyright
11c0f93ec1Sjmcneill * notice, this list of conditions and the following disclaimer.
12c0f93ec1Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
13c0f93ec1Sjmcneill * notice, this list of conditions and the following disclaimer in the
14c0f93ec1Sjmcneill * documentation and/or other materials provided with the distribution.
15c0f93ec1Sjmcneill *
16c0f93ec1Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17c0f93ec1Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18c0f93ec1Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19c0f93ec1Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20c0f93ec1Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21c0f93ec1Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22c0f93ec1Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23c0f93ec1Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24c0f93ec1Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25c0f93ec1Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26c0f93ec1Sjmcneill * SUCH DAMAGE.
27c0f93ec1Sjmcneill */
28c0f93ec1Sjmcneill
29c0f93ec1Sjmcneill #include <sys/cdefs.h>
30*abdf7c82Sjmcneill __KERNEL_RCSID(0, "$NetBSD: rtcsram.c,v 1.2 2024/02/10 11:00:15 jmcneill Exp $");
31c0f93ec1Sjmcneill
32c0f93ec1Sjmcneill #include <sys/param.h>
33c0f93ec1Sjmcneill #include <sys/bus.h>
34c0f93ec1Sjmcneill #include <sys/device.h>
35c0f93ec1Sjmcneill #include <sys/systm.h>
36c0f93ec1Sjmcneill #include <dev/clock_subr.h>
37c0f93ec1Sjmcneill
38c0f93ec1Sjmcneill #include <lib/libkern/libkern.h>
39c0f93ec1Sjmcneill
40c0f93ec1Sjmcneill #include "exi.h"
41c0f93ec1Sjmcneill
42c0f93ec1Sjmcneill #define WII_RTCSRAM_ID 0xfffff308
43*abdf7c82Sjmcneill #define WII_RTCSRAM_FREQ EXI_FREQ_8MHZ
44c0f93ec1Sjmcneill
45c0f93ec1Sjmcneill #define RTC_BASE 0x20000000
46c0f93ec1Sjmcneill #define SRAM_BASE 0x20000100
47c0f93ec1Sjmcneill
48c0f93ec1Sjmcneill #define WRITE_OFFSET 0x80000000
49c0f93ec1Sjmcneill
50c0f93ec1Sjmcneill struct rtcsram_sram {
51c0f93ec1Sjmcneill uint16_t checksum[2];
52c0f93ec1Sjmcneill uint16_t ead[2];
53c0f93ec1Sjmcneill int32_t counter_bias;
54c0f93ec1Sjmcneill int8_t display_offset_h;
55c0f93ec1Sjmcneill uint8_t ntd;
56c0f93ec1Sjmcneill uint8_t language;
57c0f93ec1Sjmcneill uint8_t flags;
58c0f93ec1Sjmcneill uint16_t flash_id[12];
59c0f93ec1Sjmcneill uint32_t wireless_keyboard_id;
60c0f93ec1Sjmcneill uint32_t wireless_pad_id[2];
61c0f93ec1Sjmcneill uint8_t last_dvd_errorcode;
62c0f93ec1Sjmcneill uint8_t padding1;
63c0f93ec1Sjmcneill uint16_t flash_id_checksum[2];
64c0f93ec1Sjmcneill uint16_t padding2;
65c0f93ec1Sjmcneill } __aligned(32);
66c0f93ec1Sjmcneill CTASSERT(sizeof(struct rtcsram_sram) == 64);
67c0f93ec1Sjmcneill
68c0f93ec1Sjmcneill struct rtcsram_softc {
69c0f93ec1Sjmcneill struct todr_chip_handle sc_todr;
70c0f93ec1Sjmcneill
71c0f93ec1Sjmcneill uint8_t sc_chan;
72c0f93ec1Sjmcneill uint8_t sc_device;
73c0f93ec1Sjmcneill
74c0f93ec1Sjmcneill struct rtcsram_sram sc_sram;
75c0f93ec1Sjmcneill };
76c0f93ec1Sjmcneill
77c0f93ec1Sjmcneill static int rtcsram_match(device_t, cfdata_t, void *);
78c0f93ec1Sjmcneill static void rtcsram_attach(device_t, device_t, void *);
79c0f93ec1Sjmcneill
80c0f93ec1Sjmcneill static uint32_t rtcsram_read_4(struct rtcsram_softc *, uint32_t);
81c0f93ec1Sjmcneill static void rtcsram_write_4(struct rtcsram_softc *, uint32_t, uint32_t);
82c0f93ec1Sjmcneill static void rtcsram_read_buf(struct rtcsram_softc *, uint32_t, void *,
83c0f93ec1Sjmcneill size_t);
84c0f93ec1Sjmcneill
85c0f93ec1Sjmcneill static int rtcsram_gettime(todr_chip_handle_t, struct timeval *);
86c0f93ec1Sjmcneill static int rtcsram_settime(todr_chip_handle_t, struct timeval *);
87c0f93ec1Sjmcneill
88c0f93ec1Sjmcneill CFATTACH_DECL_NEW(rtcsram, sizeof(struct rtcsram_softc),
89c0f93ec1Sjmcneill rtcsram_match, rtcsram_attach, NULL, NULL);
90c0f93ec1Sjmcneill
91c0f93ec1Sjmcneill static int
rtcsram_match(device_t parent,cfdata_t cf,void * aux)92c0f93ec1Sjmcneill rtcsram_match(device_t parent, cfdata_t cf, void *aux)
93c0f93ec1Sjmcneill {
94c0f93ec1Sjmcneill struct exi_attach_args * const eaa = aux;
95c0f93ec1Sjmcneill
96c0f93ec1Sjmcneill return eaa->eaa_id == WII_RTCSRAM_ID;
97c0f93ec1Sjmcneill }
98c0f93ec1Sjmcneill
99c0f93ec1Sjmcneill static void
rtcsram_attach(device_t parent,device_t self,void * aux)100c0f93ec1Sjmcneill rtcsram_attach(device_t parent, device_t self, void *aux)
101c0f93ec1Sjmcneill {
102c0f93ec1Sjmcneill struct rtcsram_softc * const sc = device_private(self);
103c0f93ec1Sjmcneill struct exi_attach_args * const eaa = aux;
104c0f93ec1Sjmcneill
105c0f93ec1Sjmcneill aprint_naive("\n");
106c0f93ec1Sjmcneill aprint_normal(": RTC/SRAM\n");
107c0f93ec1Sjmcneill
108c0f93ec1Sjmcneill sc->sc_chan = eaa->eaa_chan;
109c0f93ec1Sjmcneill sc->sc_device = eaa->eaa_device;
110c0f93ec1Sjmcneill
111c0f93ec1Sjmcneill /* Read RTC counter bias from SRAM. */
112c0f93ec1Sjmcneill rtcsram_read_buf(sc, SRAM_BASE, &sc->sc_sram, sizeof(sc->sc_sram));
113c0f93ec1Sjmcneill aprint_debug_dev(self, "counter bias %d\n", sc->sc_sram.counter_bias);
114c0f93ec1Sjmcneill hexdump(aprint_debug, device_xname(self), &sc->sc_sram,
115c0f93ec1Sjmcneill sizeof(sc->sc_sram));
116c0f93ec1Sjmcneill
117c0f93ec1Sjmcneill sc->sc_todr.cookie = sc;
118c0f93ec1Sjmcneill sc->sc_todr.todr_gettime = rtcsram_gettime;
119c0f93ec1Sjmcneill sc->sc_todr.todr_settime = rtcsram_settime;
120c0f93ec1Sjmcneill todr_attach(&sc->sc_todr);
121c0f93ec1Sjmcneill }
122c0f93ec1Sjmcneill
123c0f93ec1Sjmcneill static uint32_t
rtcsram_read_4(struct rtcsram_softc * sc,uint32_t offset)124c0f93ec1Sjmcneill rtcsram_read_4(struct rtcsram_softc *sc, uint32_t offset)
125c0f93ec1Sjmcneill {
126c0f93ec1Sjmcneill uint32_t val;
127c0f93ec1Sjmcneill
128*abdf7c82Sjmcneill exi_select(sc->sc_chan, sc->sc_device, WII_RTCSRAM_FREQ);
129c0f93ec1Sjmcneill exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset));
130c0f93ec1Sjmcneill exi_recv_imm(sc->sc_chan, sc->sc_device, &val, sizeof(val));
131c0f93ec1Sjmcneill exi_unselect(sc->sc_chan);
132c0f93ec1Sjmcneill
133c0f93ec1Sjmcneill return val;
134c0f93ec1Sjmcneill }
135c0f93ec1Sjmcneill
136c0f93ec1Sjmcneill static void
rtcsram_write_4(struct rtcsram_softc * sc,uint32_t offset,uint32_t val)137c0f93ec1Sjmcneill rtcsram_write_4(struct rtcsram_softc *sc, uint32_t offset, uint32_t val)
138c0f93ec1Sjmcneill {
139c0f93ec1Sjmcneill offset |= WRITE_OFFSET;
140c0f93ec1Sjmcneill
141*abdf7c82Sjmcneill exi_select(sc->sc_chan, sc->sc_device, WII_RTCSRAM_FREQ);
142c0f93ec1Sjmcneill exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset));
143c0f93ec1Sjmcneill exi_send_imm(sc->sc_chan, sc->sc_device, &val, sizeof(val));
144c0f93ec1Sjmcneill exi_unselect(sc->sc_chan);
145c0f93ec1Sjmcneill }
146c0f93ec1Sjmcneill
147c0f93ec1Sjmcneill static void
rtcsram_read_buf(struct rtcsram_softc * sc,uint32_t offset,void * data,size_t datalen)148c0f93ec1Sjmcneill rtcsram_read_buf(struct rtcsram_softc *sc, uint32_t offset, void *data,
149c0f93ec1Sjmcneill size_t datalen)
150c0f93ec1Sjmcneill {
151*abdf7c82Sjmcneill exi_select(sc->sc_chan, sc->sc_device, WII_RTCSRAM_FREQ);
152c0f93ec1Sjmcneill exi_send_imm(sc->sc_chan, sc->sc_device, &offset, sizeof(offset));
153c0f93ec1Sjmcneill exi_recv_dma(sc->sc_chan, sc->sc_device, data, datalen);
154c0f93ec1Sjmcneill exi_unselect(sc->sc_chan);
155c0f93ec1Sjmcneill }
156c0f93ec1Sjmcneill
157c0f93ec1Sjmcneill static int
rtcsram_gettime(todr_chip_handle_t ch,struct timeval * tv)158c0f93ec1Sjmcneill rtcsram_gettime(todr_chip_handle_t ch, struct timeval *tv)
159c0f93ec1Sjmcneill {
160c0f93ec1Sjmcneill struct rtcsram_softc * const sc = ch->cookie;
161c0f93ec1Sjmcneill uint32_t val;
162c0f93ec1Sjmcneill
163c0f93ec1Sjmcneill val = rtcsram_read_4(sc, RTC_BASE);
164c0f93ec1Sjmcneill tv->tv_sec = (uint64_t)val + sc->sc_sram.counter_bias;
165c0f93ec1Sjmcneill tv->tv_usec = 0;
166c0f93ec1Sjmcneill
167c0f93ec1Sjmcneill return 0;
168c0f93ec1Sjmcneill }
169c0f93ec1Sjmcneill
170c0f93ec1Sjmcneill static int
rtcsram_settime(todr_chip_handle_t ch,struct timeval * tv)171c0f93ec1Sjmcneill rtcsram_settime(todr_chip_handle_t ch, struct timeval *tv)
172c0f93ec1Sjmcneill {
173c0f93ec1Sjmcneill struct rtcsram_softc * const sc = ch->cookie;
174c0f93ec1Sjmcneill
175c0f93ec1Sjmcneill rtcsram_write_4(sc, RTC_BASE, tv->tv_sec - sc->sc_sram.counter_bias);
176c0f93ec1Sjmcneill
177c0f93ec1Sjmcneill return 0;
178c0f93ec1Sjmcneill }
179