NetBSD/sys/arch/arm/xscale/pxa2x0_rtc.c

267 lines
7.6 KiB
C

/* $NetBSD: pxa2x0_rtc.c,v 1.4 2011/05/14 15:01:50 nonaka Exp $ */
/*
* Copyright (c) 2007 NONAKA Kimihiro <nonaka@netbsd.org>
*
* 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.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: pxa2x0_rtc.c,v 1.4 2011/05/14 15:01:50 nonaka Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <dev/clock_subr.h>
#include <machine/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(struct device *, struct cfdata *, void *);
static void pxartc_attach(struct device *, struct device *, void *);
CFATTACH_DECL_NEW(pxartc, sizeof(struct pxartc_softc),
pxartc_match, pxartc_attach, NULL, NULL);
/* todr(9) interface */
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_read(struct pxartc_softc *,struct clock_ymdhms *);
static int pxartc_wristwatch_write(struct pxartc_softc *,struct clock_ymdhms *);
static int
pxartc_match(struct device *parent, struct cfdata *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(struct device *parent, struct device *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;
}
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.cookie = sc;
sc->sc_todr.todr_gettime = pxartc_todr_gettime;
sc->sc_todr.todr_settime = pxartc_todr_settime;
sc->sc_todr.todr_setwen = NULL;
todr_attach(&sc->sc_todr);
}
static int
pxartc_todr_gettime(todr_chip_handle_t ch, struct timeval *tv)
{
struct pxartc_softc *sc = ch->cookie;
struct clock_ymdhms dt;
if ((sc->sc_flags & FLAG_WRISTWATCH) == 0) {
tv->tv_sec = bus_space_read_4(sc->sc_iot, sc->sc_ioh, RTC_RCNR);
tv->tv_usec = 0;
#ifdef PXARTC_DEBUG
DPRINTF(("%s: RCNR = %08llx\n", device_xname(sc->sc_dev),
tv->tv_sec));
clock_secs_to_ymdhms(tv->tv_sec, &dt);
DPRINTF(("%s: %02d/%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;
}
memset(&dt, 0, sizeof(dt));
if (pxartc_wristwatch_read(sc, &dt) == 0)
return -1;
tv->tv_sec = clock_ymdhms_to_secs(&dt);
tv->tv_usec = 0;
return 0;
}
static int
pxartc_todr_settime(todr_chip_handle_t ch, struct timeval *tv)
{
struct pxartc_softc *sc = ch->cookie;
struct clock_ymdhms dt;
if ((sc->sc_flags & FLAG_WRISTWATCH) == 0) {
#ifdef PXARTC_DEBUG
DPRINTF(("%s: RCNR = %08llx\n", device_xname(sc->sc_dev),
tv->tv_sec));
clock_secs_to_ymdhms(tv->tv_sec, &dt);
DPRINTF(("%s: %02d/%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: %02d/%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;
}
clock_secs_to_ymdhms(tv->tv_sec, &dt);
if (pxartc_wristwatch_write(sc, &dt) == 0)
return -1;
return 0;
}
static int
pxartc_wristwatch_read(struct pxartc_softc *sc, struct clock_ymdhms *dt)
{
uint32_t dayr, yearr;
int s;
DPRINTF(("%s: pxartc_wristwatch_read()\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: %02d/%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 1;
}
static int
pxartc_wristwatch_write(struct pxartc_softc *sc, struct clock_ymdhms *dt)
{
uint32_t dayr, yearr;
uint32_t wom; /* week of month: 1=first week of month */
int s;
DPRINTF(("%s: pxartc_wristwatch_write()\n", device_xname(sc->sc_dev)));
DPRINTF(("%s: %02d/%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));
/*
* We must write RYCR register before write RDCR register.
*
* See PXA270 Processor Family Developer's Manual p.946
* 21.4.2.3.1 Writing RDCR and RYCR Counter Registers with Valid Data.
*/
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_read(sc, &dummy);
}
#endif
return 1;
}