1 /* $NetBSD: rtc.c,v 1.7 2009/04/05 00:17:56 uwe Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by UCHIYAMA Yasushi. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: rtc.c,v 1.7 2009/04/05 00:17:56 uwe Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/kernel.h> 37 #include <sys/device.h> 38 #include <sys/malloc.h> 39 #include <sys/systm.h> 40 #ifdef GPROF 41 #include <sys/gmon.h> 42 #endif 43 44 #include <dev/clock_subr.h> 45 46 #include <sh3/rtcreg.h> 47 48 #if defined(DEBUG) && !defined(RTC_DEBUG) 49 #define RTC_DEBUG 50 #endif 51 52 53 struct rtc_softc { 54 device_t sc_dev; 55 56 int sc_valid; 57 struct todr_chip_handle sc_todr; 58 }; 59 60 static int rtc_match(device_t, cfdata_t, void *); 61 static void rtc_attach(device_t, device_t, void *); 62 63 CFATTACH_DECL_NEW(rtc, sizeof(struct rtc_softc), 64 rtc_match, rtc_attach, NULL, NULL); 65 66 67 /* todr(9) methods */ 68 static int rtc_gettime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *); 69 static int rtc_settime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *); 70 71 72 73 static int 74 rtc_match(device_t parent, cfdata_t cfp, void *aux) 75 { 76 77 return 1; 78 } 79 80 81 static void 82 rtc_attach(device_t parent, device_t self, void *aux) 83 { 84 struct rtc_softc *sc; 85 uint8_t r; 86 #ifdef RTC_DEBUG 87 char bits[128]; 88 #endif 89 90 aprint_naive("\n"); 91 aprint_normal("\n"); 92 93 sc = device_private(self); 94 sc->sc_dev = self; 95 96 r = _reg_read_1(SH_(RCR2)); 97 98 #ifdef RTC_DEBUG 99 snprintb(bits, sizeof(bits), SH_RCR2_BITS, r); 100 aprint_debug_dev(sc->sc_dev, "RCR2=%s\n", bits); 101 #endif 102 103 /* Was the clock running? */ 104 if ((r & (SH_RCR2_ENABLE | SH_RCR2_START)) == (SH_RCR2_ENABLE 105 | SH_RCR2_START)) 106 sc->sc_valid = 1; 107 else { 108 sc->sc_valid = 0; 109 aprint_error_dev(sc->sc_dev, "WARNING: clock was stopped\n"); 110 } 111 112 /* Disable carry and alarm interrupts */ 113 _reg_write_1(SH_(RCR1), 0); 114 115 /* Clock runs, no periodic interrupts, no 30-sec adjustment */ 116 _reg_write_1(SH_(RCR2), SH_RCR2_ENABLE | SH_RCR2_START); 117 118 sc->sc_todr.cookie = sc; 119 sc->sc_todr.todr_gettime_ymdhms = rtc_gettime_ymdhms; 120 sc->sc_todr.todr_settime_ymdhms = rtc_settime_ymdhms; 121 122 todr_attach(&sc->sc_todr); 123 124 #ifdef RTC_DEBUG 125 { 126 struct clock_ymdhms dt; 127 rtc_gettime_ymdhms(&sc->sc_todr, &dt); 128 } 129 #endif 130 131 if (!pmf_device_register(self, NULL, NULL)) 132 aprint_error_dev(self, "unable to establish power handler\n"); 133 } 134 135 136 static int 137 rtc_gettime_ymdhms(todr_chip_handle_t h, struct clock_ymdhms *dt) 138 { 139 struct rtc_softc *sc = h->cookie; 140 unsigned int year; 141 int retry = 8; 142 143 if (!sc->sc_valid) { 144 #ifdef RTC_DEBUG 145 aprint_debug_dev(sc->sc_dev, "gettime: not valid\n"); 146 /* but proceed and read/print it anyway */ 147 #else 148 return EIO; 149 #endif 150 } 151 152 /* disable carry interrupt */ 153 _reg_bclr_1(SH_(RCR1), SH_RCR1_CIE); 154 155 do { 156 uint8_t r = _reg_read_1(SH_(RCR1)); 157 r &= ~SH_RCR1_CF; 158 r |= SH_RCR1_AF; /* don't clear alarm flag */ 159 _reg_write_1(SH_(RCR1), r); 160 161 if (CPU_IS_SH3) 162 year = _reg_read_1(SH3_RYRCNT); 163 else 164 year = _reg_read_2(SH4_RYRCNT) & 0x00ff; 165 dt->dt_year = FROMBCD(year); 166 167 /* read counter */ 168 #define RTCGET(x, y) \ 169 dt->dt_ ## x = FROMBCD(_reg_read_1(SH_(R ## y ## CNT))) 170 171 RTCGET(mon, MON); 172 RTCGET(wday, WK); 173 RTCGET(day, DAY); 174 RTCGET(hour, HR); 175 RTCGET(min, MIN); 176 RTCGET(sec, SEC); 177 #undef RTCGET 178 } while ((_reg_read_1(SH_(RCR1)) & SH_RCR1_CF) && --retry > 0); 179 180 if (retry == 0) { 181 #ifdef RTC_DEBUG 182 aprint_debug_dev(sc->sc_dev, "gettime: retry failed\n"); 183 #endif 184 return EIO; 185 } 186 187 dt->dt_year += 1900; 188 if (dt->dt_year < POSIX_BASE_YEAR) 189 dt->dt_year += 100; 190 191 #ifdef RTC_DEBUG 192 aprint_debug_dev(sc->sc_dev, 193 "gettime: %04d-%02d-%02d %02d:%02d:%02d\n", 194 dt->dt_year, dt->dt_mon, dt->dt_day, 195 dt->dt_hour, dt->dt_min, dt->dt_sec); 196 197 if (!sc->sc_valid) 198 return EIO; 199 #endif 200 201 return 0; 202 } 203 204 205 static int 206 rtc_settime_ymdhms(todr_chip_handle_t h, struct clock_ymdhms *dt) 207 { 208 struct rtc_softc *sc = h->cookie; 209 unsigned int year; 210 uint8_t r; 211 212 year = TOBCD(dt->dt_year % 100); 213 214 r = _reg_read_1(SH_(RCR2)); 215 216 /* stop clock */ 217 _reg_write_1(SH_(RCR2), (r & ~SH_RCR2_START) | SH_RCR2_RESET); 218 219 /* set time */ 220 if (CPU_IS_SH3) 221 _reg_write_1(SH3_RYRCNT, year); 222 else 223 _reg_write_2(SH4_RYRCNT, year); 224 225 #define RTCSET(x, y) \ 226 _reg_write_1(SH_(R ## x ## CNT), TOBCD(dt->dt_ ## y)) 227 228 RTCSET(MON, mon); 229 RTCSET(WK, wday); 230 RTCSET(DAY, day); 231 RTCSET(HR, hour); 232 RTCSET(MIN, min); 233 RTCSET(SEC, sec); 234 235 #undef RTCSET 236 237 /* start clock */ 238 _reg_write_1(SH_(RCR2), r); 239 sc->sc_valid = 1; 240 241 #ifdef RTC_DEBUG 242 aprint_debug_dev(sc->sc_dev, 243 "settime: %04d-%02d-%02d %02d:%02d:%02d\n", 244 dt->dt_year, dt->dt_mon, dt->dt_day, 245 dt->dt_hour, dt->dt_min, dt->dt_sec); 246 #endif 247 248 return 0; 249 } 250