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