1ef2ee5d0SMichal Meloun /*- 2ef2ee5d0SMichal Meloun * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> 3ef2ee5d0SMichal Meloun * All rights reserved. 4ef2ee5d0SMichal Meloun * 5ef2ee5d0SMichal Meloun * Redistribution and use in source and binary forms, with or without 6ef2ee5d0SMichal Meloun * modification, are permitted provided that the following conditions 7ef2ee5d0SMichal Meloun * are met: 8ef2ee5d0SMichal Meloun * 1. Redistributions of source code must retain the above copyright 9ef2ee5d0SMichal Meloun * notice, this list of conditions and the following disclaimer. 10ef2ee5d0SMichal Meloun * 2. Redistributions in binary form must reproduce the above copyright 11ef2ee5d0SMichal Meloun * notice, this list of conditions and the following disclaimer in the 12ef2ee5d0SMichal Meloun * documentation and/or other materials provided with the distribution. 13ef2ee5d0SMichal Meloun * 14ef2ee5d0SMichal Meloun * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15ef2ee5d0SMichal Meloun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16ef2ee5d0SMichal Meloun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17ef2ee5d0SMichal Meloun * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18ef2ee5d0SMichal Meloun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19ef2ee5d0SMichal Meloun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20ef2ee5d0SMichal Meloun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21ef2ee5d0SMichal Meloun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22ef2ee5d0SMichal Meloun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23ef2ee5d0SMichal Meloun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24ef2ee5d0SMichal Meloun * SUCH DAMAGE. 25ef2ee5d0SMichal Meloun */ 26ef2ee5d0SMichal Meloun 27ef2ee5d0SMichal Meloun #include <sys/cdefs.h> 28ef2ee5d0SMichal Meloun /* 29ef2ee5d0SMichal Meloun * RTC driver for Tegra SoCs. 30ef2ee5d0SMichal Meloun */ 31ef2ee5d0SMichal Meloun #include <sys/param.h> 32ef2ee5d0SMichal Meloun #include <sys/systm.h> 33ef2ee5d0SMichal Meloun #include <sys/bus.h> 34ef2ee5d0SMichal Meloun #include <sys/clock.h> 35ef2ee5d0SMichal Meloun #include <sys/kernel.h> 36ef2ee5d0SMichal Meloun #include <sys/limits.h> 37ef2ee5d0SMichal Meloun #include <sys/lock.h> 38ef2ee5d0SMichal Meloun #include <sys/mutex.h> 39ef2ee5d0SMichal Meloun #include <sys/module.h> 40ef2ee5d0SMichal Meloun #include <sys/resource.h> 41ef2ee5d0SMichal Meloun 42ef2ee5d0SMichal Meloun #include <machine/bus.h> 43ef2ee5d0SMichal Meloun #include <machine/resource.h> 44ef2ee5d0SMichal Meloun #include <sys/rman.h> 45ef2ee5d0SMichal Meloun 46be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h> 47ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus.h> 48ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus_subr.h> 49ef2ee5d0SMichal Meloun 50ef2ee5d0SMichal Meloun #include "clock_if.h" 51ef2ee5d0SMichal Meloun 52ef2ee5d0SMichal Meloun #define RTC_CONTROL 0x00 53ef2ee5d0SMichal Meloun #define RTC_BUSY 0x04 54ef2ee5d0SMichal Meloun #define RTC_BUSY_STATUS (1 << 0) 55ef2ee5d0SMichal Meloun #define RTC_SECONDS 0x08 56ef2ee5d0SMichal Meloun #define RTC_SHADOW_SECONDS 0x0c 57ef2ee5d0SMichal Meloun #define RTC_MILLI_SECONDS 0x10 58ef2ee5d0SMichal Meloun #define RTC_SECONDS_ALARM0 0x14 59ef2ee5d0SMichal Meloun #define RTC_SECONDS_ALARM1 0x18 60ef2ee5d0SMichal Meloun #define RTC_MILLI_SECONDS_ALARM 0x1c 61ef2ee5d0SMichal Meloun #define RTC_SECONDS_COUNTDOWN_ALARM 0x20 62ef2ee5d0SMichal Meloun #define RTC_MILLI_SECONDS_COUNTDOW_ALARM 0x24 63ef2ee5d0SMichal Meloun #define RTC_INTR_MASK 0x28 64ef2ee5d0SMichal Meloun #define RTC_INTR_MSEC_CDN_ALARM (1 << 4) 65ef2ee5d0SMichal Meloun #define RTC_INTR_SEC_CDN_ALARM (1 << 3) 66ef2ee5d0SMichal Meloun #define RTC_INTR_MSEC_ALARM (1 << 2) 67ef2ee5d0SMichal Meloun #define RTC_INTR_SEC_ALARM1 (1 << 1) 68ef2ee5d0SMichal Meloun #define RTC_INTR_SEC_ALARM0 (1 << 0) 69ef2ee5d0SMichal Meloun 70ef2ee5d0SMichal Meloun #define RTC_INTR_STATUS 0x2c 71ef2ee5d0SMichal Meloun #define RTC_INTR_SOURCE 0x30 72ef2ee5d0SMichal Meloun #define RTC_INTR_SET 0x34 73ef2ee5d0SMichal Meloun #define RTC_CORRECTION_FACTOR 0x38 74ef2ee5d0SMichal Meloun 75ef2ee5d0SMichal Meloun #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v)) 76ef2ee5d0SMichal Meloun #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) 77ef2ee5d0SMichal Meloun 78ef2ee5d0SMichal Meloun #define LOCK(_sc) mtx_lock(&(_sc)->mtx) 79ef2ee5d0SMichal Meloun #define UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) 80ef2ee5d0SMichal Meloun #define SLEEP(_sc, timeout) \ 81ef2ee5d0SMichal Meloun mtx_sleep(sc, &sc->mtx, 0, "rtcwait", timeout); 82ef2ee5d0SMichal Meloun #define LOCK_INIT(_sc) \ 83ef2ee5d0SMichal Meloun mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_rtc", MTX_DEF) 84ef2ee5d0SMichal Meloun #define LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx) 85ef2ee5d0SMichal Meloun #define ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED) 86ef2ee5d0SMichal Meloun #define ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED) 87ef2ee5d0SMichal Meloun 88ef2ee5d0SMichal Meloun static struct ofw_compat_data compat_data[] = { 89ef2ee5d0SMichal Meloun {"nvidia,tegra124-rtc", 1}, 90ef2ee5d0SMichal Meloun {NULL, 0} 91ef2ee5d0SMichal Meloun }; 92ef2ee5d0SMichal Meloun 93ef2ee5d0SMichal Meloun struct tegra_rtc_softc { 94ef2ee5d0SMichal Meloun device_t dev; 95ef2ee5d0SMichal Meloun struct mtx mtx; 96ef2ee5d0SMichal Meloun 97ef2ee5d0SMichal Meloun struct resource *mem_res; 98ef2ee5d0SMichal Meloun struct resource *irq_res; 99ef2ee5d0SMichal Meloun void *irq_h; 100ef2ee5d0SMichal Meloun 101ef2ee5d0SMichal Meloun clk_t clk; 102ef2ee5d0SMichal Meloun uint32_t core_freq; 103ef2ee5d0SMichal Meloun }; 104ef2ee5d0SMichal Meloun 105ef2ee5d0SMichal Meloun static void 106ef2ee5d0SMichal Meloun tegra_rtc_wait(struct tegra_rtc_softc *sc) 107ef2ee5d0SMichal Meloun { 108ef2ee5d0SMichal Meloun int timeout; 109ef2ee5d0SMichal Meloun 110ef2ee5d0SMichal Meloun for (timeout = 500; timeout >0; timeout--) { 111ef2ee5d0SMichal Meloun if ((RD4(sc, RTC_BUSY) & RTC_BUSY_STATUS) == 0) 112ef2ee5d0SMichal Meloun break; 113ef2ee5d0SMichal Meloun DELAY(1); 114ef2ee5d0SMichal Meloun } 115ef2ee5d0SMichal Meloun if (timeout <= 0) 116ef2ee5d0SMichal Meloun device_printf(sc->dev, "Device busy timeouted\n"); 117ef2ee5d0SMichal Meloun 118ef2ee5d0SMichal Meloun } 119ef2ee5d0SMichal Meloun 120ef2ee5d0SMichal Meloun /* 121ef2ee5d0SMichal Meloun * Get the time of day clock and return it in ts. 122ef2ee5d0SMichal Meloun * Return 0 on success, an error number otherwise. 123ef2ee5d0SMichal Meloun */ 124ef2ee5d0SMichal Meloun static int 125ef2ee5d0SMichal Meloun tegra_rtc_gettime(device_t dev, struct timespec *ts) 126ef2ee5d0SMichal Meloun { 127ef2ee5d0SMichal Meloun struct tegra_rtc_softc *sc; 128ef2ee5d0SMichal Meloun struct timeval tv; 129ef2ee5d0SMichal Meloun uint32_t msec, sec; 130ef2ee5d0SMichal Meloun 131ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 132ef2ee5d0SMichal Meloun 133ef2ee5d0SMichal Meloun LOCK(sc); 134ef2ee5d0SMichal Meloun msec = RD4(sc, RTC_MILLI_SECONDS); 135ef2ee5d0SMichal Meloun sec = RD4(sc, RTC_SHADOW_SECONDS); 136ef2ee5d0SMichal Meloun UNLOCK(sc); 137ef2ee5d0SMichal Meloun tv.tv_sec = sec; 138ef2ee5d0SMichal Meloun tv.tv_usec = msec * 1000; 139ef2ee5d0SMichal Meloun TIMEVAL_TO_TIMESPEC(&tv, ts); 140ef2ee5d0SMichal Meloun return (0); 141ef2ee5d0SMichal Meloun } 142ef2ee5d0SMichal Meloun 143ef2ee5d0SMichal Meloun static int 144ef2ee5d0SMichal Meloun tegra_rtc_settime(device_t dev, struct timespec *ts) 145ef2ee5d0SMichal Meloun { 146ef2ee5d0SMichal Meloun struct tegra_rtc_softc *sc; 147ef2ee5d0SMichal Meloun struct timeval tv; 148ef2ee5d0SMichal Meloun 149ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 150ef2ee5d0SMichal Meloun 151ef2ee5d0SMichal Meloun LOCK(sc); 152ef2ee5d0SMichal Meloun TIMESPEC_TO_TIMEVAL(&tv, ts); 153ef2ee5d0SMichal Meloun tegra_rtc_wait(sc); 154ef2ee5d0SMichal Meloun WR4(sc, RTC_SECONDS, tv.tv_sec); 155ef2ee5d0SMichal Meloun UNLOCK(sc); 156ef2ee5d0SMichal Meloun 157ef2ee5d0SMichal Meloun return (0); 158ef2ee5d0SMichal Meloun } 159ef2ee5d0SMichal Meloun 160ef2ee5d0SMichal Meloun static void 161ef2ee5d0SMichal Meloun tegra_rtc_intr(void *arg) 162ef2ee5d0SMichal Meloun { 163ef2ee5d0SMichal Meloun struct tegra_rtc_softc *sc; 164ef2ee5d0SMichal Meloun uint32_t status; 165ef2ee5d0SMichal Meloun 166ef2ee5d0SMichal Meloun sc = (struct tegra_rtc_softc *)arg; 167ef2ee5d0SMichal Meloun LOCK(sc); 168ef2ee5d0SMichal Meloun status = RD4(sc, RTC_INTR_STATUS); 169ef2ee5d0SMichal Meloun WR4(sc, RTC_INTR_STATUS, status); 170ef2ee5d0SMichal Meloun UNLOCK(sc); 171ef2ee5d0SMichal Meloun } 172ef2ee5d0SMichal Meloun 173ef2ee5d0SMichal Meloun static int 174ef2ee5d0SMichal Meloun tegra_rtc_probe(device_t dev) 175ef2ee5d0SMichal Meloun { 176ef2ee5d0SMichal Meloun if (!ofw_bus_status_okay(dev)) 177ef2ee5d0SMichal Meloun return (ENXIO); 178ef2ee5d0SMichal Meloun 179ef2ee5d0SMichal Meloun if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 180ef2ee5d0SMichal Meloun return (ENXIO); 181ef2ee5d0SMichal Meloun 182ef2ee5d0SMichal Meloun return (BUS_PROBE_DEFAULT); 183ef2ee5d0SMichal Meloun } 184ef2ee5d0SMichal Meloun 185ef2ee5d0SMichal Meloun static int 186ef2ee5d0SMichal Meloun tegra_rtc_attach(device_t dev) 187ef2ee5d0SMichal Meloun { 188ef2ee5d0SMichal Meloun int rv, rid; 189ef2ee5d0SMichal Meloun struct tegra_rtc_softc *sc; 190ef2ee5d0SMichal Meloun 191ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 192ef2ee5d0SMichal Meloun sc->dev = dev; 193ef2ee5d0SMichal Meloun 194ef2ee5d0SMichal Meloun LOCK_INIT(sc); 195ef2ee5d0SMichal Meloun 196ef2ee5d0SMichal Meloun /* Get the memory resource for the register mapping. */ 197ef2ee5d0SMichal Meloun rid = 0; 198ef2ee5d0SMichal Meloun sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 199ef2ee5d0SMichal Meloun RF_ACTIVE); 200ef2ee5d0SMichal Meloun if (sc->mem_res == NULL) { 201ef2ee5d0SMichal Meloun device_printf(dev, "Cannot map registers.\n"); 202ef2ee5d0SMichal Meloun rv = ENXIO; 203ef2ee5d0SMichal Meloun goto fail; 204ef2ee5d0SMichal Meloun } 205ef2ee5d0SMichal Meloun 206ef2ee5d0SMichal Meloun /* Allocate our IRQ resource. */ 207ef2ee5d0SMichal Meloun rid = 0; 208ef2ee5d0SMichal Meloun sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 209ef2ee5d0SMichal Meloun RF_ACTIVE); 210ef2ee5d0SMichal Meloun if (sc->irq_res == NULL) { 211ef2ee5d0SMichal Meloun device_printf(dev, "Cannot allocate interrupt.\n"); 212ef2ee5d0SMichal Meloun rv = ENXIO; 213ef2ee5d0SMichal Meloun goto fail; 214ef2ee5d0SMichal Meloun } 215ef2ee5d0SMichal Meloun 216ef2ee5d0SMichal Meloun /* OFW resources. */ 217dac93553SMichal Meloun rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk); 218ef2ee5d0SMichal Meloun if (rv != 0) { 219ef2ee5d0SMichal Meloun device_printf(dev, "Cannot get i2c clock: %d\n", rv); 220ef2ee5d0SMichal Meloun goto fail; 221ef2ee5d0SMichal Meloun } 222ef2ee5d0SMichal Meloun rv = clk_enable(sc->clk); 223ef2ee5d0SMichal Meloun if (rv != 0) { 224ef2ee5d0SMichal Meloun device_printf(dev, "Cannot enable clock: %d\n", rv); 225ef2ee5d0SMichal Meloun goto fail; 226ef2ee5d0SMichal Meloun } 227ef2ee5d0SMichal Meloun 228ef2ee5d0SMichal Meloun /* Init hardware. */ 229ef2ee5d0SMichal Meloun WR4(sc, RTC_SECONDS_ALARM0, 0); 230ef2ee5d0SMichal Meloun WR4(sc, RTC_SECONDS_ALARM1, 0); 231ef2ee5d0SMichal Meloun WR4(sc, RTC_INTR_STATUS, 0xFFFFFFFF); 232ef2ee5d0SMichal Meloun WR4(sc, RTC_INTR_MASK, 0); 233ef2ee5d0SMichal Meloun 234ef2ee5d0SMichal Meloun /* Setup interrupt */ 235ef2ee5d0SMichal Meloun rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 236ef2ee5d0SMichal Meloun NULL, tegra_rtc_intr, sc, &sc->irq_h); 237ef2ee5d0SMichal Meloun if (rv) { 238ef2ee5d0SMichal Meloun device_printf(dev, "Cannot setup interrupt.\n"); 239ef2ee5d0SMichal Meloun goto fail; 240ef2ee5d0SMichal Meloun } 241ef2ee5d0SMichal Meloun 242ef2ee5d0SMichal Meloun /* 243ef2ee5d0SMichal Meloun * Register as a time of day clock with 1-second resolution. 244ef2ee5d0SMichal Meloun * 245ef2ee5d0SMichal Meloun * XXXX Not yet, we don't have support for multiple RTCs 246ef2ee5d0SMichal Meloun */ 247ef2ee5d0SMichal Meloun /* clock_register(dev, 1000000); */ 248ef2ee5d0SMichal Meloun 249*18250ec6SJohn Baldwin bus_attach_children(dev); 250*18250ec6SJohn Baldwin return (0); 251ef2ee5d0SMichal Meloun 252ef2ee5d0SMichal Meloun fail: 253ef2ee5d0SMichal Meloun if (sc->clk != NULL) 254ef2ee5d0SMichal Meloun clk_release(sc->clk); 255ef2ee5d0SMichal Meloun if (sc->irq_h != NULL) 256ef2ee5d0SMichal Meloun bus_teardown_intr(dev, sc->irq_res, sc->irq_h); 257ef2ee5d0SMichal Meloun if (sc->irq_res != NULL) 258ef2ee5d0SMichal Meloun bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 259ef2ee5d0SMichal Meloun if (sc->mem_res != NULL) 260ef2ee5d0SMichal Meloun bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); 261ef2ee5d0SMichal Meloun LOCK_DESTROY(sc); 262ef2ee5d0SMichal Meloun 263ef2ee5d0SMichal Meloun return (rv); 264ef2ee5d0SMichal Meloun } 265ef2ee5d0SMichal Meloun 266ef2ee5d0SMichal Meloun static int 267ef2ee5d0SMichal Meloun tegra_rtc_detach(device_t dev) 268ef2ee5d0SMichal Meloun { 269ef2ee5d0SMichal Meloun struct tegra_rtc_softc *sc; 270d412c076SJohn Baldwin int error; 271d412c076SJohn Baldwin 272d412c076SJohn Baldwin error = bus_generic_detach(dev); 273d412c076SJohn Baldwin if (error != 0) 274d412c076SJohn Baldwin return (error); 275ef2ee5d0SMichal Meloun 276ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 277ef2ee5d0SMichal Meloun if (sc->irq_h != NULL) 278ef2ee5d0SMichal Meloun bus_teardown_intr(dev, sc->irq_res, sc->irq_h); 279ef2ee5d0SMichal Meloun if (sc->irq_res != NULL) 280ef2ee5d0SMichal Meloun bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 281ef2ee5d0SMichal Meloun if (sc->mem_res != NULL) 282ef2ee5d0SMichal Meloun bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); 283ef2ee5d0SMichal Meloun 284ef2ee5d0SMichal Meloun LOCK_DESTROY(sc); 285d412c076SJohn Baldwin return (0); 286ef2ee5d0SMichal Meloun } 287ef2ee5d0SMichal Meloun 288ef2ee5d0SMichal Meloun static device_method_t tegra_rtc_methods[] = { 289ef2ee5d0SMichal Meloun /* Device interface */ 290ef2ee5d0SMichal Meloun DEVMETHOD(device_probe, tegra_rtc_probe), 291ef2ee5d0SMichal Meloun DEVMETHOD(device_attach, tegra_rtc_attach), 292ef2ee5d0SMichal Meloun DEVMETHOD(device_detach, tegra_rtc_detach), 293ef2ee5d0SMichal Meloun 294ef2ee5d0SMichal Meloun /* clock interface */ 295ef2ee5d0SMichal Meloun DEVMETHOD(clock_gettime, tegra_rtc_gettime), 296ef2ee5d0SMichal Meloun DEVMETHOD(clock_settime, tegra_rtc_settime), 297ef2ee5d0SMichal Meloun 298ef2ee5d0SMichal Meloun DEVMETHOD_END 299ef2ee5d0SMichal Meloun }; 300ef2ee5d0SMichal Meloun 3014bda238aSMichal Meloun static DEFINE_CLASS_0(rtc, tegra_rtc_driver, tegra_rtc_methods, 3024bda238aSMichal Meloun sizeof(struct tegra_rtc_softc)); 303289f133bSJohn Baldwin DRIVER_MODULE(tegra_rtc, simplebus, tegra_rtc_driver, NULL, NULL); 304