/* $NetBSD: pxa2x0_rtc.c,v 1.6 2012/10/27 17:17:42 chs Exp $ */ /* * Copyright (c) 2007 NONAKA Kimihiro * * 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 __KERNEL_RCSID(0, "$NetBSD: pxa2x0_rtc.c,v 1.6 2012/10/27 17:17:42 chs Exp $"); #include #include #include #include #include #include #include #include #include #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); /* 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(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; } 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; }