1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2022 Jessica Clarke <jrtc27@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 /* Dialog Semiconductor DA9063 RTC */ 29 30 #include <sys/param.h> 31 #include <sys/kernel.h> 32 #include <sys/bus.h> 33 #include <sys/clock.h> 34 #include <sys/lock.h> 35 #include <sys/module.h> 36 #include <sys/mutex.h> 37 38 #include <dev/dialog/da9063/da9063reg.h> 39 #include <dev/ofw/ofw_bus_subr.h> 40 41 #include "clock_if.h" 42 #include "da9063_if.h" 43 44 #define DA9063_RTC_BASE_YEAR 2000 45 46 struct da9063_rtc_softc { 47 device_t dev; 48 device_t parent; 49 struct mtx mtx; 50 }; 51 52 #define DA9063_RTC_LOCK(sc) mtx_lock(&(sc)->mtx) 53 #define DA9063_RTC_UNLOCK(sc) mtx_unlock(&(sc)->mtx) 54 #define DA9063_RTC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mtx, MA_OWNED); 55 #define DA9063_RTC_ASSERT_UNLOCKED(sc) mtx_assert(&(sc)->mtx, MA_NOTOWNED); 56 57 static struct ofw_compat_data compat_data[] = { 58 { "dlg,da9063-rtc", 1 }, 59 { NULL, 0 } 60 }; 61 62 static int 63 da9063_rtc_read_ct(struct da9063_rtc_softc *sc, struct clocktime *ct) 64 { 65 uint8_t sec, min, hour, day, mon, year; 66 int error; 67 68 DA9063_RTC_ASSERT_LOCKED(sc) 69 70 error = DA9063_READ(sc->parent, DA9063_COUNT_S, &sec); 71 if (error != 0) 72 return (error); 73 if ((sec & DA9063_COUNT_S_RTC_READ) == 0) 74 return (EAGAIN); 75 76 error = DA9063_READ(sc->parent, DA9063_COUNT_MI, &min); 77 if (error != 0) 78 return (error); 79 80 error = DA9063_READ(sc->parent, DA9063_COUNT_H, &hour); 81 if (error != 0) 82 return (error); 83 84 error = DA9063_READ(sc->parent, DA9063_COUNT_D, &day); 85 if (error != 0) 86 return (error); 87 88 error = DA9063_READ(sc->parent, DA9063_COUNT_MO, &mon); 89 if (error != 0) 90 return (error); 91 92 error = DA9063_READ(sc->parent, DA9063_COUNT_Y, &year); 93 if (error != 0) 94 return (error); 95 96 ct->nsec = 0; 97 ct->dow = -1; 98 ct->sec = sec & DA9063_COUNT_S_COUNT_SEC_MASK; 99 ct->min = min & DA9063_COUNT_MI_COUNT_MIN_MASK; 100 ct->hour = hour & DA9063_COUNT_H_COUNT_HOUR_MASK; 101 ct->day = day & DA9063_COUNT_D_COUNT_DAY_MASK; 102 ct->mon = mon & DA9063_COUNT_MO_COUNT_MONTH_MASK; 103 ct->year = (year & DA9063_COUNT_Y_COUNT_YEAR_MASK) + 104 DA9063_RTC_BASE_YEAR; 105 106 return (0); 107 } 108 109 static int 110 da9063_rtc_write_ct(struct da9063_rtc_softc *sc, struct clocktime *ct) 111 { 112 int error; 113 114 DA9063_RTC_ASSERT_LOCKED(sc) 115 116 error = DA9063_WRITE(sc->parent, DA9063_COUNT_S, ct->sec); 117 if (error != 0) 118 return (error); 119 120 error = DA9063_WRITE(sc->parent, DA9063_COUNT_MI, ct->min); 121 if (error != 0) 122 return (error); 123 124 error = DA9063_WRITE(sc->parent, DA9063_COUNT_H, ct->hour); 125 if (error != 0) 126 return (error); 127 128 error = DA9063_WRITE(sc->parent, DA9063_COUNT_D, ct->day); 129 if (error != 0) 130 return (error); 131 132 error = DA9063_WRITE(sc->parent, DA9063_COUNT_MO, ct->mon); 133 if (error != 0) 134 return (error); 135 136 error = DA9063_WRITE(sc->parent, DA9063_COUNT_Y, 137 (ct->year - DA9063_RTC_BASE_YEAR) & 138 DA9063_COUNT_Y_COUNT_YEAR_MASK); 139 if (error != 0) 140 return (error); 141 142 return (0); 143 } 144 145 static int 146 da9063_rtc_gettime(device_t dev, struct timespec *ts) 147 { 148 struct da9063_rtc_softc *sc; 149 struct clocktime ct, oldct; 150 int error; 151 152 sc = device_get_softc(dev); 153 154 DA9063_RTC_LOCK(sc); 155 156 error = da9063_rtc_read_ct(sc, &ct); 157 if (error != 0) 158 goto error; 159 160 /* 161 * Reading seconds only latches the other registers for "approx 0.5s", 162 * which should almost always be sufficient but is not guaranteed to 163 * be, so re-read to get a consistent set of values. 164 */ 165 do { 166 oldct = ct; 167 error = da9063_rtc_read_ct(sc, &ct); 168 if (error != 0) 169 goto error; 170 } while (ct.min != oldct.min || ct.hour != oldct.hour || 171 ct.day != oldct.day || ct.mon != oldct.mon || 172 ct.year != oldct.year); 173 174 DA9063_RTC_UNLOCK(sc); 175 176 error = clock_ct_to_ts(&ct, ts); 177 if (error != 0) 178 return (error); 179 180 return (0); 181 182 error: 183 DA9063_RTC_UNLOCK(sc); 184 return (error); 185 } 186 187 static int 188 da9063_rtc_settime(device_t dev, struct timespec *ts) 189 { 190 struct da9063_rtc_softc *sc; 191 struct clocktime ct; 192 int error; 193 194 sc = device_get_softc(dev); 195 196 /* 197 * We request a timespec with no resolution-adjustment. That also 198 * disables utc adjustment, so apply that ourselves. 199 */ 200 ts->tv_sec -= utc_offset(); 201 clock_ts_to_ct(ts, &ct); 202 203 DA9063_RTC_LOCK(sc); 204 error = da9063_rtc_write_ct(sc, &ct); 205 DA9063_RTC_UNLOCK(sc); 206 207 return (error); 208 } 209 210 static int 211 da9063_rtc_probe(device_t dev) 212 { 213 if (!ofw_bus_status_okay(dev)) 214 return (ENXIO); 215 216 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 217 return (ENXIO); 218 219 device_set_desc(dev, "Dialog DA9063 RTC"); 220 221 return (BUS_PROBE_DEFAULT); 222 } 223 224 static int 225 da9063_rtc_attach(device_t dev) 226 { 227 struct da9063_rtc_softc *sc; 228 int error; 229 230 sc = device_get_softc(dev); 231 232 sc->dev = dev; 233 sc->parent = device_get_parent(dev); 234 235 /* Power on RTC and 32 kHz oscillator */ 236 error = DA9063_MODIFY(sc->parent, DA9063_CONTROL_E, 0, 237 DA9063_CONTROL_E_RTC_EN); 238 if (error != 0) 239 return (error); 240 241 /* Connect 32 kHz oscillator */ 242 error = DA9063_MODIFY(sc->parent, DA9063_EN_32K, 0, 243 DA9063_EN_32K_CRYSTAL); 244 if (error != 0) 245 return (error); 246 247 /* Disable alarms */ 248 error = DA9063_MODIFY(sc->parent, DA9063_ALARM_Y, 249 DA9063_ALARM_Y_ALARM_ON | DA9063_ALARM_Y_TICK_ON, 0); 250 if (error != 0) 251 return (error); 252 253 mtx_init(&sc->mtx, device_get_nameunit(sc->dev), NULL, MTX_DEF); 254 255 /* 256 * Register as a system realtime clock with 1 second resolution. 257 */ 258 clock_register_flags(dev, 1000000, CLOCKF_SETTIME_NO_ADJ); 259 clock_schedule(dev, 1); 260 261 return (0); 262 } 263 264 static int 265 da9063_rtc_detach(device_t dev) 266 { 267 struct da9063_rtc_softc *sc; 268 269 sc = device_get_softc(dev); 270 271 clock_unregister(dev); 272 mtx_destroy(&sc->mtx); 273 274 return (0); 275 } 276 277 static device_method_t da9063_rtc_methods[] = { 278 /* Device interface */ 279 DEVMETHOD(device_probe, da9063_rtc_probe), 280 DEVMETHOD(device_attach, da9063_rtc_attach), 281 DEVMETHOD(device_detach, da9063_rtc_detach), 282 283 /* Clock interface */ 284 DEVMETHOD(clock_gettime, da9063_rtc_gettime), 285 DEVMETHOD(clock_settime, da9063_rtc_settime), 286 287 DEVMETHOD_END, 288 }; 289 290 DEFINE_CLASS_0(da9063_rtc, da9063_rtc_driver, da9063_rtc_methods, 291 sizeof(struct da9063_rtc_softc)); 292 293 DRIVER_MODULE(da9063_rtc, da9063_pmic, da9063_rtc_driver, NULL, NULL); 294