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