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#include <sys/types.h>
27#include <sys/conf.h>
28#include <sys/kmem.h>
29#include <sys/open.h>
30#include <sys/ddi.h>
31#include <sys/sunddi.h>
32
33#include <sys/todm5819.h>
34#include <sys/modctl.h>
35#include <sys/stat.h>
36#include <sys/clock.h>
37#include <sys/reboot.h>
38#include <sys/machsystm.h>
39#include <sys/poll.h>
40#include <sys/pbio.h>
41
42static timestruc_t	todm5819_get(void);
43static void		todm5819_set(timestruc_t);
44static uint_t		todm5819_set_watchdog_timer(uint_t);
45static uint_t		todm5819_clear_watchdog_timer(void);
46static void		todm5819_set_power_alarm(timestruc_t);
47static void		todm5819_clear_power_alarm(void);
48static uint64_t		todm5819_get_cpufrequency(void);
49
50extern uint64_t		find_cpufrequency(volatile uint8_t *);
51
52/*
53 * External variables
54 */
55extern int		watchdog_enable;
56extern int		watchdog_available;
57extern int		boothowto;
58
59/*
60 * Global variables
61 */
62int m5819_debug_flags;
63
64static todinfo_t	rtc_to_tod(struct rtc_t *);
65static uint_t		read_rtc(struct rtc_t *);
66static void		write_rtc_time(struct rtc_t *);
67static void		write_rtc_alarm(struct rtc_t *);
68
69
70static struct modlmisc modlmisc = {
71	&mod_miscops, "tod module for ALI M5819",
72};
73
74static struct modlinkage modlinkage = {
75	MODREV_1, &modlmisc, NULL
76};
77
78
79int
80_init(void)
81{
82	if (strcmp(tod_module_name, "todm5819") == 0 ||
83	    strcmp(tod_module_name, "m5819") == 0) {
84		RTC_PUT8(RTC_B, (RTC_DM | RTC_HM));
85
86		tod_ops.tod_get = todm5819_get;
87		tod_ops.tod_set = todm5819_set;
88		tod_ops.tod_set_watchdog_timer = todm5819_set_watchdog_timer;
89		tod_ops.tod_clear_watchdog_timer =
90		    todm5819_clear_watchdog_timer;
91		tod_ops.tod_set_power_alarm = todm5819_set_power_alarm;
92		tod_ops.tod_clear_power_alarm = todm5819_clear_power_alarm;
93		tod_ops.tod_get_cpufrequency = todm5819_get_cpufrequency;
94
95		/*
96		 * check if hardware watchdog timer is available and user
97		 * enabled it.
98		 */
99		if (watchdog_enable) {
100			if (!watchdog_available) {
101				cmn_err(CE_WARN, "m5819: Hardware watchdog "
102				    "unavailable");
103			} else if (boothowto & RB_DEBUG) {
104				cmn_err(CE_WARN, "m5819: Hardware watchdog "
105				    "disabled [debugger]");
106			}
107		}
108	}
109
110	return (mod_install(&modlinkage));
111}
112
113int
114_fini(void)
115{
116	if (strcmp(tod_module_name, "m5819") == 0 ||
117	    strcmp(tod_module_name, "todm5819") == 0) {
118		return (EBUSY);
119	} else {
120		return (mod_remove(&modlinkage));
121	}
122}
123
124/*
125 * The loadable-module _info(9E) entry point
126 */
127int
128_info(struct modinfo *modinfop)
129{
130	return (mod_info(&modlinkage, modinfop));
131}
132
133
134/*
135 * Read the current time from the clock chip and convert to UNIX form.
136 * Assumes that the year in the clock chip is valid.
137 * Must be called with tod_lock held.
138 */
139static timestruc_t
140todm5819_get(void)
141{
142	int i;
143	timestruc_t ts;
144	struct rtc_t rtc;
145
146	ASSERT(MUTEX_HELD(&tod_lock));
147
148	/*
149	 * Read from the tod, and if it isnt accessible wait
150	 * before retrying.
151	 */
152	for (i = 0; i < TODM5819_UIP_RETRY_THRESH; i++) {
153		if (read_rtc(&rtc))
154			break;
155		drv_usecwait(TODM5819_UIP_WAIT_USEC);
156	}
157	if (i == TODM5819_UIP_RETRY_THRESH) {
158		/*
159		 * We couldn't read from the TOD.
160		 */
161		tod_status_set(TOD_GET_FAILED);
162		return (hrestime);
163	}
164
165	DPRINTF("todm5819_get: century=%d year=%d dom=%d hrs=%d\n",
166	    rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs);
167
168	/* read was successful so ensure failure flag is clear */
169	tod_status_clear(TOD_GET_FAILED);
170
171	ts.tv_sec = tod_to_utc(rtc_to_tod(&rtc));
172	ts.tv_nsec = 0;
173	return (ts);
174}
175
176static todinfo_t
177rtc_to_tod(struct rtc_t *rtc)
178{
179	todinfo_t tod;
180
181	/*
182	 * tod_year is base 1900 so this code needs to adjust the true
183	 * year retrieved from the rtc's century and year fields.
184	 */
185	tod.tod_year	= rtc->rtc_year + (rtc->rtc_century * 100) - 1900;
186	tod.tod_month	= rtc->rtc_mon;
187	tod.tod_day	= rtc->rtc_dom;
188	tod.tod_dow	= rtc->rtc_dow;
189	tod.tod_hour	= rtc->rtc_hrs;
190	tod.tod_min	= rtc->rtc_min;
191	tod.tod_sec	= rtc->rtc_sec;
192
193	return (tod);
194}
195
196uint_t
197read_rtc(struct rtc_t *rtc)
198{
199	int s;
200	uint_t rtc_readable = 0;
201
202	s = splhi();
203	/*
204	 * If UIP bit is not set we have at least 274us
205	 * to read the values. Otherwise we have up to
206	 * 336us to wait before we can read it
207	 */
208	if (!(RTC_GET8(RTC_A) & RTC_UIP)) {
209		rtc_readable = 1;
210
211		rtc->rtc_sec = RTC_GET8(RTC_SEC);
212		rtc->rtc_asec = RTC_GET8(RTC_ASEC);
213		rtc->rtc_min = RTC_GET8(RTC_MIN);
214		rtc->rtc_amin = RTC_GET8(RTC_AMIN);
215
216		rtc->rtc_hrs = RTC_GET8(RTC_HRS);
217		rtc->rtc_ahrs = RTC_GET8(RTC_AHRS);
218		rtc->rtc_dow = RTC_GET8(RTC_DOW);
219		rtc->rtc_dom = RTC_GET8(RTC_DOM);
220		rtc->rtc_adom = RTC_GET8(RTC_D) & 0x3f;
221
222		rtc->rtc_mon = RTC_GET8(RTC_MON);
223		rtc->rtc_year = RTC_GET8(RTC_YEAR);
224		rtc->rtc_century = RTC_GET8(RTC_CENTURY);
225		rtc->rtc_amon = 0;
226
227		/* Clear wakeup data */
228		rtc->apc_wdwr = 0;
229		rtc->apc_wdmr = 0;
230		rtc->apc_wmr = 0;
231		rtc->apc_wyr = 0;
232		rtc->apc_wcr = 0;
233	}
234	splx(s);
235	return (rtc_readable);
236}
237
238/*
239 * Write the specified time into the clock chip.
240 * Must be called with tod_lock held.
241 */
242static void
243todm5819_set(timestruc_t ts)
244{
245	struct rtc_t	rtc;
246	todinfo_t tod = utc_to_tod(ts.tv_sec);
247	int year;
248
249	ASSERT(MUTEX_HELD(&tod_lock));
250
251	/* tod_year is base 1900 so this code needs to adjust */
252	year = 1900 + tod.tod_year;
253	rtc.rtc_year	= year % 100;
254	rtc.rtc_century = year / 100;
255	rtc.rtc_mon	= (uint8_t)tod.tod_month;
256	rtc.rtc_dom	= (uint8_t)tod.tod_day;
257	rtc.rtc_dow	= (uint8_t)tod.tod_dow;
258	rtc.rtc_hrs	= (uint8_t)tod.tod_hour;
259	rtc.rtc_min	= (uint8_t)tod.tod_min;
260	rtc.rtc_sec	= (uint8_t)tod.tod_sec;
261	DPRINTF("todm5819_set: century=%d year=%d dom=%d hrs=%d\n",
262	    rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs);
263
264	write_rtc_time(&rtc);
265}
266
267void
268write_rtc_time(struct rtc_t *rtc)
269{
270	uint8_t	regb;
271	int	i;
272
273	/*
274	 * Freeze
275	 */
276	regb = RTC_GET8(RTC_B);
277	RTC_PUT8(RTC_B, (regb | RTC_SET));
278
279	/*
280	 * If an update is in progress wait for the UIP flag to clear.
281	 * If we write whilst UIP is still set there is a slight but real
282	 * possibility of corrupting the RTC date and time registers.
283	 *
284	 * The expected wait is one internal cycle of the chip.  We could
285	 * simply spin but this may hang a CPU if we were to have a broken
286	 * RTC chip where UIP is stuck, so we use a retry loop instead.
287	 * No critical section is needed here as the UIP flag will not be
288	 * re-asserted until we clear RTC_SET.
289	 */
290	for (i = 0; i < TODM5819_UIP_RETRY_THRESH; i++) {
291		if (!(RTC_GET8(RTC_A) & RTC_UIP)) {
292			break;
293		}
294		drv_usecwait(TODM5819_UIP_WAIT_USEC);
295	}
296	if (i < TODM5819_UIP_RETRY_THRESH) {
297		RTC_PUT8(RTC_SEC, (rtc->rtc_sec));
298		RTC_PUT8(RTC_ASEC, (rtc->rtc_asec));
299		RTC_PUT8(RTC_MIN, (rtc->rtc_min));
300		RTC_PUT8(RTC_AMIN, (rtc->rtc_amin));
301
302		RTC_PUT8(RTC_HRS, (rtc->rtc_hrs));
303		RTC_PUT8(RTC_AHRS, (rtc->rtc_ahrs));
304		RTC_PUT8(RTC_DOW, (rtc->rtc_dow));
305		RTC_PUT8(RTC_DOM, (rtc->rtc_dom));
306
307		RTC_PUT8(RTC_MON, (rtc->rtc_mon));
308		RTC_PUT8(RTC_YEAR, (rtc->rtc_year));
309		RTC_PUT8(RTC_CENTURY, (rtc->rtc_century));
310	} else {
311		cmn_err(CE_WARN, "todm5819: Could not write the RTC\n");
312	}
313
314	/*
315	 * Unfreeze
316	 */
317	RTC_PUT8(RTC_B, regb);
318}
319
320
321void
322write_rtc_alarm(struct rtc_t *rtc)
323{
324	RTC_PUT8(RTC_ASEC, (rtc->rtc_asec));
325	RTC_PUT8(RTC_AMIN, (rtc->rtc_amin));
326	RTC_PUT8(RTC_AHRS, (rtc->rtc_ahrs));
327	RTC_PUT8(RTC_D, (rtc->rtc_adom));
328}
329
330/*
331 * program the rtc registers for alarm to go off at the specified time
332 */
333static void
334todm5819_set_power_alarm(timestruc_t ts)
335{
336	todinfo_t	tod;
337	uint8_t		regb;
338	struct rtc_t	rtc;
339
340	ASSERT(MUTEX_HELD(&tod_lock));
341	tod = utc_to_tod(ts.tv_sec);
342
343	/*
344	 * disable alarms
345	 */
346	regb = RTC_GET8(RTC_B);
347	RTC_PUT8(RTC_B, (regb & ~RTC_AIE));
348
349
350	rtc.rtc_asec = (uint8_t)tod.tod_sec;
351	rtc.rtc_amin = (uint8_t)tod.tod_min;
352	rtc.rtc_ahrs = (uint8_t)tod.tod_hour;
353	rtc.rtc_adom = (uint8_t)tod.tod_day;
354
355	write_rtc_alarm(&rtc);
356	/*
357	 * Enable alarm.
358	 */
359	RTC_PUT8(RTC_B, (regb | RTC_AIE));
360}
361
362/*
363 * clear alarm interrupt
364 */
365static void
366todm5819_clear_power_alarm(void)
367{
368	uint8_t regb;
369	ASSERT(MUTEX_HELD(&tod_lock));
370
371	regb = RTC_GET8(RTC_B);
372	RTC_PUT8(RTC_B, (regb & ~RTC_AIE));
373}
374
375/*
376 * Determine the cpu frequency by watching the TOD chip rollover twice.
377 * Cpu clock rate is determined by computing the ticks added (in tick register)
378 * during one second interval on TOD.
379 */
380uint64_t
381todm5819_get_cpufrequency(void)
382{
383	ASSERT(MUTEX_HELD(&tod_lock));
384	M5819_ADDR_REG = RTC_SEC;
385	return (find_cpufrequency(v_rtc_data_reg));
386}
387
388
389/*ARGSUSED*/
390static uint_t
391todm5819_set_watchdog_timer(uint_t timeoutval)
392{
393	ASSERT(MUTEX_HELD(&tod_lock));
394	return (0);
395}
396
397static uint_t
398todm5819_clear_watchdog_timer(void)
399{
400	ASSERT(MUTEX_HELD(&tod_lock));
401	return (0);
402}
403