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