xref: /freebsd/sys/dev/dialog/da9063/da9063_rtc.c (revision 4d846d260e2b9a3d4d0a701462568268cbfe7a5b)
1129028c7SJessica Clarke /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3129028c7SJessica Clarke  *
4129028c7SJessica Clarke  * Copyright (c) 2022 Jessica Clarke <jrtc27@FreeBSD.org>
5129028c7SJessica Clarke  *
6129028c7SJessica Clarke  * Redistribution and use in source and binary forms, with or without
7129028c7SJessica Clarke  * modification, are permitted provided that the following conditions
8129028c7SJessica Clarke  * are met:
9129028c7SJessica Clarke  * 1. Redistributions of source code must retain the above copyright
10129028c7SJessica Clarke  *    notice, this list of conditions and the following disclaimer.
11129028c7SJessica Clarke  * 2. Redistributions in binary form must reproduce the above copyright
12129028c7SJessica Clarke  *    notice, this list of conditions and the following disclaimer in the
13129028c7SJessica Clarke  *    documentation and/or other materials provided with the distribution.
14129028c7SJessica Clarke  *
15129028c7SJessica Clarke  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16129028c7SJessica Clarke  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17129028c7SJessica Clarke  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18129028c7SJessica Clarke  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19129028c7SJessica Clarke  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20129028c7SJessica Clarke  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21129028c7SJessica Clarke  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22129028c7SJessica Clarke  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23129028c7SJessica Clarke  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24129028c7SJessica Clarke  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25129028c7SJessica Clarke  * SUCH DAMAGE.
26129028c7SJessica Clarke  */
27129028c7SJessica Clarke 
28129028c7SJessica Clarke /* Dialog Semiconductor DA9063 RTC */
29129028c7SJessica Clarke 
30129028c7SJessica Clarke #include <sys/param.h>
31129028c7SJessica Clarke #include <sys/kernel.h>
32129028c7SJessica Clarke #include <sys/bus.h>
33129028c7SJessica Clarke #include <sys/clock.h>
34129028c7SJessica Clarke #include <sys/lock.h>
35129028c7SJessica Clarke #include <sys/module.h>
36129028c7SJessica Clarke #include <sys/mutex.h>
37129028c7SJessica Clarke 
38129028c7SJessica Clarke #include <dev/dialog/da9063/da9063reg.h>
39129028c7SJessica Clarke #include <dev/ofw/ofw_bus_subr.h>
40129028c7SJessica Clarke 
41129028c7SJessica Clarke #include "clock_if.h"
42129028c7SJessica Clarke #include "da9063_if.h"
43129028c7SJessica Clarke 
44129028c7SJessica Clarke #define	DA9063_RTC_BASE_YEAR	2000
45129028c7SJessica Clarke 
46129028c7SJessica Clarke struct da9063_rtc_softc {
47129028c7SJessica Clarke 	device_t	dev;
48129028c7SJessica Clarke 	device_t	parent;
49129028c7SJessica Clarke 	struct mtx	mtx;
50129028c7SJessica Clarke };
51129028c7SJessica Clarke 
52129028c7SJessica Clarke #define	DA9063_RTC_LOCK(sc)		mtx_lock(&(sc)->mtx)
53129028c7SJessica Clarke #define	DA9063_RTC_UNLOCK(sc)		mtx_unlock(&(sc)->mtx)
54129028c7SJessica Clarke #define	DA9063_RTC_ASSERT_LOCKED(sc)	mtx_assert(&(sc)->mtx, MA_OWNED);
55129028c7SJessica Clarke #define	DA9063_RTC_ASSERT_UNLOCKED(sc)	mtx_assert(&(sc)->mtx, MA_NOTOWNED);
56129028c7SJessica Clarke 
57129028c7SJessica Clarke static struct ofw_compat_data compat_data[] = {
58129028c7SJessica Clarke 	{ "dlg,da9063-rtc",	1 },
59129028c7SJessica Clarke 	{ NULL,			0 }
60129028c7SJessica Clarke };
61129028c7SJessica Clarke 
62129028c7SJessica Clarke static int
da9063_rtc_read_ct(struct da9063_rtc_softc * sc,struct clocktime * ct)63129028c7SJessica Clarke da9063_rtc_read_ct(struct da9063_rtc_softc *sc, struct clocktime *ct)
64129028c7SJessica Clarke {
65129028c7SJessica Clarke 	uint8_t sec, min, hour, day, mon, year;
66129028c7SJessica Clarke 	int error;
67129028c7SJessica Clarke 
68129028c7SJessica Clarke 	DA9063_RTC_ASSERT_LOCKED(sc)
69129028c7SJessica Clarke 
70129028c7SJessica Clarke 	error = DA9063_READ(sc->parent, DA9063_COUNT_S, &sec);
71129028c7SJessica Clarke 	if (error != 0)
72129028c7SJessica Clarke 		return (error);
73129028c7SJessica Clarke 	if ((sec & DA9063_COUNT_S_RTC_READ) == 0)
74129028c7SJessica Clarke 		return (EAGAIN);
75129028c7SJessica Clarke 
76129028c7SJessica Clarke 	error = DA9063_READ(sc->parent, DA9063_COUNT_MI, &min);
77129028c7SJessica Clarke 	if (error != 0)
78129028c7SJessica Clarke 		return (error);
79129028c7SJessica Clarke 
80129028c7SJessica Clarke 	error = DA9063_READ(sc->parent, DA9063_COUNT_H, &hour);
81129028c7SJessica Clarke 	if (error != 0)
82129028c7SJessica Clarke 		return (error);
83129028c7SJessica Clarke 
84129028c7SJessica Clarke 	error = DA9063_READ(sc->parent, DA9063_COUNT_D, &day);
85129028c7SJessica Clarke 	if (error != 0)
86129028c7SJessica Clarke 		return (error);
87129028c7SJessica Clarke 
88129028c7SJessica Clarke 	error = DA9063_READ(sc->parent, DA9063_COUNT_MO, &mon);
89129028c7SJessica Clarke 	if (error != 0)
90129028c7SJessica Clarke 		return (error);
91129028c7SJessica Clarke 
92129028c7SJessica Clarke 	error = DA9063_READ(sc->parent, DA9063_COUNT_Y, &year);
93129028c7SJessica Clarke 	if (error != 0)
94129028c7SJessica Clarke 		return (error);
95129028c7SJessica Clarke 
96129028c7SJessica Clarke 	ct->nsec = 0;
97129028c7SJessica Clarke 	ct->dow = -1;
98129028c7SJessica Clarke 	ct->sec = sec & DA9063_COUNT_S_COUNT_SEC_MASK;
99129028c7SJessica Clarke 	ct->min = min & DA9063_COUNT_MI_COUNT_MIN_MASK;
100129028c7SJessica Clarke 	ct->hour = hour & DA9063_COUNT_H_COUNT_HOUR_MASK;
101129028c7SJessica Clarke 	ct->day = day & DA9063_COUNT_D_COUNT_DAY_MASK;
102129028c7SJessica Clarke 	ct->mon = mon & DA9063_COUNT_MO_COUNT_MONTH_MASK;
103129028c7SJessica Clarke 	ct->year = (year & DA9063_COUNT_Y_COUNT_YEAR_MASK) +
104129028c7SJessica Clarke 	    DA9063_RTC_BASE_YEAR;
105129028c7SJessica Clarke 
106129028c7SJessica Clarke 	return (0);
107129028c7SJessica Clarke }
108129028c7SJessica Clarke 
109129028c7SJessica Clarke static int
da9063_rtc_write_ct(struct da9063_rtc_softc * sc,struct clocktime * ct)110129028c7SJessica Clarke da9063_rtc_write_ct(struct da9063_rtc_softc *sc, struct clocktime *ct)
111129028c7SJessica Clarke {
112129028c7SJessica Clarke 	int error;
113129028c7SJessica Clarke 
114129028c7SJessica Clarke 	DA9063_RTC_ASSERT_LOCKED(sc)
115129028c7SJessica Clarke 
116129028c7SJessica Clarke 	error = DA9063_WRITE(sc->parent, DA9063_COUNT_S, ct->sec);
117129028c7SJessica Clarke 	if (error != 0)
118129028c7SJessica Clarke 		return (error);
119129028c7SJessica Clarke 
120129028c7SJessica Clarke 	error = DA9063_WRITE(sc->parent, DA9063_COUNT_MI, ct->min);
121129028c7SJessica Clarke 	if (error != 0)
122129028c7SJessica Clarke 		return (error);
123129028c7SJessica Clarke 
124129028c7SJessica Clarke 	error = DA9063_WRITE(sc->parent, DA9063_COUNT_H, ct->hour);
125129028c7SJessica Clarke 	if (error != 0)
126129028c7SJessica Clarke 		return (error);
127129028c7SJessica Clarke 
128129028c7SJessica Clarke 	error = DA9063_WRITE(sc->parent, DA9063_COUNT_D, ct->day);
129129028c7SJessica Clarke 	if (error != 0)
130129028c7SJessica Clarke 		return (error);
131129028c7SJessica Clarke 
132129028c7SJessica Clarke 	error = DA9063_WRITE(sc->parent, DA9063_COUNT_MO, ct->mon);
133129028c7SJessica Clarke 	if (error != 0)
134129028c7SJessica Clarke 		return (error);
135129028c7SJessica Clarke 
136129028c7SJessica Clarke 	error = DA9063_WRITE(sc->parent, DA9063_COUNT_Y,
137129028c7SJessica Clarke 	    (ct->year - DA9063_RTC_BASE_YEAR) &
138129028c7SJessica Clarke 	    DA9063_COUNT_Y_COUNT_YEAR_MASK);
139129028c7SJessica Clarke 	if (error != 0)
140129028c7SJessica Clarke 		return (error);
141129028c7SJessica Clarke 
142129028c7SJessica Clarke 	return (0);
143129028c7SJessica Clarke }
144129028c7SJessica Clarke 
145129028c7SJessica Clarke static int
da9063_rtc_gettime(device_t dev,struct timespec * ts)146129028c7SJessica Clarke da9063_rtc_gettime(device_t dev, struct timespec *ts)
147129028c7SJessica Clarke {
148129028c7SJessica Clarke 	struct da9063_rtc_softc *sc;
149129028c7SJessica Clarke 	struct clocktime ct, oldct;
150129028c7SJessica Clarke 	int error;
151129028c7SJessica Clarke 
152129028c7SJessica Clarke 	sc = device_get_softc(dev);
153129028c7SJessica Clarke 
154129028c7SJessica Clarke 	DA9063_RTC_LOCK(sc);
155129028c7SJessica Clarke 
156129028c7SJessica Clarke 	error = da9063_rtc_read_ct(sc, &ct);
157129028c7SJessica Clarke 	if (error != 0)
158129028c7SJessica Clarke 		goto error;
159129028c7SJessica Clarke 
160129028c7SJessica Clarke 	/*
161129028c7SJessica Clarke 	 * Reading seconds only latches the other registers for "approx 0.5s",
162129028c7SJessica Clarke 	 * which should almost always be sufficient but is not guaranteed to
163129028c7SJessica Clarke 	 * be, so re-read to get a consistent set of values.
164129028c7SJessica Clarke 	 */
165129028c7SJessica Clarke 	do {
166129028c7SJessica Clarke 		oldct = ct;
167129028c7SJessica Clarke 		error = da9063_rtc_read_ct(sc, &ct);
168129028c7SJessica Clarke 		if (error != 0)
169129028c7SJessica Clarke 			goto error;
170129028c7SJessica Clarke 	} while (ct.min != oldct.min || ct.hour != oldct.hour ||
171129028c7SJessica Clarke 	    ct.day != oldct.day || ct.mon != oldct.mon ||
172129028c7SJessica Clarke 	    ct.year != oldct.year);
173129028c7SJessica Clarke 
174129028c7SJessica Clarke 	DA9063_RTC_UNLOCK(sc);
175129028c7SJessica Clarke 
176129028c7SJessica Clarke 	error = clock_ct_to_ts(&ct, ts);
177129028c7SJessica Clarke 	if (error != 0)
178129028c7SJessica Clarke 		return (error);
179129028c7SJessica Clarke 
180129028c7SJessica Clarke 	return (0);
181129028c7SJessica Clarke 
182129028c7SJessica Clarke error:
183129028c7SJessica Clarke 	DA9063_RTC_UNLOCK(sc);
184129028c7SJessica Clarke 	return (error);
185129028c7SJessica Clarke }
186129028c7SJessica Clarke 
187129028c7SJessica Clarke static int
da9063_rtc_settime(device_t dev,struct timespec * ts)188129028c7SJessica Clarke da9063_rtc_settime(device_t dev, struct timespec *ts)
189129028c7SJessica Clarke {
190129028c7SJessica Clarke 	struct da9063_rtc_softc *sc;
191129028c7SJessica Clarke 	struct clocktime ct;
192129028c7SJessica Clarke 	int error;
193129028c7SJessica Clarke 
194129028c7SJessica Clarke 	sc = device_get_softc(dev);
195129028c7SJessica Clarke 
196129028c7SJessica Clarke 	/*
197129028c7SJessica Clarke 	 * We request a timespec with no resolution-adjustment.  That also
198129028c7SJessica Clarke 	 * disables utc adjustment, so apply that ourselves.
199129028c7SJessica Clarke 	 */
200129028c7SJessica Clarke 	ts->tv_sec -= utc_offset();
201129028c7SJessica Clarke 	clock_ts_to_ct(ts, &ct);
202129028c7SJessica Clarke 
203129028c7SJessica Clarke 	DA9063_RTC_LOCK(sc);
204129028c7SJessica Clarke 	error = da9063_rtc_write_ct(sc, &ct);
205129028c7SJessica Clarke 	DA9063_RTC_UNLOCK(sc);
206129028c7SJessica Clarke 
207129028c7SJessica Clarke 	return (error);
208129028c7SJessica Clarke }
209129028c7SJessica Clarke 
210129028c7SJessica Clarke static int
da9063_rtc_probe(device_t dev)211129028c7SJessica Clarke da9063_rtc_probe(device_t dev)
212129028c7SJessica Clarke {
213129028c7SJessica Clarke 	if (!ofw_bus_status_okay(dev))
214129028c7SJessica Clarke 		return (ENXIO);
215129028c7SJessica Clarke 
216129028c7SJessica Clarke 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
217129028c7SJessica Clarke 		return (ENXIO);
218129028c7SJessica Clarke 
219129028c7SJessica Clarke 	device_set_desc(dev, "Dialog DA9063 RTC");
220129028c7SJessica Clarke 
221129028c7SJessica Clarke 	return (BUS_PROBE_DEFAULT);
222129028c7SJessica Clarke }
223129028c7SJessica Clarke 
224129028c7SJessica Clarke static int
da9063_rtc_attach(device_t dev)225129028c7SJessica Clarke da9063_rtc_attach(device_t dev)
226129028c7SJessica Clarke {
227129028c7SJessica Clarke 	struct da9063_rtc_softc *sc;
228129028c7SJessica Clarke 	int error;
229129028c7SJessica Clarke 
230129028c7SJessica Clarke 	sc = device_get_softc(dev);
231129028c7SJessica Clarke 
232129028c7SJessica Clarke 	sc->dev = dev;
233129028c7SJessica Clarke 	sc->parent = device_get_parent(dev);
234129028c7SJessica Clarke 
235129028c7SJessica Clarke 	/* Power on RTC and 32 kHz oscillator */
236129028c7SJessica Clarke 	error = DA9063_MODIFY(sc->parent, DA9063_CONTROL_E, 0,
237129028c7SJessica Clarke 	    DA9063_CONTROL_E_RTC_EN);
238129028c7SJessica Clarke 	if (error != 0)
239129028c7SJessica Clarke 		return (error);
240129028c7SJessica Clarke 
241129028c7SJessica Clarke 	/* Connect 32 kHz oscillator */
242129028c7SJessica Clarke 	error = DA9063_MODIFY(sc->parent, DA9063_EN_32K, 0,
243129028c7SJessica Clarke 	    DA9063_EN_32K_CRYSTAL);
244129028c7SJessica Clarke 	if (error != 0)
245129028c7SJessica Clarke 		return (error);
246129028c7SJessica Clarke 
247129028c7SJessica Clarke 	/* Disable alarms */
248129028c7SJessica Clarke 	error = DA9063_MODIFY(sc->parent, DA9063_ALARM_Y,
249129028c7SJessica Clarke 	    DA9063_ALARM_Y_ALARM_ON | DA9063_ALARM_Y_TICK_ON, 0);
250129028c7SJessica Clarke 	if (error != 0)
251129028c7SJessica Clarke 		return (error);
252129028c7SJessica Clarke 
253129028c7SJessica Clarke 	mtx_init(&sc->mtx, device_get_nameunit(sc->dev), NULL, MTX_DEF);
254129028c7SJessica Clarke 
255129028c7SJessica Clarke 	/*
256129028c7SJessica Clarke 	 * Register as a system realtime clock with 1 second resolution.
257129028c7SJessica Clarke 	 */
258129028c7SJessica Clarke 	clock_register_flags(dev, 1000000, CLOCKF_SETTIME_NO_ADJ);
259129028c7SJessica Clarke 	clock_schedule(dev, 1);
260129028c7SJessica Clarke 
261129028c7SJessica Clarke 	return (0);
262129028c7SJessica Clarke }
263129028c7SJessica Clarke 
264129028c7SJessica Clarke static int
da9063_rtc_detach(device_t dev)265129028c7SJessica Clarke da9063_rtc_detach(device_t dev)
266129028c7SJessica Clarke {
267129028c7SJessica Clarke 	struct da9063_rtc_softc *sc;
268129028c7SJessica Clarke 
269129028c7SJessica Clarke 	sc = device_get_softc(dev);
270129028c7SJessica Clarke 
271129028c7SJessica Clarke 	clock_unregister(dev);
272129028c7SJessica Clarke 	mtx_destroy(&sc->mtx);
273129028c7SJessica Clarke 
274129028c7SJessica Clarke 	return (0);
275129028c7SJessica Clarke }
276129028c7SJessica Clarke 
277129028c7SJessica Clarke static device_method_t da9063_rtc_methods[] = {
278129028c7SJessica Clarke 	/* Device interface */
279129028c7SJessica Clarke 	DEVMETHOD(device_probe,		da9063_rtc_probe),
280129028c7SJessica Clarke 	DEVMETHOD(device_attach,	da9063_rtc_attach),
281129028c7SJessica Clarke 	DEVMETHOD(device_detach,	da9063_rtc_detach),
282129028c7SJessica Clarke 
283129028c7SJessica Clarke 	/* Clock interface */
284129028c7SJessica Clarke 	DEVMETHOD(clock_gettime,	da9063_rtc_gettime),
285129028c7SJessica Clarke 	DEVMETHOD(clock_settime,	da9063_rtc_settime),
286129028c7SJessica Clarke 
287129028c7SJessica Clarke 	DEVMETHOD_END,
288129028c7SJessica Clarke };
289129028c7SJessica Clarke 
290129028c7SJessica Clarke DEFINE_CLASS_0(da9063_rtc, da9063_rtc_driver, da9063_rtc_methods,
291129028c7SJessica Clarke     sizeof(struct da9063_rtc_softc));
292129028c7SJessica Clarke 
293129028c7SJessica Clarke DRIVER_MODULE(da9063_rtc, da9063_pmic, da9063_rtc_driver, NULL, NULL);
294