1 /* $NetBSD: rs5c313.c,v 1.2 2007/11/06 01:46:02 uwe Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the NetBSD 18 * Foundation, Inc. and its contributors. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: rs5c313.c,v 1.2 2007/11/06 01:46:02 uwe Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/device.h> 39 #include <sys/kernel.h> 40 41 #include <dev/clock_subr.h> 42 43 #include <dev/ic/rs5c313reg.h> 44 #include <dev/ic/rs5c313var.h> 45 46 47 /* todr(9) methods */ 48 static int rs5c313_todr_gettime(todr_chip_handle_t, volatile struct timeval *); 49 static int rs5c313_todr_settime(todr_chip_handle_t, volatile struct timeval *); 50 51 /* sugar for chip access */ 52 #define rtc_begin(sc) ((*sc->sc_ops->rs5c313_op_begin)(sc)) 53 #define rtc_ce(sc, onoff) ((*sc->sc_ops->rs5c313_op_ce)(sc, onoff)) 54 #define rtc_clk(sc, onoff) ((*sc->sc_ops->rs5c313_op_clk)(sc, onoff)) 55 #define rtc_dir(sc, output) ((*sc->sc_ops->rs5c313_op_dir)(sc, output)) 56 #define rtc_di(sc) ((*sc->sc_ops->rs5c313_op_read)(sc)) 57 #define rtc_do(sc, bit) ((*sc->sc_ops->rs5c313_op_write)(sc, bit)) 58 59 static int rs5c313_init(struct rs5c313_softc *); 60 static int rs5c313_read_reg(struct rs5c313_softc *, int); 61 static void rs5c313_write_reg(struct rs5c313_softc *, int, int); 62 63 64 void 65 rs5c313_attach(struct rs5c313_softc *sc) 66 { 67 68 aprint_naive("\n"); 69 aprint_normal(": real time clock\n"); 70 71 sc->sc_todr.cookie = sc; 72 sc->sc_todr.todr_gettime = rs5c313_todr_gettime; 73 sc->sc_todr.todr_settime = rs5c313_todr_settime; 74 sc->sc_todr.todr_setwen = NULL; 75 76 if (rs5c313_init(sc) != 0) { 77 aprint_error_dev(&sc->sc_dev, "init failed\n"); 78 return; 79 } 80 81 todr_attach(&sc->sc_todr); 82 } 83 84 85 static int 86 rs5c313_init(struct rs5c313_softc *sc) 87 { 88 int status = 0; 89 int retry; 90 91 rtc_ce(sc, 0); 92 93 rtc_begin(sc); 94 rtc_ce(sc, 1); 95 96 if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_XSTP) == 0) { 97 sc->sc_valid = 1; 98 goto done; 99 } 100 101 sc->sc_valid = 0; 102 aprint_error_dev(&sc->sc_dev, "time not valid\n"); 103 104 rs5c313_write_reg(sc, RS5C313_TINT, 0); 105 rs5c313_write_reg(sc, RS5C313_CTRL, (CTRL_BASE | CTRL_ADJ)); 106 107 for (retry = 1000; retry > 0; --retry) { 108 if (rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY) 109 delay(1); 110 else 111 break; 112 } 113 114 if (retry == 0) { 115 status = EIO; 116 goto done; 117 } 118 119 rs5c313_write_reg(sc, RS5C313_CTRL, CTRL_BASE); 120 121 done: 122 rtc_ce(sc, 0); 123 return status; 124 } 125 126 127 static int 128 rs5c313_todr_gettime(todr_chip_handle_t todr, volatile struct timeval *tv) 129 { 130 struct rs5c313_softc *sc = todr->cookie; 131 struct clock_ymdhms dt; 132 int retry; 133 int s; 134 135 /* 136 * If chip had invalid data on init, don't bother reading 137 * bogus values, let todr(9) cope. 138 */ 139 if (sc->sc_valid == 0) 140 return EIO; 141 142 s = splhigh(); 143 144 rtc_begin(sc); 145 for (retry = 10; retry > 0; --retry) { 146 rtc_ce(sc, 1); 147 148 rs5c313_write_reg(sc, RS5C313_CTRL, CTRL_BASE); 149 if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY) == 0) 150 break; 151 152 rtc_ce(sc, 0); 153 delay(1); 154 } 155 156 if (retry == 0) { 157 splx(s); 158 return EIO; 159 } 160 161 #define RTCGET(x, y) \ 162 do { \ 163 int ones = rs5c313_read_reg(sc, RS5C313_ ## y ## 1); \ 164 int tens = rs5c313_read_reg(sc, RS5C313_ ## y ## 10); \ 165 dt.dt_ ## x = tens * 10 + ones; \ 166 } while (/* CONSTCOND */0) 167 168 RTCGET(sec, SEC); 169 RTCGET(min, MIN); 170 RTCGET(hour, HOUR); 171 RTCGET(day, DAY); 172 RTCGET(mon, MON); 173 RTCGET(year, YEAR); 174 #undef RTCGET 175 dt.dt_wday = rs5c313_read_reg(sc, RS5C313_WDAY); 176 177 rtc_ce(sc, 0); 178 splx(s); 179 180 dt.dt_year = (dt.dt_year % 100) + 1900; 181 if (dt.dt_year < 1970) { 182 dt.dt_year += 100; 183 } 184 185 /* 186 * If time_t is 32 bits, then the "End of Time" is 187 * Mon Jan 18 22:14:07 2038 (US/Eastern) 188 * This code copes with RTC's past the end of time if time_t 189 * is an int32 or less. Needed because sometimes RTCs screw 190 * up or are badly set, and that would cause the time to go 191 * negative in the calculation below, which causes Very Bad 192 * Mojo. This at least lets the user boot and fix the problem. 193 * Note the code is self eliminating once time_t goes to 64 bits. 194 */ 195 if (/* CONSTCOND */ sizeof(time_t) <= sizeof(int32_t)) { 196 if (dt.dt_year >= 2038) { 197 return -1; 198 } 199 } 200 201 tv->tv_sec = clock_ymdhms_to_secs(&dt) + rtc_offset * 60; 202 tv->tv_usec = 0; 203 204 return 0; 205 } 206 207 208 static int 209 rs5c313_todr_settime(todr_chip_handle_t todr, volatile struct timeval *tv) 210 { 211 struct rs5c313_softc *sc = todr->cookie; 212 struct clock_ymdhms dt; 213 int retry; 214 int t; 215 int s; 216 217 clock_secs_to_ymdhms(tv->tv_sec - rtc_offset * 60, &dt); 218 219 s = splhigh(); 220 221 rtc_begin(sc); 222 for (retry = 10; retry > 0; --retry) { 223 rtc_ce(sc, 1); 224 225 rs5c313_write_reg(sc, RS5C313_CTRL, CTRL_BASE); 226 if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY) == 0) 227 break; 228 229 rtc_ce(sc, 0); 230 delay(1); 231 } 232 233 if (retry == 0) { 234 splx(s); 235 return EIO; 236 } 237 238 #define RTCSET(x, y) \ 239 do { \ 240 t = TOBCD(dt.dt_ ## y) & 0xff; \ 241 rs5c313_write_reg(sc, RS5C313_ ## x ## 1, t & 0x0f); \ 242 rs5c313_write_reg(sc, RS5C313_ ## x ## 10, (t >> 4) & 0x0f); \ 243 } while (/* CONSTCOND */0) 244 245 RTCSET(SEC, sec); 246 RTCSET(MIN, min); 247 RTCSET(HOUR, hour); 248 RTCSET(DAY, day); 249 RTCSET(MON, mon); 250 251 #undef RTCSET 252 253 t = dt.dt_year % 100; 254 t = TOBCD(t); 255 rs5c313_write_reg(sc, RS5C313_YEAR1, t & 0x0f); 256 rs5c313_write_reg(sc, RS5C313_YEAR10, (t >> 4) & 0x0f); 257 258 rs5c313_write_reg(sc, RS5C313_WDAY, dt.dt_wday); 259 260 rtc_ce(sc, 0); 261 splx(s); 262 263 sc->sc_valid = 1; 264 return 0; 265 } 266 267 268 static int 269 rs5c313_read_reg(struct rs5c313_softc *sc, int addr) 270 { 271 int data; 272 273 /* output */ 274 rtc_dir(sc, 1); 275 276 /* control */ 277 rtc_do(sc, 1); /* ignored */ 278 rtc_do(sc, 1); /* R/#W = 1(READ) */ 279 rtc_do(sc, 1); /* AD = 1 */ 280 rtc_do(sc, 0); /* DT = 0 */ 281 282 /* address */ 283 rtc_do(sc, addr & 0x8); /* A3 */ 284 rtc_do(sc, addr & 0x4); /* A2 */ 285 rtc_do(sc, addr & 0x2); /* A1 */ 286 rtc_do(sc, addr & 0x1); /* A0 */ 287 288 /* input */ 289 rtc_dir(sc, 0); 290 291 /* ignore */ 292 (void)rtc_di(sc); 293 (void)rtc_di(sc); 294 (void)rtc_di(sc); 295 (void)rtc_di(sc); 296 297 /* data */ 298 data = rtc_di(sc); /* D3 */ 299 data <<= 1; 300 data |= rtc_di(sc); /* D2 */ 301 data <<= 1; 302 data |= rtc_di(sc); /* D1 */ 303 data <<= 1; 304 data |= rtc_di(sc); /* D0 */ 305 306 return data; 307 } 308 309 310 static void 311 rs5c313_write_reg(struct rs5c313_softc *sc, int addr, int data) 312 { 313 314 /* output */ 315 rtc_dir(sc, 1); 316 317 /* control */ 318 rtc_do(sc, 1); /* ignored */ 319 rtc_do(sc, 0); /* R/#W = 0 (WRITE) */ 320 rtc_do(sc, 1); /* AD = 1 */ 321 rtc_do(sc, 0); /* DT = 0 */ 322 323 /* address */ 324 rtc_do(sc, addr & 0x8); /* A3 */ 325 rtc_do(sc, addr & 0x4); /* A2 */ 326 rtc_do(sc, addr & 0x2); /* A1 */ 327 rtc_do(sc, addr & 0x1); /* A0 */ 328 329 /* control */ 330 rtc_do(sc, 1); /* ignored */ 331 rtc_do(sc, 0); /* R/#W = 0(WRITE) */ 332 rtc_do(sc, 0); /* AD = 0 */ 333 rtc_do(sc, 1); /* DT = 1 */ 334 335 /* data */ 336 rtc_do(sc, data & 0x8); /* D3 */ 337 rtc_do(sc, data & 0x4); /* D2 */ 338 rtc_do(sc, data & 0x2); /* D1 */ 339 rtc_do(sc, data & 0x1); /* D0 */ 340 } 341