xref: /illumos-gate/usr/src/uts/sun4u/io/todds1287.c (revision bbf21555)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5903a11ebSrh  * Common Development and Distribution License (the "License").
6903a11ebSrh  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22903a11ebSrh  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate /*
287c478bd9Sstevel@tonic-gate  *	The "todds1287" module has implementation for both tod
297c478bd9Sstevel@tonic-gate  *	and power button (pbio) interfaces.  This driver controls
307c478bd9Sstevel@tonic-gate  *	RTC & APC units of National Semiconductor's 87317 SuperI/O
317c478bd9Sstevel@tonic-gate  *	chip.  The tod interface accesses the RTC unit and pbio
327c478bd9Sstevel@tonic-gate  *	interface accesses the APC unit of SuperI/O.  Since both
337c478bd9Sstevel@tonic-gate  *	units are implemented in the same Logical Device, registers
347c478bd9Sstevel@tonic-gate  *	for both units are accessible through a common set of index
357c478bd9Sstevel@tonic-gate  *	address & data registers.  That is why both interfaces are
367c478bd9Sstevel@tonic-gate  *	implemented in a same driver.
377c478bd9Sstevel@tonic-gate  *
387c478bd9Sstevel@tonic-gate  *	The APC unit is used to implement the power button.  When the
397c478bd9Sstevel@tonic-gate  *	button momentarily is pressed, an interrupt is generated and
407c478bd9Sstevel@tonic-gate  *	at the same time a Fail-safe timer starts to run.  If the
417c478bd9Sstevel@tonic-gate  *	timer is not stopped in 21 seconds, the power to system is
427c478bd9Sstevel@tonic-gate  *	turned off.  So the first task in the interrupt handler is to
437c478bd9Sstevel@tonic-gate  *	reset the Fail-safe timer.  Note that OBP is not clearing
447c478bd9Sstevel@tonic-gate  *	the Fail-safe timer due to limitation in handling interrupts,
457c478bd9Sstevel@tonic-gate  *	so when OBP is running, the power button should be pressed
467c478bd9Sstevel@tonic-gate  *	and held for 4 seconds for the power to go off, otherwise
477c478bd9Sstevel@tonic-gate  *	a momentarily press will delay the power-off for 21 seconds.
487c478bd9Sstevel@tonic-gate  *
49*bbf21555SRichard Lowe  *	PSARC/1999/393 describes the pbio(4I) interface.
507c478bd9Sstevel@tonic-gate  */
517c478bd9Sstevel@tonic-gate 
527c478bd9Sstevel@tonic-gate #include <sys/types.h>
537c478bd9Sstevel@tonic-gate #include <sys/conf.h>
547c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
557c478bd9Sstevel@tonic-gate #include <sys/open.h>
567c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
577c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate #include <sys/todds1287.h>
607c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
617c478bd9Sstevel@tonic-gate #include <sys/stat.h>
627c478bd9Sstevel@tonic-gate #include <sys/clock.h>
637c478bd9Sstevel@tonic-gate #include <sys/reboot.h>
647c478bd9Sstevel@tonic-gate #include <sys/machsystm.h>
657c478bd9Sstevel@tonic-gate #include <sys/poll.h>
667c478bd9Sstevel@tonic-gate #include <sys/pbio.h>
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate #define	ABORT_INCREMENT_DELAY	10
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate static timestruc_t todds_get(void);
717c478bd9Sstevel@tonic-gate static void todds_set(timestruc_t);
727c478bd9Sstevel@tonic-gate static uint_t todds_set_watchdog_timer(uint_t);
737c478bd9Sstevel@tonic-gate static uint_t todds_clear_watchdog_timer(void);
747c478bd9Sstevel@tonic-gate static void todds_set_power_alarm(timestruc_t);
757c478bd9Sstevel@tonic-gate static void todds_clear_power_alarm(void);
767c478bd9Sstevel@tonic-gate static uint64_t todds_get_cpufrequency(void);
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate extern uint64_t find_cpufrequency(volatile uint8_t *);
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate /*
817c478bd9Sstevel@tonic-gate  * External variables
827c478bd9Sstevel@tonic-gate  */
837c478bd9Sstevel@tonic-gate extern int	watchdog_activated;
847c478bd9Sstevel@tonic-gate extern uint_t	watchdog_timeout_seconds;
857c478bd9Sstevel@tonic-gate extern volatile uint8_t	*v_pmc_addr_reg;
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate /*
887c478bd9Sstevel@tonic-gate  * Global variables
897c478bd9Sstevel@tonic-gate  */
907c478bd9Sstevel@tonic-gate int ds1287_debug_flags;
917c478bd9Sstevel@tonic-gate int ds1287_caddr_warn;
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate /*
947c478bd9Sstevel@tonic-gate  * cb ops
957c478bd9Sstevel@tonic-gate  */
967c478bd9Sstevel@tonic-gate static int ds1287_open(dev_t *, int, int, cred_t *);
977c478bd9Sstevel@tonic-gate static int ds1287_close(dev_t, int, int, cred_t *);
987c478bd9Sstevel@tonic-gate static int ds1287_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
997c478bd9Sstevel@tonic-gate static int ds1287_chpoll(dev_t, short, int, short *, struct pollhead **);
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate static void read_rtc(struct rtc_t *);
1027c478bd9Sstevel@tonic-gate static void write_rtc_time(struct rtc_t *);
1037c478bd9Sstevel@tonic-gate static void write_rtc_alarm(struct rtc_t *);
1047c478bd9Sstevel@tonic-gate static void select_bank(int bank);
1057c478bd9Sstevel@tonic-gate static uint_t ds1287_intr(caddr_t);
1067c478bd9Sstevel@tonic-gate static uint_t ds1287_softintr(caddr_t);
1077c478bd9Sstevel@tonic-gate static void ds1287_timeout(caddr_t);
1087c478bd9Sstevel@tonic-gate static uint_t ds1287_issue_shutdown(caddr_t);
1097c478bd9Sstevel@tonic-gate static void ds1287_log_message(void);
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate static struct cb_ops ds1287_cbops = {
1127c478bd9Sstevel@tonic-gate 	ds1287_open,			/* open */
1137c478bd9Sstevel@tonic-gate 	ds1287_close,			/* close */
1147c478bd9Sstevel@tonic-gate 	nodev,				/* strategy */
1157c478bd9Sstevel@tonic-gate 	nodev,				/* print */
1167c478bd9Sstevel@tonic-gate 	nodev,				/* dump */
1177c478bd9Sstevel@tonic-gate 	nodev,				/* read */
1187c478bd9Sstevel@tonic-gate 	nodev,				/* write */
1197c478bd9Sstevel@tonic-gate 	ds1287_ioctl,			/* ioctl */
1207c478bd9Sstevel@tonic-gate 	nodev,				/* devmap */
1217c478bd9Sstevel@tonic-gate 	nodev,				/* mmap */
1227c478bd9Sstevel@tonic-gate 	nodev,				/* segmap */
1237c478bd9Sstevel@tonic-gate 	ds1287_chpoll,			/* poll */
1247c478bd9Sstevel@tonic-gate 	ddi_prop_op,			/* cb_prop_op */
1257c478bd9Sstevel@tonic-gate 	NULL,				/* streamtab */
1267c478bd9Sstevel@tonic-gate 	D_NEW | D_MP,			/* Driver compatibility flag */
1277c478bd9Sstevel@tonic-gate 	CB_REV,				/* rev */
1287c478bd9Sstevel@tonic-gate 	nodev,				/* int (*cb_aread)() */
1297c478bd9Sstevel@tonic-gate 	nodev				/* int (*cb_awrite)() */
1307c478bd9Sstevel@tonic-gate };
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate /*
1337c478bd9Sstevel@tonic-gate  * dev ops
1347c478bd9Sstevel@tonic-gate  */
1357c478bd9Sstevel@tonic-gate static int ds1287_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1367c478bd9Sstevel@tonic-gate static int ds1287_attach(dev_info_t *, ddi_attach_cmd_t);
1377c478bd9Sstevel@tonic-gate static int ds1287_detach(dev_info_t *, ddi_detach_cmd_t);
1387c478bd9Sstevel@tonic-gate 
1397c478bd9Sstevel@tonic-gate static struct dev_ops ds1287_ops = {
1407c478bd9Sstevel@tonic-gate 	DEVO_REV,			/* devo_rev */
1417c478bd9Sstevel@tonic-gate 	0,				/* refcnt */
1427c478bd9Sstevel@tonic-gate 	ds1287_getinfo,			/* getinfo */
1437c478bd9Sstevel@tonic-gate 	nulldev,			/* identify */
1447c478bd9Sstevel@tonic-gate 	nulldev,			/* probe */
1457c478bd9Sstevel@tonic-gate 	ds1287_attach,			/* attach */
1467c478bd9Sstevel@tonic-gate 	ds1287_detach,			/* detach */
1477c478bd9Sstevel@tonic-gate 	nodev,				/* reset */
1487c478bd9Sstevel@tonic-gate 	&ds1287_cbops,			/* cb_ops */
1497c478bd9Sstevel@tonic-gate 	(struct bus_ops *)NULL,		/* bus_ops */
15019397407SSherry Moore 	NULL,				/* power */
15119397407SSherry Moore 	ddi_quiesce_not_supported,	/* devo_quiesce */
1527c478bd9Sstevel@tonic-gate };
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate static void	*ds1287_state;
1567c478bd9Sstevel@tonic-gate static int	instance = -1;
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate /* Driver Tunables */
1597c478bd9Sstevel@tonic-gate static int	ds1287_interrupt_priority = 15;
1607c478bd9Sstevel@tonic-gate static int	ds1287_softint_priority = 2;
16119449258SJosef 'Jeff' Sipek static hrtime_t power_button_debounce = MSEC2NSEC(10);
1627c478bd9Sstevel@tonic-gate static hrtime_t power_button_abort_interval = 1.5 * NANOSEC;
1637c478bd9Sstevel@tonic-gate static int	power_button_abort_presses = 3;
1647c478bd9Sstevel@tonic-gate static int	power_button_abort_enable = 1;
1657c478bd9Sstevel@tonic-gate static int	power_button_enable = 1;
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate static int	power_button_pressed = 0;
1687c478bd9Sstevel@tonic-gate static int	power_button_cancel = 0;
1697c478bd9Sstevel@tonic-gate static int	power_button_timeouts = 0;
1707c478bd9Sstevel@tonic-gate static int	timeout_cancel = 0;
1717c478bd9Sstevel@tonic-gate static int	additional_presses = 0;
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate static ddi_iblock_cookie_t ds1287_lo_iblock;
1747c478bd9Sstevel@tonic-gate static ddi_iblock_cookie_t ds1287_hi_iblock;
1757c478bd9Sstevel@tonic-gate static ddi_softintr_t	ds1287_softintr_id;
1767c478bd9Sstevel@tonic-gate static kmutex_t ds1287_reg_mutex;	/* Protects ds1287 Registers */
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
17947a041a0SToomas Soome 	&mod_driverops,		/* Type of module. This one is a driver */
180903a11ebSrh 	"ds1287 clock driver",	/* Name of the module. */
181903a11ebSrh 	&ds1287_ops,		/* driver ops */
1827c478bd9Sstevel@tonic-gate };
1837c478bd9Sstevel@tonic-gate 
1847c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
1857c478bd9Sstevel@tonic-gate 	MODREV_1, &modldrv, NULL
1867c478bd9Sstevel@tonic-gate };
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate int
_init(void)1907c478bd9Sstevel@tonic-gate _init(void)
1917c478bd9Sstevel@tonic-gate {
1927c478bd9Sstevel@tonic-gate 	int status;
1937c478bd9Sstevel@tonic-gate 
1947c478bd9Sstevel@tonic-gate 	status = ddi_soft_state_init(&ds1287_state, sizeof (struct ds1287), 0);
1957c478bd9Sstevel@tonic-gate 	if (status != 0) {
1967c478bd9Sstevel@tonic-gate 		return (status);
1977c478bd9Sstevel@tonic-gate 	}
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	if ((status = mod_install(&modlinkage)) != 0) {
2007c478bd9Sstevel@tonic-gate 		ddi_soft_state_fini(&ds1287_state);
2017c478bd9Sstevel@tonic-gate 		return (status);
2027c478bd9Sstevel@tonic-gate 	}
2037c478bd9Sstevel@tonic-gate 
2047c478bd9Sstevel@tonic-gate 
205b0b35aceSmike_s 	ds1287_hi_iblock = (ddi_iblock_cookie_t)(uintptr_t)
2067c478bd9Sstevel@tonic-gate 	    ipltospl(ds1287_interrupt_priority);
2077c478bd9Sstevel@tonic-gate 	mutex_init(&ds1287_reg_mutex, NULL, MUTEX_DRIVER, ds1287_hi_iblock);
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
2107c478bd9Sstevel@tonic-gate 	/* Select Bank 1 */
2117c478bd9Sstevel@tonic-gate 	select_bank(1);
2127c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_B;
2137c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = (RTC_DM | RTC_HM);
2147c478bd9Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
2157c478bd9Sstevel@tonic-gate 
2167c478bd9Sstevel@tonic-gate 	tod_ops.tod_get = todds_get;
2177c478bd9Sstevel@tonic-gate 	tod_ops.tod_set = todds_set;
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate 	/*
2207c478bd9Sstevel@tonic-gate 	 * If v_pmc_addr_reg isn't set, it's because it wasn't set in
2217c478bd9Sstevel@tonic-gate 	 * sun4u/os/fillsysinfo.c:have_pmc(). This means the real (pmc)
2227c478bd9Sstevel@tonic-gate 	 * watchdog routines (sun4u/io/pmc.c) will not be used. If the
2237c478bd9Sstevel@tonic-gate 	 * user were to set watchdog_enable in /etc/system, we'll need to
2247c478bd9Sstevel@tonic-gate 	 * use our own NOP routines.
2257c478bd9Sstevel@tonic-gate 	 */
2267c478bd9Sstevel@tonic-gate 	if (v_pmc_addr_reg == NULL) {
2277c478bd9Sstevel@tonic-gate 		tod_ops.tod_set_watchdog_timer = todds_set_watchdog_timer;
2287c478bd9Sstevel@tonic-gate 		tod_ops.tod_clear_watchdog_timer = todds_clear_watchdog_timer;
2297c478bd9Sstevel@tonic-gate 	}
2307c478bd9Sstevel@tonic-gate 	tod_ops.tod_set_power_alarm = todds_set_power_alarm;
2317c478bd9Sstevel@tonic-gate 	tod_ops.tod_clear_power_alarm = todds_clear_power_alarm;
2327c478bd9Sstevel@tonic-gate 	tod_ops.tod_get_cpufrequency = todds_get_cpufrequency;
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate 	return (status);
2357c478bd9Sstevel@tonic-gate }
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate int
_fini(void)2387c478bd9Sstevel@tonic-gate _fini(void)
2397c478bd9Sstevel@tonic-gate {
2407c478bd9Sstevel@tonic-gate 	if (strcmp(tod_module_name, "todds1287") == 0)
2417c478bd9Sstevel@tonic-gate 		return (EBUSY);
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
2447c478bd9Sstevel@tonic-gate }
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate /*
2477c478bd9Sstevel@tonic-gate  * The loadable-module _info(9E) entry point
2487c478bd9Sstevel@tonic-gate  */
2497c478bd9Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2507c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
2517c478bd9Sstevel@tonic-gate {
2527c478bd9Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2537c478bd9Sstevel@tonic-gate }
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2567c478bd9Sstevel@tonic-gate static int
ds1287_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)2577c478bd9Sstevel@tonic-gate ds1287_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
2587c478bd9Sstevel@tonic-gate     void **result)
2597c478bd9Sstevel@tonic-gate {
2607c478bd9Sstevel@tonic-gate 	struct ds1287 *softsp;
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 	if (instance == -1)
2637c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate 	switch (infocmd) {
2667c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
2677c478bd9Sstevel@tonic-gate 		if ((softsp = ddi_get_soft_state(ds1287_state, instance))
2687c478bd9Sstevel@tonic-gate 		    == NULL)
2697c478bd9Sstevel@tonic-gate 			return (DDI_FAILURE);
2707c478bd9Sstevel@tonic-gate 		*result = (void *)softsp->dip;
2717c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
2727c478bd9Sstevel@tonic-gate 
2737c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
274b0b35aceSmike_s 		*result = (void *)(uintptr_t)instance;
2757c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
2767c478bd9Sstevel@tonic-gate 
2777c478bd9Sstevel@tonic-gate 	default:
2787c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
2797c478bd9Sstevel@tonic-gate 	}
2807c478bd9Sstevel@tonic-gate }
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate static int
ds1287_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2837c478bd9Sstevel@tonic-gate ds1287_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2847c478bd9Sstevel@tonic-gate {
2857c478bd9Sstevel@tonic-gate 	struct ds1287 *softsp;
2867c478bd9Sstevel@tonic-gate 
2877c478bd9Sstevel@tonic-gate 	DPRINTF("ds1287_attach\n");
2887c478bd9Sstevel@tonic-gate 	switch (cmd) {
2897c478bd9Sstevel@tonic-gate 	case DDI_ATTACH:
2907c478bd9Sstevel@tonic-gate 		break;
2917c478bd9Sstevel@tonic-gate 	case DDI_RESUME:
2927c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
2937c478bd9Sstevel@tonic-gate 	default:
2947c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
2957c478bd9Sstevel@tonic-gate 	}
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate 	if (instance != -1) {
2987c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287_attach: Another instance is already "
2997c478bd9Sstevel@tonic-gate 		    "attached.");
3007c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3017c478bd9Sstevel@tonic-gate 	}
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 	if (v_rtc_addr_reg == NULL) {
3067c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287_attach: v_rtc_addr_reg is NULL");
3077c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3087c478bd9Sstevel@tonic-gate 	}
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate 	/*
3117c478bd9Sstevel@tonic-gate 	 * Allocate softc information.
3127c478bd9Sstevel@tonic-gate 	 */
3137c478bd9Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(ds1287_state, instance) != DDI_SUCCESS) {
3147c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287_attach: Failed to allocate "
3157c478bd9Sstevel@tonic-gate 		    "soft states.");
3167c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3177c478bd9Sstevel@tonic-gate 	}
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate 	softsp = ddi_get_soft_state(ds1287_state, instance);
320903a11ebSrh 	DPRINTF("ds1287_attach: instance=%d softsp=0x%p\n", instance,
321903a11ebSrh 	    (void *)softsp);
3227c478bd9Sstevel@tonic-gate 
3237c478bd9Sstevel@tonic-gate 	softsp->dip = dip;
3247c478bd9Sstevel@tonic-gate 
3257c478bd9Sstevel@tonic-gate 	if (ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
3267c478bd9Sstevel@tonic-gate 	    "interrupt-priorities", (caddr_t)&ds1287_interrupt_priority,
3277c478bd9Sstevel@tonic-gate 	    sizeof (int)) != DDI_PROP_SUCCESS) {
3287c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287_attach: Failed to create \""
3297c478bd9Sstevel@tonic-gate 		    "interrupt-priorities\" property.");
3307c478bd9Sstevel@tonic-gate 		goto error;
3317c478bd9Sstevel@tonic-gate 	}
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate 	/* add the softint */
334b0b35aceSmike_s 	ds1287_lo_iblock = (ddi_iblock_cookie_t)(uintptr_t)
3357c478bd9Sstevel@tonic-gate 	    ipltospl(ds1287_softint_priority);
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate 	if (ddi_add_softintr(dip, DDI_SOFTINT_FIXED, &ds1287_softintr_id,
3387c478bd9Sstevel@tonic-gate 	    &ds1287_lo_iblock, NULL, ds1287_softintr, (caddr_t)softsp) !=
3397c478bd9Sstevel@tonic-gate 	    DDI_SUCCESS) {
3407c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287_attach: Failed to add low interrupt.");
3417c478bd9Sstevel@tonic-gate 		goto error1;
3427c478bd9Sstevel@tonic-gate 	}
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 	/* add the hi interrupt */
3457c478bd9Sstevel@tonic-gate 	if (ddi_add_intr(dip, 0, NULL, (ddi_idevice_cookie_t *)
3467c478bd9Sstevel@tonic-gate 	    &ds1287_hi_iblock, ds1287_intr, NULL) != DDI_SUCCESS) {
3477c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287_attach: Failed to add high "
3487c478bd9Sstevel@tonic-gate 		    "interrupt.");
3497c478bd9Sstevel@tonic-gate 		goto error2;
3507c478bd9Sstevel@tonic-gate 	}
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate 	/*
3537c478bd9Sstevel@tonic-gate 	 * Combination of instance number and clone number 0 is used for
3547c478bd9Sstevel@tonic-gate 	 * creating the minor node.
3557c478bd9Sstevel@tonic-gate 	 */
3567c478bd9Sstevel@tonic-gate 	if (ddi_create_minor_node(dip, "power_button", S_IFCHR,
35747a041a0SToomas Soome 	    (instance << 8) + 0, "ddi_power_button", 0) == DDI_FAILURE) {
3587c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287_attach: Failed to create minor node");
3597c478bd9Sstevel@tonic-gate 		goto error3;
3607c478bd9Sstevel@tonic-gate 	}
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 	ddi_report_dev(dip);
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate error3:
3677c478bd9Sstevel@tonic-gate 	ddi_remove_intr(dip, 0, NULL);
3687c478bd9Sstevel@tonic-gate error2:
3697c478bd9Sstevel@tonic-gate 	ddi_remove_softintr(ds1287_softintr_id);
3707c478bd9Sstevel@tonic-gate error1:
3717c478bd9Sstevel@tonic-gate 	(void) ddi_prop_remove(DDI_DEV_T_NONE, dip, "interrupt-priorities");
3727c478bd9Sstevel@tonic-gate error:
3737c478bd9Sstevel@tonic-gate 	ddi_soft_state_free(ds1287_state, instance);
3747c478bd9Sstevel@tonic-gate 	return (DDI_FAILURE);
3757c478bd9Sstevel@tonic-gate }
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3787c478bd9Sstevel@tonic-gate static int
ds1287_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3797c478bd9Sstevel@tonic-gate ds1287_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3807c478bd9Sstevel@tonic-gate {
3817c478bd9Sstevel@tonic-gate 	DPRINTF("ds1287_detach\n");
3827c478bd9Sstevel@tonic-gate 	switch (cmd) {
3837c478bd9Sstevel@tonic-gate 	case DDI_DETACH:
3847c478bd9Sstevel@tonic-gate 		/*
3857c478bd9Sstevel@tonic-gate 		 * Since it needs to always handle the power button, fail
3867c478bd9Sstevel@tonic-gate 		 * to detach.
3877c478bd9Sstevel@tonic-gate 		 */
3887c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3897c478bd9Sstevel@tonic-gate 	case DDI_SUSPEND:
3907c478bd9Sstevel@tonic-gate 		return (DDI_SUCCESS);
3917c478bd9Sstevel@tonic-gate 	default:
3927c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
3937c478bd9Sstevel@tonic-gate 	}
3947c478bd9Sstevel@tonic-gate }
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate /*ARGSUSED1*/
3977c478bd9Sstevel@tonic-gate static int
ds1287_open(dev_t * devp,int flags,int otyp,cred_t * credp)3987c478bd9Sstevel@tonic-gate ds1287_open(dev_t *devp, int flags, int otyp, cred_t *credp)
3997c478bd9Sstevel@tonic-gate {
4007c478bd9Sstevel@tonic-gate 	struct ds1287 *softsp;
4017c478bd9Sstevel@tonic-gate 	int clone;
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
4047c478bd9Sstevel@tonic-gate 		return (EINVAL);
4057c478bd9Sstevel@tonic-gate 
4067c478bd9Sstevel@tonic-gate 	if ((softsp = ddi_get_soft_state(ds1287_state, instance)) ==
4077c478bd9Sstevel@tonic-gate 	    NULL)
4087c478bd9Sstevel@tonic-gate 		return (ENXIO);
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate 	mutex_enter(&softsp->ds1287_mutex);
4117c478bd9Sstevel@tonic-gate 	for (clone = 1; clone < DS1287_MAX_CLONE; clone++)
4127c478bd9Sstevel@tonic-gate 		if (!softsp->clones[clone])
4137c478bd9Sstevel@tonic-gate 			break;
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate 	if (clone == DS1287_MAX_CLONE) {
4167c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287_open: No more allocation left "
4177c478bd9Sstevel@tonic-gate 		    "to clone a minor.");
4187c478bd9Sstevel@tonic-gate 		mutex_exit(&softsp->ds1287_mutex);
4197c478bd9Sstevel@tonic-gate 		return (ENXIO);
4207c478bd9Sstevel@tonic-gate 	}
4217c478bd9Sstevel@tonic-gate 
4227c478bd9Sstevel@tonic-gate 	*devp = makedevice(getmajor(*devp), (instance << 8) + clone);
4237c478bd9Sstevel@tonic-gate 	softsp->clones[clone] = 1;
4247c478bd9Sstevel@tonic-gate 	mutex_exit(&softsp->ds1287_mutex);
4257c478bd9Sstevel@tonic-gate 
4267c478bd9Sstevel@tonic-gate 	return (0);
4277c478bd9Sstevel@tonic-gate }
4287c478bd9Sstevel@tonic-gate 
4297c478bd9Sstevel@tonic-gate /*ARGSUSED*/
4307c478bd9Sstevel@tonic-gate static int
ds1287_close(dev_t dev,int flags,int otyp,cred_t * credp)4317c478bd9Sstevel@tonic-gate ds1287_close(dev_t dev, int flags, int otyp, cred_t *credp)
4327c478bd9Sstevel@tonic-gate {
4337c478bd9Sstevel@tonic-gate 	struct ds1287 *softsp;
4347c478bd9Sstevel@tonic-gate 	int clone;
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
4377c478bd9Sstevel@tonic-gate 		return (EINVAL);
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 	if ((softsp = ddi_get_soft_state(ds1287_state, instance)) ==
4407c478bd9Sstevel@tonic-gate 	    NULL)
4417c478bd9Sstevel@tonic-gate 		return (ENXIO);
4427c478bd9Sstevel@tonic-gate 
4437c478bd9Sstevel@tonic-gate 	clone = DS1287_MINOR_TO_CLONE(getminor(dev));
4447c478bd9Sstevel@tonic-gate 	mutex_enter(&softsp->ds1287_mutex);
4457c478bd9Sstevel@tonic-gate 	if (softsp->monitor_on == clone)
4467c478bd9Sstevel@tonic-gate 		softsp->monitor_on = 0;
4477c478bd9Sstevel@tonic-gate 	softsp->clones[clone] = 0;
4487c478bd9Sstevel@tonic-gate 	mutex_exit(&softsp->ds1287_mutex);
4497c478bd9Sstevel@tonic-gate 
4507c478bd9Sstevel@tonic-gate 	return (0);
4517c478bd9Sstevel@tonic-gate }
4527c478bd9Sstevel@tonic-gate 
4537c478bd9Sstevel@tonic-gate /*ARGSUSED4*/
4547c478bd9Sstevel@tonic-gate static int
ds1287_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)4557c478bd9Sstevel@tonic-gate ds1287_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
45647a041a0SToomas Soome     cred_t *credp, int *rvalp)
4577c478bd9Sstevel@tonic-gate {
4587c478bd9Sstevel@tonic-gate 	struct ds1287 *softsp;
4597c478bd9Sstevel@tonic-gate 	int clone;
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate 	if ((softsp = ddi_get_soft_state(ds1287_state, instance)) ==
4627c478bd9Sstevel@tonic-gate 	    NULL)
4637c478bd9Sstevel@tonic-gate 		return (ENXIO);
4647c478bd9Sstevel@tonic-gate 
4657c478bd9Sstevel@tonic-gate 	clone = DS1287_MINOR_TO_CLONE(getminor(dev));
4667c478bd9Sstevel@tonic-gate 	switch (cmd) {
4677c478bd9Sstevel@tonic-gate 	case PB_BEGIN_MONITOR:
4687c478bd9Sstevel@tonic-gate 		DPRINTF("ds1287_ioctl: PB_BEGIN_MONITOR is called.\n");
4697c478bd9Sstevel@tonic-gate 		mutex_enter(&softsp->ds1287_mutex);
4707c478bd9Sstevel@tonic-gate 		if (softsp->monitor_on) {
4717c478bd9Sstevel@tonic-gate 			mutex_exit(&softsp->ds1287_mutex);
4727c478bd9Sstevel@tonic-gate 			return (EBUSY);
4737c478bd9Sstevel@tonic-gate 		}
4747c478bd9Sstevel@tonic-gate 		softsp->monitor_on = clone;
4757c478bd9Sstevel@tonic-gate 		mutex_exit(&softsp->ds1287_mutex);
4767c478bd9Sstevel@tonic-gate 		return (0);
4777c478bd9Sstevel@tonic-gate 
4787c478bd9Sstevel@tonic-gate 	case PB_END_MONITOR:
4797c478bd9Sstevel@tonic-gate 		DPRINTF("ds1287_ioctl: PB_END_MONITOR is called.\n");
4807c478bd9Sstevel@tonic-gate 		mutex_enter(&softsp->ds1287_mutex);
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate 		/*
4837c478bd9Sstevel@tonic-gate 		 * If PB_END_MONITOR is called without first
4847c478bd9Sstevel@tonic-gate 		 * calling PB_BEGIN_MONITOR, an error will be
4857c478bd9Sstevel@tonic-gate 		 * returned.
4867c478bd9Sstevel@tonic-gate 		 */
4877c478bd9Sstevel@tonic-gate 		if (!softsp->monitor_on) {
4887c478bd9Sstevel@tonic-gate 			mutex_exit(&softsp->ds1287_mutex);
4897c478bd9Sstevel@tonic-gate 			return (ENXIO);
4907c478bd9Sstevel@tonic-gate 		}
4917c478bd9Sstevel@tonic-gate 
4927c478bd9Sstevel@tonic-gate 		/*
4937c478bd9Sstevel@tonic-gate 		 * This clone is not monitoring the button.
4947c478bd9Sstevel@tonic-gate 		 */
4957c478bd9Sstevel@tonic-gate 		if (softsp->monitor_on != clone) {
4967c478bd9Sstevel@tonic-gate 			mutex_exit(&softsp->ds1287_mutex);
4977c478bd9Sstevel@tonic-gate 			return (EINVAL);
4987c478bd9Sstevel@tonic-gate 		}
4997c478bd9Sstevel@tonic-gate 		softsp->monitor_on = 0;
5007c478bd9Sstevel@tonic-gate 		mutex_exit(&softsp->ds1287_mutex);
5017c478bd9Sstevel@tonic-gate 		return (0);
5027c478bd9Sstevel@tonic-gate 
5037c478bd9Sstevel@tonic-gate 	case PB_GET_EVENTS:
5047c478bd9Sstevel@tonic-gate 		DPRINTF("ds1287_ioctl: PB_GET_EVENTS is called.\n");
5057c478bd9Sstevel@tonic-gate 		mutex_enter(&softsp->ds1287_mutex);
5067c478bd9Sstevel@tonic-gate 		if (ddi_copyout((void *)&softsp->events, (void *)arg,
5077c478bd9Sstevel@tonic-gate 		    sizeof (int), mode) != 0) {
5087c478bd9Sstevel@tonic-gate 			mutex_exit(&softsp->ds1287_mutex);
5097c478bd9Sstevel@tonic-gate 			return (EFAULT);
5107c478bd9Sstevel@tonic-gate 		}
5117c478bd9Sstevel@tonic-gate 
5127c478bd9Sstevel@tonic-gate 		/*
5137c478bd9Sstevel@tonic-gate 		 * This ioctl returned the events detected since last
5147c478bd9Sstevel@tonic-gate 		 * call.  Note that any application can get the events
5157c478bd9Sstevel@tonic-gate 		 * and clear the event register.
5167c478bd9Sstevel@tonic-gate 		 */
5177c478bd9Sstevel@tonic-gate 		softsp->events = 0;
5187c478bd9Sstevel@tonic-gate 		mutex_exit(&softsp->ds1287_mutex);
5197c478bd9Sstevel@tonic-gate 		return (0);
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate 	/*
5227c478bd9Sstevel@tonic-gate 	 * This ioctl is used by the test suite.
5237c478bd9Sstevel@tonic-gate 	 */
5247c478bd9Sstevel@tonic-gate 	case PB_CREATE_BUTTON_EVENT:
5257c478bd9Sstevel@tonic-gate 		DPRINTF("ds1287_ioctl: PB_CREATE_BUTTON_EVENT is called.\n");
5267c478bd9Sstevel@tonic-gate 		(void) ds1287_intr(NULL);
5277c478bd9Sstevel@tonic-gate 		return (0);
5287c478bd9Sstevel@tonic-gate 
5297c478bd9Sstevel@tonic-gate 	default:
5307c478bd9Sstevel@tonic-gate 		return (ENOTTY);
5317c478bd9Sstevel@tonic-gate 	}
5327c478bd9Sstevel@tonic-gate }
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5357c478bd9Sstevel@tonic-gate static int
ds1287_chpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)5367c478bd9Sstevel@tonic-gate ds1287_chpoll(dev_t dev, short events, int anyyet,
5377c478bd9Sstevel@tonic-gate     short *reventsp, struct pollhead **phpp)
5387c478bd9Sstevel@tonic-gate {
5397c478bd9Sstevel@tonic-gate 	struct ds1287 *softsp;
5407c478bd9Sstevel@tonic-gate 
5417c478bd9Sstevel@tonic-gate 	if ((softsp = ddi_get_soft_state(ds1287_state, instance)) == NULL)
5427c478bd9Sstevel@tonic-gate 		return (ENXIO);
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	mutex_enter(&softsp->ds1287_mutex);
5457c478bd9Sstevel@tonic-gate 	*reventsp = 0;
5467c478bd9Sstevel@tonic-gate 	if (softsp->events)
5477c478bd9Sstevel@tonic-gate 		*reventsp = POLLRDNORM|POLLIN;
5487c478bd9Sstevel@tonic-gate 	else {
5497c478bd9Sstevel@tonic-gate 		if (!anyyet)
5507c478bd9Sstevel@tonic-gate 			*phpp = &softsp->pollhd;
5517c478bd9Sstevel@tonic-gate 	}
5527c478bd9Sstevel@tonic-gate 	mutex_exit(&softsp->ds1287_mutex);
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate 	return (0);
5557c478bd9Sstevel@tonic-gate }
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate static void
ds1287_log_message(void)5587c478bd9Sstevel@tonic-gate ds1287_log_message(void)
5597c478bd9Sstevel@tonic-gate {
5607c478bd9Sstevel@tonic-gate 	struct ds1287 *softsp;
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate 	if ((softsp = ddi_get_soft_state(ds1287_state, instance)) == NULL) {
5637c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287: Failed to get internal state!");
5647c478bd9Sstevel@tonic-gate 		return;
5657c478bd9Sstevel@tonic-gate 	}
5667c478bd9Sstevel@tonic-gate 
5677c478bd9Sstevel@tonic-gate 	mutex_enter(&softsp->ds1287_mutex);
5687c478bd9Sstevel@tonic-gate 	softsp->shutdown_pending = 0;
5697c478bd9Sstevel@tonic-gate 	cmn_err(CE_WARN, "ds1287: Failed to shut down the system!");
5707c478bd9Sstevel@tonic-gate 	mutex_exit(&softsp->ds1287_mutex);
5717c478bd9Sstevel@tonic-gate }
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate /*
5747c478bd9Sstevel@tonic-gate  * To facilitate a power button abort, ds1287_intr() now posts
5757c478bd9Sstevel@tonic-gate  * a softint (calling ds1287_softintr()) for all power button presses and
5767c478bd9Sstevel@tonic-gate  * counts the number of button presses. An abort is issued if the desired
5777c478bd9Sstevel@tonic-gate  * number of button presses within the given time interval.
5787c478bd9Sstevel@tonic-gate  *
5797c478bd9Sstevel@tonic-gate  * Two variables are used to synchronize between the high level intr;
5807c478bd9Sstevel@tonic-gate  * the softint handler and timeout handler
5817c478bd9Sstevel@tonic-gate  *
5827c478bd9Sstevel@tonic-gate  * power_button_cancel  - Indicates that an abort happened and the number
5837c478bd9Sstevel@tonic-gate  *                        of outstanding timeouts that have to be cancelled
5847c478bd9Sstevel@tonic-gate  *
5857c478bd9Sstevel@tonic-gate  * power_button_pressed - Indicates the number of button presses outstanding
5867c478bd9Sstevel@tonic-gate  *                        which have not been serviced
5877c478bd9Sstevel@tonic-gate  */
5887c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5897c478bd9Sstevel@tonic-gate static uint_t
ds1287_intr(caddr_t ignore)5907c478bd9Sstevel@tonic-gate ds1287_intr(caddr_t ignore)
5917c478bd9Sstevel@tonic-gate {
5927c478bd9Sstevel@tonic-gate 	hrtime_t tstamp;
5937c478bd9Sstevel@tonic-gate 	static hrtime_t o_tstamp = 0;
5947c478bd9Sstevel@tonic-gate 	static hrtime_t power_button_tstamp = 0;
5957c478bd9Sstevel@tonic-gate 	static int power_button_cnt;
5967c478bd9Sstevel@tonic-gate 	uint8_t	apcr1;
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 	/*
5997c478bd9Sstevel@tonic-gate 	 * Stop the Fail-safe timer that starts running
6007c478bd9Sstevel@tonic-gate 	 * after power button is pressed.  If it is not
6017c478bd9Sstevel@tonic-gate 	 * stopped in 21 seconds, system powers off.
6027c478bd9Sstevel@tonic-gate 	 */
6037c478bd9Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
6047c478bd9Sstevel@tonic-gate 	select_bank(2);
6057c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_APCR1;
6067c478bd9Sstevel@tonic-gate 	apcr1 = DS1287_DATA_REG;
6077c478bd9Sstevel@tonic-gate 	apcr1 |= APC_FSTRC;
6087c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = apcr1;
6097c478bd9Sstevel@tonic-gate 	select_bank(1);
6107c478bd9Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate 	tstamp = gethrtime();
6137c478bd9Sstevel@tonic-gate 
6147c478bd9Sstevel@tonic-gate 	/* need to deal with power button debounce */
6157c478bd9Sstevel@tonic-gate 	if (o_tstamp && (tstamp - o_tstamp) < power_button_debounce) {
6167c478bd9Sstevel@tonic-gate 		o_tstamp = tstamp;
6177c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
6187c478bd9Sstevel@tonic-gate 	}
6197c478bd9Sstevel@tonic-gate 	o_tstamp = tstamp;
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 	power_button_cnt++;
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
6247c478bd9Sstevel@tonic-gate 	power_button_pressed++;
6257c478bd9Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
6267c478bd9Sstevel@tonic-gate 
6277c478bd9Sstevel@tonic-gate 	/*
6287c478bd9Sstevel@tonic-gate 	 * If power button abort is enabled and power button was pressed
6297c478bd9Sstevel@tonic-gate 	 * power_button_abort_presses times within power_button_abort_interval
6307c478bd9Sstevel@tonic-gate 	 * then call abort_sequence_enter();
6317c478bd9Sstevel@tonic-gate 	 */
6327c478bd9Sstevel@tonic-gate 	if (power_button_abort_enable) {
6337c478bd9Sstevel@tonic-gate 		if (power_button_abort_presses == 1 ||
6347c478bd9Sstevel@tonic-gate 		    tstamp < (power_button_tstamp +
6357c478bd9Sstevel@tonic-gate 		    power_button_abort_interval)) {
6367c478bd9Sstevel@tonic-gate 			if (power_button_cnt == power_button_abort_presses) {
6377c478bd9Sstevel@tonic-gate 				mutex_enter(&ds1287_reg_mutex);
6387c478bd9Sstevel@tonic-gate 				power_button_cancel += power_button_timeouts;
6397c478bd9Sstevel@tonic-gate 				power_button_pressed = 0;
6407c478bd9Sstevel@tonic-gate 				mutex_exit(&ds1287_reg_mutex);
6417c478bd9Sstevel@tonic-gate 				power_button_cnt = 0;
6427c478bd9Sstevel@tonic-gate 				abort_sequence_enter("Power Button Abort");
6437c478bd9Sstevel@tonic-gate 				return (DDI_INTR_CLAIMED);
6447c478bd9Sstevel@tonic-gate 			}
6457c478bd9Sstevel@tonic-gate 		} else {
6467c478bd9Sstevel@tonic-gate 			power_button_cnt = 1;
6477c478bd9Sstevel@tonic-gate 			power_button_tstamp = tstamp;
6487c478bd9Sstevel@tonic-gate 		}
6497c478bd9Sstevel@tonic-gate 	}
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate 	if (!power_button_enable)
6527c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
6537c478bd9Sstevel@tonic-gate 
6547c478bd9Sstevel@tonic-gate 	/* post softint to issue timeout for power button action */
6557c478bd9Sstevel@tonic-gate 	ddi_trigger_softintr(ds1287_softintr_id);
6567c478bd9Sstevel@tonic-gate 
6577c478bd9Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
6587c478bd9Sstevel@tonic-gate }
6597c478bd9Sstevel@tonic-gate 
6607c478bd9Sstevel@tonic-gate /*
6617c478bd9Sstevel@tonic-gate  * Handle the softints....
6627c478bd9Sstevel@tonic-gate  *
6637c478bd9Sstevel@tonic-gate  * If only one softint is posted for several button presses, record
6647c478bd9Sstevel@tonic-gate  * the number of additional presses just incase this was actually not quite
6657c478bd9Sstevel@tonic-gate  * an Abort sequence so that we can log this event later.
6667c478bd9Sstevel@tonic-gate  *
6677c478bd9Sstevel@tonic-gate  * Issue a timeout with a duration being a fraction larger than
6687c478bd9Sstevel@tonic-gate  * the specified Abort interval inorder to perform a power down if required.
6697c478bd9Sstevel@tonic-gate  */
6707c478bd9Sstevel@tonic-gate static uint_t
ds1287_softintr(caddr_t arg)6717c478bd9Sstevel@tonic-gate ds1287_softintr(caddr_t arg)
6727c478bd9Sstevel@tonic-gate {
6737c478bd9Sstevel@tonic-gate 	struct ds1287 *softsp = (struct ds1287 *)arg;
6747c478bd9Sstevel@tonic-gate 
6757c478bd9Sstevel@tonic-gate 	DPRINTF("ds1287_softintr\n");
6767c478bd9Sstevel@tonic-gate 
6777c478bd9Sstevel@tonic-gate 	if (!power_button_abort_enable)
6787c478bd9Sstevel@tonic-gate 		return (ds1287_issue_shutdown(arg));
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
6817c478bd9Sstevel@tonic-gate 	if (!power_button_pressed) {
6827c478bd9Sstevel@tonic-gate 		mutex_exit(&ds1287_reg_mutex);
6837c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
6847c478bd9Sstevel@tonic-gate 	}
6857c478bd9Sstevel@tonic-gate 
6867c478bd9Sstevel@tonic-gate 	/*
6877c478bd9Sstevel@tonic-gate 	 * Schedule a timeout to do the necessary
6887c478bd9Sstevel@tonic-gate 	 * work for shutdown, only one timeout for
6897c478bd9Sstevel@tonic-gate 	 * n presses if power button was pressed
6907c478bd9Sstevel@tonic-gate 	 * more than once before softint fired
6917c478bd9Sstevel@tonic-gate 	 */
6927c478bd9Sstevel@tonic-gate 	if (power_button_pressed > 1)
6937c478bd9Sstevel@tonic-gate 		additional_presses += power_button_pressed - 1;
6947c478bd9Sstevel@tonic-gate 
6957c478bd9Sstevel@tonic-gate 	timeout_cancel = 0;
6967c478bd9Sstevel@tonic-gate 	power_button_pressed = 0;
6977c478bd9Sstevel@tonic-gate 	power_button_timeouts++;
6987c478bd9Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
6997c478bd9Sstevel@tonic-gate 	(void) timeout((void(*)(void *))ds1287_timeout,
7007c478bd9Sstevel@tonic-gate 	    softsp, NSEC_TO_TICK(power_button_abort_interval) +
7017c478bd9Sstevel@tonic-gate 	    ABORT_INCREMENT_DELAY);
7027c478bd9Sstevel@tonic-gate 
7037c478bd9Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
7047c478bd9Sstevel@tonic-gate }
7057c478bd9Sstevel@tonic-gate 
7067c478bd9Sstevel@tonic-gate /*
7077c478bd9Sstevel@tonic-gate  * Upon receiving a timeout the following is determined:
7087c478bd9Sstevel@tonic-gate  *
7097c478bd9Sstevel@tonic-gate  * If an  Abort sequence was issued, then we cancel all outstanding timeouts
7107c478bd9Sstevel@tonic-gate  * and additional presses prior to the Abort sequence.
7117c478bd9Sstevel@tonic-gate  *
7127c478bd9Sstevel@tonic-gate  * If we had multiple timeouts issued and the abort sequence was not met,
7137c478bd9Sstevel@tonic-gate  * then we had more than one button press to power down the machine. We
7147c478bd9Sstevel@tonic-gate  * were probably trying to issue an abort. So log a message indicating this
7157c478bd9Sstevel@tonic-gate  * and cancel all outstanding timeouts.
7167c478bd9Sstevel@tonic-gate  *
7177c478bd9Sstevel@tonic-gate  * If we had just one timeout and the abort sequence was not met then
7187c478bd9Sstevel@tonic-gate  * we really did want to power down the machine, so call ds1287_issue_shutdown()
7197c478bd9Sstevel@tonic-gate  * to do the work and schedule a power down
7207c478bd9Sstevel@tonic-gate  */
7217c478bd9Sstevel@tonic-gate static void
ds1287_timeout(caddr_t arg)7227c478bd9Sstevel@tonic-gate ds1287_timeout(caddr_t arg)
7237c478bd9Sstevel@tonic-gate {
7247c478bd9Sstevel@tonic-gate 	static int first = 0;
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate 	DPRINTF("ds1287_timeout\n");
7277c478bd9Sstevel@tonic-gate 
7287c478bd9Sstevel@tonic-gate 	/*
7297c478bd9Sstevel@tonic-gate 	 * Abort was generated cancel all outstanding power
7307c478bd9Sstevel@tonic-gate 	 * button timeouts
7317c478bd9Sstevel@tonic-gate 	 */
7327c478bd9Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
7337c478bd9Sstevel@tonic-gate 	if (power_button_cancel) {
7347c478bd9Sstevel@tonic-gate 		power_button_cancel--;
7357c478bd9Sstevel@tonic-gate 		power_button_timeouts--;
7367c478bd9Sstevel@tonic-gate 		if (!first) {
7377c478bd9Sstevel@tonic-gate 			first++;
7387c478bd9Sstevel@tonic-gate 			additional_presses = 0;
7397c478bd9Sstevel@tonic-gate 		}
7407c478bd9Sstevel@tonic-gate 		mutex_exit(&ds1287_reg_mutex);
7417c478bd9Sstevel@tonic-gate 		return;
7427c478bd9Sstevel@tonic-gate 	}
7437c478bd9Sstevel@tonic-gate 	first = 0;
7447c478bd9Sstevel@tonic-gate 
7457c478bd9Sstevel@tonic-gate 	/*
7467c478bd9Sstevel@tonic-gate 	 * We get here if the timeout(s) have fired and they were
7477c478bd9Sstevel@tonic-gate 	 * not issued prior to an abort.
7487c478bd9Sstevel@tonic-gate 	 *
7497c478bd9Sstevel@tonic-gate 	 * If we had more than one press in the interval we were
7507c478bd9Sstevel@tonic-gate 	 * probably trying to issue an abort, but didnt press the
7517c478bd9Sstevel@tonic-gate 	 * required number within the interval. Hence cancel all
7527c478bd9Sstevel@tonic-gate 	 * timeouts and do not continue towards shutdown.
7537c478bd9Sstevel@tonic-gate 	 */
7547c478bd9Sstevel@tonic-gate 	if (!timeout_cancel) {
7557c478bd9Sstevel@tonic-gate 		timeout_cancel = power_button_timeouts +
7567c478bd9Sstevel@tonic-gate 		    additional_presses;
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate 		power_button_timeouts--;
7597c478bd9Sstevel@tonic-gate 		if (!power_button_timeouts)
7607c478bd9Sstevel@tonic-gate 			additional_presses = 0;
7617c478bd9Sstevel@tonic-gate 
7627c478bd9Sstevel@tonic-gate 		if (timeout_cancel > 1) {
7637c478bd9Sstevel@tonic-gate 			mutex_exit(&ds1287_reg_mutex);
7647c478bd9Sstevel@tonic-gate 			cmn_err(CE_NOTE, "Power Button pressed "
7657c478bd9Sstevel@tonic-gate 			    "%d times, cancelling all requests",
7667c478bd9Sstevel@tonic-gate 			    timeout_cancel);
7677c478bd9Sstevel@tonic-gate 			return;
7687c478bd9Sstevel@tonic-gate 		}
7697c478bd9Sstevel@tonic-gate 		mutex_exit(&ds1287_reg_mutex);
7707c478bd9Sstevel@tonic-gate 
7717c478bd9Sstevel@tonic-gate 		/* Go and do the work to request shutdown */
7727c478bd9Sstevel@tonic-gate 		(void) ds1287_issue_shutdown(arg);
7737c478bd9Sstevel@tonic-gate 		return;
7747c478bd9Sstevel@tonic-gate 	}
7757c478bd9Sstevel@tonic-gate 
7767c478bd9Sstevel@tonic-gate 	power_button_timeouts--;
7777c478bd9Sstevel@tonic-gate 	if (!power_button_timeouts)
7787c478bd9Sstevel@tonic-gate 		additional_presses = 0;
7797c478bd9Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
7807c478bd9Sstevel@tonic-gate }
7817c478bd9Sstevel@tonic-gate 
7827c478bd9Sstevel@tonic-gate static uint_t
ds1287_issue_shutdown(caddr_t arg)7837c478bd9Sstevel@tonic-gate ds1287_issue_shutdown(caddr_t arg)
7847c478bd9Sstevel@tonic-gate {
7857c478bd9Sstevel@tonic-gate 	struct ds1287 *softsp = (struct ds1287 *)arg;
7867c478bd9Sstevel@tonic-gate 
7877c478bd9Sstevel@tonic-gate 	DPRINTF("ds1287_issue_shutdown\n");
7887c478bd9Sstevel@tonic-gate 
7897c478bd9Sstevel@tonic-gate 	mutex_enter(&softsp->ds1287_mutex);
7907c478bd9Sstevel@tonic-gate 	softsp->events |= PB_BUTTON_PRESS;
7917c478bd9Sstevel@tonic-gate 	if (softsp->monitor_on != 0) {
7927c478bd9Sstevel@tonic-gate 		mutex_exit(&softsp->ds1287_mutex);
7937c478bd9Sstevel@tonic-gate 		pollwakeup(&softsp->pollhd, POLLRDNORM);
7947c478bd9Sstevel@tonic-gate 		pollwakeup(&softsp->pollhd, POLLIN);
7957c478bd9Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
7967c478bd9Sstevel@tonic-gate 	}
7977c478bd9Sstevel@tonic-gate 
7987c478bd9Sstevel@tonic-gate 	if (!softsp->shutdown_pending) {
7997c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "Power button is pressed, powering down "
8007c478bd9Sstevel@tonic-gate 		    "the system!");
8017c478bd9Sstevel@tonic-gate 		softsp->shutdown_pending = 1;
8027c478bd9Sstevel@tonic-gate 		do_shutdown();
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate 		/*
8057c478bd9Sstevel@tonic-gate 		 * Wait a while for "do_shutdown()" to shut down the system
8067c478bd9Sstevel@tonic-gate 		 * before logging an error message.
8077c478bd9Sstevel@tonic-gate 		 */
8087c478bd9Sstevel@tonic-gate 		(void) timeout((void(*)(void *))ds1287_log_message, NULL,
8097c478bd9Sstevel@tonic-gate 		    100 * hz);
8107c478bd9Sstevel@tonic-gate 	}
8117c478bd9Sstevel@tonic-gate 	mutex_exit(&softsp->ds1287_mutex);
8127c478bd9Sstevel@tonic-gate 
8137c478bd9Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
8147c478bd9Sstevel@tonic-gate }
8157c478bd9Sstevel@tonic-gate 
8167c478bd9Sstevel@tonic-gate /*
8177c478bd9Sstevel@tonic-gate  * Read the current time from the clock chip and convert to UNIX form.
8187c478bd9Sstevel@tonic-gate  * Assumes that the year in the clock chip is valid.
8197c478bd9Sstevel@tonic-gate  * Must be called with tod_lock held.
8207c478bd9Sstevel@tonic-gate  */
8217c478bd9Sstevel@tonic-gate static timestruc_t
todds_get(void)8227c478bd9Sstevel@tonic-gate todds_get(void)
8237c478bd9Sstevel@tonic-gate {
8247c478bd9Sstevel@tonic-gate 	timestruc_t ts;
8257c478bd9Sstevel@tonic-gate 	todinfo_t tod;
8267c478bd9Sstevel@tonic-gate 	struct rtc_t rtc;
8277c478bd9Sstevel@tonic-gate 
8287c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
8297c478bd9Sstevel@tonic-gate 
8307c478bd9Sstevel@tonic-gate 	read_rtc(&rtc);
8317c478bd9Sstevel@tonic-gate 	DPRINTF("todds_get: century=%d year=%d dom=%d hrs=%d\n",
8327c478bd9Sstevel@tonic-gate 	    rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs);
8337c478bd9Sstevel@tonic-gate 
8347c478bd9Sstevel@tonic-gate 	/*
8357c478bd9Sstevel@tonic-gate 	 * tod_year is base 1900 so this code needs to adjust the true
8367c478bd9Sstevel@tonic-gate 	 * year retrieved from the rtc's century and year fields.
8377c478bd9Sstevel@tonic-gate 	 */
8387c478bd9Sstevel@tonic-gate 	tod.tod_year	= rtc.rtc_year + (rtc.rtc_century * 100) - 1900;
8397c478bd9Sstevel@tonic-gate 	tod.tod_month	= rtc.rtc_mon;
8407c478bd9Sstevel@tonic-gate 	tod.tod_day	= rtc.rtc_dom;
8417c478bd9Sstevel@tonic-gate 	tod.tod_dow	= rtc.rtc_dow;
8427c478bd9Sstevel@tonic-gate 	tod.tod_hour	= rtc.rtc_hrs;
8437c478bd9Sstevel@tonic-gate 	tod.tod_min	= rtc.rtc_min;
8447c478bd9Sstevel@tonic-gate 	tod.tod_sec	= rtc.rtc_sec;
8457c478bd9Sstevel@tonic-gate 
8467c478bd9Sstevel@tonic-gate 	ts.tv_sec = tod_to_utc(tod);
8477c478bd9Sstevel@tonic-gate 	ts.tv_nsec = 0;
8487c478bd9Sstevel@tonic-gate 
8497c478bd9Sstevel@tonic-gate 	/* set the hw watchdog timer if it's been activated */
8507c478bd9Sstevel@tonic-gate 	if (watchdog_activated) {
8517c478bd9Sstevel@tonic-gate 		int ret = 0;
8527c478bd9Sstevel@tonic-gate 		ret = tod_ops.tod_set_watchdog_timer(watchdog_timeout_seconds);
8537c478bd9Sstevel@tonic-gate 		if (ret == 0)
8547c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "ds1287: failed to set hardware "
855903a11ebSrh 			    "watchdog timer.");
8567c478bd9Sstevel@tonic-gate 	}
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate 	return (ts);
8597c478bd9Sstevel@tonic-gate }
8607c478bd9Sstevel@tonic-gate 
8617c478bd9Sstevel@tonic-gate void
read_rtc(struct rtc_t * rtc)8627c478bd9Sstevel@tonic-gate read_rtc(struct rtc_t *rtc)
8637c478bd9Sstevel@tonic-gate {
8647c478bd9Sstevel@tonic-gate 	uint8_t regb;
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate 	/*
8677c478bd9Sstevel@tonic-gate 	 * Some SuperIO tod devices don't seem to properly initialize
8687c478bd9Sstevel@tonic-gate 	 * the CADDR register to place the Century register at bank 1
8697c478bd9Sstevel@tonic-gate 	 * address 0x48.
8707c478bd9Sstevel@tonic-gate 	 */
8717c478bd9Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
8727c478bd9Sstevel@tonic-gate 
8737c478bd9Sstevel@tonic-gate 	select_bank(2);
8747c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_CADDR;
8757c478bd9Sstevel@tonic-gate 	regb = DS1287_DATA_REG;
8767c478bd9Sstevel@tonic-gate 	if (regb != 0xc8) {
8777c478bd9Sstevel@tonic-gate 		if (!ds1287_caddr_warn) {
8787c478bd9Sstevel@tonic-gate 			ds1287_caddr_warn = 1;
8797c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "ds1287: century address register "
8807c478bd9Sstevel@tonic-gate 			    "incorrect (exp 0xc8, obs %x)", regb);
8817c478bd9Sstevel@tonic-gate 		}
8827c478bd9Sstevel@tonic-gate 		DS1287_DATA_REG = 0xc8;
8837c478bd9Sstevel@tonic-gate 	}
8847c478bd9Sstevel@tonic-gate 
8857c478bd9Sstevel@tonic-gate 	select_bank(1);
8867c478bd9Sstevel@tonic-gate 	/*
8877c478bd9Sstevel@tonic-gate 	 * Freeze clock update
8887c478bd9Sstevel@tonic-gate 	 */
8897c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_B;
8907c478bd9Sstevel@tonic-gate 	regb = DS1287_DATA_REG;
8917c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = (regb | RTC_SET);
8927c478bd9Sstevel@tonic-gate 
8937c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_SEC;
8947c478bd9Sstevel@tonic-gate 	rtc->rtc_sec = DS1287_DATA_REG;
8957c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_ASEC;
8967c478bd9Sstevel@tonic-gate 	rtc->rtc_asec = DS1287_DATA_REG;
8977c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_MIN;
8987c478bd9Sstevel@tonic-gate 	rtc->rtc_min = DS1287_DATA_REG;
8997c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_AMIN;
9007c478bd9Sstevel@tonic-gate 	rtc->rtc_amin = DS1287_DATA_REG;
9017c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_HRS;
9027c478bd9Sstevel@tonic-gate 	rtc->rtc_hrs = DS1287_DATA_REG;
9037c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_AHRS;
9047c478bd9Sstevel@tonic-gate 	rtc->rtc_ahrs = DS1287_DATA_REG;
9057c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_DOW;
9067c478bd9Sstevel@tonic-gate 	rtc->rtc_dow = DS1287_DATA_REG;
9077c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_DOM;
9087c478bd9Sstevel@tonic-gate 	rtc->rtc_dom = DS1287_DATA_REG;
9097c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_MON;
9107c478bd9Sstevel@tonic-gate 	rtc->rtc_mon = DS1287_DATA_REG;
9117c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_YEAR;
9127c478bd9Sstevel@tonic-gate 	rtc->rtc_year = DS1287_DATA_REG;
9137c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_CENTURY;
9147c478bd9Sstevel@tonic-gate 	rtc->rtc_century = DS1287_DATA_REG;
9157c478bd9Sstevel@tonic-gate 
9167c478bd9Sstevel@tonic-gate 	/* Read date alarm */
9177c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_ADOM;
9187c478bd9Sstevel@tonic-gate 	rtc->rtc_adom = DS1287_DATA_REG;
9197c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_AMON;
9207c478bd9Sstevel@tonic-gate 	rtc->rtc_amon = DS1287_DATA_REG;
9217c478bd9Sstevel@tonic-gate 
9227c478bd9Sstevel@tonic-gate 	/* Read wakeup data */
9237c478bd9Sstevel@tonic-gate 	select_bank(2);
9247c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WDWR;
9257c478bd9Sstevel@tonic-gate 	rtc->apc_wdwr = DS1287_DATA_REG;
9267c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WDMR;
9277c478bd9Sstevel@tonic-gate 	rtc->apc_wdmr = DS1287_DATA_REG;
9287c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WMR;
9297c478bd9Sstevel@tonic-gate 	rtc->apc_wmr = DS1287_DATA_REG;
9307c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WYR;
9317c478bd9Sstevel@tonic-gate 	rtc->apc_wyr = DS1287_DATA_REG;
9327c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WCR;
9337c478bd9Sstevel@tonic-gate 	rtc->apc_wcr = DS1287_DATA_REG;
9347c478bd9Sstevel@tonic-gate 
9357c478bd9Sstevel@tonic-gate 	/*
9367c478bd9Sstevel@tonic-gate 	 * Unfreeze clock update
9377c478bd9Sstevel@tonic-gate 	 */
9387c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_B;
9397c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = regb;
9407c478bd9Sstevel@tonic-gate 
9417c478bd9Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
9427c478bd9Sstevel@tonic-gate }
9437c478bd9Sstevel@tonic-gate 
9447c478bd9Sstevel@tonic-gate /*
9457c478bd9Sstevel@tonic-gate  * Write the specified time into the clock chip.
9467c478bd9Sstevel@tonic-gate  * Must be called with tod_lock held.
9477c478bd9Sstevel@tonic-gate  */
9487c478bd9Sstevel@tonic-gate static void
todds_set(timestruc_t ts)9497c478bd9Sstevel@tonic-gate todds_set(timestruc_t ts)
9507c478bd9Sstevel@tonic-gate {
9517c478bd9Sstevel@tonic-gate 	struct rtc_t	rtc;
9527c478bd9Sstevel@tonic-gate 	todinfo_t tod = utc_to_tod(ts.tv_sec);
9537c478bd9Sstevel@tonic-gate 	int year;
9547c478bd9Sstevel@tonic-gate 
9557c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
9567c478bd9Sstevel@tonic-gate 
9577c478bd9Sstevel@tonic-gate 	/* tod_year is base 1900 so this code needs to adjust */
9587c478bd9Sstevel@tonic-gate 	year = 1900 + tod.tod_year;
9597c478bd9Sstevel@tonic-gate 	rtc.rtc_year	= year % 100;
9607c478bd9Sstevel@tonic-gate 	rtc.rtc_century = year / 100;
9617c478bd9Sstevel@tonic-gate 	rtc.rtc_mon	= (uint8_t)tod.tod_month;
9627c478bd9Sstevel@tonic-gate 	rtc.rtc_dom	= (uint8_t)tod.tod_day;
9637c478bd9Sstevel@tonic-gate 	rtc.rtc_dow	= (uint8_t)tod.tod_dow;
9647c478bd9Sstevel@tonic-gate 	rtc.rtc_hrs	= (uint8_t)tod.tod_hour;
9657c478bd9Sstevel@tonic-gate 	rtc.rtc_min	= (uint8_t)tod.tod_min;
9667c478bd9Sstevel@tonic-gate 	rtc.rtc_sec	= (uint8_t)tod.tod_sec;
9677c478bd9Sstevel@tonic-gate 	DPRINTF("todds_set: century=%d year=%d dom=%d hrs=%d\n",
9687c478bd9Sstevel@tonic-gate 	    rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs);
9697c478bd9Sstevel@tonic-gate 
9707c478bd9Sstevel@tonic-gate 	write_rtc_time(&rtc);
9717c478bd9Sstevel@tonic-gate }
9727c478bd9Sstevel@tonic-gate 
9737c478bd9Sstevel@tonic-gate void
write_rtc_time(struct rtc_t * rtc)9747c478bd9Sstevel@tonic-gate write_rtc_time(struct rtc_t *rtc)
9757c478bd9Sstevel@tonic-gate {
9767c478bd9Sstevel@tonic-gate 	uint8_t	regb;
9777c478bd9Sstevel@tonic-gate 
9787c478bd9Sstevel@tonic-gate 	/*
9797c478bd9Sstevel@tonic-gate 	 * Some SuperIO tod devices don't seem to properly initialize
9807c478bd9Sstevel@tonic-gate 	 * the CADDR register to place the Century register at bank 1
9817c478bd9Sstevel@tonic-gate 	 * address 0x48.
9827c478bd9Sstevel@tonic-gate 	 */
9837c478bd9Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
9847c478bd9Sstevel@tonic-gate 
9857c478bd9Sstevel@tonic-gate 	select_bank(2);
9867c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_CADDR;
9877c478bd9Sstevel@tonic-gate 	regb = DS1287_DATA_REG;
9887c478bd9Sstevel@tonic-gate 	if (regb != 0xc8) {
9897c478bd9Sstevel@tonic-gate 		if (!ds1287_caddr_warn) {
9907c478bd9Sstevel@tonic-gate 			ds1287_caddr_warn = 1;
9917c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "ds1287: century address register "
9927c478bd9Sstevel@tonic-gate 			    "incorrect (exp 0xc8, obs %x)", regb);
9937c478bd9Sstevel@tonic-gate 		}
9947c478bd9Sstevel@tonic-gate 		DS1287_DATA_REG = 0xc8;
9957c478bd9Sstevel@tonic-gate 	}
9967c478bd9Sstevel@tonic-gate 
9977c478bd9Sstevel@tonic-gate 	select_bank(1);
9987c478bd9Sstevel@tonic-gate 
9997c478bd9Sstevel@tonic-gate 	/*
10007c478bd9Sstevel@tonic-gate 	 * Freeze
10017c478bd9Sstevel@tonic-gate 	 */
10027c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_B;
10037c478bd9Sstevel@tonic-gate 	regb = DS1287_DATA_REG;
10047c478bd9Sstevel@tonic-gate 
10057c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = (regb | RTC_SET);
10067c478bd9Sstevel@tonic-gate 
10077c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_SEC;
10087c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_sec;
10097c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_MIN;
10107c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_min;
10117c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_HRS;
10127c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_hrs;
10137c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_DOW;
10147c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_dow;
10157c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_DOM;
10167c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_dom;
10177c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_MON;
10187c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_mon;
10197c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_YEAR;
10207c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_year;
10217c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_CENTURY;
10227c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_century;
10237c478bd9Sstevel@tonic-gate 
10247c478bd9Sstevel@tonic-gate 	/*
10257c478bd9Sstevel@tonic-gate 	 * Unfreeze
10267c478bd9Sstevel@tonic-gate 	 */
10277c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_B;
10287c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = regb;
10297c478bd9Sstevel@tonic-gate 
10307c478bd9Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
10317c478bd9Sstevel@tonic-gate }
10327c478bd9Sstevel@tonic-gate 
10337c478bd9Sstevel@tonic-gate void
write_rtc_alarm(struct rtc_t * rtc)10347c478bd9Sstevel@tonic-gate write_rtc_alarm(struct rtc_t *rtc)
10357c478bd9Sstevel@tonic-gate {
10367c478bd9Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
10377c478bd9Sstevel@tonic-gate 
10387c478bd9Sstevel@tonic-gate 	select_bank(1);
10397c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_ASEC;
10407c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_asec;
10417c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_AMIN;
10427c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_amin;
10437c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_AHRS;
10447c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_ahrs;
10457c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_ADOM;
10467c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_adom;
10477c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_AMON;
10487c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_amon;
10497c478bd9Sstevel@tonic-gate 
10507c478bd9Sstevel@tonic-gate 	select_bank(2);
10517c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WDWR;
10527c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->apc_wdwr;
10537c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WDMR;
10547c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->apc_wdmr;
10557c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WMR;
10567c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->apc_wmr;
10577c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WYR;
10587c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->apc_wyr;
10597c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WCR;
10607c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->apc_wcr;
10617c478bd9Sstevel@tonic-gate 
10627c478bd9Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
10637c478bd9Sstevel@tonic-gate }
10647c478bd9Sstevel@tonic-gate 
10657c478bd9Sstevel@tonic-gate /*
10667c478bd9Sstevel@tonic-gate  * program the rtc registers for alarm to go off at the specified time
10677c478bd9Sstevel@tonic-gate  */
10687c478bd9Sstevel@tonic-gate static void
todds_set_power_alarm(timestruc_t ts)10697c478bd9Sstevel@tonic-gate todds_set_power_alarm(timestruc_t ts)
10707c478bd9Sstevel@tonic-gate {
10717c478bd9Sstevel@tonic-gate 	todinfo_t	tod;
10727c478bd9Sstevel@tonic-gate 	uint8_t		apcr2;
10737c478bd9Sstevel@tonic-gate 	struct rtc_t	rtc;
10747c478bd9Sstevel@tonic-gate 
10757c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
10767c478bd9Sstevel@tonic-gate 	tod = utc_to_tod(ts.tv_sec);
10777c478bd9Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
10787c478bd9Sstevel@tonic-gate 
10797c478bd9Sstevel@tonic-gate 	/* Clear Time Match Detect */
10807c478bd9Sstevel@tonic-gate 	select_bank(2);
10817c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_APSR;
10827c478bd9Sstevel@tonic-gate 	apcr2 = DS1287_DATA_REG;
10837c478bd9Sstevel@tonic-gate 
10847c478bd9Sstevel@tonic-gate 	/* Disable Time Match Enable */
10857c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_APCR2;
10867c478bd9Sstevel@tonic-gate 	apcr2 = DS1287_DATA_REG;
10877c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = (apcr2 & (~APC_TME));
10887c478bd9Sstevel@tonic-gate 
10897c478bd9Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
10907c478bd9Sstevel@tonic-gate 
10917c478bd9Sstevel@tonic-gate 	rtc.rtc_asec = (uint8_t)tod.tod_sec;
10927c478bd9Sstevel@tonic-gate 	rtc.rtc_amin = (uint8_t)tod.tod_min;
10937c478bd9Sstevel@tonic-gate 	rtc.rtc_ahrs = (uint8_t)tod.tod_hour;
10947c478bd9Sstevel@tonic-gate 	rtc.rtc_adom = (uint8_t)tod.tod_day;
10957c478bd9Sstevel@tonic-gate 	rtc.rtc_amon = (uint8_t)tod.tod_month;
10967c478bd9Sstevel@tonic-gate 
10977c478bd9Sstevel@tonic-gate 	rtc.apc_wdwr = (uint8_t)tod.tod_dow;
10987c478bd9Sstevel@tonic-gate 	rtc.apc_wdmr = (uint8_t)tod.tod_day;
10997c478bd9Sstevel@tonic-gate 	rtc.apc_wmr = (uint8_t)tod.tod_month;
11007c478bd9Sstevel@tonic-gate 	rtc.apc_wyr = tod.tod_year % 100;
11017c478bd9Sstevel@tonic-gate 	rtc.apc_wcr = (tod.tod_year / 100) + 19;
11027c478bd9Sstevel@tonic-gate 
11037c478bd9Sstevel@tonic-gate 	write_rtc_alarm(&rtc);
11047c478bd9Sstevel@tonic-gate 
11057c478bd9Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
11067c478bd9Sstevel@tonic-gate 	/* Enable Time Match enable */
11077c478bd9Sstevel@tonic-gate 	select_bank(2);
11087c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_APCR2;
11097c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = (apcr2 | APC_TME);
11107c478bd9Sstevel@tonic-gate 
11117c478bd9Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
11127c478bd9Sstevel@tonic-gate }
11137c478bd9Sstevel@tonic-gate 
11147c478bd9Sstevel@tonic-gate /*
11157c478bd9Sstevel@tonic-gate  * clear alarm interrupt
11167c478bd9Sstevel@tonic-gate  */
11177c478bd9Sstevel@tonic-gate static void
todds_clear_power_alarm(void)11187c478bd9Sstevel@tonic-gate todds_clear_power_alarm(void)
11197c478bd9Sstevel@tonic-gate {
11207c478bd9Sstevel@tonic-gate 	uint8_t	apcr2;
11217c478bd9Sstevel@tonic-gate 
11227c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
11237c478bd9Sstevel@tonic-gate 
11247c478bd9Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
11257c478bd9Sstevel@tonic-gate 
11267c478bd9Sstevel@tonic-gate 	/* Clear Time Match Detect */
11277c478bd9Sstevel@tonic-gate 	select_bank(2);
11287c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_APSR;
11297c478bd9Sstevel@tonic-gate 	apcr2 = DS1287_DATA_REG;
11307c478bd9Sstevel@tonic-gate 
11317c478bd9Sstevel@tonic-gate 	/* Disable Time Match Enable */
11327c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_APCR2;
11337c478bd9Sstevel@tonic-gate 	apcr2 = DS1287_DATA_REG;
11347c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = (apcr2 & (~APC_TME));
11357c478bd9Sstevel@tonic-gate 
11367c478bd9Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
11377c478bd9Sstevel@tonic-gate }
11387c478bd9Sstevel@tonic-gate 
11397c478bd9Sstevel@tonic-gate /*
11407c478bd9Sstevel@tonic-gate  * Determine the cpu frequency by watching the TOD chip rollover twice.
11417c478bd9Sstevel@tonic-gate  * Cpu clock rate is determined by computing the ticks added (in tick register)
11427c478bd9Sstevel@tonic-gate  * during one second interval on TOD.
11437c478bd9Sstevel@tonic-gate  */
11447c478bd9Sstevel@tonic-gate uint64_t
todds_get_cpufrequency(void)11457c478bd9Sstevel@tonic-gate todds_get_cpufrequency(void)
11467c478bd9Sstevel@tonic-gate {
11477c478bd9Sstevel@tonic-gate 	uint64_t cpu_freq;
11487c478bd9Sstevel@tonic-gate 
11497c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
11507c478bd9Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
11517c478bd9Sstevel@tonic-gate 
11527c478bd9Sstevel@tonic-gate 	select_bank(1);
11537c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_SEC;
11547c478bd9Sstevel@tonic-gate 	cpu_freq = find_cpufrequency(v_rtc_data_reg);
11557c478bd9Sstevel@tonic-gate 
11567c478bd9Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
11577c478bd9Sstevel@tonic-gate 	return (cpu_freq);
11587c478bd9Sstevel@tonic-gate }
11597c478bd9Sstevel@tonic-gate 
11607c478bd9Sstevel@tonic-gate static void
select_bank(int bank)11617c478bd9Sstevel@tonic-gate select_bank(int bank)
11627c478bd9Sstevel@tonic-gate {
11637c478bd9Sstevel@tonic-gate 	uint8_t	rega;
11647c478bd9Sstevel@tonic-gate 	int banksel;
11657c478bd9Sstevel@tonic-gate 
11667c478bd9Sstevel@tonic-gate 	/* Select Bank 1 */
11677c478bd9Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_A;
11687c478bd9Sstevel@tonic-gate 	rega = DS1287_DATA_REG;
11697c478bd9Sstevel@tonic-gate 	rega = rega & ~(RTC_DIV0 | RTC_DIV1 | RTC_DIV2);
11707c478bd9Sstevel@tonic-gate 	switch (bank) {
11717c478bd9Sstevel@tonic-gate 	case 0:
11727c478bd9Sstevel@tonic-gate 		banksel = RTC_DIV1;
11737c478bd9Sstevel@tonic-gate 		break;
11747c478bd9Sstevel@tonic-gate 	case 1:
11757c478bd9Sstevel@tonic-gate 		banksel = RTC_DIV0 | RTC_DIV1;
11767c478bd9Sstevel@tonic-gate 		break;
11777c478bd9Sstevel@tonic-gate 	case 2:
11787c478bd9Sstevel@tonic-gate 		banksel = RTC_DIV2;
11797c478bd9Sstevel@tonic-gate 		break;
11807c478bd9Sstevel@tonic-gate 	}
11817c478bd9Sstevel@tonic-gate 	rega |= banksel;
11827c478bd9Sstevel@tonic-gate 	DS1287_DATA_REG = rega;
11837c478bd9Sstevel@tonic-gate }
11847c478bd9Sstevel@tonic-gate 
11857c478bd9Sstevel@tonic-gate /*ARGSUSED*/
11867c478bd9Sstevel@tonic-gate static uint_t
todds_set_watchdog_timer(uint_t timeoutval)11877c478bd9Sstevel@tonic-gate todds_set_watchdog_timer(uint_t timeoutval)
11887c478bd9Sstevel@tonic-gate {
11897c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
11907c478bd9Sstevel@tonic-gate 	return (0);
11917c478bd9Sstevel@tonic-gate }
11927c478bd9Sstevel@tonic-gate 
11937c478bd9Sstevel@tonic-gate static uint_t
todds_clear_watchdog_timer(void)11947c478bd9Sstevel@tonic-gate todds_clear_watchdog_timer(void)
11957c478bd9Sstevel@tonic-gate {
11967c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
11977c478bd9Sstevel@tonic-gate 	return (0);
11987c478bd9Sstevel@tonic-gate }
1199