aw_rtc.c revision 296357
1296161Sjmcneill/*- 2296161Sjmcneill * Copyright (c) 2016 Vladimir Belian <fate10@gmail.com> 3296161Sjmcneill * All rights reserved. 4296161Sjmcneill * 5296161Sjmcneill * Redistribution and use in source and binary forms, with or without 6296161Sjmcneill * modification, are permitted provided that the following conditions 7296161Sjmcneill * are met: 8296161Sjmcneill * 1. Redistributions of source code must retain the above copyright 9296161Sjmcneill * notice, this list of conditions and the following disclaimer. 10296161Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright 11296161Sjmcneill * notice, this list of conditions and the following disclaimer in the 12296161Sjmcneill * documentation and/or other materials provided with the distribution. 13296161Sjmcneill * 14296161Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15296161Sjmcneill * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16296161Sjmcneill * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17296161Sjmcneill * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18296161Sjmcneill * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19296161Sjmcneill * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20296161Sjmcneill * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21296161Sjmcneill * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22296161Sjmcneill * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23296161Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24296161Sjmcneill * SUCH DAMAGE. 25296161Sjmcneill */ 26296161Sjmcneill 27296161Sjmcneill#include <sys/cdefs.h> 28296161Sjmcneill__FBSDID("$FreeBSD: head/sys/arm/allwinner/aw_rtc.c 296357 2016-03-03 22:19:39Z jmcneill $"); 29296161Sjmcneill 30296161Sjmcneill#include <sys/param.h> 31296161Sjmcneill#include <sys/bus.h> 32296161Sjmcneill#include <sys/time.h> 33296161Sjmcneill#include <sys/rman.h> 34296161Sjmcneill#include <sys/clock.h> 35296161Sjmcneill#include <sys/systm.h> 36296161Sjmcneill#include <sys/kernel.h> 37296161Sjmcneill#include <sys/module.h> 38296161Sjmcneill#include <sys/resource.h> 39296161Sjmcneill 40296161Sjmcneill#include <machine/bus.h> 41296161Sjmcneill#include <machine/resource.h> 42296161Sjmcneill 43296161Sjmcneill#include <dev/ofw/ofw_bus.h> 44296161Sjmcneill#include <dev/ofw/ofw_bus_subr.h> 45296161Sjmcneill 46296161Sjmcneill#include <arm/allwinner/allwinner_machdep.h> 47296161Sjmcneill 48296161Sjmcneill#include "clock_if.h" 49296161Sjmcneill 50296161Sjmcneill#define LOSC_CTRL_REG 0x00 51296357Sjmcneill#define A10_RTC_DATE_REG 0x04 52296357Sjmcneill#define A10_RTC_TIME_REG 0x08 53296357Sjmcneill#define A31_LOSC_AUTO_SWT_STA 0x04 54296357Sjmcneill#define A31_RTC_DATE_REG 0x10 55296357Sjmcneill#define A31_RTC_TIME_REG 0x14 56296161Sjmcneill 57296161Sjmcneill#define TIME_MASK 0x001f3f3f 58296161Sjmcneill 59296357Sjmcneill#define LOSC_OSC_SRC (1 << 0) 60296357Sjmcneill#define LOSC_GSM (1 << 3) 61296357Sjmcneill#define LOSC_AUTO_SW_EN (1 << 14) 62296161Sjmcneill#define LOSC_MAGIC 0x16aa0000 63296161Sjmcneill#define LOSC_BUSY_MASK 0x00000380 64296161Sjmcneill 65296161Sjmcneill#define IS_SUN7I (allwinner_soc_family() == ALLWINNERSOC_SUN7I) 66296161Sjmcneill 67296161Sjmcneill#define YEAR_MIN (IS_SUN7I ? 1970 : 2010) 68296161Sjmcneill#define YEAR_MAX (IS_SUN7I ? 2100 : 2073) 69296161Sjmcneill#define YEAR_OFFSET (IS_SUN7I ? 1900 : 2010) 70296161Sjmcneill#define YEAR_MASK (IS_SUN7I ? 0xff : 0x3f) 71296161Sjmcneill#define LEAP_BIT (IS_SUN7I ? 24 : 22) 72296161Sjmcneill 73296161Sjmcneill#define GET_SEC_VALUE(x) ((x) & 0x0000003f) 74296161Sjmcneill#define GET_MIN_VALUE(x) (((x) & 0x00003f00) >> 8) 75296161Sjmcneill#define GET_HOUR_VALUE(x) (((x) & 0x001f0000) >> 16) 76296161Sjmcneill#define GET_DAY_VALUE(x) ((x) & 0x0000001f) 77296161Sjmcneill#define GET_MON_VALUE(x) (((x) & 0x00000f00) >> 8) 78296161Sjmcneill#define GET_YEAR_VALUE(x) (((x) >> 16) & YEAR_MASK) 79296161Sjmcneill 80296161Sjmcneill#define SET_DAY_VALUE(x) GET_DAY_VALUE(x) 81296161Sjmcneill#define SET_MON_VALUE(x) (((x) & 0x0000000f) << 8) 82296161Sjmcneill#define SET_YEAR_VALUE(x) (((x) & YEAR_MASK) << 16) 83296161Sjmcneill#define SET_LEAP_VALUE(x) (((x) & 0x00000001) << LEAP_BIT) 84296161Sjmcneill#define SET_SEC_VALUE(x) GET_SEC_VALUE(x) 85296161Sjmcneill#define SET_MIN_VALUE(x) (((x) & 0x0000003f) << 8) 86296161Sjmcneill#define SET_HOUR_VALUE(x) (((x) & 0x0000001f) << 16) 87296161Sjmcneill 88296161Sjmcneill#define HALF_OF_SEC_NS 500000000 89296161Sjmcneill#define RTC_RES_US 1000000 90296161Sjmcneill#define RTC_TIMEOUT 70 91296161Sjmcneill 92296161Sjmcneill#define RTC_READ(sc, reg) bus_read_4((sc)->res, (reg)) 93296161Sjmcneill#define RTC_WRITE(sc, reg, val) bus_write_4((sc)->res, (reg), (val)) 94296161Sjmcneill 95296161Sjmcneill#define IS_LEAP_YEAR(y) \ 96296161Sjmcneill (((y) % 400) == 0 || (((y) % 100) != 0 && ((y) % 4) == 0)) 97296161Sjmcneill 98296357Sjmcneill#define A10_RTC 1 99296357Sjmcneill#define A20_RTC 2 100296357Sjmcneill#define A31_RTC 3 101296161Sjmcneill 102296161Sjmcneillstatic struct ofw_compat_data compat_data[] = { 103296357Sjmcneill { "allwinner,sun4i-a10-rtc", A10_RTC }, 104296357Sjmcneill { "allwinner,sun7i-a20-rtc", A20_RTC }, 105296357Sjmcneill { "allwinner,sun6i-a31-rtc", A31_RTC }, 106296357Sjmcneill { NULL, 0 } 107296161Sjmcneill}; 108296161Sjmcneill 109296161Sjmcneillstruct aw_rtc_softc { 110296357Sjmcneill struct resource *res; 111296357Sjmcneill bus_size_t rtc_date; 112296357Sjmcneill bus_size_t rtc_time; 113296161Sjmcneill}; 114296161Sjmcneill 115296161Sjmcneillstatic int aw_rtc_probe(device_t dev); 116296161Sjmcneillstatic int aw_rtc_attach(device_t dev); 117296161Sjmcneillstatic int aw_rtc_detach(device_t dev); 118296161Sjmcneill 119296161Sjmcneillstatic int aw_rtc_gettime(device_t dev, struct timespec *ts); 120296161Sjmcneillstatic int aw_rtc_settime(device_t dev, struct timespec *ts); 121296161Sjmcneill 122296161Sjmcneillstatic device_method_t aw_rtc_methods[] = { 123296161Sjmcneill DEVMETHOD(device_probe, aw_rtc_probe), 124296161Sjmcneill DEVMETHOD(device_attach, aw_rtc_attach), 125296161Sjmcneill DEVMETHOD(device_detach, aw_rtc_detach), 126296161Sjmcneill 127296161Sjmcneill DEVMETHOD(clock_gettime, aw_rtc_gettime), 128296161Sjmcneill DEVMETHOD(clock_settime, aw_rtc_settime), 129296161Sjmcneill 130296161Sjmcneill DEVMETHOD_END 131296161Sjmcneill}; 132296161Sjmcneill 133296161Sjmcneillstatic driver_t aw_rtc_driver = { 134296161Sjmcneill "rtc", 135296161Sjmcneill aw_rtc_methods, 136296161Sjmcneill sizeof(struct aw_rtc_softc), 137296161Sjmcneill}; 138296161Sjmcneill 139296161Sjmcneillstatic devclass_t aw_rtc_devclass; 140296161Sjmcneill 141296161SjmcneillEARLY_DRIVER_MODULE(aw_rtc, simplebus, aw_rtc_driver, aw_rtc_devclass, 0, 0, 142296161Sjmcneill BUS_PASS_TIMER + BUS_PASS_ORDER_MIDDLE); 143296161Sjmcneill 144296161Sjmcneill 145296161Sjmcneillstatic int 146296161Sjmcneillaw_rtc_probe(device_t dev) 147296161Sjmcneill{ 148296161Sjmcneill if (!ofw_bus_status_okay(dev)) 149296161Sjmcneill return (ENXIO); 150296161Sjmcneill 151296161Sjmcneill if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 152296161Sjmcneill return (ENXIO); 153296161Sjmcneill 154296161Sjmcneill device_set_desc(dev, "Allwinner RTC"); 155296161Sjmcneill 156296161Sjmcneill return (BUS_PROBE_DEFAULT); 157296161Sjmcneill} 158296161Sjmcneill 159296161Sjmcneillstatic int 160296161Sjmcneillaw_rtc_attach(device_t dev) 161296161Sjmcneill{ 162296161Sjmcneill struct aw_rtc_softc *sc = device_get_softc(dev); 163296357Sjmcneill bus_size_t rtc_losc_sta; 164296161Sjmcneill uint32_t val; 165296161Sjmcneill int rid = 0; 166296161Sjmcneill 167296161Sjmcneill sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 168296161Sjmcneill if (!sc->res) { 169296161Sjmcneill device_printf(dev, "could not allocate resources\n"); 170296161Sjmcneill return (ENXIO); 171296161Sjmcneill } 172296161Sjmcneill 173296357Sjmcneill switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { 174296357Sjmcneill case A10_RTC: 175296357Sjmcneill case A20_RTC: 176296357Sjmcneill sc->rtc_date = A10_RTC_DATE_REG; 177296357Sjmcneill sc->rtc_time = A10_RTC_TIME_REG; 178296357Sjmcneill rtc_losc_sta = LOSC_CTRL_REG; 179296357Sjmcneill break; 180296357Sjmcneill case A31_RTC: 181296357Sjmcneill sc->rtc_date = A31_RTC_DATE_REG; 182296357Sjmcneill sc->rtc_time = A31_RTC_TIME_REG; 183296357Sjmcneill rtc_losc_sta = A31_LOSC_AUTO_SWT_STA; 184296357Sjmcneill break; 185296357Sjmcneill } 186296161Sjmcneill val = RTC_READ(sc, LOSC_CTRL_REG); 187296357Sjmcneill val |= LOSC_AUTO_SW_EN; 188296161Sjmcneill val |= LOSC_MAGIC | LOSC_GSM | LOSC_OSC_SRC; 189296161Sjmcneill RTC_WRITE(sc, LOSC_CTRL_REG, val); 190296357Sjmcneill 191296161Sjmcneill DELAY(100); 192296357Sjmcneill 193296357Sjmcneill if (bootverbose) { 194296357Sjmcneill val = RTC_READ(sc, rtc_losc_sta); 195296357Sjmcneill if ((val & LOSC_OSC_SRC) == 0) 196296357Sjmcneill device_printf(dev, "Using internal oscillator\n"); 197296357Sjmcneill else 198296357Sjmcneill device_printf(dev, "Using external oscillator\n"); 199296161Sjmcneill } 200296357Sjmcneill 201296161Sjmcneill clock_register(dev, RTC_RES_US); 202296161Sjmcneill 203296161Sjmcneill return (0); 204296161Sjmcneill} 205296161Sjmcneill 206296161Sjmcneillstatic int 207296161Sjmcneillaw_rtc_detach(device_t dev) 208296161Sjmcneill{ 209296161Sjmcneill /* can't support detach, since there's no clock_unregister function */ 210296161Sjmcneill return (EBUSY); 211296161Sjmcneill} 212296161Sjmcneill 213296161Sjmcneillstatic int 214296161Sjmcneillaw_rtc_gettime(device_t dev, struct timespec *ts) 215296161Sjmcneill{ 216296161Sjmcneill struct aw_rtc_softc *sc = device_get_softc(dev); 217296161Sjmcneill struct clocktime ct; 218296161Sjmcneill uint32_t rdate, rtime; 219296161Sjmcneill 220296357Sjmcneill rdate = RTC_READ(sc, sc->rtc_date); 221296357Sjmcneill rtime = RTC_READ(sc, sc->rtc_time); 222296161Sjmcneill 223296161Sjmcneill if ((rtime & TIME_MASK) == 0) 224296357Sjmcneill rdate = RTC_READ(sc, sc->rtc_date); 225296161Sjmcneill 226296161Sjmcneill ct.sec = GET_SEC_VALUE(rtime); 227296161Sjmcneill ct.min = GET_MIN_VALUE(rtime); 228296161Sjmcneill ct.hour = GET_HOUR_VALUE(rtime); 229296161Sjmcneill ct.day = GET_DAY_VALUE(rdate); 230296161Sjmcneill ct.mon = GET_MON_VALUE(rdate); 231296161Sjmcneill ct.year = GET_YEAR_VALUE(rdate) + YEAR_OFFSET; 232296161Sjmcneill ct.dow = -1; 233296161Sjmcneill /* RTC resolution is 1 sec */ 234296161Sjmcneill ct.nsec = 0; 235296161Sjmcneill 236296161Sjmcneill return (clock_ct_to_ts(&ct, ts)); 237296161Sjmcneill} 238296161Sjmcneill 239296161Sjmcneillstatic int 240296161Sjmcneillaw_rtc_settime(device_t dev, struct timespec *ts) 241296161Sjmcneill{ 242296161Sjmcneill struct aw_rtc_softc *sc = device_get_softc(dev); 243296161Sjmcneill struct clocktime ct; 244296161Sjmcneill uint32_t clk, rdate, rtime; 245296161Sjmcneill 246296161Sjmcneill /* RTC resolution is 1 sec */ 247296161Sjmcneill if (ts->tv_nsec >= HALF_OF_SEC_NS) 248296161Sjmcneill ts->tv_sec++; 249296161Sjmcneill ts->tv_nsec = 0; 250296161Sjmcneill 251296161Sjmcneill clock_ts_to_ct(ts, &ct); 252296161Sjmcneill 253296161Sjmcneill if ((ct.year < YEAR_MIN) || (ct.year > YEAR_MAX)) { 254296161Sjmcneill device_printf(dev, "could not set time, year out of range\n"); 255296161Sjmcneill return (EINVAL); 256296161Sjmcneill } 257296161Sjmcneill 258296161Sjmcneill for (clk = 0; RTC_READ(sc, LOSC_CTRL_REG) & LOSC_BUSY_MASK; clk++) { 259296161Sjmcneill if (clk > RTC_TIMEOUT) { 260296161Sjmcneill device_printf(dev, "could not set time, RTC busy\n"); 261296161Sjmcneill return (EINVAL); 262296161Sjmcneill } 263296161Sjmcneill DELAY(1); 264296161Sjmcneill } 265296161Sjmcneill /* reset time register to avoid unexpected date increment */ 266296357Sjmcneill RTC_WRITE(sc, sc->rtc_time, 0); 267296161Sjmcneill 268296161Sjmcneill rdate = SET_DAY_VALUE(ct.day) | SET_MON_VALUE(ct.mon) | 269296161Sjmcneill SET_YEAR_VALUE(ct.year - YEAR_OFFSET) | 270296161Sjmcneill SET_LEAP_VALUE(IS_LEAP_YEAR(ct.year)); 271296161Sjmcneill 272296161Sjmcneill rtime = SET_SEC_VALUE(ct.sec) | SET_MIN_VALUE(ct.min) | 273296161Sjmcneill SET_HOUR_VALUE(ct.hour); 274296161Sjmcneill 275296161Sjmcneill for (clk = 0; RTC_READ(sc, LOSC_CTRL_REG) & LOSC_BUSY_MASK; clk++) { 276296161Sjmcneill if (clk > RTC_TIMEOUT) { 277296161Sjmcneill device_printf(dev, "could not set date, RTC busy\n"); 278296161Sjmcneill return (EINVAL); 279296161Sjmcneill } 280296161Sjmcneill DELAY(1); 281296161Sjmcneill } 282296357Sjmcneill RTC_WRITE(sc, sc->rtc_date, rdate); 283296161Sjmcneill 284296161Sjmcneill for (clk = 0; RTC_READ(sc, LOSC_CTRL_REG) & LOSC_BUSY_MASK; clk++) { 285296161Sjmcneill if (clk > RTC_TIMEOUT) { 286296161Sjmcneill device_printf(dev, "could not set time, RTC busy\n"); 287296161Sjmcneill return (EINVAL); 288296161Sjmcneill } 289296161Sjmcneill DELAY(1); 290296161Sjmcneill } 291296357Sjmcneill RTC_WRITE(sc, sc->rtc_time, rtime); 292296161Sjmcneill 293296161Sjmcneill DELAY(RTC_TIMEOUT); 294296161Sjmcneill 295296161Sjmcneill return (0); 296296161Sjmcneill} 297