1183840Sraj/*- 2183840Sraj * Copyright (C) 2008 MARVELL INTERNATIONAL LTD. 3183840Sraj * All rights reserved. 4183840Sraj * 5183840Sraj * Developed by Semihalf. 6183840Sraj * 7183840Sraj * Redistribution and use in source and binary forms, with or without 8183840Sraj * modification, are permitted provided that the following conditions 9183840Sraj * are met: 10183840Sraj * 1. Redistributions of source code must retain the above copyright 11183840Sraj * notice, this list of conditions and the following disclaimer. 12183840Sraj * 2. Redistributions in binary form must reproduce the above copyright 13183840Sraj * notice, this list of conditions and the following disclaimer in the 14183840Sraj * documentation and/or other materials provided with the distribution. 15183840Sraj * 3. Neither the name of MARVELL nor the names of contributors 16183840Sraj * may be used to endorse or promote products derived from this software 17183840Sraj * without specific prior written permission. 18183840Sraj * 19183840Sraj * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20183840Sraj * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21183840Sraj * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22183840Sraj * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 23183840Sraj * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24183840Sraj * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25183840Sraj * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26183840Sraj * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27183840Sraj * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28183840Sraj * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29183840Sraj * SUCH DAMAGE. 30183840Sraj */ 31183840Sraj 32183840Sraj#include <sys/cdefs.h> 33183840Sraj__FBSDID("$FreeBSD$"); 34183840Sraj#include <sys/param.h> 35183840Sraj#include <sys/bus.h> 36183840Sraj#include <sys/lock.h> 37183840Sraj#include <sys/time.h> 38183840Sraj#include <sys/clock.h> 39183840Sraj#include <sys/resource.h> 40183840Sraj#include <sys/systm.h> 41183840Sraj#include <sys/rman.h> 42183840Sraj#include <sys/kernel.h> 43183840Sraj#include <sys/module.h> 44183840Sraj 45183840Sraj#include <machine/bus.h> 46183840Sraj#include <machine/resource.h> 47183840Sraj 48209131Sraj#include <dev/ofw/ofw_bus.h> 49209131Sraj#include <dev/ofw/ofw_bus_subr.h> 50209131Sraj 51183840Sraj#include "clock_if.h" 52183840Sraj 53183840Sraj#define MV_RTC_TIME_REG 0x00 54183840Sraj#define MV_RTC_DATE_REG 0x04 55183840Sraj#define YEAR_BASE 2000 56183840Sraj 57183840Srajstruct mv_rtc_softc { 58183840Sraj device_t dev; 59183840Sraj struct resource *res[1]; 60183840Sraj}; 61183840Sraj 62183840Srajstatic struct resource_spec res_spec[] = { 63183840Sraj { SYS_RES_MEMORY, 0, RF_ACTIVE }, 64183840Sraj { -1, 0 } 65183840Sraj}; 66183840Sraj 67183840Srajstatic int mv_rtc_probe(device_t dev); 68183840Srajstatic int mv_rtc_attach(device_t dev); 69183840Sraj 70183840Srajstatic int mv_rtc_gettime(device_t dev, struct timespec *ts); 71183840Srajstatic int mv_rtc_settime(device_t dev, struct timespec *ts); 72183840Sraj 73183840Srajstatic uint32_t mv_rtc_reg_read(struct mv_rtc_softc *sc, bus_size_t off); 74183840Srajstatic int mv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off, 75183840Sraj uint32_t val); 76183840Sraj 77183840Srajstatic device_method_t mv_rtc_methods[] = { 78183840Sraj DEVMETHOD(device_probe, mv_rtc_probe), 79183840Sraj DEVMETHOD(device_attach, mv_rtc_attach), 80183840Sraj 81183840Sraj DEVMETHOD(clock_gettime, mv_rtc_gettime), 82183840Sraj DEVMETHOD(clock_settime, mv_rtc_settime), 83183840Sraj 84183840Sraj { 0, 0 }, 85183840Sraj}; 86183840Sraj 87183840Srajstatic driver_t mv_rtc_driver = { 88183840Sraj "rtc", 89183840Sraj mv_rtc_methods, 90183840Sraj sizeof(struct mv_rtc_softc), 91183840Sraj}; 92183840Srajstatic devclass_t mv_rtc_devclass; 93183840Sraj 94209131SrajDRIVER_MODULE(mv_rtc, simplebus, mv_rtc_driver, mv_rtc_devclass, 0, 0); 95183840Sraj 96183840Srajstatic int 97183840Srajmv_rtc_probe(device_t dev) 98183840Sraj{ 99183840Sraj 100209131Sraj if (!ofw_bus_is_compatible(dev, "mrvl,rtc")) 101209131Sraj return (ENXIO); 102209131Sraj 103183840Sraj device_set_desc(dev, "Marvell Integrated RTC"); 104209131Sraj return (BUS_PROBE_DEFAULT); 105183840Sraj} 106183840Sraj 107183840Srajstatic int 108183840Srajmv_rtc_attach(device_t dev) 109183840Sraj{ 110183840Sraj struct mv_rtc_softc *sc; 111183840Sraj 112183840Sraj sc = device_get_softc(dev); 113183840Sraj sc->dev = dev; 114183840Sraj 115183840Sraj clock_register(dev, 1000000); 116183840Sraj 117183840Sraj if (bus_alloc_resources(dev, res_spec, sc->res)) { 118183840Sraj device_printf(dev, "could not allocate resources\n"); 119183840Sraj return (ENXIO); 120183840Sraj } 121183840Sraj 122183840Sraj return (0); 123183840Sraj} 124183840Sraj 125183840Srajstatic int 126183840Srajmv_rtc_gettime(device_t dev, struct timespec *ts) 127183840Sraj{ 128183840Sraj struct clocktime ct; 129183840Sraj struct mv_rtc_softc *sc; 130183840Sraj uint32_t val; 131183840Sraj 132183840Sraj sc = device_get_softc(dev); 133183840Sraj 134183840Sraj val = mv_rtc_reg_read(sc, MV_RTC_TIME_REG); 135183840Sraj 136183840Sraj ct.nsec = 0; 137183840Sraj ct.sec = FROMBCD(val & 0x7f); 138183840Sraj ct.min = FROMBCD((val & 0x7f00) >> 8); 139183840Sraj ct.hour = FROMBCD((val & 0x3f0000) >> 16); 140183840Sraj ct.dow = FROMBCD((val & 0x7000000) >> 24) - 1; 141183840Sraj 142183840Sraj val = mv_rtc_reg_read(sc, MV_RTC_DATE_REG); 143183840Sraj 144183840Sraj ct.day = FROMBCD(val & 0x7f); 145183840Sraj ct.mon = FROMBCD((val & 0x1f00) >> 8); 146183840Sraj ct.year = YEAR_BASE + FROMBCD((val & 0xff0000) >> 16); 147183840Sraj 148183840Sraj return (clock_ct_to_ts(&ct, ts)); 149183840Sraj} 150183840Sraj 151183840Srajstatic int 152183840Srajmv_rtc_settime(device_t dev, struct timespec *ts) 153183840Sraj{ 154183840Sraj struct clocktime ct; 155183840Sraj struct mv_rtc_softc *sc; 156183840Sraj uint32_t val; 157183840Sraj 158183840Sraj sc = device_get_softc(dev); 159183840Sraj 160183840Sraj /* Resolution: 1 sec */ 161183840Sraj if (ts->tv_nsec >= 500000000) 162183840Sraj ts->tv_sec++; 163183840Sraj ts->tv_nsec = 0; 164183840Sraj clock_ts_to_ct(ts, &ct); 165183840Sraj 166183840Sraj val = TOBCD(ct.sec) | (TOBCD(ct.min) << 8) | 167183840Sraj (TOBCD(ct.hour) << 16) | (TOBCD( ct.dow + 1) << 24); 168183840Sraj mv_rtc_reg_write(sc, MV_RTC_TIME_REG, val); 169183840Sraj 170183840Sraj val = TOBCD(ct.day) | (TOBCD(ct.mon) << 8) | 171183840Sraj (TOBCD(ct.year - YEAR_BASE) << 16); 172183840Sraj mv_rtc_reg_write(sc, MV_RTC_DATE_REG, val); 173183840Sraj 174183840Sraj return (0); 175183840Sraj} 176183840Sraj 177183840Srajstatic uint32_t 178183840Srajmv_rtc_reg_read(struct mv_rtc_softc *sc, bus_size_t off) 179183840Sraj{ 180183840Sraj 181183840Sraj return (bus_read_4(sc->res[0], off)); 182183840Sraj} 183183840Sraj 184183840Srajstatic int 185183840Srajmv_rtc_reg_write(struct mv_rtc_softc *sc, bus_size_t off, uint32_t val) 186183840Sraj{ 187183840Sraj 188183840Sraj bus_write_4(sc->res[0], off, val); 189183840Sraj return (0); 190183840Sraj} 191