#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pxa2x0_rtc.c,v 1.9 2025/09/07 21:45:12 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <dev/clock_subr.h>
#include <sys/bus.h>
#include <arm/xscale/pxa2x0cpu.h>
#include <arm/xscale/pxa2x0reg.h>
#include <arm/xscale/pxa2x0var.h>
#ifdef PXARTC_DEBUG
#define DPRINTF(s) printf s
#else
#define DPRINTF(s)
#endif
struct pxartc_softc {
device_t sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
struct todr_chip_handle sc_todr;
int sc_flags;
#define FLAG_WRISTWATCH (1 << 0)
};
static int pxartc_match(device_t, cfdata_t, void *);
static void pxartc_attach(device_t, device_t, void *);
CFATTACH_DECL_NEW(pxartc, sizeof(struct pxartc_softc),
pxartc_match, pxartc_attach, NULL, NULL);
static int pxartc_todr_gettime(todr_chip_handle_t, struct timeval *);
static int pxartc_todr_settime(todr_chip_handle_t, struct timeval *);
static int pxartc_wristwatch_gettime(todr_chip_handle_t, struct clock_ymdhms *);
static int pxartc_wristwatch_settime(todr_chip_handle_t, struct clock_ymdhms *);
static int
pxartc_match(device_t parent, cfdata_t cf, void *aux)
{
struct pxaip_attach_args *pxa = aux;
if (strcmp(pxa->pxa_name, cf->cf_name) != 0)
return 0;
if (pxa->pxa_size == 0) {
pxa->pxa_size =
CPU_IS_PXA270 ? PXA270_RTC_SIZE : PXA250_RTC_SIZE;
}
return 1;
}
static void
pxartc_attach(device_t parent, device_t self, void *aux)
{
struct pxartc_softc *sc = device_private(self);
struct pxaip_attach_args *pxa = aux;
sc->sc_dev = self;
sc->sc_iot = pxa->pxa_iot;
aprint_normal(": Real-time Clock\n");
if (bus_space_map(sc->sc_iot, pxa->pxa_addr, pxa->pxa_size, 0,
&sc->sc_ioh)) {
aprint_error("%s: couldn't map registers\n",
device_xname(sc->sc_dev));
return;
}
memset(&sc->sc_todr, 0, sizeof(sc->sc_todr));
sc->sc_todr.todr_dev = self;
if (pxa->pxa_size == PXA270_RTC_SIZE) {
aprint_normal("%s: using wristwatch register\n",
device_xname(sc->sc_dev));
sc->sc_flags |= FLAG_WRISTWATCH;
sc->sc_todr.todr_gettime_ymdhms = pxartc_wristwatch_gettime;
sc->sc_todr.todr_settime_ymdhms = pxartc_wristwatch_settime;
} else {
sc->sc_todr.todr_gettime = pxartc_todr_gettime;
sc->sc_todr.todr_settime = pxartc_todr_settime;
}
todr_attach(&sc->sc_todr);
}
static int
pxartc_todr_gettime(todr_chip_handle_t ch, struct timeval *tv)
{
struct pxartc_softc *sc = device_private(ch->todr_dev);
tv->tv_sec = bus_space_read_4(sc->sc_iot, sc->sc_ioh, RTC_RCNR);
tv->tv_usec = 0;
#ifdef PXARTC_DEBUG
struct clock_ymdhms dt;
DPRINTF(("%s: RCNR = %08llx\n", device_xname(sc->sc_dev),
tv->tv_sec));
clock_secs_to_ymdhms(tv->tv_sec, &dt);
DPRINTF(("%s: %02lld/%02d/%02d %02d:%02d:%02d\n",
device_xname(sc->sc_dev),
dt.dt_year, dt.dt_mon, dt.dt_day,
dt.dt_hour, dt.dt_min, dt.dt_sec));
#endif
return 0;
}
static int
pxartc_todr_settime(todr_chip_handle_t ch, struct timeval *tv)
{
struct pxartc_softc *sc = device_private(ch->todr_dev);
#ifdef PXARTC_DEBUG
struct clock_ymdhms dt;
DPRINTF(("%s: RCNR = %08llx\n", device_xname(sc->sc_dev),
tv->tv_sec));
clock_secs_to_ymdhms(tv->tv_sec, &dt);
DPRINTF(("%s: %02lld/%02d/%02d %02d:%02d:%02d\n",
device_xname(sc->sc_dev),
dt.dt_year, dt.dt_mon, dt.dt_day,
dt.dt_hour, dt.dt_min, dt.dt_sec));
#endif
bus_space_write_4(sc->sc_iot, sc->sc_ioh, RTC_RCNR, tv->tv_sec);
#ifdef PXARTC_DEBUG
{
uint32_t cntr;
delay(1);
cntr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, RTC_RCNR);
DPRINTF(("%s: new RCNR = %08x\n", device_xname(sc->sc_dev),
cntr));
clock_secs_to_ymdhms(cntr, &dt);
DPRINTF(("%s: %02lld/%02d/%02d %02d:%02d:%02d\n",
device_xname(sc->sc_dev),
dt.dt_year, dt.dt_mon, dt.dt_day,
dt.dt_hour, dt.dt_min, dt.dt_sec));
}
#endif
return 0;
}
static int
pxartc_wristwatch_gettime(todr_chip_handle_t ch, struct clock_ymdhms *dt)
{
struct pxartc_softc *sc = device_private(ch->todr_dev);
uint32_t dayr, yearr;
int s;
DPRINTF(("%s: pxartc_wristwatch_gettime()\n",
device_xname(sc->sc_dev)));
s = splhigh();
dayr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, RTC_RDCR);
yearr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, RTC_RYCR);
splx(s);
DPRINTF(("%s: RDCR = %08x, RYCR = %08x\n", device_xname(sc->sc_dev),
dayr, yearr));
dt->dt_sec = (dayr >> RDCR_SECOND_SHIFT) & RDCR_SECOND_MASK;
dt->dt_min = (dayr >> RDCR_MINUTE_SHIFT) & RDCR_MINUTE_MASK;
dt->dt_hour = (dayr >> RDCR_HOUR_SHIFT) & RDCR_HOUR_MASK;
dt->dt_day = (yearr >> RYCR_DOM_SHIFT) & RYCR_DOM_MASK;
dt->dt_mon = (yearr >> RYCR_MONTH_SHIFT) & RYCR_MONTH_MASK;
dt->dt_year = (yearr >> RYCR_YEAR_SHIFT) & RYCR_YEAR_MASK;
DPRINTF(("%s: %02lld/%02d/%02d %02d:%02d:%02d\n",
device_xname(sc->sc_dev),
dt->dt_year, dt->dt_mon, dt->dt_day,
dt->dt_hour, dt->dt_min, dt->dt_sec));
return 0;
}
static int
pxartc_wristwatch_settime(todr_chip_handle_t ch, struct clock_ymdhms *dt)
{
struct pxartc_softc *sc = device_private(ch->todr_dev);
uint32_t dayr, yearr;
uint32_t wom;
int s;
DPRINTF(("%s: pxartc_wristwatch_settime()\n",
device_xname(sc->sc_dev)));
DPRINTF(("%s: %02lld/%02d/%02d %02d:%02d:%02d\n",
device_xname(sc->sc_dev),
dt->dt_year, dt->dt_mon, dt->dt_day,
dt->dt_hour, dt->dt_min, dt->dt_sec));
dayr = (dt->dt_sec & RDCR_SECOND_MASK) << RDCR_SECOND_SHIFT;
dayr |= (dt->dt_min & RDCR_MINUTE_MASK) << RDCR_MINUTE_SHIFT;
dayr |= (dt->dt_hour & RDCR_HOUR_MASK) << RDCR_HOUR_SHIFT;
dayr |= ((dt->dt_wday + 1) & RDCR_DOW_MASK) << RDCR_DOW_SHIFT;
wom = ((dt->dt_day - 1 + 6 - dt->dt_wday) / 7) + 1;
dayr |= (wom & RDCR_WOM_MASK) << RDCR_WOM_SHIFT;
yearr = (dt->dt_day & RYCR_DOM_MASK) << RYCR_DOM_SHIFT;
yearr |= (dt->dt_mon & RYCR_MONTH_MASK) << RYCR_MONTH_SHIFT;
yearr |= (dt->dt_year & RYCR_YEAR_MASK) << RYCR_YEAR_SHIFT;
DPRINTF(("%s: RDCR = %08x, RYCR = %08x\n", device_xname(sc->sc_dev),
dayr, yearr));
s = splhigh();
bus_space_write_4(sc->sc_iot, sc->sc_ioh, RTC_RYCR, yearr);
bus_space_write_4(sc->sc_iot, sc->sc_ioh, RTC_RDCR, dayr);
splx(s);
#ifdef PXARTC_DEBUG
{
struct clock_ymdhms dummy;
pxartc_wristwatch_gettime(ch, &dummy);
}
#endif
return 0;
}