xref: /netbsd-src/sys/dev/goldfish/gfrtc.c (revision 4b71250eb0b8a729278229c676eb1b659e8d2767)
1 /*	$NetBSD: gfrtc.c,v 1.5 2024/03/05 11:19:30 isaki Exp $	*/
2 
3 /*-
4  * Copyright (c) 2023 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Nick Hudson and by Jason R. Thorpe.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: gfrtc.c,v 1.5 2024/03/05 11:19:30 isaki Exp $");
34 
35 #include <sys/param.h>
36 
37 #include <sys/bus.h>
38 #include <sys/device.h>
39 
40 #include <dev/goldfish/gfrtcvar.h>
41 
42 #define	GOLDFISH_RTC_TIME_LOW		0x00
43 #define	GOLDFISH_RTC_TIME_HIGH		0x04
44 #define	GOLDFISH_RTC_ALARM_LOW		0x08
45 #define	GOLDFISH_RTC_ALARM_HIGH		0x0c
46 #define	GOLDFISH_RTC_IRQ_ENABLED	0x10
47 #define	GOLDFISH_RTC_CLEAR_ALARM	0x14
48 #define	GOLDFISH_RTC_ALARM_STATUS	0x18
49 #define	GOLDFISH_RTC_CLEAR_INTERRUPT	0x1c
50 
51 /*
52  * https://android.googlesource.com/platform/external/qemu/+/master/docs/GOLDFISH-VIRTUAL-HARDWARE.TXT
53  *
54  * (Despite what the Google docs say, the Qemu goldfish-rtc implements
55  * the timer functionality.)
56  */
57 
58 #define	GOLDFISH_RTC_READ(sc, reg) \
59 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
60 #define	GOLDFISH_RTC_WRITE(sc, reg, val) \
61 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
62 
63 static int
gfrtc_gettime(struct todr_chip_handle * ch,struct timeval * tv)64 gfrtc_gettime(struct todr_chip_handle *ch, struct timeval *tv)
65 {
66 	struct gfrtc_softc *sc = ch->cookie;
67 	const uint64_t nsec = gfrtc_get_time(sc);
68 
69 	tv->tv_sec = nsec / 1000000000;
70 	tv->tv_usec = (nsec - tv->tv_sec * 1000000000) / 1000;
71 
72 	return 0;
73 }
74 
75 static int
gfrtc_settime(struct todr_chip_handle * ch,struct timeval * tv)76 gfrtc_settime(struct todr_chip_handle *ch, struct timeval *tv)
77 {
78 	struct gfrtc_softc *sc = ch->cookie;
79 
80 	const uint64_t nsec = (tv->tv_sec * 1000000 + tv->tv_usec) * 1000;
81 	const uint32_t hi = (uint32_t)(nsec >> 32);
82 	const uint32_t lo = (uint32_t)nsec;
83 
84 	GOLDFISH_RTC_WRITE(sc, GOLDFISH_RTC_TIME_HIGH, hi);
85 	GOLDFISH_RTC_WRITE(sc, GOLDFISH_RTC_TIME_LOW, lo);
86 
87 	return 0;
88 }
89 
90 
91 void
gfrtc_attach(struct gfrtc_softc * const sc,bool todr)92 gfrtc_attach(struct gfrtc_softc * const sc, bool todr)
93 {
94 
95 	aprint_naive("\n");
96 	aprint_normal(": Google Goldfish RTC + timer\n");
97 
98 	/* Cancel any alarms, make sure interrupt is disabled. */
99 	GOLDFISH_RTC_WRITE(sc, GOLDFISH_RTC_IRQ_ENABLED, 0);
100 	GOLDFISH_RTC_WRITE(sc, GOLDFISH_RTC_CLEAR_ALARM, 0);
101 	GOLDFISH_RTC_WRITE(sc, GOLDFISH_RTC_CLEAR_INTERRUPT, 0);
102 
103 	if (todr) {
104 		aprint_normal_dev(sc->sc_dev,
105 		    "using as Time of Day Register.\n");
106 		sc->sc_todr.cookie = sc;
107 		sc->sc_todr.todr_gettime = gfrtc_gettime;
108 		sc->sc_todr.todr_settime = gfrtc_settime;
109 		sc->sc_todr.todr_setwen = NULL;
110 		todr_attach(&sc->sc_todr);
111 	}
112 }
113 
114 
115 void
gfrtc_delay(device_t dev,unsigned int usec)116 gfrtc_delay(device_t dev, unsigned int usec)
117 {
118 	struct gfrtc_softc *sc = device_private(dev);
119 	uint64_t start_ns, end_ns, min_ns;
120 
121 	/* Get the start time now while we do the setup work. */
122 	start_ns = gfrtc_get_time(sc);
123 
124 	/* Delay for this many nsec. */
125 	min_ns = (uint64_t)usec * 1000;
126 
127 	do {
128 		end_ns = gfrtc_get_time(sc);
129 	} while ((end_ns - start_ns) < min_ns);
130 }
131 
132 
133 uint64_t
gfrtc_get_time(struct gfrtc_softc * const sc)134 gfrtc_get_time(struct gfrtc_softc * const sc)
135 {
136 	const uint64_t lo = GOLDFISH_RTC_READ(sc, GOLDFISH_RTC_TIME_LOW);
137 	const uint64_t hi = GOLDFISH_RTC_READ(sc, GOLDFISH_RTC_TIME_HIGH);
138 
139 	return (hi << 32) | lo;
140 }
141 
142 
143 void
gfrtc_set_alarm(struct gfrtc_softc * const sc,const uint64_t next)144 gfrtc_set_alarm(struct gfrtc_softc * const sc, const uint64_t next)
145 {
146 	GOLDFISH_RTC_WRITE(sc, GOLDFISH_RTC_ALARM_HIGH, (uint32_t)(next >> 32));
147 	GOLDFISH_RTC_WRITE(sc, GOLDFISH_RTC_ALARM_LOW, (uint32_t)next);
148 }
149 
150 
151 void
gfrtc_clear_alarm(struct gfrtc_softc * const sc)152 gfrtc_clear_alarm(struct gfrtc_softc * const sc)
153 {
154 	GOLDFISH_RTC_WRITE(sc, GOLDFISH_RTC_CLEAR_ALARM, 0);
155 }
156 
157 
158 void
gfrtc_enable_interrupt(struct gfrtc_softc * const sc)159 gfrtc_enable_interrupt(struct gfrtc_softc * const sc)
160 {
161 	GOLDFISH_RTC_WRITE(sc, GOLDFISH_RTC_IRQ_ENABLED, 1);
162 }
163 
164 
165 void
gfrtc_disable_interrupt(struct gfrtc_softc * const sc)166 gfrtc_disable_interrupt(struct gfrtc_softc * const sc)
167 {
168 	GOLDFISH_RTC_WRITE(sc, GOLDFISH_RTC_IRQ_ENABLED, 0);
169 }
170 
171 
172 void
gfrtc_clear_interrupt(struct gfrtc_softc * const sc)173 gfrtc_clear_interrupt(struct gfrtc_softc * const sc)
174 {
175 	GOLDFISH_RTC_WRITE(sc, GOLDFISH_RTC_CLEAR_INTERRUPT, 0);
176 }
177