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
63static int
64gfrtc_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
75static int
76gfrtc_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
91void
92gfrtc_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
115void
116gfrtc_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
133uint64_t
134gfrtc_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
143void
144gfrtc_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
151void
152gfrtc_clear_alarm(struct gfrtc_softc * const sc)
153{
154	GOLDFISH_RTC_WRITE(sc, GOLDFISH_RTC_CLEAR_ALARM, 0);
155}
156
157
158void
159gfrtc_enable_interrupt(struct gfrtc_softc * const sc)
160{
161	GOLDFISH_RTC_WRITE(sc, GOLDFISH_RTC_IRQ_ENABLED, 1);
162}
163
164
165void
166gfrtc_disable_interrupt(struct gfrtc_softc * const sc)
167{
168	GOLDFISH_RTC_WRITE(sc, GOLDFISH_RTC_IRQ_ENABLED, 0);
169}
170
171
172void
173gfrtc_clear_interrupt(struct gfrtc_softc * const sc)
174{
175	GOLDFISH_RTC_WRITE(sc, GOLDFISH_RTC_CLEAR_INTERRUPT, 0);
176}
177