1157086Simp/*- 2157086Simp * Copyright (c) 2006 M. Warner Losh. All rights reserved. 3157086Simp * 4157086Simp * Redistribution and use in source and binary forms, with or without 5157086Simp * modification, are permitted provided that the following conditions 6157086Simp * are met: 7157086Simp * 1. Redistributions of source code must retain the above copyright 8157086Simp * notice, this list of conditions and the following disclaimer. 9157086Simp * 2. Redistributions in binary form must reproduce the above copyright 10157086Simp * notice, this list of conditions and the following disclaimer in the 11157086Simp * documentation and/or other materials provided with the distribution. 12157086Simp * 13185265Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14185265Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15185265Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16185265Simp * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17185265Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18185265Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19185265Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20185265Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21185265Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22185265Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23185265Simp * SUCH DAMAGE. 24157086Simp */ 25157086Simp 26157086Simp#include <sys/cdefs.h> 27157086Simp__FBSDID("$FreeBSD$"); 28157086Simp 29157086Simp#include <sys/param.h> 30157086Simp#include <sys/systm.h> 31157086Simp#include <sys/bus.h> 32157086Simp#include <sys/clock.h> 33157086Simp#include <sys/conf.h> 34157086Simp#include <sys/kernel.h> 35157086Simp#include <sys/lock.h> 36157086Simp#include <sys/mbuf.h> 37157086Simp#include <sys/malloc.h> 38157086Simp#include <sys/module.h> 39157086Simp#include <sys/mutex.h> 40157086Simp#include <sys/rman.h> 41157086Simp#include <machine/bus.h> 42157086Simp 43157086Simp#include <arm/at91/at91_rtcreg.h> 44157086Simp 45157086Simp#include "clock_if.h" 46157086Simp 47157086Simpstruct at91_rtc_softc 48157086Simp{ 49157086Simp device_t dev; /* Myself */ 50157086Simp void *intrhand; /* Interrupt handle */ 51157086Simp struct resource *irq_res; /* IRQ resource */ 52157086Simp struct resource *mem_res; /* Memory resource */ 53157086Simp struct mtx sc_mtx; /* basically a perimeter lock */ 54157086Simp}; 55157086Simp 56157086Simpstatic inline uint32_t 57157086SimpRD4(struct at91_rtc_softc *sc, bus_size_t off) 58157086Simp{ 59157086Simp return bus_read_4(sc->mem_res, off); 60157086Simp} 61157086Simp 62157086Simpstatic inline void 63157086SimpWR4(struct at91_rtc_softc *sc, bus_size_t off, uint32_t val) 64157086Simp{ 65157086Simp bus_write_4(sc->mem_res, off, val); 66157086Simp} 67157086Simp 68157086Simp#define AT91_RTC_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) 69157086Simp#define AT91_RTC_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) 70157086Simp#define AT91_RTC_LOCK_INIT(_sc) \ 71157086Simp mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ 72157086Simp "rtc", MTX_SPIN) 73157086Simp#define AT91_RTC_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 74157086Simp#define AT91_RTC_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); 75157086Simp#define AT91_RTC_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); 76157086Simp 77157086Simpstatic devclass_t at91_rtc_devclass; 78157086Simp 79157086Simp/* bus entry points */ 80157086Simp 81157086Simpstatic int at91_rtc_probe(device_t dev); 82157086Simpstatic int at91_rtc_attach(device_t dev); 83157086Simpstatic int at91_rtc_detach(device_t dev); 84167069Spisostatic int at91_rtc_intr(void *); 85157086Simp 86157086Simp/* helper routines */ 87157086Simpstatic int at91_rtc_activate(device_t dev); 88157086Simpstatic void at91_rtc_deactivate(device_t dev); 89157086Simp 90157086Simpstatic int 91157086Simpat91_rtc_probe(device_t dev) 92157086Simp{ 93157086Simp device_set_desc(dev, "RTC"); 94157086Simp return (0); 95157086Simp} 96157086Simp 97157086Simpstatic int 98157086Simpat91_rtc_attach(device_t dev) 99157086Simp{ 100157086Simp struct at91_rtc_softc *sc = device_get_softc(dev); 101157086Simp int err; 102157086Simp 103157086Simp sc->dev = dev; 104157086Simp err = at91_rtc_activate(dev); 105157086Simp if (err) 106157086Simp goto out; 107157086Simp 108157086Simp AT91_RTC_LOCK_INIT(sc); 109157086Simp 110157086Simp /* 111157086Simp * Activate the interrupt, but disable all interrupts in the hardware 112157086Simp */ 113157086Simp WR4(sc, RTC_IDR, 0xffffffff); 114166901Spiso err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC, 115166901Spiso at91_rtc_intr, NULL, sc, &sc->intrhand); 116157086Simp if (err) { 117157086Simp AT91_RTC_LOCK_DESTROY(sc); 118157086Simp goto out; 119157086Simp } 120157086Simp clock_register(dev, 1000000); 121237093Smariusout: 122157086Simp if (err) 123157086Simp at91_rtc_deactivate(dev); 124157086Simp return (err); 125157086Simp} 126157086Simp 127157086Simpstatic int 128157086Simpat91_rtc_detach(device_t dev) 129157086Simp{ 130157086Simp return (EBUSY); /* XXX */ 131157086Simp} 132157086Simp 133157086Simpstatic int 134157086Simpat91_rtc_activate(device_t dev) 135157086Simp{ 136157086Simp struct at91_rtc_softc *sc; 137157086Simp int rid; 138157086Simp 139157086Simp sc = device_get_softc(dev); 140157086Simp rid = 0; 141157086Simp sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 142157086Simp RF_ACTIVE); 143157086Simp if (sc->mem_res == NULL) 144157086Simp goto errout; 145157086Simp rid = 0; 146157086Simp sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 147157086Simp RF_ACTIVE | RF_SHAREABLE); 148157086Simp if (sc->irq_res == NULL) 149157086Simp goto errout; 150157086Simp return (0); 151157086Simperrout: 152157086Simp at91_rtc_deactivate(dev); 153157086Simp return (ENOMEM); 154157086Simp} 155157086Simp 156157086Simpstatic void 157157086Simpat91_rtc_deactivate(device_t dev) 158157086Simp{ 159157086Simp struct at91_rtc_softc *sc; 160157086Simp 161157086Simp sc = device_get_softc(dev); 162157086Simp if (sc->intrhand) 163157086Simp bus_teardown_intr(dev, sc->irq_res, sc->intrhand); 164157086Simp sc->intrhand = 0; 165157086Simp bus_generic_detach(sc->dev); 166157086Simp if (sc->mem_res) 167157086Simp bus_release_resource(dev, SYS_RES_IOPORT, 168157086Simp rman_get_rid(sc->mem_res), sc->mem_res); 169157086Simp sc->mem_res = 0; 170157086Simp if (sc->irq_res) 171157086Simp bus_release_resource(dev, SYS_RES_IRQ, 172157086Simp rman_get_rid(sc->irq_res), sc->irq_res); 173157086Simp sc->irq_res = 0; 174157086Simp return; 175157086Simp} 176157086Simp 177166901Spisostatic int 178157086Simpat91_rtc_intr(void *xsc) 179157086Simp{ 180157086Simp struct at91_rtc_softc *sc = xsc; 181157086Simp#if 0 182157086Simp uint32_t status; 183157086Simp 184157086Simp /* Reading the status also clears the interrupt */ 185157086Simp status = RD4(sc, RTC_SR); 186157086Simp if (status == 0) 187157086Simp return; 188157086Simp AT91_RTC_LOCK(sc); 189157086Simp AT91_RTC_UNLOCK(sc); 190157086Simp#endif 191157086Simp wakeup(sc); 192166901Spiso return (FILTER_HANDLED); 193157086Simp} 194157086Simp 195157086Simp/* 196157086Simp * Get the time of day clock and return it in ts. 197157086Simp * Return 0 on success, an error number otherwise. 198157086Simp */ 199157086Simpstatic int 200157086Simpat91_rtc_gettime(device_t dev, struct timespec *ts) 201157086Simp{ 202157086Simp struct clocktime ct; 203157086Simp uint32_t timr, calr; 204157086Simp struct at91_rtc_softc *sc; 205157086Simp 206157086Simp sc = device_get_softc(dev); 207157086Simp timr = RD4(sc, RTC_TIMR); 208157086Simp calr = RD4(sc, RTC_CALR); 209157086Simp ct.nsec = 0; 210157086Simp ct.sec = RTC_TIMR_SEC(timr); 211157086Simp ct.min = RTC_TIMR_MIN(timr); 212157086Simp ct.hour = RTC_TIMR_HR(timr); 213157086Simp ct.year = RTC_CALR_CEN(calr) * 100 + RTC_CALR_YEAR(calr); 214157086Simp ct.mon = RTC_CALR_MON(calr); 215157086Simp ct.day = RTC_CALR_DAY(calr); 216157086Simp ct.dow = -1; 217157086Simp return clock_ct_to_ts(&ct, ts); 218157086Simp} 219157086Simp 220157086Simp/* 221157086Simp * Set the time of day clock based on the value of the struct timespec arg. 222157086Simp * Return 0 on success, an error number otherwise. 223157086Simp */ 224157086Simpstatic int 225157086Simpat91_rtc_settime(device_t dev, struct timespec *ts) 226157086Simp{ 227160359Simp struct at91_rtc_softc *sc; 228160359Simp struct clocktime ct; 229160359Simp 230160359Simp sc = device_get_softc(dev); 231160359Simp clock_ts_to_ct(ts, &ct); 232160359Simp WR4(sc, RTC_TIMR, RTC_TIMR_MK(ct.hour, ct.min, ct.sec)); 233160359Simp WR4(sc, RTC_CALR, RTC_CALR_MK(ct.year, ct.mon, ct.day, ct.dow)); 234160359Simp return (0); 235157086Simp} 236157086Simp 237157086Simpstatic device_method_t at91_rtc_methods[] = { 238157086Simp /* Device interface */ 239157086Simp DEVMETHOD(device_probe, at91_rtc_probe), 240157086Simp DEVMETHOD(device_attach, at91_rtc_attach), 241157086Simp DEVMETHOD(device_detach, at91_rtc_detach), 242157086Simp 243157086Simp /* clock interface */ 244157086Simp DEVMETHOD(clock_gettime, at91_rtc_gettime), 245157086Simp DEVMETHOD(clock_settime, at91_rtc_settime), 246157086Simp 247157086Simp { 0, 0 } 248157086Simp}; 249157086Simp 250157086Simpstatic driver_t at91_rtc_driver = { 251157086Simp "at91_rtc", 252157086Simp at91_rtc_methods, 253157086Simp sizeof(struct at91_rtc_softc), 254157086Simp}; 255157086Simp 256157086SimpDRIVER_MODULE(at91_rtc, atmelarm, at91_rtc_driver, at91_rtc_devclass, 0, 0); 257