Home | History | Annotate | Line # | Download | only in ic
      1 /*      $NetBSD: msm6242b.c,v 1.7 2025/09/07 21:45:16 thorpej 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.7 2025/09/07 21:45:16 thorpej Exp $");
     34 
     35 /*
     36  * Driver for OKI MSM6242B Real Time Clock. Somewhat based on an ancient, amiga
     37  * specific 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 
     51 static int msm6242b_gettime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
     52 int msm6242b_settime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
     53 
     54 bool msm6242b_hold(struct msm6242b_softc *sc);
     55 void msm6242b_free(struct msm6242b_softc *sc);
     56 static uint8_t msm6242b_read(struct msm6242b_softc *, uint8_t);
     57 static void msm6242b_write(struct msm6242b_softc *, uint8_t, uint8_t);
     58 static void msm6242b_set(struct msm6242b_softc *, uint8_t, uint8_t);
     59 static void msm6242b_unset(struct msm6242b_softc *, uint8_t, uint8_t);
     60 
     61 void
     62 msm6242b_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->todr_dev = sc->sc_dev;
     71 	handle->todr_gettime_ymdhms = msm6242b_gettime_ymdhms;
     72 	handle->todr_settime_ymdhms = msm6242b_settime_ymdhms;
     73 
     74 	if (msm6242b_gettime_ymdhms(handle, &dt) != 0) {
     75 		aprint_error_dev(sc->sc_dev, "RTC does not work correctly\n");
     76 		return;
     77 	}
     78 
     79 #ifdef MSM6242B_DEBUG
     80 	aprint_normal_dev(sc->sc_dev, "the time is %d %d %d %d %d %d\n",
     81 	    dt.dt_year, dt.dt_mon, dt.dt_day, dt.dt_hour, dt.dt_min, dt.dt_sec);
     82 #endif /* MSM6242B_DEBUG */
     83 	todr_attach(handle);
     84 }
     85 
     86 static int
     87 msm6242b_gettime_ymdhms(todr_chip_handle_t handle, struct clock_ymdhms *dt)
     88 {
     89 	struct msm6242b_softc *sc = device_private(handle->todr_dev);
     90 
     91 	/* XXX: splsched(); */
     92 
     93 	if(!msm6242b_hold(sc))
     94 		return (ENXIO);
     95 
     96 	dt->dt_sec = msm6242b_read(sc, MSM6242B_10SECOND) * 10 +
     97 	    msm6242b_read(sc, MSM6242B_1SECOND);
     98 	dt->dt_min = msm6242b_read(sc, MSM6242B_10MINUTE) * 10 +
     99 	    msm6242b_read(sc, MSM6242B_1MINUTE);
    100 	dt->dt_hour = (msm6242b_read(sc, MSM6242B_10HOUR_PMAM) &
    101 	    MSM6242B_10HOUR_MASK) * 10 + msm6242b_read(sc, MSM6242B_1HOUR);
    102 	dt->dt_day = msm6242b_read(sc, MSM6242B_10DAY) * 10 +
    103 	    msm6242b_read(sc, MSM6242B_1DAY);
    104 	dt->dt_mon = msm6242b_read(sc, MSM6242B_10MONTH) * 10 +
    105 	    msm6242b_read(sc, MSM6242B_1MONTH);
    106 	dt->dt_year = msm6242b_read(sc, MSM6242B_10YEAR) * 10 +
    107 	    msm6242b_read(sc, MSM6242B_1YEAR);
    108 	dt->dt_wday = msm6242b_read(sc, MSM6242B_WEEK);
    109 
    110 #ifdef MSM6242B_DEBUG
    111 	aprint_normal_dev(sc->sc_dev, "the time is %d %d %d %d %d %d\n",
    112 	    dt->dt_year, dt->dt_mon, dt->dt_day, dt->dt_hour, dt->dt_min, dt->dt_sec);
    113 #endif /* MSM6242B_DEBUG */
    114 
    115 	/* handle 12h mode */
    116 	if ((msm6242b_read(sc, MSM6242B_CONTROL_F) &
    117 	    MSM6242B_CONTROL_F_24H) == 0) {
    118 		if ((msm6242b_read(sc, MSM6242B_10HOUR_PMAM) &
    119 		    MSM6242B_PMAM_BIT) == 0 && dt->dt_hour == 12)
    120 			dt->dt_hour = 0;
    121 		else if ((msm6242b_read(sc, MSM6242B_10HOUR_PMAM) &
    122 		    MSM6242B_PMAM_BIT) && dt->dt_hour != 12)
    123 			dt->dt_hour += 12;
    124 	}
    125 
    126 	msm6242b_free(sc);
    127 
    128 	dt->dt_year += MSM6242B_BASE_YEAR;
    129 	if (dt->dt_year < POSIX_BASE_YEAR)
    130 		dt->dt_year += 100;
    131 
    132 	if ((dt->dt_hour > 23) ||
    133 	    (dt->dt_day  > 31) ||
    134 	    (dt->dt_mon  > 12) ||
    135 	    (dt->dt_year > 2036))
    136 		return (EINVAL);
    137 
    138 	return 0;
    139 }
    140 
    141 bool
    142 msm6242b_hold(struct msm6242b_softc *sc)
    143 {
    144 	int try;
    145 
    146 #define TRY_MAX 10
    147 	for (try = 0; try < TRY_MAX; try++) {
    148 		msm6242b_set(sc, MSM6242B_CONTROL_D, MSM6242B_CONTROL_D_HOLD);
    149 		if (msm6242b_read(sc, MSM6242B_CONTROL_D)
    150 		    & MSM6242B_CONTROL_D_BUSY) {
    151 #ifdef MSM6242B_DEBUG
    152 			aprint_normal_dev(sc->sc_dev, "gotta idle\n");
    153 #endif /* MSM6242B_DEBUG */
    154 			msm6242b_unset(sc, MSM6242B_CONTROL_D,
    155 			    MSM6242B_CONTROL_D_HOLD);
    156 			delay(70);
    157 		} else {
    158 #ifdef MSM6242B_DEBUG
    159 			aprint_normal_dev(sc->sc_dev, "not busy\n");
    160 #endif /* MSM6242B_DEBUG */
    161 			break;
    162 		}
    163 	}
    164 
    165 	if (try == TRY_MAX) {
    166 		aprint_error_dev(sc->sc_dev, "can't hold the chip\n");
    167 		return false;
    168 	}
    169 	return true;
    170 }
    171 
    172 void
    173 msm6242b_free(struct msm6242b_softc *sc)
    174 {
    175 	msm6242b_unset(sc, MSM6242B_CONTROL_D, MSM6242B_CONTROL_D_HOLD);
    176 }
    177 
    178 static uint8_t
    179 msm6242b_read(struct msm6242b_softc *sc, uint8_t reg)
    180 {
    181 	uint8_t r;
    182 	r = bus_space_read_1(sc->sc_iot, sc->sc_ioh, reg) & MSM6242B_MASK;
    183 	return r;
    184 }
    185 
    186 static void
    187 msm6242b_write(struct msm6242b_softc *sc, uint8_t reg, uint8_t val)
    188 {
    189 	bus_space_write_1(sc->sc_iot, sc->sc_ioh, reg, val);
    190 }
    191 
    192 static void
    193 msm6242b_set(struct msm6242b_softc *sc, uint8_t reg, uint8_t bits)
    194 {
    195 	uint8_t v;
    196 	v = msm6242b_read(sc, reg) | bits;
    197 	msm6242b_write(sc, reg, v);
    198 }
    199 
    200 static void
    201 msm6242b_unset(struct msm6242b_softc *sc, uint8_t reg, uint8_t bits)
    202 {
    203 	uint8_t v;
    204 	v = msm6242b_read(sc, reg) & ~bits;
    205 	msm6242b_write(sc, reg, v);
    206 }
    207 
    208 int
    209 msm6242b_settime_ymdhms(todr_chip_handle_t handle, struct clock_ymdhms *dt)
    210 {
    211 	struct msm6242b_softc *sc = device_private(handle->todr_dev);
    212 	int ampm;
    213 
    214 	/* XXX: splsched(); */
    215 
    216 	if(!msm6242b_hold(sc))
    217 		return (ENXIO);
    218 
    219 	ampm = 0;
    220 	if ((msm6242b_read(sc, MSM6242B_CONTROL_F) &
    221 	    MSM6242B_CONTROL_F_24H) == 0) {
    222 		if (dt->dt_hour >= 12) {
    223 			ampm = MSM6242B_CONTROL_F_24H;
    224 			if (dt->dt_hour != 12)
    225 				dt->dt_hour -= 12;
    226 		} else if (dt->dt_hour == 0) {
    227 			dt->dt_hour = 12;
    228 		}
    229 	}
    230 
    231 	msm6242b_write(sc, MSM6242B_10HOUR_PMAM, (dt->dt_hour / 10) | ampm);
    232 	msm6242b_write(sc, MSM6242B_1HOUR, dt->dt_hour % 10);
    233 	msm6242b_write(sc, MSM6242B_10SECOND, dt->dt_sec / 10);
    234 	msm6242b_write(sc, MSM6242B_1SECOND, dt->dt_sec % 10);
    235 	msm6242b_write(sc, MSM6242B_10MINUTE, dt->dt_min / 10);
    236 	msm6242b_write(sc, MSM6242B_1MINUTE, dt->dt_min % 10);
    237 	msm6242b_write(sc, MSM6242B_10DAY, dt->dt_day / 10);
    238 	msm6242b_write(sc, MSM6242B_1DAY, dt->dt_day % 10);
    239 	msm6242b_write(sc, MSM6242B_10MONTH, dt->dt_mon / 10);
    240 	msm6242b_write(sc, MSM6242B_1MONTH, dt->dt_mon % 10);
    241 	msm6242b_write(sc, MSM6242B_10YEAR, (dt->dt_year / 10) % 10);
    242 	msm6242b_write(sc, MSM6242B_1YEAR, dt->dt_year % 10);
    243 	msm6242b_write(sc, MSM6242B_WEEK, dt->dt_wday);
    244 
    245 	msm6242b_free(sc);
    246 
    247 	return 0;
    248 }
    249 
    250