Home | History | Annotate | Line # | Download | only in ic
      1 /*	$NetBSD: rs5c313.c,v 1.11 2025/09/07 21:45:16 thorpej 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  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: rs5c313.c,v 1.11 2025/09/07 21:45:16 thorpej Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/systm.h>
     34 #include <sys/device.h>
     35 #include <sys/kernel.h>
     36 
     37 #include <dev/clock_subr.h>
     38 
     39 #include <dev/ic/rs5c313reg.h>
     40 #include <dev/ic/rs5c313var.h>
     41 
     42 
     43 /* todr(9) methods */
     44 static int rs5c313_todr_gettime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
     45 static int rs5c313_todr_settime_ymdhms(todr_chip_handle_t, struct clock_ymdhms *);
     46 
     47 /* sugar for chip access */
     48 #define rtc_begin(sc)		((*sc->sc_ops->rs5c313_op_begin)(sc))
     49 #define rtc_ce(sc, onoff)	((*sc->sc_ops->rs5c313_op_ce)(sc, onoff))
     50 #define rtc_clk(sc, onoff)	((*sc->sc_ops->rs5c313_op_clk)(sc, onoff))
     51 #define rtc_dir(sc, output)	((*sc->sc_ops->rs5c313_op_dir)(sc, output))
     52 #define rtc_di(sc)		((*sc->sc_ops->rs5c313_op_read)(sc))
     53 #define rtc_do(sc, bit)		((*sc->sc_ops->rs5c313_op_write)(sc, bit))
     54 
     55 static int rs5c313_init(struct rs5c313_softc *);
     56 static int rs5c313_read_reg(struct rs5c313_softc *, int);
     57 static void rs5c313_write_reg(struct rs5c313_softc *, int, int);
     58 
     59 
     60 void
     61 rs5c313_attach(struct rs5c313_softc *sc)
     62 {
     63 	device_t self = sc->sc_dev;
     64 	const char *model;
     65 
     66 	switch (sc->sc_model) {
     67 	case MODEL_5C313:
     68 		model = "5C313";
     69 		sc->sc_ctrl[0] = CTRL_24H;
     70 		sc->sc_ctrl[1] = CTRL2_NTEST;
     71 		break;
     72 
     73 	case MODEL_5C316:
     74 		model = "5C316";
     75 		sc->sc_ctrl[0] = 0;
     76 		sc->sc_ctrl[1] = CTRL2_24H|CTRL2_NTEST;
     77 		break;
     78 
     79 	default:
     80 		aprint_error("unknown model (%d)\n", sc->sc_model);
     81 		return;
     82 	}
     83 
     84 	aprint_naive("\n");
     85 	aprint_normal(": RICOH %s real time clock\n", model);
     86 
     87 	sc->sc_todr.todr_dev = self;
     88 	sc->sc_todr.todr_gettime_ymdhms = rs5c313_todr_gettime_ymdhms;
     89 	sc->sc_todr.todr_settime_ymdhms = rs5c313_todr_settime_ymdhms;
     90 
     91 	if (rs5c313_init(sc) != 0) {
     92 		aprint_error_dev(self, "init failed\n");
     93 		return;
     94 	}
     95 
     96 	todr_attach(&sc->sc_todr);
     97 }
     98 
     99 
    100 static int
    101 rs5c313_init(struct rs5c313_softc *sc)
    102 {
    103 	device_t self = sc->sc_dev;
    104 	int status = 0;
    105 	int retry;
    106 
    107 	rtc_ce(sc, 0);
    108 
    109 	rtc_begin(sc);
    110 	rtc_ce(sc, 1);
    111 
    112 	if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_XSTP) == 0) {
    113 		sc->sc_valid = 1;
    114 		goto done;
    115 	}
    116 
    117 	sc->sc_valid = 0;
    118 	aprint_error_dev(self, "time not valid\n");
    119 
    120 	rs5c313_write_reg(sc, RS5C313_TINT, 0);
    121 	rs5c313_write_reg(sc, RS5C313_CTRL, (sc->sc_ctrl[0] | CTRL_ADJ));
    122 
    123 	for (retry = 1000; retry > 0; --retry) {
    124 		if (rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY)
    125 			delay(1);
    126 		else
    127 			break;
    128 	}
    129 	if (retry == 0) {
    130 		status = EIO;
    131 		goto done;
    132 	}
    133 
    134 	rs5c313_write_reg(sc, RS5C313_CTRL, sc->sc_ctrl[0]);
    135 	rs5c313_write_reg(sc, RS5C313_CTRL2, sc->sc_ctrl[1]);
    136 
    137   done:
    138 	rtc_ce(sc, 0);
    139 	return status;
    140 }
    141 
    142 
    143 static int
    144 rs5c313_todr_gettime_ymdhms(todr_chip_handle_t todr, struct clock_ymdhms *dt)
    145 {
    146 	struct rs5c313_softc *sc = device_private(todr->todr_dev);
    147 	int retry;
    148 	int s;
    149 
    150 	/*
    151 	 * If chip had invalid data on init, don't bother reading
    152 	 * bogus values, let todr(9) cope.
    153 	 */
    154 	if (sc->sc_valid == 0)
    155 		return EIO;
    156 
    157 	s = splhigh();
    158 
    159 	rtc_begin(sc);
    160 	for (retry = 10; retry > 0; --retry) {
    161 		rtc_ce(sc, 1);
    162 
    163 		rs5c313_write_reg(sc, RS5C313_CTRL, sc->sc_ctrl[0]);
    164 		if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY) == 0)
    165 			break;
    166 
    167 		rtc_ce(sc, 0);
    168 		delay(1);
    169 	}
    170 	if (retry == 0) {
    171 		splx(s);
    172 		return EIO;
    173 	}
    174 
    175 #define RTCGET(x, y)							\
    176 	do {								\
    177 		int ones = rs5c313_read_reg(sc, RS5C313_ ## y ## 1);	\
    178 		int tens = rs5c313_read_reg(sc, RS5C313_ ## y ## 10);	\
    179 		dt->dt_ ## x = tens * 10 + ones;			\
    180 	} while (/* CONSTCOND */0)
    181 
    182 	RTCGET(sec, SEC);
    183 	RTCGET(min, MIN);
    184 	RTCGET(hour, HOUR);
    185 	RTCGET(day, DAY);
    186 	RTCGET(mon, MON);
    187 	RTCGET(year, YEAR);
    188 #undef	RTCGET
    189 	dt->dt_wday = rs5c313_read_reg(sc, RS5C313_WDAY);
    190 
    191 	rtc_ce(sc, 0);
    192 	splx(s);
    193 
    194 	dt->dt_year = (dt->dt_year % 100) + 1900;
    195 	if (dt->dt_year < POSIX_BASE_YEAR) {
    196 		dt->dt_year += 100;
    197 	}
    198 
    199 	return 0;
    200 }
    201 
    202 
    203 static int
    204 rs5c313_todr_settime_ymdhms(todr_chip_handle_t todr, struct clock_ymdhms *dt)
    205 {
    206 	struct rs5c313_softc *sc = device_private(todr->todr_dev);
    207 	int retry;
    208 	int t;
    209 	int s;
    210 
    211 	s = splhigh();
    212 
    213 	rtc_begin(sc);
    214 	for (retry = 10; retry > 0; --retry) {
    215 		rtc_ce(sc, 1);
    216 
    217 		rs5c313_write_reg(sc, RS5C313_CTRL, sc->sc_ctrl[0]);
    218 		if ((rs5c313_read_reg(sc, RS5C313_CTRL) & CTRL_BSY) == 0)
    219 			break;
    220 
    221 		rtc_ce(sc, 0);
    222 		delay(1);
    223 	}
    224 
    225 	if (retry == 0) {
    226 		splx(s);
    227 		return EIO;
    228 	}
    229 
    230 #define	RTCSET(x, y)							     \
    231 	do {								     \
    232 		t = bintobcd(dt->dt_ ## y) & 0xff;				     \
    233 		rs5c313_write_reg(sc, RS5C313_ ## x ## 1, t & 0x0f);	     \
    234 		rs5c313_write_reg(sc, RS5C313_ ## x ## 10, (t >> 4) & 0x0f); \
    235 	} while (/* CONSTCOND */0)
    236 
    237 	RTCSET(SEC, sec);
    238 	RTCSET(MIN, min);
    239 	RTCSET(HOUR, hour);
    240 	RTCSET(DAY, day);
    241 	RTCSET(MON, mon);
    242 
    243 #undef	RTCSET
    244 
    245 	t = dt->dt_year % 100;
    246 	t = bintobcd(t);
    247 	rs5c313_write_reg(sc, RS5C313_YEAR1, t & 0x0f);
    248 	rs5c313_write_reg(sc, RS5C313_YEAR10, (t >> 4) & 0x0f);
    249 
    250 	rs5c313_write_reg(sc, RS5C313_WDAY, dt->dt_wday);
    251 
    252 	rtc_ce(sc, 0);
    253 	splx(s);
    254 
    255 	sc->sc_valid = 1;
    256 	return 0;
    257 }
    258 
    259 
    260 static int
    261 rs5c313_read_reg(struct rs5c313_softc *sc, int addr)
    262 {
    263 	int data;
    264 
    265 	/* output */
    266 	rtc_dir(sc, 1);
    267 
    268 	/* control */
    269 	rtc_do(sc, 1);		/* ignored */
    270 	rtc_do(sc, 1);		/* R/#W = 1(READ) */
    271 	rtc_do(sc, 1);		/* AD = 1 */
    272 	rtc_do(sc, 0);		/* DT = 0 */
    273 
    274 	/* address */
    275 	rtc_do(sc, addr & 0x8);	/* A3 */
    276 	rtc_do(sc, addr & 0x4);	/* A2 */
    277 	rtc_do(sc, addr & 0x2);	/* A1 */
    278 	rtc_do(sc, addr & 0x1);	/* A0 */
    279 
    280 	/* input */
    281 	rtc_dir(sc, 0);
    282 
    283 	/* ignore */
    284 	(void)rtc_di(sc);
    285 	(void)rtc_di(sc);
    286 	(void)rtc_di(sc);
    287 	(void)rtc_di(sc);
    288 
    289 	/* data */
    290 	data = rtc_di(sc);	/* D3 */
    291 	data <<= 1;
    292 	data |= rtc_di(sc);	/* D2 */
    293 	data <<= 1;
    294 	data |= rtc_di(sc);	/* D1 */
    295 	data <<= 1;
    296 	data |= rtc_di(sc);	/* D0 */
    297 
    298 	return data;
    299 }
    300 
    301 
    302 static void
    303 rs5c313_write_reg(struct rs5c313_softc *sc, int addr, int data)
    304 {
    305 
    306 	/* output */
    307 	rtc_dir(sc, 1);
    308 
    309 	/* control */
    310 	rtc_do(sc, 1);		/* ignored */
    311 	rtc_do(sc, 0);		/* R/#W = 0 (WRITE) */
    312 	rtc_do(sc, 1);		/* AD = 1 */
    313 	rtc_do(sc, 0);		/* DT = 0 */
    314 
    315 	/* address */
    316 	rtc_do(sc, addr & 0x8);	/* A3 */
    317 	rtc_do(sc, addr & 0x4);	/* A2 */
    318 	rtc_do(sc, addr & 0x2);	/* A1 */
    319 	rtc_do(sc, addr & 0x1);	/* A0 */
    320 
    321 	/* control */
    322 	rtc_do(sc, 1);		/* ignored */
    323 	rtc_do(sc, 0);		/* R/#W = 0(WRITE) */
    324 	rtc_do(sc, 0);		/* AD = 0 */
    325 	rtc_do(sc, 1);		/* DT = 1 */
    326 
    327 	/* data */
    328 	rtc_do(sc, data & 0x8);	/* D3 */
    329 	rtc_do(sc, data & 0x4);	/* D2 */
    330 	rtc_do(sc, data & 0x2);	/* D1 */
    331 	rtc_do(sc, data & 0x1);	/* D0 */
    332 }
    333