1/* $NetBSD: msm6242b.c,v 1.4 2018/02/06 13:26:32 rin Exp $ */ 2 3/*- 4 * Copyright (c) 2012 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Radoslaw Kujawa. 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: msm6242b.c,v 1.4 2018/02/06 13:26:32 rin Exp $"); 34 35/* 36 * Driver for OKI MSM6242B Real Time Clock. Somewhat based on an ancient, amiga 37 * specifc a2kbbc driver (which was turned into frontend to this driver). 38 */ 39 40#include <sys/param.h> 41#include <sys/device.h> 42#include <sys/bus.h> 43 44#include <dev/clock_subr.h> 45 46#include <dev/ic/msm6242bvar.h> 47#include <dev/ic/msm6242breg.h> 48 49/* #define MSM6242B_DEBUG 1 */ 50 51static int msm6242b_gettime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *); 52int msm6242b_settime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *); 53 54bool msm6242b_hold(struct msm6242b_softc *sc); 55void msm6242b_free(struct msm6242b_softc *sc); 56static uint8_t msm6242b_read(struct msm6242b_softc *, uint8_t); 57static void msm6242b_write(struct msm6242b_softc *, uint8_t, uint8_t); 58static void msm6242b_set(struct msm6242b_softc *, uint8_t, uint8_t); 59static void msm6242b_unset(struct msm6242b_softc *, uint8_t, uint8_t); 60 61void 62msm6242b_attach(struct msm6242b_softc *sc) 63{ 64 struct clock_ymdhms dt; 65 66 todr_chip_handle_t handle; 67 aprint_normal(": OKI MSM6242B\n"); 68 69 handle = &sc->sc_handle; 70 handle->cookie = sc; 71 handle->todr_gettime = NULL; 72 handle->todr_settime = NULL; 73 handle->todr_gettime_ymdhms = msm6242b_gettime_ymdhms; 74 handle->todr_settime_ymdhms = msm6242b_settime_ymdhms; 75 handle->todr_setwen = NULL; 76 77 if (msm6242b_gettime_ymdhms(handle, &dt) != 0) { 78 aprint_error_dev(sc->sc_dev, "RTC does not work correctly\n"); 79 return; 80 } 81 82#ifdef MSM6242B_DEBUG 83 aprint_normal_dev(sc->sc_dev, "the time is %d %d %d %d %d %d\n", 84 dt.dt_year, dt.dt_mon, dt.dt_day, dt.dt_hour, dt.dt_min, dt.dt_sec); 85#endif /* MSM6242B_DEBUG */ 86 todr_attach(handle); 87} 88 89static int 90msm6242b_gettime_ymdhms(todr_chip_handle_t handle, struct clock_ymdhms *dt) 91{ 92 struct msm6242b_softc *sc; 93 94 sc = handle->cookie; 95 /* XXX: splsched(); */ 96 97 if(!msm6242b_hold(sc)) 98 return (ENXIO); 99 100 dt->dt_sec = msm6242b_read(sc, MSM6242B_10SECOND) * 10 + 101 msm6242b_read(sc, MSM6242B_1SECOND); 102 dt->dt_min = msm6242b_read(sc, MSM6242B_10MINUTE) * 10 + 103 msm6242b_read(sc, MSM6242B_1MINUTE); 104 dt->dt_hour = (msm6242b_read(sc, MSM6242B_10HOUR_PMAM) & 105 MSM6242B_10HOUR_MASK) * 10 + msm6242b_read(sc, MSM6242B_1HOUR); 106 dt->dt_day = msm6242b_read(sc, MSM6242B_10DAY) * 10 + 107 msm6242b_read(sc, MSM6242B_1DAY); 108 dt->dt_mon = msm6242b_read(sc, MSM6242B_10MONTH) * 10 + 109 msm6242b_read(sc, MSM6242B_1MONTH); 110 dt->dt_year = msm6242b_read(sc, MSM6242B_10YEAR) * 10 + 111 msm6242b_read(sc, MSM6242B_1YEAR); 112 dt->dt_wday = msm6242b_read(sc, MSM6242B_WEEK); 113 114#ifdef MSM6242B_DEBUG 115 aprint_normal_dev(sc->sc_dev, "the time is %d %d %d %d %d %d\n", 116 dt->dt_year, dt->dt_mon, dt->dt_day, dt->dt_hour, dt->dt_min, dt->dt_sec); 117#endif /* MSM6242B_DEBUG */ 118 119 /* handle 12h mode */ 120 if ((msm6242b_read(sc, MSM6242B_CONTROL_F) & 121 MSM6242B_CONTROL_F_24H) == 0) { 122 if ((msm6242b_read(sc, MSM6242B_10HOUR_PMAM) & 123 MSM6242B_PMAM_BIT) == 0 && dt->dt_hour == 12) 124 dt->dt_hour = 0; 125 else if ((msm6242b_read(sc, MSM6242B_10HOUR_PMAM) & 126 MSM6242B_PMAM_BIT) && dt->dt_hour != 12) 127 dt->dt_hour += 12; 128 } 129 130 msm6242b_free(sc); 131 132 dt->dt_year += MSM6242B_BASE_YEAR; 133 if (dt->dt_year < POSIX_BASE_YEAR) 134 dt->dt_year += 100; 135 136 if ((dt->dt_hour > 23) || 137 (dt->dt_day > 31) || 138 (dt->dt_mon > 12) || 139 (dt->dt_year > 2036)) 140 return (EINVAL); 141 142 return 0; 143} 144 145bool 146msm6242b_hold(struct msm6242b_softc *sc) 147{ 148 int try; 149 150#define TRY_MAX 10 151 for (try = 0; try < TRY_MAX; try++) { 152 msm6242b_set(sc, MSM6242B_CONTROL_D, MSM6242B_CONTROL_D_HOLD); 153 if (msm6242b_read(sc, MSM6242B_CONTROL_D) 154 & MSM6242B_CONTROL_D_BUSY) { 155#ifdef MSM6242B_DEBUG 156 aprint_normal_dev(sc->sc_dev, "gotta idle\n"); 157#endif /* MSM6242B_DEBUG */ 158 msm6242b_unset(sc, MSM6242B_CONTROL_D, 159 MSM6242B_CONTROL_D_HOLD); 160 delay(70); 161 } else { 162#ifdef MSM6242B_DEBUG 163 aprint_normal_dev(sc->sc_dev, "not busy\n"); 164#endif /* MSM6242B_DEBUG */ 165 break; 166 } 167 } 168 169 if (try == TRY_MAX) { 170 aprint_error_dev(sc->sc_dev, "can't hold the chip\n"); 171 return false; 172 } 173 return true; 174} 175 176void 177msm6242b_free(struct msm6242b_softc *sc) 178{ 179 msm6242b_unset(sc, MSM6242B_CONTROL_D, MSM6242B_CONTROL_D_HOLD); 180} 181 182static uint8_t 183msm6242b_read(struct msm6242b_softc *sc, uint8_t reg) 184{ 185 uint8_t r; 186 r = bus_space_read_1(sc->sc_iot, sc->sc_ioh, reg) & MSM6242B_MASK; 187 return r; 188} 189 190static void 191msm6242b_write(struct msm6242b_softc *sc, uint8_t reg, uint8_t val) 192{ 193 bus_space_write_1(sc->sc_iot, sc->sc_ioh, reg, val); 194} 195 196static void 197msm6242b_set(struct msm6242b_softc *sc, uint8_t reg, uint8_t bits) 198{ 199 uint8_t v; 200 v = msm6242b_read(sc, reg) | bits; 201 msm6242b_write(sc, reg, v); 202} 203 204static void 205msm6242b_unset(struct msm6242b_softc *sc, uint8_t reg, uint8_t bits) 206{ 207 uint8_t v; 208 v = msm6242b_read(sc, reg) & ~bits; 209 msm6242b_write(sc, reg, v); 210} 211 212int 213msm6242b_settime_ymdhms(todr_chip_handle_t handle, struct clock_ymdhms *dt) 214{ 215 struct msm6242b_softc *sc; 216 int ampm; 217 /* XXX: splsched(); */ 218 219 sc = handle->cookie; 220 221 if(!msm6242b_hold(sc)) 222 return (ENXIO); 223 224 ampm = 0; 225 if ((msm6242b_read(sc, MSM6242B_CONTROL_F) & 226 MSM6242B_CONTROL_F_24H) == 0) { 227 if (dt->dt_hour >= 12) { 228 ampm = MSM6242B_CONTROL_F_24H; 229 if (dt->dt_hour != 12) 230 dt->dt_hour -= 12; 231 } else if (dt->dt_hour == 0) { 232 dt->dt_hour = 12; 233 } 234 } 235 236 msm6242b_write(sc, MSM6242B_10HOUR_PMAM, (dt->dt_hour / 10) | ampm); 237 msm6242b_write(sc, MSM6242B_1HOUR, dt->dt_hour % 10); 238 msm6242b_write(sc, MSM6242B_10SECOND, dt->dt_sec / 10); 239 msm6242b_write(sc, MSM6242B_1SECOND, dt->dt_sec % 10); 240 msm6242b_write(sc, MSM6242B_10MINUTE, dt->dt_min / 10); 241 msm6242b_write(sc, MSM6242B_1MINUTE, dt->dt_min % 10); 242 msm6242b_write(sc, MSM6242B_10DAY, dt->dt_day / 10); 243 msm6242b_write(sc, MSM6242B_1DAY, dt->dt_day % 10); 244 msm6242b_write(sc, MSM6242B_10MONTH, dt->dt_mon / 10); 245 msm6242b_write(sc, MSM6242B_1MONTH, dt->dt_mon % 10); 246 msm6242b_write(sc, MSM6242B_10YEAR, (dt->dt_year / 10) % 10); 247 msm6242b_write(sc, MSM6242B_1YEAR, dt->dt_year % 10); 248 msm6242b_write(sc, MSM6242B_WEEK, dt->dt_wday); 249 250 msm6242b_free(sc); 251 252 return 0; 253} 254 255