Home | History | Annotate | Line # | Download | only in ic
msm6242b.c revision 1.5
      1 /*      $NetBSD: msm6242b.c,v 1.5 2024/09/01 19:56:18 andvar 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.5 2024/09/01 19:56:18 andvar 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 = 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 
     89 static int
     90 msm6242b_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 
    145 bool
    146 msm6242b_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 
    176 void
    177 msm6242b_free(struct msm6242b_softc *sc)
    178 {
    179 	msm6242b_unset(sc, MSM6242B_CONTROL_D, MSM6242B_CONTROL_D_HOLD);
    180 }
    181 
    182 static uint8_t
    183 msm6242b_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 
    190 static void
    191 msm6242b_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 
    196 static void
    197 msm6242b_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 
    204 static void
    205 msm6242b_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 
    212 int
    213 msm6242b_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