xref: /onnv-gate/usr/src/uts/sun4u/io/todbq4802.c (revision 1868:b89c5d8dd52d)
1511Sminht /*
2511Sminht  * CDDL HEADER START
3511Sminht  *
4511Sminht  * The contents of this file are subject to the terms of the
5*1868Srameshc  * Common Development and Distribution License (the "License").
6*1868Srameshc  * You may not use this file except in compliance with the License.
7511Sminht  *
8511Sminht  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9511Sminht  * or http://www.opensolaris.org/os/licensing.
10511Sminht  * See the License for the specific language governing permissions
11511Sminht  * and limitations under the License.
12511Sminht  *
13511Sminht  * When distributing Covered Code, include this CDDL HEADER in each
14511Sminht  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15511Sminht  * If applicable, add the following below this CDDL HEADER, with the
16511Sminht  * fields enclosed by brackets "[]" replaced with your own identifying
17511Sminht  * information: Portions Copyright [yyyy] [name of copyright owner]
18511Sminht  *
19511Sminht  * CDDL HEADER END
20511Sminht  */
21511Sminht /*
22*1868Srameshc  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23511Sminht  * Use is subject to license terms.
24511Sminht  */
25511Sminht 
26511Sminht #pragma ident	"%Z%%M%	%I%	%E% SMI"
27511Sminht 
28511Sminht /*
29511Sminht  * tod driver module for TI BQ4802 part
30511Sminht  *
31511Sminht  * Note: The way to access the bq4802's RTC registers is different than
32511Sminht  * the previous RTC devices (m5823, m5819p, ds1287, etc) that we used.
33511Sminht  * The address returns from OBP is mapped directly to the bq4802's RTC
34511Sminht  * registers. To read/write the data from/to the bq4802 registers, one
35511Sminht  * just add the register offset to the base address.
36511Sminht  * To access the previous RTC devices, we write the register index to
37511Sminht  * the address port (v_rtc_addr_reg) then read/write the data from/to
38511Sminht  * the data port (v_rtc_data_reg).
39511Sminht  */
40511Sminht 
41511Sminht #include <sys/types.h>
42511Sminht #include <sys/conf.h>
43511Sminht #include <sys/kmem.h>
44511Sminht #include <sys/open.h>
45511Sminht #include <sys/ddi.h>
46511Sminht #include <sys/sunddi.h>
47511Sminht #include <sys/sysmacros.h>
48511Sminht 
49511Sminht #include <sys/todbq4802.h>
50511Sminht #include <sys/modctl.h>
51511Sminht #include <sys/stat.h>
52511Sminht #include <sys/clock.h>
53511Sminht #include <sys/reboot.h>
54511Sminht #include <sys/machsystm.h>
55511Sminht 
56511Sminht /*
57511Sminht  * tod_ops entry routines
58511Sminht  */
59511Sminht static timestruc_t	todbq4802_get(void);
60511Sminht static void		todbq4802_set(timestruc_t);
61511Sminht static uint_t		todbq4802_set_watchdog_timer(uint_t);
62511Sminht static uint_t		todbq4802_clear_watchdog_timer(void);
63511Sminht static void		todbq4802_set_power_alarm(timestruc_t);
64511Sminht static void		todbq4802_clear_power_alarm(void);
65511Sminht static uint64_t		todbq4802_get_cpufrequency(void);
66511Sminht 
67511Sminht extern uint64_t		find_cpufrequency(volatile uint8_t *);
68511Sminht 
69511Sminht /*
70511Sminht  * External variables
71511Sminht  */
72511Sminht extern int watchdog_enable;
73511Sminht extern int watchdog_available;
74511Sminht extern int boothowto;
75511Sminht 
76511Sminht /*
77511Sminht  * Global variables
78511Sminht  */
79511Sminht int bq4802_debug_flags;
80511Sminht uint_t bq4802_hrestime_count = 0;
81511Sminht uint_t bq4802_uip_count = 0;
82511Sminht 
83511Sminht /*
84511Sminht  * Module linkage information for the kernel.
85511Sminht  */
86511Sminht static struct modlmisc modlmisc = {
87511Sminht 	&mod_miscops, "tod module for TI BQ4802"
88511Sminht };
89511Sminht 
90511Sminht static struct modlinkage modlinkage = {
91511Sminht 	MODREV_1, (void *)&modlmisc, NULL
92511Sminht };
93511Sminht 
94511Sminht static void read_rtc(struct rtc_t *);
95511Sminht static void write_rtc_time(struct rtc_t *);
96511Sminht static void write_rtc_alarm(struct rtc_t *);
97511Sminht 
98511Sminht int
_init(void)99511Sminht _init(void)
100511Sminht {
101511Sminht 	if (strcmp(tod_module_name, "todbq4802") == 0) {
102511Sminht 		if (v_rtc_addr_reg == NULL)
103511Sminht 			cmn_err(CE_PANIC, "addr not set, cannot read RTC\n");
104511Sminht 
105*1868Srameshc 		BQ4802_DATA_REG(RTC_CNTRL) = (RTC_HM | RTC_STOP_N);
106511Sminht 
107511Sminht 		/* Clear AF flag by reading reg Flags (D) */
108511Sminht 		(void) BQ4802_DATA_REG(RTC_FLAGS);
109511Sminht 
110511Sminht 		tod_ops.tod_get = todbq4802_get;
111511Sminht 		tod_ops.tod_set = todbq4802_set;
112511Sminht 		tod_ops.tod_set_watchdog_timer =
113511Sminht 		    todbq4802_set_watchdog_timer;
114511Sminht 		tod_ops.tod_clear_watchdog_timer =
115511Sminht 		    todbq4802_clear_watchdog_timer;
116511Sminht 		tod_ops.tod_set_power_alarm = todbq4802_set_power_alarm;
117511Sminht 		tod_ops.tod_clear_power_alarm = todbq4802_clear_power_alarm;
118511Sminht 		tod_ops.tod_get_cpufrequency = todbq4802_get_cpufrequency;
119511Sminht 
120511Sminht 		/*
121511Sminht 		 * check if hardware watchdog timer is available and user
122511Sminht 		 * enabled it.
123511Sminht 		 */
124511Sminht 		if (watchdog_enable) {
125511Sminht 			if (!watchdog_available) {
126511Sminht 				cmn_err(CE_WARN, "bq4802: Hardware watchdog "
127511Sminht 				    "unavailable");
128511Sminht 			} else if (boothowto & RB_DEBUG) {
129511Sminht 				cmn_err(CE_WARN, "bq4802: Hardware watchdog"
130511Sminht 				    " disabled [debugger]");
131511Sminht 			}
132511Sminht 		}
133511Sminht 	}
134511Sminht 
135511Sminht 	return (mod_install(&modlinkage));
136511Sminht }
137511Sminht 
138511Sminht int
_fini(void)139511Sminht _fini(void)
140511Sminht {
141511Sminht 	if (strcmp(tod_module_name, "todbq4802") == 0)
142511Sminht 		return (EBUSY);
143511Sminht 
144511Sminht 	return (mod_remove(&modlinkage));
145511Sminht }
146511Sminht 
147511Sminht /*
148511Sminht  * The loadable-module _info(9E) entry point
149511Sminht  */
150511Sminht int
_info(struct modinfo * modinfop)151511Sminht _info(struct modinfo *modinfop)
152511Sminht {
153511Sminht 	return (mod_info(&modlinkage, modinfop));
154511Sminht }
155511Sminht 
156511Sminht /*
157511Sminht  * Read the current time from the clock chip and convert to UNIX form.
158511Sminht  * Assumes that the year in the clock chip is valid.
159511Sminht  * Must be called with tod_lock held.
160511Sminht  */
161511Sminht static timestruc_t
todbq4802_get(void)162511Sminht todbq4802_get(void)
163511Sminht {
164511Sminht 	timestruc_t ts;
165511Sminht 	todinfo_t tod;
166511Sminht 	struct rtc_t rtc;
167511Sminht 
168511Sminht 	ASSERT(MUTEX_HELD(&tod_lock));
169511Sminht 
170511Sminht 	read_rtc(&rtc);
171511Sminht 	DPRINTF("todbq4802_get: century=%d year=%d dom=%d hrs=%d min=%d"
172511Sminht 	    " sec=%d\n", rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom,
173511Sminht 	    rtc.rtc_hrs, rtc.rtc_min, rtc.rtc_sec);
174511Sminht 
175511Sminht 	/*
176511Sminht 	 * tod_year is base 1900 so this code needs to adjust the true
177511Sminht 	 * year retrieved from the rtc's century and year fields.
178511Sminht 	 */
179511Sminht 	tod.tod_year	= rtc.rtc_year + (rtc.rtc_century * 100) - 1900;
180511Sminht 	tod.tod_month	= rtc.rtc_mon;
181511Sminht 	tod.tod_day	= rtc.rtc_dom;
182511Sminht 	tod.tod_dow	= rtc.rtc_dow;
183511Sminht 	tod.tod_hour	= rtc.rtc_hrs;
184511Sminht 	tod.tod_min	= rtc.rtc_min;
185511Sminht 	tod.tod_sec	= rtc.rtc_sec;
186511Sminht 
187511Sminht 	ts.tv_sec = tod_to_utc(tod);
188511Sminht 	ts.tv_nsec = 0;
189511Sminht 	return (ts);
190511Sminht }
191511Sminht 
192511Sminht /*
193511Sminht  * Once every second, the user-accessible clock/calendar
194511Sminht  * locations are updated simultaneously from the internal
195511Sminht  * real-time counters. To prevent reading data in transition,
196511Sminht  * updates to the bq4802 clock registers should be halted.
197511Sminht  * Updating is halted by setting the Update Transfer Inhibit
198511Sminht  * (UTI) bit D3 of the control register E. As long as the
199511Sminht  * UTI bit is 1, updates to user-accessible clock locations are
200511Sminht  * inhibited. Once the frozen clock information is retrieved by
201511Sminht  * reading the appropriate clock memory locations, the UTI
202511Sminht  * bit should be reset to 0 in order to allow updates to occur
203511Sminht  * from the internal counters. Because the internal counters
204511Sminht  * are not halted by setting the UTI bit, reading the clock
205511Sminht  * locations has no effect on clock accuracy. Once the UTI bit
206511Sminht  * is reset to 0, the internal registers update within one
207511Sminht  * second the user-accessible registers with the correct time.
208511Sminht  * A halt command issued during a clock update allows the
209511Sminht  * update to occur before freezing the data.
210511Sminht  */
211511Sminht static void
read_rtc(struct rtc_t * rtc)212511Sminht read_rtc(struct rtc_t *rtc)
213511Sminht {
214511Sminht 	uint8_t	reg_cntrl;
215511Sminht 
216511Sminht 	/*
217511Sminht 	 * Freeze
218511Sminht 	 */
219511Sminht 	reg_cntrl = BQ4802_DATA_REG(RTC_CNTRL);
220511Sminht 	BQ4802_DATA_REG(RTC_CNTRL) = (reg_cntrl | RTC_UTI);
221511Sminht 
222511Sminht 	rtc->rtc_sec = BCD_TO_BYTE(BQ4802_DATA_REG(RTC_SEC));
223511Sminht 	rtc->rtc_asec = BCD_TO_BYTE(BQ4802_DATA_REG(RTC_ASEC));
224511Sminht 	rtc->rtc_min = BCD_TO_BYTE(BQ4802_DATA_REG(RTC_MIN));
225511Sminht 	rtc->rtc_amin = BCD_TO_BYTE(BQ4802_DATA_REG(RTC_AMIN));
226511Sminht 	rtc->rtc_hrs = BCD_TO_BYTE(BQ4802_DATA_REG(RTC_HRS));
227511Sminht 	rtc->rtc_ahrs = BCD_TO_BYTE(BQ4802_DATA_REG(RTC_AHRS));
228511Sminht 	rtc->rtc_dom = BCD_TO_BYTE(BQ4802_DATA_REG(RTC_DOM));
229511Sminht 	rtc->rtc_adom = BCD_TO_BYTE(BQ4802_DATA_REG(RTC_ADOM));
230511Sminht 	rtc->rtc_dow = BCD_TO_BYTE(BQ4802_DATA_REG(RTC_DOW));
231511Sminht 	rtc->rtc_mon = BCD_TO_BYTE(BQ4802_DATA_REG(RTC_MON));
232511Sminht 	rtc->rtc_year = BCD_TO_BYTE(BQ4802_DATA_REG(RTC_YEAR));
233511Sminht 	rtc->rtc_century = BCD_TO_BYTE(BQ4802_DATA_REG(RTC_CENTURY));
234511Sminht 
235511Sminht 	/*
236511Sminht 	 * Unfreeze
237511Sminht 	 */
238511Sminht 	BQ4802_DATA_REG(RTC_CNTRL) = reg_cntrl;
239511Sminht }
240511Sminht 
241511Sminht /*
242511Sminht  * Write the specified time into the clock chip.
243511Sminht  * Must be called with tod_lock held.
244511Sminht  */
245511Sminht static void
todbq4802_set(timestruc_t ts)246511Sminht todbq4802_set(timestruc_t ts)
247511Sminht {
248511Sminht 	struct rtc_t	rtc;
249511Sminht 	todinfo_t tod = utc_to_tod(ts.tv_sec);
250511Sminht 	int year;
251511Sminht 
252511Sminht 	ASSERT(MUTEX_HELD(&tod_lock));
253511Sminht 
254511Sminht 	/* tod_year is base 1900 so this code needs to adjust */
255511Sminht 	year = 1900 + tod.tod_year;
256511Sminht 	rtc.rtc_year	= year % 100;
257511Sminht 	rtc.rtc_century = year / 100;
258511Sminht 	rtc.rtc_mon	= (uint8_t)tod.tod_month;
259511Sminht 	rtc.rtc_dom	= (uint8_t)tod.tod_day;
260511Sminht 	rtc.rtc_dow	= (uint8_t)tod.tod_dow;
261511Sminht 	rtc.rtc_hrs	= (uint8_t)tod.tod_hour;
262511Sminht 	rtc.rtc_min	= (uint8_t)tod.tod_min;
263511Sminht 	rtc.rtc_sec	= (uint8_t)tod.tod_sec;
264511Sminht 	DPRINTF("todbq4802_set: year=%d dom=%d hrs=%d min=%d sec=%d\n",
265511Sminht 	    rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs, rtc.rtc_min, rtc.rtc_sec);
266511Sminht 
267511Sminht 	write_rtc_time(&rtc);
268511Sminht }
269511Sminht 
270511Sminht /*
271511Sminht  * The UTI bit must be used to set the bq4802 clock.
272511Sminht  * Once set, the locations can be written with the desired
273511Sminht  * information in BCD format. Resetting the UTI bit to 0 causes
274511Sminht  * the written values to be transferred to the internal clock
275511Sminht  * counters and allows updates to the user-accessible registers
276511Sminht  * to resume within one second.
277511Sminht  */
278511Sminht void
write_rtc_time(struct rtc_t * rtc)279511Sminht write_rtc_time(struct rtc_t *rtc)
280511Sminht {
281511Sminht 	uint8_t	reg_cntrl;
282511Sminht 
283511Sminht 	/*
284511Sminht 	 * Freeze
285511Sminht 	 */
286511Sminht 	reg_cntrl = BQ4802_DATA_REG(RTC_CNTRL);
287511Sminht 	BQ4802_DATA_REG(RTC_CNTRL) = (reg_cntrl | RTC_UTI);
288511Sminht 
289511Sminht 	BQ4802_DATA_REG(RTC_SEC) = BYTE_TO_BCD(rtc->rtc_sec);
290511Sminht 	BQ4802_DATA_REG(RTC_MIN) = BYTE_TO_BCD(rtc->rtc_min);
291511Sminht 	BQ4802_DATA_REG(RTC_HRS) = BYTE_TO_BCD(rtc->rtc_hrs);
292511Sminht 	BQ4802_DATA_REG(RTC_DOM) = BYTE_TO_BCD(rtc->rtc_dom);
293511Sminht 	BQ4802_DATA_REG(RTC_DOW) = BYTE_TO_BCD(rtc->rtc_dow);
294511Sminht 	BQ4802_DATA_REG(RTC_MON) = BYTE_TO_BCD(rtc->rtc_mon);
295511Sminht 	BQ4802_DATA_REG(RTC_YEAR) = BYTE_TO_BCD(rtc->rtc_year);
296511Sminht 	BQ4802_DATA_REG(RTC_CENTURY) = BYTE_TO_BCD(rtc->rtc_century);
297511Sminht 
298511Sminht 	/*
299511Sminht 	 * Unfreeze
300511Sminht 	 */
301511Sminht 	BQ4802_DATA_REG(RTC_CNTRL) = reg_cntrl;
302511Sminht }
303511Sminht 
304511Sminht void
write_rtc_alarm(struct rtc_t * rtc)305511Sminht write_rtc_alarm(struct rtc_t *rtc)
306511Sminht {
307511Sminht 	BQ4802_DATA_REG(RTC_ASEC) = BYTE_TO_BCD(rtc->rtc_asec);
308511Sminht 	BQ4802_DATA_REG(RTC_AMIN) = BYTE_TO_BCD(rtc->rtc_amin);
309511Sminht 	BQ4802_DATA_REG(RTC_AHRS) = BYTE_TO_BCD(rtc->rtc_ahrs);
310511Sminht 	BQ4802_DATA_REG(RTC_ADOM) = BYTE_TO_BCD(rtc->rtc_adom);
311511Sminht }
312511Sminht 
313511Sminht /*
314511Sminht  * program the rtc registers for alarm to go off at the specified time
315511Sminht  */
316511Sminht static void
todbq4802_set_power_alarm(timestruc_t ts)317511Sminht todbq4802_set_power_alarm(timestruc_t ts)
318511Sminht {
319511Sminht 	todinfo_t	tod;
320511Sminht 	uint8_t		regc;
321511Sminht 	struct rtc_t	rtc;
322511Sminht 
323511Sminht 	ASSERT(MUTEX_HELD(&tod_lock));
324511Sminht 	tod = utc_to_tod(ts.tv_sec);
325511Sminht 
326511Sminht 	/*
327511Sminht 	 * disable alarms and clear AF flag by reading reg Flags (D)
328511Sminht 	 */
329511Sminht 	regc = BQ4802_DATA_REG(RTC_ENABLES);
330511Sminht 	BQ4802_DATA_REG(RTC_ENABLES) = regc & ~(RTC_AIE | RTC_ABE);
331511Sminht 	(void) BQ4802_DATA_REG(RTC_FLAGS);
332511Sminht 
333511Sminht 	rtc.rtc_asec = (uint8_t)tod.tod_sec;
334511Sminht 	rtc.rtc_amin = (uint8_t)tod.tod_min;
335511Sminht 	rtc.rtc_ahrs = (uint8_t)tod.tod_hour;
336511Sminht 	rtc.rtc_adom = (uint8_t)tod.tod_day;
337511Sminht 	DPRINTF("todbq4802_set_alarm: dom=%d hrs=%d min=%d sec=%d\n",
338511Sminht 	    rtc.rtc_adom, rtc.rtc_ahrs, rtc.rtc_amin, rtc.rtc_asec);
339511Sminht 
340511Sminht 	/*
341511Sminht 	 * Write alarm values and enable alarm
342511Sminht 	 */
343511Sminht 	write_rtc_alarm(&rtc);
344511Sminht 
345511Sminht 	BQ4802_DATA_REG(RTC_ENABLES) = regc | RTC_AIE | RTC_ABE;
346511Sminht }
347511Sminht 
348511Sminht /*
349511Sminht  * clear alarm interrupt
350511Sminht  */
351511Sminht static void
todbq4802_clear_power_alarm(void)352511Sminht todbq4802_clear_power_alarm(void)
353511Sminht {
354511Sminht 	uint8_t regc;
355511Sminht 
356511Sminht 	ASSERT(MUTEX_HELD(&tod_lock));
357511Sminht 
358511Sminht 	regc = BQ4802_DATA_REG(RTC_ENABLES);
359511Sminht 	BQ4802_DATA_REG(RTC_ENABLES) = regc & ~(RTC_AIE | RTC_ABE);
360511Sminht }
361511Sminht 
362511Sminht /*
363511Sminht  * Determine the cpu frequency by watching the TOD chip rollover twice.
364511Sminht  * Cpu clock rate is determined by computing the ticks added (in tick register)
365511Sminht  * during one second interval on TOD.
366511Sminht  */
367511Sminht uint64_t
todbq4802_get_cpufrequency(void)368511Sminht todbq4802_get_cpufrequency(void)
369511Sminht {
370511Sminht 	ASSERT(MUTEX_HELD(&tod_lock));
371511Sminht 	return (find_cpufrequency((volatile uint8_t *)v_rtc_addr_reg));
372511Sminht }
373511Sminht 
374511Sminht /*ARGSUSED*/
375511Sminht static uint_t
todbq4802_set_watchdog_timer(uint_t timeoutval)376511Sminht todbq4802_set_watchdog_timer(uint_t timeoutval)
377511Sminht {
378511Sminht 	ASSERT(MUTEX_HELD(&tod_lock));
379511Sminht 	return (0);
380511Sminht }
381511Sminht 
382511Sminht static uint_t
todbq4802_clear_watchdog_timer(void)383511Sminht todbq4802_clear_watchdog_timer(void)
384511Sminht {
385511Sminht 	ASSERT(MUTEX_HELD(&tod_lock));
386511Sminht 	return (0);
387511Sminht }
388