1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27 /*
28 * tod driver module for ALI M5819P part
29 */
30
31 #include <sys/types.h>
32 #include <sys/conf.h>
33 #include <sys/kmem.h>
34 #include <sys/open.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37
38 #include <sys/todm5819p.h>
39 #include <sys/rmc_comm_dp.h>
40 #include <sys/rmc_comm_drvintf.h>
41 #include <sys/modctl.h>
42 #include <sys/stat.h>
43 #include <sys/clock.h>
44 #include <sys/reboot.h>
45 #include <sys/machsystm.h>
46
47 static timestruc_t todm5819p_rmc_get(void);
48 static void todm5819p_rmc_set(timestruc_t);
49 static uint_t todm5819p_rmc_set_watchdog_timer(uint_t);
50 static uint_t todm5819p_rmc_clear_watchdog_timer(void);
51 static void todm5819p_rmc_set_power_alarm(timestruc_t);
52 static void todm5819p_rmc_clear_power_alarm(void);
53 static uint64_t todm5819p_rmc_get_cpufrequency(void);
54
55 extern uint64_t find_cpufrequency(volatile uint8_t *);
56
57 /*
58 * External variables
59 */
60 extern int watchdog_enable;
61 extern int watchdog_available;
62 extern int boothowto;
63
64 /*
65 * Global variables
66 */
67 int m5819p_debug_flags;
68
69 /*
70 * Module linkage information for the kernel.
71 */
72 static struct modlmisc modlmisc = {
73 &mod_miscops, "tod module for ALI M5819P"
74 };
75
76 static struct modlinkage modlinkage = {
77 MODREV_1, (void *)&modlmisc, NULL
78 };
79
80 static todinfo_t rtc_to_tod(struct rtc_t *);
81 static void read_rtc(struct rtc_t *);
82 static void write_rtc_time(struct rtc_t *);
83 static void write_rtc_alarm(struct rtc_t *);
84
85
86 int
_init(void)87 _init(void)
88 {
89 if (strcmp(tod_module_name, "todm5819p_rmc") == 0) {
90 M5819P_ADDR_REG = RTC_B;
91 M5819P_DATA_REG = (RTC_DM | RTC_HM);
92
93 tod_ops.tod_get = todm5819p_rmc_get;
94 tod_ops.tod_set = todm5819p_rmc_set;
95
96 tod_ops.tod_set_watchdog_timer =
97 todm5819p_rmc_set_watchdog_timer;
98 tod_ops.tod_clear_watchdog_timer =
99 todm5819p_rmc_clear_watchdog_timer;
100 tod_ops.tod_set_power_alarm = todm5819p_rmc_set_power_alarm;
101 tod_ops.tod_clear_power_alarm = todm5819p_rmc_clear_power_alarm;
102 tod_ops.tod_get_cpufrequency = todm5819p_rmc_get_cpufrequency;
103 if (boothowto & RB_DEBUG) {
104 cmn_err(CE_WARN, "todm5819p_rmc: kernel debugger "
105 "detected: hardware watchdog disabled");
106 }
107 }
108
109 return (mod_install(&modlinkage));
110 }
111
112 int
_fini(void)113 _fini(void)
114 {
115 if (strcmp(tod_module_name, "todm5819p_rmc") == 0)
116 return (EBUSY);
117
118 return (mod_remove(&modlinkage));
119 }
120
121 /*
122 * The loadable-module _info(9E) entry point
123 */
124 int
_info(struct modinfo * modinfop)125 _info(struct modinfo *modinfop)
126 {
127 return (mod_info(&modlinkage, modinfop));
128 }
129
130
131 /*
132 * Read the current time from the clock chip and convert to UNIX form.
133 * Assumes that the year in the clock chip is valid.
134 * Must be called with tod_lock held.
135 */
136 static timestruc_t
todm5819p_rmc_get(void)137 todm5819p_rmc_get(void)
138 {
139 int i;
140 int s;
141 timestruc_t ts;
142 struct rtc_t rtc;
143
144 ASSERT(MUTEX_HELD(&tod_lock));
145
146 /* set the hw watchdog timer if it's been activated */
147 if (watchdog_activated) {
148 int ret = 0;
149 ret = tod_ops.tod_set_watchdog_timer(0);
150 /*
151 * The empty set_watchdog routine returns a 0. So if a
152 * coded routine fails we will look for a -1 for a failure.
153 */
154 if (ret == -1)
155 cmn_err(CE_WARN, "todm5819p: failed to set hardware "
156 "watchdog timer.");
157 }
158
159 /*
160 * Read current time from the tod. If the tod isn't accessible, wait and
161 * retry.
162 * Run critical in the time critical section to avoid being interrupted
163 */
164 for (i = 0; i < TODM5819_UIP_RETRY_THRESH; i++) {
165 s = ddi_enter_critical();
166 M5819P_ADDR_REG = RTC_A;
167 if (!(M5819P_DATA_REG & RTC_UIP)) {
168 read_rtc(&rtc);
169 ddi_exit_critical(s);
170 break;
171 }
172 ddi_exit_critical(s);
173 drv_usecwait(TODM5819_UIP_WAIT_USEC);
174 }
175 if (i == TODM5819_UIP_RETRY_THRESH) {
176 /*
177 * tod is inaccessible: just return current software time
178 */
179 tod_status_set(TOD_GET_FAILED);
180 return (hrestime);
181 }
182
183 /* read was successful so ensure failure flag is clear */
184 tod_status_clear(TOD_GET_FAILED);
185
186 ts.tv_sec = tod_to_utc(rtc_to_tod(&rtc));
187 ts.tv_nsec = 0;
188 return (ts);
189 }
190
191 static todinfo_t
rtc_to_tod(struct rtc_t * rtc)192 rtc_to_tod(struct rtc_t *rtc)
193 {
194 todinfo_t tod;
195
196 /*
197 * tod_year is base 1900 so this code needs to adjust the true year
198 * retrieved from the rtc's century and year fields.
199 */
200 tod.tod_year = rtc->rtc_year + (rtc->rtc_century * 100) - 1900;
201 tod.tod_month = rtc->rtc_mon;
202 tod.tod_day = rtc->rtc_dom;
203 tod.tod_dow = rtc->rtc_dow;
204 tod.tod_hour = rtc->rtc_hrs;
205 tod.tod_min = rtc->rtc_min;
206 tod.tod_sec = rtc->rtc_sec;
207
208 return (tod);
209 }
210
211 static void
read_rtc(struct rtc_t * rtc)212 read_rtc(struct rtc_t *rtc)
213 {
214 M5819P_ADDR_REG = RTC_SEC;
215 rtc->rtc_sec = M5819P_DATA_REG;
216 M5819P_ADDR_REG = RTC_ASEC;
217 rtc->rtc_asec = M5819P_DATA_REG;
218 M5819P_ADDR_REG = RTC_MIN;
219 rtc->rtc_min = M5819P_DATA_REG;
220 M5819P_ADDR_REG = RTC_AMIN;
221 rtc->rtc_amin = M5819P_DATA_REG;
222 M5819P_ADDR_REG = RTC_HRS;
223 rtc->rtc_hrs = M5819P_DATA_REG;
224 M5819P_ADDR_REG = RTC_AHRS;
225 rtc->rtc_ahrs = M5819P_DATA_REG;
226 M5819P_ADDR_REG = RTC_DOW;
227 rtc->rtc_dow = M5819P_DATA_REG;
228 M5819P_ADDR_REG = RTC_DOM;
229 rtc->rtc_dom = M5819P_DATA_REG;
230 M5819P_ADDR_REG = RTC_MON;
231 rtc->rtc_mon = M5819P_DATA_REG;
232 M5819P_ADDR_REG = RTC_YEAR;
233 rtc->rtc_year = M5819P_DATA_REG;
234 M5819P_ADDR_REG = RTC_CENTURY;
235 rtc->rtc_century = M5819P_DATA_REG;
236
237 /* Read date alarm */
238 M5819P_ADDR_REG = RTC_ADOM_REG;
239 rtc->rtc_adom = (M5819P_DATA_REG) & RTC_ADOM;
240 }
241
242 /*
243 * Write the specified time into the clock chip.
244 * Must be called with tod_lock held.
245 */
246 static void
todm5819p_rmc_set(timestruc_t ts)247 todm5819p_rmc_set(timestruc_t ts)
248 {
249 struct rtc_t rtc;
250 todinfo_t tod = utc_to_tod(ts.tv_sec);
251 int year;
252 rmc_comm_msg_t request;
253 dp_set_date_time_t set_time_msg;
254
255 ASSERT(MUTEX_HELD(&tod_lock));
256
257 /* tod_year is base 1900 so this code needs to adjust */
258 year = 1900 + tod.tod_year;
259 rtc.rtc_year = year % 100;
260 rtc.rtc_century = year / 100;
261 rtc.rtc_mon = (uint8_t)tod.tod_month;
262 rtc.rtc_dom = (uint8_t)tod.tod_day;
263 rtc.rtc_dow = (uint8_t)tod.tod_dow;
264 rtc.rtc_hrs = (uint8_t)tod.tod_hour;
265 rtc.rtc_min = (uint8_t)tod.tod_min;
266 rtc.rtc_sec = (uint8_t)tod.tod_sec;
267
268 write_rtc_time(&rtc);
269
270 set_time_msg.year = year - 1900;
271 set_time_msg.month = tod.tod_month - 1;
272 set_time_msg.day = tod.tod_day;
273 set_time_msg.hour = tod.tod_hour;
274 set_time_msg.minute = tod.tod_min;
275 set_time_msg.second = tod.tod_sec;
276
277 request.msg_type = DP_SET_DATE_TIME;
278 request.msg_len = sizeof (set_time_msg);
279 request.msg_buf = (caddr_t)&set_time_msg;
280
281 (void) rmc_comm_request_nowait(&request, 0);
282 }
283
284 void
write_rtc_time(struct rtc_t * rtc)285 write_rtc_time(struct rtc_t *rtc)
286 {
287 uint8_t regb;
288 int i;
289
290 /*
291 * Freeze
292 */
293 M5819P_ADDR_REG = RTC_B;
294 regb = M5819P_DATA_REG;
295 M5819P_DATA_REG = (regb | RTC_SET);
296
297 /*
298 * If an update cycle is in progress wait for the UIP flag to
299 * clear. If we write whilst UIP is still set there is a slight
300 * but real possibility of corrupting the RTC date and time
301 * registers.
302 *
303 * The expected wait is one internal cycle of the chip. We could
304 * simply spin but this may hang a CPU if we were to have a broken
305 * RTC chip where UIP is stuck, so we use a retry loop instead.
306 * No critical section is needed here as the UIP flag will not be
307 * re-asserted until we clear RTC_SET.
308 */
309 M5819P_ADDR_REG = RTC_A;
310 for (i = 0; i < TODM5819_UIP_RETRY_THRESH; i++) {
311 if (!(M5819P_DATA_REG & RTC_UIP)) {
312 break;
313 }
314 drv_usecwait(TODM5819_UIP_WAIT_USEC);
315 }
316 if (i < TODM5819_UIP_RETRY_THRESH) {
317 M5819P_ADDR_REG = RTC_SEC;
318 M5819P_DATA_REG = rtc->rtc_sec;
319 M5819P_ADDR_REG = RTC_MIN;
320 M5819P_DATA_REG = rtc->rtc_min;
321 M5819P_ADDR_REG = RTC_HRS;
322 M5819P_DATA_REG = rtc->rtc_hrs;
323 M5819P_ADDR_REG = RTC_DOW;
324 M5819P_DATA_REG = rtc->rtc_dow;
325 M5819P_ADDR_REG = RTC_DOM;
326 M5819P_DATA_REG = rtc->rtc_dom;
327 M5819P_ADDR_REG = RTC_MON;
328 M5819P_DATA_REG = rtc->rtc_mon;
329 M5819P_ADDR_REG = RTC_YEAR;
330 M5819P_DATA_REG = rtc->rtc_year;
331 M5819P_ADDR_REG = RTC_CENTURY;
332 M5819P_DATA_REG = rtc->rtc_century;
333 } else {
334 cmn_err(CE_WARN, "todm5819p_rmc: Could not write the RTC\n");
335 }
336
337 /*
338 * Unfreeze
339 */
340 M5819P_ADDR_REG = RTC_B;
341 M5819P_DATA_REG = regb;
342 }
343
344 void
write_rtc_alarm(struct rtc_t * rtc)345 write_rtc_alarm(struct rtc_t *rtc)
346 {
347 M5819P_ADDR_REG = RTC_ASEC;
348 M5819P_DATA_REG = rtc->rtc_asec;
349 M5819P_ADDR_REG = RTC_AMIN;
350 M5819P_DATA_REG = rtc->rtc_amin;
351 M5819P_ADDR_REG = RTC_AHRS;
352 M5819P_DATA_REG = rtc->rtc_ahrs;
353
354 M5819P_ADDR_REG = RTC_ADOM_REG;
355 M5819P_DATA_REG = rtc->rtc_adom;
356 }
357
358 /*
359 * program the rtc registers for alarm to go off at the specified time
360 */
361 static void
todm5819p_rmc_set_power_alarm(timestruc_t ts)362 todm5819p_rmc_set_power_alarm(timestruc_t ts)
363 {
364 todinfo_t tod;
365 uint8_t regb;
366 struct rtc_t rtc;
367
368 ASSERT(MUTEX_HELD(&tod_lock));
369 tod = utc_to_tod(ts.tv_sec);
370
371 /*
372 * disable alarms and clear AF flag by reading reg C
373 */
374 M5819P_ADDR_REG = RTC_B;
375 regb = M5819P_DATA_REG;
376 M5819P_DATA_REG = regb & ~RTC_AIE;
377 M5819P_ADDR_REG = RTC_C;
378 (void) M5819P_DATA_REG;
379
380 rtc.rtc_asec = (uint8_t)tod.tod_sec;
381 rtc.rtc_amin = (uint8_t)tod.tod_min;
382 rtc.rtc_ahrs = (uint8_t)tod.tod_hour;
383 rtc.rtc_adom = (uint8_t)tod.tod_day;
384
385 /*
386 * Write alarm values and enable alarm
387 */
388 write_rtc_alarm(&rtc);
389
390 M5819P_ADDR_REG = RTC_B;
391 M5819P_DATA_REG = regb | RTC_AIE;
392 }
393
394 /*
395 * clear alarm interrupt
396 */
397 static void
todm5819p_rmc_clear_power_alarm(void)398 todm5819p_rmc_clear_power_alarm(void)
399 {
400 uint8_t regb;
401
402 ASSERT(MUTEX_HELD(&tod_lock));
403
404 M5819P_ADDR_REG = RTC_B;
405 regb = M5819P_DATA_REG;
406 M5819P_DATA_REG = regb & ~RTC_AIE;
407 }
408
409 /*
410 * Determine the cpu frequency by watching the TOD chip rollover twice.
411 * Cpu clock rate is determined by computing the ticks added (in tick register)
412 * during one second interval on TOD.
413 */
414 uint64_t
todm5819p_rmc_get_cpufrequency(void)415 todm5819p_rmc_get_cpufrequency(void)
416 {
417 ASSERT(MUTEX_HELD(&tod_lock));
418 M5819P_ADDR_REG = RTC_SEC;
419 return (find_cpufrequency(v_rtc_data_reg));
420 }
421
422 /*ARGSUSED*/
423 static uint_t
todm5819p_rmc_set_watchdog_timer(uint_t timeoutval)424 todm5819p_rmc_set_watchdog_timer(uint_t timeoutval)
425 {
426 ASSERT(MUTEX_HELD(&tod_lock));
427 return (0);
428 }
429
430 static uint_t
todm5819p_rmc_clear_watchdog_timer(void)431 todm5819p_rmc_clear_watchdog_timer(void)
432 {
433 ASSERT(MUTEX_HELD(&tod_lock));
434 return (0);
435 }
436