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