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.
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.
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]
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
28 * tod driver module for ALI M5819P part
31 #include <sys/types.h>
36 #include <sys/sunddi.h>
38 #include <sys/todm5819p.h>
39 #include <sys/rmc_comm_dp.h>
40 #include <sys/rmc_comm_drvintf.h>
41 #include <sys/modctl.h>
43 #include <sys/clock.h>
44 #include <sys/reboot.h>
45 #include <sys/machsystm.h>
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);
55 extern uint64_t find_cpufrequency(volatile uint8_t *);
60 extern int watchdog_enable
;
61 extern int watchdog_available
;
67 int m5819p_debug_flags
;
70 * Module linkage information for the kernel.
72 static struct modlmisc modlmisc
= {
73 &mod_miscops
, "tod module for ALI M5819P"
76 static struct modlinkage modlinkage
= {
77 MODREV_1
, (void *)&modlmisc
, NULL
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
*);
89 if (strcmp(tod_module_name
, "todm5819p_rmc") == 0) {
90 M5819P_ADDR_REG
= RTC_B
;
91 M5819P_DATA_REG
= (RTC_DM
| RTC_HM
);
93 tod_ops
.tod_get
= todm5819p_rmc_get
;
94 tod_ops
.tod_set
= todm5819p_rmc_set
;
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");
109 return (mod_install(&modlinkage
));
115 if (strcmp(tod_module_name
, "todm5819p_rmc") == 0)
118 return (mod_remove(&modlinkage
));
122 * The loadable-module _info(9E) entry point
125 _info(struct modinfo
*modinfop
)
127 return (mod_info(&modlinkage
, modinfop
));
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.
137 todm5819p_rmc_get(void)
144 ASSERT(MUTEX_HELD(&tod_lock
));
146 /* set the hw watchdog timer if it's been activated */
147 if (watchdog_activated
) {
149 ret
= tod_ops
.tod_set_watchdog_timer(0);
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.
155 cmn_err(CE_WARN
, "todm5819p: failed to set hardware "
160 * Read current time from the tod. If the tod isn't accessible, wait and
162 * Run critical in the time critical section to avoid being interrupted
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
)) {
169 ddi_exit_critical(s
);
172 ddi_exit_critical(s
);
173 drv_usecwait(TODM5819_UIP_WAIT_USEC
);
175 if (i
== TODM5819_UIP_RETRY_THRESH
) {
177 * tod is inaccessible: just return current software time
179 tod_status_set(TOD_GET_FAILED
);
183 /* read was successful so ensure failure flag is clear */
184 tod_status_clear(TOD_GET_FAILED
);
186 ts
.tv_sec
= tod_to_utc(rtc_to_tod(&rtc
));
192 rtc_to_tod(struct rtc_t
*rtc
)
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.
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
;
212 read_rtc(struct rtc_t
*rtc
)
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
;
237 /* Read date alarm */
238 M5819P_ADDR_REG
= RTC_ADOM_REG
;
239 rtc
->rtc_adom
= (M5819P_DATA_REG
) & RTC_ADOM
;
243 * Write the specified time into the clock chip.
244 * Must be called with tod_lock held.
247 todm5819p_rmc_set(timestruc_t ts
)
250 todinfo_t tod
= utc_to_tod(ts
.tv_sec
);
252 rmc_comm_msg_t request
;
253 dp_set_date_time_t set_time_msg
;
255 ASSERT(MUTEX_HELD(&tod_lock
));
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
;
268 write_rtc_time(&rtc
);
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
;
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
;
281 (void) rmc_comm_request_nowait(&request
, 0);
285 write_rtc_time(struct rtc_t
*rtc
)
293 M5819P_ADDR_REG
= RTC_B
;
294 regb
= M5819P_DATA_REG
;
295 M5819P_DATA_REG
= (regb
| RTC_SET
);
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
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.
309 M5819P_ADDR_REG
= RTC_A
;
310 for (i
= 0; i
< TODM5819_UIP_RETRY_THRESH
; i
++) {
311 if (!(M5819P_DATA_REG
& RTC_UIP
)) {
314 drv_usecwait(TODM5819_UIP_WAIT_USEC
);
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
;
334 cmn_err(CE_WARN
, "todm5819p_rmc: Could not write the RTC\n");
340 M5819P_ADDR_REG
= RTC_B
;
341 M5819P_DATA_REG
= regb
;
345 write_rtc_alarm(struct rtc_t
*rtc
)
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
;
354 M5819P_ADDR_REG
= RTC_ADOM_REG
;
355 M5819P_DATA_REG
= rtc
->rtc_adom
;
359 * program the rtc registers for alarm to go off at the specified time
362 todm5819p_rmc_set_power_alarm(timestruc_t ts
)
368 ASSERT(MUTEX_HELD(&tod_lock
));
369 tod
= utc_to_tod(ts
.tv_sec
);
372 * disable alarms and clear AF flag by reading reg C
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
;
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
;
386 * Write alarm values and enable alarm
388 write_rtc_alarm(&rtc
);
390 M5819P_ADDR_REG
= RTC_B
;
391 M5819P_DATA_REG
= regb
| RTC_AIE
;
395 * clear alarm interrupt
398 todm5819p_rmc_clear_power_alarm(void)
402 ASSERT(MUTEX_HELD(&tod_lock
));
404 M5819P_ADDR_REG
= RTC_B
;
405 regb
= M5819P_DATA_REG
;
406 M5819P_DATA_REG
= regb
& ~RTC_AIE
;
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.
415 todm5819p_rmc_get_cpufrequency(void)
417 ASSERT(MUTEX_HELD(&tod_lock
));
418 M5819P_ADDR_REG
= RTC_SEC
;
419 return (find_cpufrequency(v_rtc_data_reg
));
424 todm5819p_rmc_set_watchdog_timer(uint_t timeoutval
)
426 ASSERT(MUTEX_HELD(&tod_lock
));
431 todm5819p_rmc_clear_watchdog_timer(void)
433 ASSERT(MUTEX_HELD(&tod_lock
));