1170530Ssam/* $NetBSD: tx39clock.c,v 1.25 2011/03/16 14:43:37 tsutsui Exp $ */ 2178354Ssam 3170530Ssam/*- 4170530Ssam * Copyright (c) 1999-2002 The NetBSD Foundation, Inc. 5170530Ssam * All rights reserved. 6170530Ssam * 7170530Ssam * This code is derived from software contributed to The NetBSD Foundation 8170530Ssam * by UCHIYAMA Yasushi. 9170530Ssam * 10170530Ssam * Redistribution and use in source and binary forms, with or without 11170530Ssam * modification, are permitted provided that the following conditions 12170530Ssam * are met: 13170530Ssam * 1. Redistributions of source code must retain the above copyright 14170530Ssam * notice, this list of conditions and the following disclaimer. 15170530Ssam * 2. Redistributions in binary form must reproduce the above copyright 16170530Ssam * notice, this list of conditions and the following disclaimer in the 17170530Ssam * documentation and/or other materials provided with the distribution. 18170530Ssam * 19170530Ssam * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20170530Ssam * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21170530Ssam * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22170530Ssam * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23170530Ssam * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24170530Ssam * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25170530Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26170530Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27170530Ssam * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28170530Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29170530Ssam * POSSIBILITY OF SUCH DAMAGE. 30170530Ssam */ 31170530Ssam 32170530Ssam#include <sys/cdefs.h> 33170530Ssam__KERNEL_RCSID(0, "$NetBSD: tx39clock.c,v 1.25 2011/03/16 14:43:37 tsutsui Exp $"); 34170530Ssam 35170530Ssam#include "opt_tx39clock_debug.h" 36178354Ssam 37170530Ssam#include <sys/param.h> 38170530Ssam#include <sys/systm.h> 39170530Ssam#include <sys/timetc.h> 40170530Ssam#include <sys/device.h> 41170530Ssam#include <sys/bus.h> 42170530Ssam 43170530Ssam#include <dev/clock_subr.h> 44170530Ssam 45170530Ssam#include <machine/sysconf.h> 46170530Ssam 47170530Ssam#include <hpcmips/tx/tx39var.h> 48170530Ssam#include <hpcmips/tx/tx39icureg.h> 49170530Ssam#include <hpcmips/tx/tx39clockvar.h> 50195377Ssam#include <hpcmips/tx/tx39clockreg.h> 51178354Ssam#include <hpcmips/tx/tx39timerreg.h> 52170530Ssam 53170530Ssam#ifdef TX39CLOCK_DEBUG 54170530Ssam#define DPRINTF_ENABLE 55170530Ssam#define DPRINTF_DEBUG tx39clock_debug 56170530Ssam#endif 57178354Ssam#include <machine/debug.h> 58205277Srpaulo 59178354Ssam#define ISSETPRINT(r, m) \ 60178354Ssam dbg_bitmask_print(r, TX39_CLOCK_EN ## m ## CLK, #m) 61178354Ssam 62178354Ssamvoid tx39clock_init(device_t); 63178354Ssam 64178354Ssamstruct platform_clock tx39_clock = { 65178354Ssam#define CLOCK_RATE 100 66178354Ssam CLOCK_RATE, tx39clock_init, 67178354Ssam}; 68178354Ssam 69178354Ssamstruct txtime { 70178354Ssam uint32_t t_hi; 71178354Ssam uint32_t t_lo; 72178354Ssam}; 73178354Ssam 74178354Ssamstruct tx39clock_softc { 75170530Ssam struct device sc_dev; 76170530Ssam tx_chipset_tag_t sc_tc; 77170530Ssam 78170530Ssam int sc_alarm; 79170530Ssam int sc_enabled; 80170530Ssam int sc_year; 81170530Ssam struct clock_ymdhms sc_epoch; 82170530Ssam struct timecounter sc_tcounter; 83173273Ssam}; 84193115Ssam 85193115Ssamint tx39clock_match(struct device *, struct cfdata *, void *); 86193115Ssamvoid tx39clock_attach(struct device *, struct device *, void *); 87193115Ssam#ifdef TX39CLOCK_DEBUG 88173273Ssamvoid tx39clock_dump(tx_chipset_tag_t); 89173273Ssam#endif 90193115Ssam 91193115Ssamvoid tx39clock_cpuspeed(int *, int *); 92193115Ssam 93193115Ssamvoid __tx39timer_rtcfreeze(tx_chipset_tag_t); 94193115Ssamvoid __tx39timer_rtcreset(tx_chipset_tag_t); 95193115Ssamvoid __tx39timer_rtcget(struct txtime *); 96193115Ssamtime_t __tx39timer_rtc2sec(struct txtime *); 97193115Ssamuint32_t tx39_timecount(struct timecounter *); 98193115Ssam 99193115SsamCFATTACH_DECL(tx39clock, sizeof(struct tx39clock_softc), 100193115Ssam tx39clock_match, tx39clock_attach, NULL, NULL); 101193115Ssam 102193115Ssamint 103193115Ssamtx39clock_match(struct device *parent, struct cfdata *cf, void *aux) 104193115Ssam{ 105193115Ssam 106193115Ssam return ATTACH_FIRST; 107193115Ssam} 108193115Ssam 109195377Ssamvoid 110195377Ssamtx39clock_attach(struct device *parent, struct device *self, void *aux) 111195377Ssam{ 112195377Ssam struct txsim_attach_args *ta = aux; 113195377Ssam struct tx39clock_softc *sc = device_private(self); 114195377Ssam tx_chipset_tag_t tc; 115195377Ssam txreg_t reg; 116195377Ssam 117195377Ssam tc = sc->sc_tc = ta->ta_tc; 118195377Ssam tx_conf_register_clock(tc, self); 119178354Ssam 120195377Ssam /* Reset timer module */ 121178354Ssam tx_conf_write(tc, TX39_TIMERCONTROL_REG, 0); 122195377Ssam 123195377Ssam /* Enable periodic timer */ 124195377Ssam reg = tx_conf_read(tc, TX39_TIMERCONTROL_REG); 125178354Ssam reg |= TX39_TIMERCONTROL_ENPERTIMER; 126178354Ssam tx_conf_write(tc, TX39_TIMERCONTROL_REG, reg); 127178354Ssam 128178354Ssam sc->sc_enabled = 0; 129178354Ssam /* 130184280Ssam * RTC and ALARM 131195377Ssam * RTCINT ... INTR5 bit 31 (roll over) 132195377Ssam * ALARMINT ... INTR5 bit 30 133195377Ssam * PERINT ... INTR5 bit 29 134195377Ssam */ 135195377Ssam 136195377Ssam platform_clock_attach(self, &tx39_clock); 137195377Ssam 138195377Ssam#ifdef TX39CLOCK_DEBUG 139195377Ssam tx39clock_dump(tc); 140195377Ssam#endif /* TX39CLOCK_DEBUG */ 141195377Ssam} 142195377Ssam 143195377Ssam/* 144195377Ssam * cpuclock ... CPU clock (Hz) 145195377Ssam * cpuspeed ... instructions-per-microsecond 146195377Ssam */ 147195377Ssamvoid 148195377Ssamtx39clock_cpuspeed(int *cpuclock, int *cpu_speed) 149195377Ssam{ 150195377Ssam struct txtime t0, t1; 151195377Ssam int elapsed; 152195377Ssam 153178354Ssam __tx39timer_rtcget(&t0); 154195377Ssam __asm volatile( 155170530Ssam ".set noreorder; \n\t" 156178354Ssam "li $8, 10000000; \n" 157178354Ssam "1: nop; \n\t" 158170530Ssam "nop; \n\t" 159170530Ssam "nop; \n\t" 160170530Ssam "nop; \n\t" 161170530Ssam "nop; \n\t" 162170530Ssam "nop; \n\t" 163170530Ssam "nop; \n\t" 164170530Ssam "add $8, $8, -1; \n\t" 165170530Ssam "bnez $8, 1b; \n\t" 166184280Ssam "nop; \n\t" 167184280Ssam ".set reorder;"); 168184280Ssam __tx39timer_rtcget(&t1); 169184280Ssam 170191552Ssam elapsed = t1.t_lo - t0.t_lo; 171191552Ssam 172191552Ssam *cpuclock = (100000000 / elapsed) * TX39_RTCLOCK; 173170530Ssam *cpu_speed = *cpuclock / 1000000; 174170530Ssam} 175170530Ssam 176170530Ssamvoid 177170530Ssam__tx39timer_rtcfreeze(tx_chipset_tag_t tc) 178195377Ssam{ 179170530Ssam txreg_t reg; 180178354Ssam 181170530Ssam reg = tx_conf_read(tc, TX39_TIMERCONTROL_REG); 182170530Ssam 183170530Ssam /* Freeze RTC */ 184184280Ssam reg |= TX39_TIMERCONTROL_FREEZEPRE; /* Upper 8bit */ 185191552Ssam reg |= TX39_TIMERCONTROL_FREEZERTC; /* Lower 32bit */ 186191552Ssam 187170530Ssam /* Freeze periodic timer */ 188173273Ssam reg |= TX39_TIMERCONTROL_FREEZETIMER; 189173273Ssam reg &= ~TX39_TIMERCONTROL_ENPERTIMER; 190178354Ssam tx_conf_write(tc, TX39_TIMERCONTROL_REG, reg); 191173273Ssam} 192178354Ssam 193178354Ssamvoid 194178354Ssam__tx39timer_rtcget(struct txtime *t) 195178354Ssam{ 196173273Ssam tx_chipset_tag_t tc; 197178354Ssam txreg_t reghi, reglo, oreghi, oreglo; 198178354Ssam int retry; 199178354Ssam 200178354Ssam tc = tx_conf_get_tag(); 201178354Ssam 202178354Ssam retry = 10; 203178354Ssam 204178354Ssam do { 205178354Ssam oreglo = tx_conf_read(tc, TX39_TIMERRTCLO_REG); 206178354Ssam reglo = tx_conf_read(tc, TX39_TIMERRTCLO_REG); 207178354Ssam 208178354Ssam oreghi = tx_conf_read(tc, TX39_TIMERRTCHI_REG); 209178354Ssam reghi = tx_conf_read(tc, TX39_TIMERRTCHI_REG); 210178354Ssam } while ((reghi != oreghi || reglo != oreglo) && (--retry > 0)); 211178354Ssam 212178354Ssam if (retry < 0) { 213170530Ssam printf("RTC timer read error.\n"); 214173273Ssam } 215173273Ssam 216170530Ssam t->t_hi = TX39_TIMERRTCHI(reghi); 217170530Ssam t->t_lo = reglo; 218193655Ssam} 219193655Ssam 220193655Ssamvoid 221178354Ssam__tx39timer_rtcreset(tx_chipset_tag_t tc) 222193655Ssam{ 223173273Ssam txreg_t reg; 224178354Ssam 225193655Ssam reg = tx_conf_read(tc, TX39_TIMERCONTROL_REG); 226178354Ssam 227193655Ssam /* Reset counter and stop */ 228170530Ssam reg |= TX39_TIMERCONTROL_RTCCLR; 229183256Ssam tx_conf_write(tc, TX39_TIMERCONTROL_REG, reg); 230183256Ssam 231193655Ssam /* Count again */ 232183256Ssam reg &= ~TX39_TIMERCONTROL_RTCCLR; 233170530Ssam tx_conf_write(tc, TX39_TIMERCONTROL_REG, reg); 234193655Ssam} 235178354Ssam 236193655Ssamuint32_t 237193655Ssamtx39_timecount(struct timecounter *tch) 238178354Ssam{ 239193655Ssam tx_chipset_tag_t tc = tch->tc_priv; 240170530Ssam 241178354Ssam /* 242178354Ssam * since we're only reading the low register, we don't care about 243193655Ssam * if the chip increments it. we assume that the single read will 244170530Ssam * always be consistent. This is much faster than the routine which 245170530Ssam * has to get both values, improving the quality. 246170530Ssam */ 247178354Ssam return tx_conf_read(tc, TX39_TIMERRTCLO_REG); 248170530Ssam} 249170530Ssam 250170530Ssamvoid 251170530Ssamtx39clock_init(device_t self) 252205277Srpaulo{ 253205277Srpaulo struct tx39clock_softc *sc = device_private(self); 254170530Ssam tx_chipset_tag_t tc = sc->sc_tc; 255170530Ssam txreg_t reg; 256170530Ssam int pcnt; 257205277Srpaulo 258172226Ssam /* 259172226Ssam * Setup periodic timer (interrupting hz times per second.) 260170530Ssam */ 261170530Ssam pcnt = TX39_TIMERCLK / CLOCK_RATE - 1; 262205277Srpaulo reg = tx_conf_read(tc, TX39_TIMERPERIODIC_REG); 263205277Srpaulo TX39_TIMERPERIODIC_PERVAL_CLR(reg); 264205277Srpaulo reg = TX39_TIMERPERIODIC_PERVAL_SET(reg, pcnt); 265205277Srpaulo tx_conf_write(tc, TX39_TIMERPERIODIC_REG, reg); 266205277Srpaulo 267205277Srpaulo /* 268205277Srpaulo * Enable periodic timer 269205277Srpaulo */ 270205277Srpaulo reg = tx_conf_read(tc, TX39_INTRENABLE6_REG); 271205277Srpaulo reg |= TX39_INTRPRI13_TIMER_PERIODIC_BIT; 272205277Srpaulo tx_conf_write(tc, TX39_INTRENABLE6_REG, reg); 273205277Srpaulo 274205277Srpaulo sc->sc_tcounter.tc_name = "tx39rtc"; 275205277Srpaulo sc->sc_tcounter.tc_get_timecount = tx39_timecount; 276205277Srpaulo sc->sc_tcounter.tc_priv = tc; 277205277Srpaulo sc->sc_tcounter.tc_counter_mask = 0xffffffff; 278205277Srpaulo sc->sc_tcounter.tc_frequency = TX39_RTCLOCK; 279205277Srpaulo sc->sc_tcounter.tc_quality = 100; 280170530Ssam tc_init(&sc->sc_tcounter); 281170530Ssam} 282170530Ssam 283170530Ssamint 284170530Ssamtx39clock_alarm_set(tx_chipset_tag_t tc, int msec) 285170530Ssam{ 286205277Srpaulo struct tx39clock_softc *sc = tc->tc_clockt; 287205277Srpaulo 288205277Srpaulo sc->sc_alarm = TX39_MSEC2RTC(msec); 289205277Srpaulo tx39clock_alarm_refill(tc); 290205277Srpaulo 291205277Srpaulo return 0; 292205277Srpaulo} 293205277Srpaulo 294205277Srpaulovoid 295205277Srpaulotx39clock_alarm_refill(tx_chipset_tag_t tc) 296205277Srpaulo{ 297205277Srpaulo struct tx39clock_softc *sc = tc->tc_clockt; 298205277Srpaulo struct txtime t; 299205277Srpaulo uint64_t mytime; 300205277Srpaulo 301205277Srpaulo __tx39timer_rtcget(&t); 302205277Srpaulo 303205277Srpaulo mytime = ((uint64_t)t.t_hi << 32) | (uint64_t)t.t_lo; 304205277Srpaulo mytime += (uint64_t)sc->sc_alarm; 305205277Srpaulo 306170530Ssam t.t_hi = (uint32_t)((mytime >> 32) & TX39_TIMERALARMHI_MASK); 307170530Ssam t.t_lo = (uint32_t)(mytime & 0xffffffff); 308170530Ssam 309170530Ssam tx_conf_write(tc, TX39_TIMERALARMHI_REG, t.t_hi); 310170530Ssam tx_conf_write(tc, TX39_TIMERALARMLO_REG, t.t_lo); 311170530Ssam} 312170530Ssam 313170530Ssam#ifdef TX39CLOCK_DEBUG 314170530Ssamvoid 315170530Ssamtx39clock_dump(tx_chipset_tag_t tc) 316170530Ssam{ 317170530Ssam txreg_t reg; 318170530Ssam 319173273Ssam reg = tx_conf_read(tc, TX39_CLOCKCTRL_REG); 320170530Ssam 321170530Ssam printf(" "); 322170530Ssam ISSETPRINT(reg, CHIM); 323170530Ssam#ifdef TX391X 324170530Ssam ISSETPRINT(reg, VID); 325170530Ssam ISSETPRINT(reg, MBUS); 326170530Ssam#endif /* TX391X */ 327170530Ssam#ifdef TX392X 328170530Ssam ISSETPRINT(reg, IRDA); 329170530Ssam#endif /* TX392X */ 330170530Ssam ISSETPRINT(reg, SPI); 331170530Ssam ISSETPRINT(reg, TIMER); 332170530Ssam ISSETPRINT(reg, FASTTIMER); 333170530Ssam#ifdef TX392X 334178354Ssam ISSETPRINT(reg, C48MOUT); 335173462Ssam#endif /* TX392X */ 336170530Ssam ISSETPRINT(reg, SIBM); 337170530Ssam ISSETPRINT(reg, CSER); 338170530Ssam ISSETPRINT(reg, IR); 339170530Ssam ISSETPRINT(reg, UARTA); 340170530Ssam ISSETPRINT(reg, UARTB); 341178354Ssam printf("\n"); 342170530Ssam} 343170530Ssam#endif /* TX39CLOCK_DEBUG */ 344170530Ssam