189 lines
5.9 KiB
C
189 lines
5.9 KiB
C
/* $NetBSD: awin_rtc.c,v 1.7 2014/11/07 18:10:16 jakllsch Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 2014 Jared D. McNeill <jmcneill@invisible.ca>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: awin_rtc.c,v 1.7 2014/11/07 18:10:16 jakllsch Exp $");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/device.h>
|
|
#include <sys/intr.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/mutex.h>
|
|
|
|
#include <arm/allwinner/awin_reg.h>
|
|
#include <arm/allwinner/awin_var.h>
|
|
|
|
#include <dev/clock_subr.h>
|
|
|
|
struct awin_rtc_softc {
|
|
device_t sc_dev;
|
|
bus_space_tag_t sc_bst;
|
|
bus_space_handle_t sc_bsh;
|
|
struct todr_chip_handle sc_todr;
|
|
uint32_t sc_loscctrl_reg;
|
|
uint32_t sc_yymmdd_reg;
|
|
uint32_t sc_hhmmss_reg;
|
|
uint32_t sc_year_base;
|
|
uint32_t sc_year_mask;
|
|
};
|
|
|
|
#define RTC_READ(sc, reg) \
|
|
bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
|
|
#define RTC_WRITE(sc, reg, val) \
|
|
bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
|
|
|
|
static int awin_rtc_match(device_t, cfdata_t, void *);
|
|
static void awin_rtc_attach(device_t, device_t, void *);
|
|
|
|
static int awin_rtc_gettime(todr_chip_handle_t, struct clock_ymdhms *);
|
|
static int awin_rtc_settime(todr_chip_handle_t, struct clock_ymdhms *);
|
|
|
|
CFATTACH_DECL_NEW(awin_rtc, sizeof(struct awin_rtc_softc),
|
|
awin_rtc_match, awin_rtc_attach, NULL, NULL);
|
|
|
|
static int
|
|
awin_rtc_match(device_t parent, cfdata_t cf, void *aux)
|
|
{
|
|
struct awinio_attach_args * const aio = aux;
|
|
const struct awin_locators * const loc = &aio->aio_loc;
|
|
|
|
if (strcmp(cf->cf_name, loc->loc_name))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
awin_rtc_attach(device_t parent, device_t self, void *aux)
|
|
{
|
|
struct awin_rtc_softc *sc = device_private(self);
|
|
struct awinio_attach_args * const aio = aux;
|
|
const struct awin_locators * const loc = &aio->aio_loc;
|
|
|
|
sc->sc_dev = self;
|
|
sc->sc_bst = aio->aio_core_bst;
|
|
bus_space_subregion(sc->sc_bst, aio->aio_core_bsh,
|
|
loc->loc_offset, loc->loc_size, &sc->sc_bsh);
|
|
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A31) {
|
|
sc->sc_loscctrl_reg = AWIN_A31_LOSC_CTRL_REG;
|
|
sc->sc_yymmdd_reg = AWIN_A31_RTC_YY_MM_DD_REG;
|
|
sc->sc_hhmmss_reg = AWIN_A31_RTC_HH_MM_SS_REG;
|
|
} else {
|
|
sc->sc_loscctrl_reg = AWIN_LOSC_CTRL_REG;
|
|
sc->sc_yymmdd_reg = AWIN_RTC_YY_MM_DD_REG;
|
|
sc->sc_hhmmss_reg = AWIN_RTC_HH_MM_SS_REG;
|
|
}
|
|
|
|
if (awin_chip_id() == AWIN_CHIP_ID_A20) {
|
|
sc->sc_year_base = 1900;
|
|
sc->sc_year_mask = AWIN_A20_RTC_YY_MM_DD_YEAR;
|
|
} else {
|
|
sc->sc_year_base = POSIX_BASE_YEAR;
|
|
sc->sc_year_mask = AWIN_RTC_YY_MM_DD_YEAR;
|
|
}
|
|
#ifdef AWIN_RTC_BASE_YEAR
|
|
sc->sc_year_base = AWIN_RTC_BASE_YEAR;
|
|
#endif
|
|
|
|
aprint_naive("\n");
|
|
aprint_normal(": RTC\n");
|
|
|
|
sc->sc_todr.todr_gettime_ymdhms = awin_rtc_gettime;
|
|
sc->sc_todr.todr_settime_ymdhms = awin_rtc_settime;
|
|
sc->sc_todr.cookie = sc;
|
|
todr_attach(&sc->sc_todr);
|
|
}
|
|
|
|
static int
|
|
awin_rtc_gettime(todr_chip_handle_t tch, struct clock_ymdhms *dt)
|
|
{
|
|
struct awin_rtc_softc *sc = tch->cookie;
|
|
uint32_t yymmdd, hhmmss;
|
|
|
|
yymmdd = RTC_READ(sc, sc->sc_yymmdd_reg);
|
|
hhmmss = RTC_READ(sc, sc->sc_hhmmss_reg);
|
|
|
|
dt->dt_year = __SHIFTOUT(yymmdd, sc->sc_year_mask) + sc->sc_year_base;
|
|
dt->dt_mon = __SHIFTOUT(yymmdd, AWIN_RTC_YY_MM_DD_MONTH);
|
|
dt->dt_day = __SHIFTOUT(yymmdd, AWIN_RTC_YY_MM_DD_DAY);
|
|
dt->dt_wday = __SHIFTOUT(hhmmss, AWIN_RTC_HH_MM_SS_WK_NO);
|
|
dt->dt_hour = __SHIFTOUT(hhmmss, AWIN_RTC_HH_MM_SS_HOUR);
|
|
dt->dt_min = __SHIFTOUT(hhmmss, AWIN_RTC_HH_MM_SS_MINUTE);
|
|
dt->dt_sec = __SHIFTOUT(hhmmss, AWIN_RTC_HH_MM_SS_SECOND);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
awin_rtc_settime(todr_chip_handle_t tch, struct clock_ymdhms *dt)
|
|
{
|
|
struct awin_rtc_softc *sc = tch->cookie;
|
|
uint32_t yymmdd, hhmmss, losc, maxyear;
|
|
|
|
losc = RTC_READ(sc, sc->sc_loscctrl_reg);
|
|
if (losc & AWIN_LOSC_CTRL_BUSY)
|
|
return EBUSY;
|
|
|
|
/*
|
|
* Sanity check the date before writing it back
|
|
*/
|
|
if (dt->dt_year < POSIX_BASE_YEAR) {
|
|
aprint_normal_dev(sc->sc_dev, "year pre the epoch: %llu, "
|
|
"not writing back time\n", dt->dt_year);
|
|
return EIO;
|
|
}
|
|
maxyear = __SHIFTOUT(0xffffffff, sc->sc_year_mask) + sc->sc_year_base;
|
|
if (dt->dt_year > maxyear) {
|
|
aprint_normal_dev(sc->sc_dev, "year exceeds available field:"
|
|
" %llu, not writing back time\n", dt->dt_year);
|
|
return EIO;
|
|
}
|
|
|
|
yymmdd = __SHIFTIN(dt->dt_year - sc->sc_year_base,
|
|
sc->sc_year_mask);
|
|
|
|
KASSERT(__SHIFTOUT(yymmdd, sc->sc_year_mask) +
|
|
sc->sc_year_base == dt->dt_year);
|
|
|
|
yymmdd |= __SHIFTIN(dt->dt_mon, AWIN_RTC_YY_MM_DD_MONTH);
|
|
yymmdd |= __SHIFTIN(dt->dt_day, AWIN_RTC_YY_MM_DD_DAY);
|
|
|
|
hhmmss = 0;
|
|
hhmmss |= __SHIFTIN(dt->dt_wday, AWIN_RTC_HH_MM_SS_WK_NO);
|
|
hhmmss |= __SHIFTIN(dt->dt_hour, AWIN_RTC_HH_MM_SS_HOUR);
|
|
hhmmss |= __SHIFTIN(dt->dt_min, AWIN_RTC_HH_MM_SS_MINUTE);
|
|
hhmmss |= __SHIFTIN(dt->dt_sec, AWIN_RTC_HH_MM_SS_SECOND);
|
|
|
|
RTC_WRITE(sc, sc->sc_yymmdd_reg, yymmdd);
|
|
RTC_WRITE(sc, sc->sc_hhmmss_reg, hhmmss);
|
|
|
|
return 0;
|
|
}
|