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
47static timestruc_t	todm5819p_rmc_get(void);
48static void		todm5819p_rmc_set(timestruc_t);
49static uint_t		todm5819p_rmc_set_watchdog_timer(uint_t);
50static uint_t		todm5819p_rmc_clear_watchdog_timer(void);
51static void		todm5819p_rmc_set_power_alarm(timestruc_t);
52static void		todm5819p_rmc_clear_power_alarm(void);
53static uint64_t		todm5819p_rmc_get_cpufrequency(void);
54
55extern uint64_t		find_cpufrequency(volatile uint8_t *);
56
57/*
58 * External variables
59 */
60extern int	watchdog_enable;
61extern int	watchdog_available;
62extern int	boothowto;
63
64/*
65 * Global variables
66 */
67int m5819p_debug_flags;
68
69/*
70 * Module linkage information for the kernel.
71 */
72static struct modlmisc modlmisc = {
73	&mod_miscops, "tod module for ALI M5819P"
74};
75
76static struct modlinkage modlinkage = {
77	MODREV_1, (void *)&modlmisc, NULL
78};
79
80static todinfo_t rtc_to_tod(struct rtc_t *);
81static void read_rtc(struct rtc_t *);
82static void write_rtc_time(struct rtc_t *);
83static void write_rtc_alarm(struct rtc_t *);
84
85
86int
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
112int
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 */
124int
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 */
136static timestruc_t
137todm5819p_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
191static todinfo_t
192rtc_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
211static void
212read_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 */
246static void
247todm5819p_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
284void
285write_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
344void
345write_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 */
361static void
362todm5819p_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 */
397static void
398todm5819p_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 */
414uint64_t
415todm5819p_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*/
423static uint_t
424todm5819p_rmc_set_watchdog_timer(uint_t timeoutval)
425{
426	ASSERT(MUTEX_HELD(&tod_lock));
427	return (0);
428}
429
430static uint_t
431todm5819p_rmc_clear_watchdog_timer(void)
432{
433	ASSERT(MUTEX_HELD(&tod_lock));
434	return (0);
435}
436