/* $NetBSD: tx39clock.c,v 1.4 1999/12/23 16:58:48 uch Exp $ */ /* * Copyright (c) 1999, by UCHIYAMA Yasushi * 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. The name of the developer may NOT be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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 "opt_tx39_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef TX39CLKDEBUG #define DPRINTF(arg) printf arg #else #define DPRINTF(arg) #endif #define ISSETPRINT(r, m) __is_set_print(r, TX39_CLOCK_EN##m##CLK, #m) void tx39clock_init __P((struct device*)); void tx39clock_get __P((struct device*, time_t, struct clocktime*)); void tx39clock_set __P((struct device*, struct clocktime*)); const struct clockfns tx39clockfns = { tx39clock_init, tx39clock_get, tx39clock_set, }; struct txtime { u_int32_t t_hi; u_int32_t t_lo; }; struct tx39clock_softc { struct device sc_dev; tx_chipset_tag_t sc_tc; int sc_enabled; int sc_year; struct clocktime sc_epoch; }; int tx39clock_match __P((struct device*, struct cfdata*, void*)); void tx39clock_attach __P((struct device*, struct device*, void*)); void tx39clock_dump __P((tx_chipset_tag_t)); void tx39clock_cpuspeed __P((int*, int*)); void __tx39timer_rtcfreeze __P((tx_chipset_tag_t)); void __tx39timer_rtcreset __P((tx_chipset_tag_t)); __inline void __tx39timer_rtcget __P((struct txtime*)); __inline time_t __tx39timer_rtc2sec __P((struct txtime*)); struct cfattach tx39clock_ca = { sizeof(struct tx39clock_softc), tx39clock_match, tx39clock_attach }; int tx39clock_match(parent, cf, aux) struct device *parent; struct cfdata *cf; void *aux; { return 2; /* 1st attach group of txsim */ } void tx39clock_attach(parent, self, aux) struct device *parent; struct device *self; void *aux; { struct txsim_attach_args *ta = aux; struct tx39clock_softc *sc = (void*)self; tx_chipset_tag_t tc; txreg_t reg; tc = sc->sc_tc = ta->ta_tc; /* Reset timer module */ tx_conf_write(tc, TX39_TIMERCONTROL_REG, 0); /* Enable periodic timer */ reg = tx_conf_read(tc, TX39_TIMERCONTROL_REG); reg |= TX39_TIMERCONTROL_ENPERTIMER; tx_conf_write(tc, TX39_TIMERCONTROL_REG, reg); sc->sc_enabled = 0; /* * RTC and ALARM * RTCINT ... INTR5 bit 31 (roll over) * ALARMINT ... INTR5 bit 30 * PERINT ... INTR5 bit 29 */ clockattach(self, &tx39clockfns); #ifdef TX39CLKDEBUG tx39clock_dump(tc); #endif /* TX39CLKDEBUG */ } /* * cpuclock ... CPU clock (Hz) * cpuspeed ... instructions-per-microsecond */ void tx39clock_cpuspeed(cpuclock, cpuspeed) int *cpuclock; int *cpuspeed; { struct txtime t0, t1; int elapsed; __tx39timer_rtcget(&t0); __asm __volatile(" .set noreorder; li $8, 10000000; 1: nop; nop; nop; nop; nop; nop; nop; add $8, $8, -1; bnez $8, 1b; nop; .set reorder; "); __tx39timer_rtcget(&t1); elapsed = t1.t_lo - t0.t_lo; *cpuclock = (100000000 / elapsed) * TX39_RTCLOCK; *cpuspeed = *cpuclock / 1000000; } void __tx39timer_rtcfreeze(tc) tx_chipset_tag_t tc; { txreg_t reg; reg = tx_conf_read(tc, TX39_TIMERCONTROL_REG); /* Freeze RTC */ reg |= TX39_TIMERCONTROL_FREEZEPRE; /* Upper 8bit */ reg |= TX39_TIMERCONTROL_FREEZERTC; /* Lower 32bit */ /* Freeze periodic timer */ reg |= TX39_TIMERCONTROL_FREEZETIMER; reg &= ~TX39_TIMERCONTROL_ENPERTIMER; tx_conf_write(tc, TX39_TIMERCONTROL_REG, reg); } __inline time_t __tx39timer_rtc2sec(t) struct txtime *t; { /* This rely on RTC is 32.768kHz */ return (t->t_lo >> 15) | (t->t_hi << 17); } __inline void __tx39timer_rtcget(t) struct txtime *t; { tx_chipset_tag_t tc; txreg_t reghi, reglo, oreghi, oreglo; int retry; tc = tx_conf_get_tag(); retry = 10; do { oreglo = tx_conf_read(tc, TX39_TIMERRTCLO_REG); reglo = tx_conf_read(tc, TX39_TIMERRTCLO_REG); oreghi = tx_conf_read(tc, TX39_TIMERRTCHI_REG); reghi = tx_conf_read(tc, TX39_TIMERRTCHI_REG); } while ((reghi != oreghi || reglo != oreglo) && (--retry > 0)); if (retry < 0) { printf("RTC timer read error.\n"); } t->t_hi = TX39_TIMERRTCHI(reghi); t->t_lo = reglo; } void __tx39timer_rtcreset(tc) tx_chipset_tag_t tc; { txreg_t reg; reg = tx_conf_read(tc, TX39_TIMERCONTROL_REG); /* Reset counter and stop */ reg |= TX39_TIMERCONTROL_RTCCLR; tx_conf_write(tc, TX39_TIMERCONTROL_REG, reg); /* Count again */ reg &= ~TX39_TIMERCONTROL_RTCCLR; tx_conf_write(tc, TX39_TIMERCONTROL_REG, reg); } void tx39clock_init(dev) struct device *dev; { tx_chipset_tag_t tc; txreg_t reg; int pcnt; tc = tx_conf_get_tag(); /* * Setup periodic timer (interrupting hz times per second.) */ pcnt = TX39_TIMERCLK / hz - 1; reg = tx_conf_read(tc, TX39_TIMERPERIODIC_REG); TX39_TIMERPERIODIC_PERVAL_CLR(reg); reg = TX39_TIMERPERIODIC_PERVAL_SET(reg, pcnt); tx_conf_write(tc, TX39_TIMERPERIODIC_REG, reg); /* * Enable periodic timer */ reg = tx_conf_read(tc, TX39_INTRENABLE6_REG); reg |= TX39_INTRPRI13_TIMER_PERIODIC_BIT; tx_conf_write(tc, TX39_INTRENABLE6_REG, reg); /* * number of microseconds between interrupts */ tick = 1000000 / hz; } void tx39clock_get(dev, base, ct) struct device *dev; time_t base; struct clocktime *ct; { struct clock_ymdhms dt; struct tx39clock_softc *sc = (void*)dev; struct txtime tt; time_t sec; __tx39timer_rtcget(&tt); sec = __tx39timer_rtc2sec(&tt); if (!sc->sc_enabled) { DPRINTF(("bootstrap: %d sec from previous reboot\n", (int)sec)); sc->sc_enabled = 1; base += sec; } else { dt.dt_year = sc->sc_year; dt.dt_mon = sc->sc_epoch.mon; dt.dt_day = sc->sc_epoch.day; dt.dt_hour = sc->sc_epoch.hour; dt.dt_min = sc->sc_epoch.min; dt.dt_sec = sc->sc_epoch.sec; dt.dt_wday = sc->sc_epoch.dow; base = sec + clock_ymdhms_to_secs(&dt); } clock_secs_to_ymdhms(base, &dt); ct->year = dt.dt_year % 100; ct->mon = dt.dt_mon; ct->day = dt.dt_day; ct->hour = dt.dt_hour; ct->min = dt.dt_min; ct->sec = dt.dt_sec; ct->dow = dt.dt_wday; sc->sc_year = dt.dt_year; } void tx39clock_set(dev, ct) struct device *dev; struct clocktime *ct; { struct tx39clock_softc *sc = (void*)dev; if (sc->sc_enabled) { sc->sc_epoch = *ct; /* Reset RTC counter */ __tx39timer_rtcreset(sc->sc_tc); } } void tx39clock_dump(tc) tx_chipset_tag_t tc; { txreg_t reg; reg = tx_conf_read(tc, TX39_CLOCKCTRL_REG); printf(" "); ISSETPRINT(reg, CHIM); #ifdef TX391X ISSETPRINT(reg, VID); ISSETPRINT(reg, MBUS); #endif /* TX391X */ #ifdef TX392X ISSETPRINT(reg, IRDA); #endif /* TX392X */ ISSETPRINT(reg, SPI); ISSETPRINT(reg, TIMER); ISSETPRINT(reg, FASTTIMER); #ifdef TX392X ISSETPRINT(reg, C48MOUT); #endif /* TX392X */ ISSETPRINT(reg, SIBM); ISSETPRINT(reg, CSER); ISSETPRINT(reg, IR); ISSETPRINT(reg, UARTA); ISSETPRINT(reg, UARTB); printf("\n"); }