1 /* $NetBSD: rtc.c,v 1.20 2003/07/15 02:29:34 lukem Exp $ */ 2 3 /*- 4 * Copyright (c) 1999 Shin Takemura. All rights reserved. 5 * Copyright (c) 1999 SATO Kazumi. All rights reserved. 6 * Copyright (c) 1999 PocketBSD Project. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the PocketBSD project 19 * and its contributors. 20 * 4. Neither the name of the project nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 */ 37 38 #include <sys/cdefs.h> 39 __KERNEL_RCSID(0, "$NetBSD: rtc.c,v 1.20 2003/07/15 02:29:34 lukem Exp $"); 40 41 #include "opt_vr41xx.h" 42 43 #include <sys/param.h> 44 #include <sys/systm.h> 45 46 #include <machine/sysconf.h> 47 #include <machine/bus.h> 48 49 #include <dev/clock_subr.h> 50 51 #include <hpcmips/vr/vr.h> 52 #include <hpcmips/vr/vrcpudef.h> 53 #include <hpcmips/vr/vripif.h> 54 #include <hpcmips/vr/vripreg.h> 55 #include <hpcmips/vr/rtcreg.h> 56 57 /* 58 * for debugging definitions 59 * VRRTCDEBUG print rtc debugging information 60 */ 61 #ifdef VRRTCDEBUG 62 #ifndef VRRTCDEBUG_CONF 63 #define VRRTCDEBUG_CONF 0 64 #endif 65 int vrrtc_debug = VRRTCDEBUG_CONF; 66 #define DPRINTF(arg) if (vrrtc_debug) printf arg; 67 #define DDUMP_REGS(arg) if (vrrtc_debug) vrrtc_dump_regs(arg); 68 #else /* VRRTCDEBUG */ 69 #define DPRINTF(arg) 70 #define DDUMP_REGS(arg) 71 #endif /* VRRTCDEBUG */ 72 73 struct vrrtc_softc { 74 struct device sc_dev; 75 bus_space_tag_t sc_iot; 76 bus_space_handle_t sc_ioh; 77 void *sc_ih; 78 #ifndef SINGLE_VRIP_BASE 79 int sc_rtcint_reg; 80 int sc_tclk_h_reg, sc_tclk_l_reg; 81 int sc_tclk_cnt_h_reg, sc_tclk_cnt_l_reg; 82 #endif /* SINGLE_VRIP_BASE */ 83 }; 84 85 void clock_init(struct device *); 86 void clock_get(struct device *, time_t, struct clock_ymdhms *); 87 void clock_set(struct device *, struct clock_ymdhms *); 88 89 struct platform_clock vr_clock = { 90 #define CLOCK_RATE 128 91 CLOCK_RATE, clock_init, clock_get, clock_set, 92 }; 93 94 int vrrtc_match(struct device *, struct cfdata *, void *); 95 void vrrtc_attach(struct device *, struct device *, void *); 96 int vrrtc_intr(void*, u_int32_t, u_int32_t); 97 void vrrtc_dump_regs(struct vrrtc_softc *); 98 99 CFATTACH_DECL(vrrtc, sizeof(struct vrrtc_softc), 100 vrrtc_match, vrrtc_attach, NULL, NULL); 101 102 static __inline__ void vrrtc_write(struct vrrtc_softc *, int, u_int16_t); 103 static __inline__ u_int16_t vrrtc_read(struct vrrtc_softc *, int); 104 void cvt_timehl_ymdhms(u_int32_t, u_int32_t, struct clock_ymdhms *); 105 106 extern int rtc_offset; 107 static int m2d[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 108 109 static __inline__ void 110 vrrtc_write(struct vrrtc_softc *sc, int port, u_int16_t val) 111 { 112 113 bus_space_write_2(sc->sc_iot, sc->sc_ioh, port, val); 114 } 115 116 static __inline__ u_int16_t 117 vrrtc_read(struct vrrtc_softc *sc, int port) 118 { 119 120 return (bus_space_read_2(sc->sc_iot, sc->sc_ioh, port)); 121 } 122 123 int 124 vrrtc_match(struct device *parent, struct cfdata *cf, void *aux) 125 { 126 127 return (1); 128 } 129 130 #ifndef SINGLE_VRIP_BASE 131 #define RTCINT_REG_W (sc->sc_rtcint_reg) 132 #define TCLK_H_REG_W (sc->sc_tclk_h_reg) 133 #define TCLK_L_REG_W (sc->sc_tclk_l_reg) 134 #define TCLK_CNT_H_REG_W (sc->sc_tclk_cnt_h_reg) 135 #define TCLK_CNT_L_REG_W (sc->sc_tclk_cnt_l_reg) 136 #endif /* SINGLE_VRIP_BASE */ 137 138 void 139 vrrtc_attach(struct device *parent, struct device *self, void *aux) 140 { 141 struct vrip_attach_args *va = aux; 142 struct vrrtc_softc *sc = (void *)self; 143 144 #ifndef SINGLE_VRIP_BASE 145 if (va->va_addr == VR4102_RTC_ADDR) { 146 sc->sc_rtcint_reg = VR4102_RTCINT_REG_W; 147 sc->sc_tclk_h_reg = VR4102_TCLK_H_REG_W; 148 sc->sc_tclk_l_reg = VR4102_TCLK_L_REG_W; 149 sc->sc_tclk_cnt_h_reg = VR4102_TCLK_CNT_H_REG_W; 150 sc->sc_tclk_cnt_l_reg = VR4102_TCLK_CNT_L_REG_W; 151 } else 152 if (va->va_addr == VR4122_RTC_ADDR) { 153 sc->sc_rtcint_reg = VR4122_RTCINT_REG_W; 154 sc->sc_tclk_h_reg = VR4122_TCLK_H_REG_W; 155 sc->sc_tclk_l_reg = VR4122_TCLK_L_REG_W; 156 sc->sc_tclk_cnt_h_reg = VR4122_TCLK_CNT_H_REG_W; 157 sc->sc_tclk_cnt_l_reg = VR4122_TCLK_CNT_L_REG_W; 158 } else 159 if (va->va_addr == VR4181_RTC_ADDR) { 160 sc->sc_rtcint_reg = VR4181_RTCINT_REG_W; 161 sc->sc_tclk_h_reg = RTC_NO_REG_W; 162 sc->sc_tclk_l_reg = RTC_NO_REG_W; 163 sc->sc_tclk_cnt_h_reg = RTC_NO_REG_W; 164 sc->sc_tclk_cnt_l_reg = RTC_NO_REG_W; 165 } else { 166 panic("%s: unknown base address 0x%lx", 167 sc->sc_dev.dv_xname, va->va_addr); 168 } 169 #endif /* SINGLE_VRIP_BASE */ 170 171 sc->sc_iot = va->va_iot; 172 if (bus_space_map(sc->sc_iot, va->va_addr, va->va_size, 173 0 /* no flags */, &sc->sc_ioh)) { 174 printf("vrrtc_attach: can't map i/o space\n"); 175 return; 176 } 177 /* RTC interrupt handler is directly dispatched from CPU intr */ 178 vr_intr_establish(VR_INTR1, vrrtc_intr, sc); 179 /* But need to set level 1 interrupt mask register, 180 * so regsiter fake interrurpt handler 181 */ 182 if (!(sc->sc_ih = vrip_intr_establish(va->va_vc, va->va_unit, 0, 183 IPL_CLOCK, 0, 0))) { 184 printf (":can't map interrupt.\n"); 185 return; 186 } 187 /* 188 * Rtc is attached to call this routine 189 * before cpu_initclock() calls clock_init(). 190 * So we must disable all interrupt for now. 191 */ 192 /* 193 * Disable all rtc interrupts 194 */ 195 /* Disable Elapse compare intr */ 196 bus_space_write_2(sc->sc_iot, sc->sc_ioh, ECMP_H_REG_W, 0); 197 bus_space_write_2(sc->sc_iot, sc->sc_ioh, ECMP_M_REG_W, 0); 198 bus_space_write_2(sc->sc_iot, sc->sc_ioh, ECMP_L_REG_W, 0); 199 /* Disable RTC Long1 intr */ 200 bus_space_write_2(sc->sc_iot, sc->sc_ioh, RTCL1_H_REG_W, 0); 201 bus_space_write_2(sc->sc_iot, sc->sc_ioh, RTCL1_L_REG_W, 0); 202 /* Disable RTC Long2 intr */ 203 bus_space_write_2(sc->sc_iot, sc->sc_ioh, RTCL2_H_REG_W, 0); 204 bus_space_write_2(sc->sc_iot, sc->sc_ioh, RTCL2_L_REG_W, 0); 205 /* Disable RTC TCLK intr */ 206 if (TCLK_H_REG_W != RTC_NO_REG_W) { 207 bus_space_write_2(sc->sc_iot, sc->sc_ioh, TCLK_H_REG_W, 0); 208 bus_space_write_2(sc->sc_iot, sc->sc_ioh, TCLK_L_REG_W, 0); 209 } 210 /* 211 * Clear all rtc intrrupts. 212 */ 213 bus_space_write_2(sc->sc_iot, sc->sc_ioh, RTCINT_REG_W, RTCINT_ALL); 214 215 platform_clock_attach(sc, &vr_clock); 216 } 217 218 int 219 vrrtc_intr(void *arg, u_int32_t pc, u_int32_t statusReg) 220 { 221 struct vrrtc_softc *sc = arg; 222 struct clockframe cf; 223 224 bus_space_write_2(sc->sc_iot, sc->sc_ioh, RTCINT_REG_W, RTCINT_ALL); 225 cf.pc = pc; 226 cf.sr = statusReg; 227 hardclock(&cf); 228 229 return 0; 230 } 231 232 void 233 clock_init(struct device *dev) 234 { 235 struct vrrtc_softc *sc = (struct vrrtc_softc *)dev; 236 237 DDUMP_REGS(sc); 238 /* 239 * Set tick (CLOCK_RATE) 240 */ 241 bus_space_write_2(sc->sc_iot, sc->sc_ioh, RTCL1_H_REG_W, 0); 242 bus_space_write_2(sc->sc_iot, sc->sc_ioh, RTCL1_L_REG_W, 243 RTCL1_L_HZ/CLOCK_RATE); 244 } 245 246 void 247 clock_get(struct device *dev, time_t base, struct clock_ymdhms *dt) 248 { 249 250 struct vrrtc_softc *sc = (struct vrrtc_softc *)dev; 251 bus_space_tag_t iot = sc->sc_iot; 252 bus_space_handle_t ioh = sc->sc_ioh; 253 u_int32_t timeh; /* elapse time (2*timeh sec) */ 254 u_int32_t timel; /* timel/32768 sec */ 255 256 timeh = bus_space_read_2(iot, ioh, ETIME_H_REG_W); 257 timeh = (timeh << 16) | bus_space_read_2(iot, ioh, ETIME_M_REG_W); 258 timel = bus_space_read_2(iot, ioh, ETIME_L_REG_W); 259 260 DPRINTF(("clock_get: timeh %08x timel %08x\n", timeh, timel)); 261 262 cvt_timehl_ymdhms(timeh, timel, dt); 263 264 DPRINTF(("clock_get: %d/%d/%d/%d/%d/%d\n", dt->dt_year, dt->dt_mon, 265 dt->dt_day, dt->dt_hour, dt->dt_min, dt->dt_sec)); 266 } 267 268 void 269 clock_set(struct device *dev, struct clock_ymdhms *dt) 270 { 271 struct vrrtc_softc *sc = (struct vrrtc_softc *)dev; 272 bus_space_tag_t iot = sc->sc_iot; 273 bus_space_handle_t ioh = sc->sc_ioh; 274 u_int32_t timeh; /* elapse time (2*timeh sec) */ 275 u_int32_t timel; /* timel/32768 sec */ 276 int year, month, sec2; 277 278 timeh = 0; 279 timel = 0; 280 281 DPRINTF(("clock_set: %d/%d/%d/%d/%d/%d\n", dt->dt_year, dt->dt_mon, 282 dt->dt_day, dt->dt_hour, dt->dt_min, dt->dt_sec)); 283 284 dt->dt_year += YBASE; 285 286 DPRINTF(("clock_set: %d/%d/%d/%d/%d/%d\n", dt->dt_year, dt->dt_mon, 287 dt->dt_day, dt->dt_hour, dt->dt_min, dt->dt_sec)); 288 289 year = EPOCHYEAR; 290 sec2 = LEAPYEAR4(year)?SEC2YR+SEC2DAY:SEC2YR; 291 while (year < dt->dt_year) { 292 year++; 293 timeh += sec2; 294 sec2 = LEAPYEAR4(year)?SEC2YR+SEC2DAY:SEC2YR; 295 } 296 month = 1; /* now month is 1..12 */ 297 sec2 = SEC2DAY * m2d[month-1]; 298 while (month < dt->dt_mon) { 299 month++; 300 timeh += sec2; 301 sec2 = SEC2DAY * m2d[month-1]; 302 if (month == 2 && LEAPYEAR4(year)) /* feb. and leapyear */ 303 sec2 += SEC2DAY; 304 } 305 306 timeh += (dt->dt_day - 1)*SEC2DAY; 307 308 timeh += dt->dt_hour*SEC2HOUR; 309 310 timeh += dt->dt_min*SEC2MIN; 311 312 timeh += dt->dt_sec/2; 313 timel += (dt->dt_sec%2)*ETIME_L_HZ; 314 315 timeh += EPOCHOFF; 316 timeh -= (rtc_offset*SEC2MIN); 317 318 #ifdef VRRTCDEBUG 319 cvt_timehl_ymdhms(timeh, timel, NULL); 320 #endif /* RTCDEBUG */ 321 322 bus_space_write_2(iot, ioh, ETIME_H_REG_W, (timeh >> 16) & 0xffff); 323 bus_space_write_2(iot, ioh, ETIME_M_REG_W, timeh & 0xffff); 324 bus_space_write_2(iot, ioh, ETIME_L_REG_W, timel); 325 } 326 327 void 328 cvt_timehl_ymdhms( 329 u_int32_t timeh, /* 2 sec */ 330 u_int32_t timel, /* 1/32768 sec */ 331 struct clock_ymdhms *dt) 332 { 333 u_int32_t year, month, date, hour, min, sec, sec2; 334 335 timeh -= EPOCHOFF; 336 337 timeh += (rtc_offset*SEC2MIN); 338 339 year = EPOCHYEAR; 340 sec2 = LEAPYEAR4(year)?SEC2YR+SEC2DAY:SEC2YR; 341 while (timeh > sec2) { 342 year++; 343 timeh -= sec2; 344 sec2 = LEAPYEAR4(year)?SEC2YR+SEC2DAY:SEC2YR; 345 } 346 347 DPRINTF(("cvt_timehl_ymdhms: timeh %08x year %d yrref %d\n", 348 timeh, year, sec2)); 349 350 month = 0; /* now month is 0..11 */ 351 sec2 = SEC2DAY * m2d[month]; 352 while (timeh > sec2) { 353 timeh -= sec2; 354 month++; 355 sec2 = SEC2DAY * m2d[month]; 356 if (month == 1 && LEAPYEAR4(year)) /* feb. and leapyear */ 357 sec2 += SEC2DAY; 358 } 359 month +=1; /* now month is 1..12 */ 360 361 DPRINTF(("cvt_timehl_ymdhms: timeh %08x month %d mref %d\n", 362 timeh, month, sec2)); 363 364 sec2 = SEC2DAY; 365 date = timeh/sec2+1; /* date is 1..31 */ 366 timeh -= (date-1)*sec2; 367 368 DPRINTF(("cvt_timehl_ymdhms: timeh %08x date %d dref %d\n", 369 timeh, date, sec2)); 370 371 sec2 = SEC2HOUR; 372 hour = timeh/sec2; 373 timeh -= hour*sec2; 374 375 sec2 = SEC2MIN; 376 min = timeh/sec2; 377 timeh -= min*sec2; 378 379 sec = timeh*2 + timel/ETIME_L_HZ; 380 381 DPRINTF(("cvt_timehl_ymdhms: hour %d min %d sec %d\n", hour, min, sec)); 382 383 if (dt) { 384 dt->dt_year = year - YBASE; /* base 1900 */ 385 dt->dt_mon = month; 386 dt->dt_day = date; 387 dt->dt_hour = hour; 388 dt->dt_min = min; 389 dt->dt_sec = sec; 390 } 391 } 392 393 void 394 vrrtc_dump_regs(struct vrrtc_softc *sc) 395 { 396 int timeh; 397 int timel; 398 399 timeh = bus_space_read_2(sc->sc_iot, sc->sc_ioh, ETIME_H_REG_W); 400 timel = bus_space_read_2(sc->sc_iot, sc->sc_ioh, ETIME_M_REG_W); 401 timel = (timel << 16) 402 | bus_space_read_2(sc->sc_iot, sc->sc_ioh, ETIME_L_REG_W); 403 printf("clock_init() Elapse Time %04x%04x\n", timeh, timel); 404 405 timeh = bus_space_read_2(sc->sc_iot, sc->sc_ioh, ECMP_H_REG_W); 406 timel = bus_space_read_2(sc->sc_iot, sc->sc_ioh, ECMP_M_REG_W); 407 timel = (timel << 16) 408 | bus_space_read_2(sc->sc_iot, sc->sc_ioh, ECMP_L_REG_W); 409 printf("clock_init() Elapse Compare %04x%04x\n", timeh, timel); 410 411 timeh = bus_space_read_2(sc->sc_iot, sc->sc_ioh, RTCL1_H_REG_W); 412 timel = bus_space_read_2(sc->sc_iot, sc->sc_ioh, RTCL1_L_REG_W); 413 printf("clock_init() LONG1 %04x%04x\n", timeh, timel); 414 415 timeh = bus_space_read_2(sc->sc_iot, sc->sc_ioh, RTCL1_CNT_H_REG_W); 416 timel = bus_space_read_2(sc->sc_iot, sc->sc_ioh, RTCL1_CNT_L_REG_W); 417 printf("clock_init() LONG1 CNTL %04x%04x\n", timeh, timel); 418 419 timeh = bus_space_read_2(sc->sc_iot, sc->sc_ioh, RTCL2_H_REG_W); 420 timel = bus_space_read_2(sc->sc_iot, sc->sc_ioh, RTCL2_L_REG_W); 421 printf("clock_init() LONG2 %04x%04x\n", timeh, timel); 422 423 timeh = bus_space_read_2(sc->sc_iot, sc->sc_ioh, RTCL2_CNT_H_REG_W); 424 timel = bus_space_read_2(sc->sc_iot, sc->sc_ioh, RTCL2_CNT_L_REG_W); 425 printf("clock_init() LONG2 CNTL %04x%04x\n", timeh, timel); 426 427 if (TCLK_H_REG_W != RTC_NO_REG_W) { 428 timeh = bus_space_read_2(sc->sc_iot, sc->sc_ioh, TCLK_H_REG_W); 429 timel = bus_space_read_2(sc->sc_iot, sc->sc_ioh, TCLK_L_REG_W); 430 printf("clock_init() TCLK %04x%04x\n", timeh, timel); 431 432 timeh = bus_space_read_2(sc->sc_iot, sc->sc_ioh, TCLK_CNT_H_REG_W); 433 timel = bus_space_read_2(sc->sc_iot, sc->sc_ioh, TCLK_CNT_L_REG_W); 434 printf("clock_init() TCLK CNTL %04x%04x\n", timeh, timel); 435 } 436 } 437