xref: /onnv-gate/usr/src/uts/sun4u/io/todds1287.c (revision 7656:2621e50fdf4a)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57240Srh87107  * Common Development and Distribution License (the "License").
67240Srh87107  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
227240Srh87107  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate 
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate  *	The "todds1287" module has implementation for both tod
290Sstevel@tonic-gate  *	and power button (pbio) interfaces.  This driver controls
300Sstevel@tonic-gate  *	RTC & APC units of National Semiconductor's 87317 SuperI/O
310Sstevel@tonic-gate  *	chip.  The tod interface accesses the RTC unit and pbio
320Sstevel@tonic-gate  *	interface accesses the APC unit of SuperI/O.  Since both
330Sstevel@tonic-gate  *	units are implemented in the same Logical Device, registers
340Sstevel@tonic-gate  *	for both units are accessible through a common set of index
350Sstevel@tonic-gate  *	address & data registers.  That is why both interfaces are
360Sstevel@tonic-gate  *	implemented in a same driver.
370Sstevel@tonic-gate  *
380Sstevel@tonic-gate  *	The APC unit is used to implement the power button.  When the
390Sstevel@tonic-gate  *	button momentarily is pressed, an interrupt is generated and
400Sstevel@tonic-gate  *	at the same time a Fail-safe timer starts to run.  If the
410Sstevel@tonic-gate  *	timer is not stopped in 21 seconds, the power to system is
420Sstevel@tonic-gate  *	turned off.  So the first task in the interrupt handler is to
430Sstevel@tonic-gate  *	reset the Fail-safe timer.  Note that OBP is not clearing
440Sstevel@tonic-gate  *	the Fail-safe timer due to limitation in handling interrupts,
450Sstevel@tonic-gate  *	so when OBP is running, the power button should be pressed
460Sstevel@tonic-gate  *	and held for 4 seconds for the power to go off, otherwise
470Sstevel@tonic-gate  *	a momentarily press will delay the power-off for 21 seconds.
480Sstevel@tonic-gate  *
490Sstevel@tonic-gate  *	PSARC/1999/393 describes the pbio(7I) interface.
500Sstevel@tonic-gate  */
510Sstevel@tonic-gate 
520Sstevel@tonic-gate #include <sys/types.h>
530Sstevel@tonic-gate #include <sys/conf.h>
540Sstevel@tonic-gate #include <sys/kmem.h>
550Sstevel@tonic-gate #include <sys/open.h>
560Sstevel@tonic-gate #include <sys/ddi.h>
570Sstevel@tonic-gate #include <sys/sunddi.h>
580Sstevel@tonic-gate 
590Sstevel@tonic-gate #include <sys/todds1287.h>
600Sstevel@tonic-gate #include <sys/modctl.h>
610Sstevel@tonic-gate #include <sys/stat.h>
620Sstevel@tonic-gate #include <sys/clock.h>
630Sstevel@tonic-gate #include <sys/reboot.h>
640Sstevel@tonic-gate #include <sys/machsystm.h>
650Sstevel@tonic-gate #include <sys/poll.h>
660Sstevel@tonic-gate #include <sys/pbio.h>
670Sstevel@tonic-gate 
680Sstevel@tonic-gate #define	ABORT_INCREMENT_DELAY	10
690Sstevel@tonic-gate 
700Sstevel@tonic-gate static timestruc_t todds_get(void);
710Sstevel@tonic-gate static void todds_set(timestruc_t);
720Sstevel@tonic-gate static uint_t todds_set_watchdog_timer(uint_t);
730Sstevel@tonic-gate static uint_t todds_clear_watchdog_timer(void);
740Sstevel@tonic-gate static void todds_set_power_alarm(timestruc_t);
750Sstevel@tonic-gate static void todds_clear_power_alarm(void);
760Sstevel@tonic-gate static uint64_t todds_get_cpufrequency(void);
770Sstevel@tonic-gate 
780Sstevel@tonic-gate extern uint64_t find_cpufrequency(volatile uint8_t *);
790Sstevel@tonic-gate 
800Sstevel@tonic-gate /*
810Sstevel@tonic-gate  * External variables
820Sstevel@tonic-gate  */
830Sstevel@tonic-gate extern int	watchdog_activated;
840Sstevel@tonic-gate extern uint_t	watchdog_timeout_seconds;
850Sstevel@tonic-gate extern volatile uint8_t	*v_pmc_addr_reg;
860Sstevel@tonic-gate 
870Sstevel@tonic-gate /*
880Sstevel@tonic-gate  * Global variables
890Sstevel@tonic-gate  */
900Sstevel@tonic-gate int ds1287_debug_flags;
910Sstevel@tonic-gate int ds1287_caddr_warn;
920Sstevel@tonic-gate 
930Sstevel@tonic-gate /*
940Sstevel@tonic-gate  * cb ops
950Sstevel@tonic-gate  */
960Sstevel@tonic-gate static int ds1287_open(dev_t *, int, int, cred_t *);
970Sstevel@tonic-gate static int ds1287_close(dev_t, int, int, cred_t *);
980Sstevel@tonic-gate static int ds1287_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
990Sstevel@tonic-gate static int ds1287_chpoll(dev_t, short, int, short *, struct pollhead **);
1000Sstevel@tonic-gate 
1010Sstevel@tonic-gate static void read_rtc(struct rtc_t *);
1020Sstevel@tonic-gate static void write_rtc_time(struct rtc_t *);
1030Sstevel@tonic-gate static void write_rtc_alarm(struct rtc_t *);
1040Sstevel@tonic-gate static void select_bank(int bank);
1050Sstevel@tonic-gate static uint_t ds1287_intr(caddr_t);
1060Sstevel@tonic-gate static uint_t ds1287_softintr(caddr_t);
1070Sstevel@tonic-gate static void ds1287_timeout(caddr_t);
1080Sstevel@tonic-gate static uint_t ds1287_issue_shutdown(caddr_t);
1090Sstevel@tonic-gate static void ds1287_log_message(void);
1100Sstevel@tonic-gate 
1110Sstevel@tonic-gate static struct cb_ops ds1287_cbops = {
1120Sstevel@tonic-gate 	ds1287_open,			/* open */
1130Sstevel@tonic-gate 	ds1287_close,			/* close */
1140Sstevel@tonic-gate 	nodev,				/* strategy */
1150Sstevel@tonic-gate 	nodev,				/* print */
1160Sstevel@tonic-gate 	nodev,				/* dump */
1170Sstevel@tonic-gate 	nodev,				/* read */
1180Sstevel@tonic-gate 	nodev,				/* write */
1190Sstevel@tonic-gate 	ds1287_ioctl,			/* ioctl */
1200Sstevel@tonic-gate 	nodev,				/* devmap */
1210Sstevel@tonic-gate 	nodev,				/* mmap */
1220Sstevel@tonic-gate 	nodev,				/* segmap */
1230Sstevel@tonic-gate 	ds1287_chpoll,			/* poll */
1240Sstevel@tonic-gate 	ddi_prop_op,			/* cb_prop_op */
1250Sstevel@tonic-gate 	NULL,				/* streamtab */
1260Sstevel@tonic-gate 	D_NEW | D_MP,			/* Driver compatibility flag */
1270Sstevel@tonic-gate 	CB_REV,				/* rev */
1280Sstevel@tonic-gate 	nodev,				/* int (*cb_aread)() */
1290Sstevel@tonic-gate 	nodev				/* int (*cb_awrite)() */
1300Sstevel@tonic-gate };
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate /*
1330Sstevel@tonic-gate  * dev ops
1340Sstevel@tonic-gate  */
1350Sstevel@tonic-gate static int ds1287_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
1360Sstevel@tonic-gate static int ds1287_attach(dev_info_t *, ddi_attach_cmd_t);
1370Sstevel@tonic-gate static int ds1287_detach(dev_info_t *, ddi_detach_cmd_t);
1380Sstevel@tonic-gate 
1390Sstevel@tonic-gate static struct dev_ops ds1287_ops = {
1400Sstevel@tonic-gate 	DEVO_REV,			/* devo_rev */
1410Sstevel@tonic-gate 	0,				/* refcnt */
1420Sstevel@tonic-gate 	ds1287_getinfo,			/* getinfo */
1430Sstevel@tonic-gate 	nulldev,			/* identify */
1440Sstevel@tonic-gate 	nulldev,			/* probe */
1450Sstevel@tonic-gate 	ds1287_attach,			/* attach */
1460Sstevel@tonic-gate 	ds1287_detach,			/* detach */
1470Sstevel@tonic-gate 	nodev,				/* reset */
1480Sstevel@tonic-gate 	&ds1287_cbops,			/* cb_ops */
1490Sstevel@tonic-gate 	(struct bus_ops *)NULL,		/* bus_ops */
150*7656SSherry.Moore@Sun.COM 	NULL,				/* power */
151*7656SSherry.Moore@Sun.COM 	ddi_quiesce_not_supported,	/* devo_quiesce */
1520Sstevel@tonic-gate };
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate static void	*ds1287_state;
1560Sstevel@tonic-gate static int	instance = -1;
1570Sstevel@tonic-gate 
1580Sstevel@tonic-gate /* Driver Tunables */
1590Sstevel@tonic-gate static int	ds1287_interrupt_priority = 15;
1600Sstevel@tonic-gate static int	ds1287_softint_priority = 2;
1610Sstevel@tonic-gate static hrtime_t power_button_debounce = NANOSEC/MILLISEC*10;
1620Sstevel@tonic-gate static hrtime_t power_button_abort_interval = 1.5 * NANOSEC;
1630Sstevel@tonic-gate static int	power_button_abort_presses = 3;
1640Sstevel@tonic-gate static int	power_button_abort_enable = 1;
1650Sstevel@tonic-gate static int	power_button_enable = 1;
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate static int	power_button_pressed = 0;
1680Sstevel@tonic-gate static int	power_button_cancel = 0;
1690Sstevel@tonic-gate static int	power_button_timeouts = 0;
1700Sstevel@tonic-gate static int	timeout_cancel = 0;
1710Sstevel@tonic-gate static int	additional_presses = 0;
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate static ddi_iblock_cookie_t ds1287_lo_iblock;
1740Sstevel@tonic-gate static ddi_iblock_cookie_t ds1287_hi_iblock;
1750Sstevel@tonic-gate static ddi_softintr_t	ds1287_softintr_id;
1760Sstevel@tonic-gate static kmutex_t ds1287_reg_mutex;	/* Protects ds1287 Registers */
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate static struct modldrv modldrv = {
1790Sstevel@tonic-gate 	&mod_driverops, 	/* Type of module. This one is a driver */
1807240Srh87107 	"ds1287 clock driver",	/* Name of the module. */
1817240Srh87107 	&ds1287_ops,		/* driver ops */
1820Sstevel@tonic-gate };
1830Sstevel@tonic-gate 
1840Sstevel@tonic-gate static struct modlinkage modlinkage = {
1850Sstevel@tonic-gate 	MODREV_1, &modldrv, NULL
1860Sstevel@tonic-gate };
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate int
_init(void)1900Sstevel@tonic-gate _init(void)
1910Sstevel@tonic-gate {
1920Sstevel@tonic-gate 	int status;
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate 	status = ddi_soft_state_init(&ds1287_state, sizeof (struct ds1287), 0);
1950Sstevel@tonic-gate 	if (status != 0) {
1960Sstevel@tonic-gate 		return (status);
1970Sstevel@tonic-gate 	}
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	if ((status = mod_install(&modlinkage)) != 0) {
2000Sstevel@tonic-gate 		ddi_soft_state_fini(&ds1287_state);
2010Sstevel@tonic-gate 		return (status);
2020Sstevel@tonic-gate 	}
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate 
2051004Smike_s 	ds1287_hi_iblock = (ddi_iblock_cookie_t)(uintptr_t)
2060Sstevel@tonic-gate 	    ipltospl(ds1287_interrupt_priority);
2070Sstevel@tonic-gate 	mutex_init(&ds1287_reg_mutex, NULL, MUTEX_DRIVER, ds1287_hi_iblock);
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
2100Sstevel@tonic-gate 	/* Select Bank 1 */
2110Sstevel@tonic-gate 	select_bank(1);
2120Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_B;
2130Sstevel@tonic-gate 	DS1287_DATA_REG = (RTC_DM | RTC_HM);
2140Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
2150Sstevel@tonic-gate 
2160Sstevel@tonic-gate 	tod_ops.tod_get = todds_get;
2170Sstevel@tonic-gate 	tod_ops.tod_set = todds_set;
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate 	/*
2200Sstevel@tonic-gate 	 * If v_pmc_addr_reg isn't set, it's because it wasn't set in
2210Sstevel@tonic-gate 	 * sun4u/os/fillsysinfo.c:have_pmc(). This means the real (pmc)
2220Sstevel@tonic-gate 	 * watchdog routines (sun4u/io/pmc.c) will not be used. If the
2230Sstevel@tonic-gate 	 * user were to set watchdog_enable in /etc/system, we'll need to
2240Sstevel@tonic-gate 	 * use our own NOP routines.
2250Sstevel@tonic-gate 	 */
2260Sstevel@tonic-gate 	if (v_pmc_addr_reg == NULL) {
2270Sstevel@tonic-gate 		tod_ops.tod_set_watchdog_timer = todds_set_watchdog_timer;
2280Sstevel@tonic-gate 		tod_ops.tod_clear_watchdog_timer = todds_clear_watchdog_timer;
2290Sstevel@tonic-gate 	}
2300Sstevel@tonic-gate 	tod_ops.tod_set_power_alarm = todds_set_power_alarm;
2310Sstevel@tonic-gate 	tod_ops.tod_clear_power_alarm = todds_clear_power_alarm;
2320Sstevel@tonic-gate 	tod_ops.tod_get_cpufrequency = todds_get_cpufrequency;
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 	return (status);
2350Sstevel@tonic-gate }
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate int
_fini(void)2380Sstevel@tonic-gate _fini(void)
2390Sstevel@tonic-gate {
2400Sstevel@tonic-gate 	if (strcmp(tod_module_name, "todds1287") == 0)
2410Sstevel@tonic-gate 		return (EBUSY);
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
2440Sstevel@tonic-gate }
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate /*
2470Sstevel@tonic-gate  * The loadable-module _info(9E) entry point
2480Sstevel@tonic-gate  */
2490Sstevel@tonic-gate int
_info(struct modinfo * modinfop)2500Sstevel@tonic-gate _info(struct modinfo *modinfop)
2510Sstevel@tonic-gate {
2520Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
2530Sstevel@tonic-gate }
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate /*ARGSUSED*/
2560Sstevel@tonic-gate static int
ds1287_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)2570Sstevel@tonic-gate ds1287_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
2580Sstevel@tonic-gate     void **result)
2590Sstevel@tonic-gate {
2600Sstevel@tonic-gate 	struct ds1287 *softsp;
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate 	if (instance == -1)
2630Sstevel@tonic-gate 		return (DDI_FAILURE);
2640Sstevel@tonic-gate 
2650Sstevel@tonic-gate 	switch (infocmd) {
2660Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
2670Sstevel@tonic-gate 		if ((softsp = ddi_get_soft_state(ds1287_state, instance))
2680Sstevel@tonic-gate 		    == NULL)
2690Sstevel@tonic-gate 			return (DDI_FAILURE);
2700Sstevel@tonic-gate 		*result = (void *)softsp->dip;
2710Sstevel@tonic-gate 		return (DDI_SUCCESS);
2720Sstevel@tonic-gate 
2730Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
2741004Smike_s 		*result = (void *)(uintptr_t)instance;
2750Sstevel@tonic-gate 		return (DDI_SUCCESS);
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate 	default:
2780Sstevel@tonic-gate 		return (DDI_FAILURE);
2790Sstevel@tonic-gate 	}
2800Sstevel@tonic-gate }
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate static int
ds1287_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2830Sstevel@tonic-gate ds1287_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2840Sstevel@tonic-gate {
2850Sstevel@tonic-gate 	struct ds1287 *softsp;
2860Sstevel@tonic-gate 
2870Sstevel@tonic-gate 	DPRINTF("ds1287_attach\n");
2880Sstevel@tonic-gate 	switch (cmd) {
2890Sstevel@tonic-gate 	case DDI_ATTACH:
2900Sstevel@tonic-gate 		break;
2910Sstevel@tonic-gate 	case DDI_RESUME:
2920Sstevel@tonic-gate 		return (DDI_SUCCESS);
2930Sstevel@tonic-gate 	default:
2940Sstevel@tonic-gate 		return (DDI_FAILURE);
2950Sstevel@tonic-gate 	}
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 	if (instance != -1) {
2980Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287_attach: Another instance is already "
2990Sstevel@tonic-gate 		    "attached.");
3000Sstevel@tonic-gate 		return (DDI_FAILURE);
3010Sstevel@tonic-gate 	}
3020Sstevel@tonic-gate 
3030Sstevel@tonic-gate 	instance = ddi_get_instance(dip);
3040Sstevel@tonic-gate 
3050Sstevel@tonic-gate 	if (v_rtc_addr_reg == NULL) {
3060Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287_attach: v_rtc_addr_reg is NULL");
3070Sstevel@tonic-gate 		return (DDI_FAILURE);
3080Sstevel@tonic-gate 	}
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate 	/*
3110Sstevel@tonic-gate 	 * Allocate softc information.
3120Sstevel@tonic-gate 	 */
3130Sstevel@tonic-gate 	if (ddi_soft_state_zalloc(ds1287_state, instance) != DDI_SUCCESS) {
3140Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287_attach: Failed to allocate "
3150Sstevel@tonic-gate 		    "soft states.");
3160Sstevel@tonic-gate 		return (DDI_FAILURE);
3170Sstevel@tonic-gate 	}
3180Sstevel@tonic-gate 
3190Sstevel@tonic-gate 	softsp = ddi_get_soft_state(ds1287_state, instance);
3207240Srh87107 	DPRINTF("ds1287_attach: instance=%d softsp=0x%p\n", instance,
3217240Srh87107 	    (void *)softsp);
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 	softsp->dip = dip;
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 	if (ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP,
3260Sstevel@tonic-gate 	    "interrupt-priorities", (caddr_t)&ds1287_interrupt_priority,
3270Sstevel@tonic-gate 	    sizeof (int)) != DDI_PROP_SUCCESS) {
3280Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287_attach: Failed to create \""
3290Sstevel@tonic-gate 		    "interrupt-priorities\" property.");
3300Sstevel@tonic-gate 		goto error;
3310Sstevel@tonic-gate 	}
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate 	/* add the softint */
3341004Smike_s 	ds1287_lo_iblock = (ddi_iblock_cookie_t)(uintptr_t)
3350Sstevel@tonic-gate 	    ipltospl(ds1287_softint_priority);
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate 	if (ddi_add_softintr(dip, DDI_SOFTINT_FIXED, &ds1287_softintr_id,
3380Sstevel@tonic-gate 	    &ds1287_lo_iblock, NULL, ds1287_softintr, (caddr_t)softsp) !=
3390Sstevel@tonic-gate 	    DDI_SUCCESS) {
3400Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287_attach: Failed to add low interrupt.");
3410Sstevel@tonic-gate 		goto error1;
3420Sstevel@tonic-gate 	}
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 	/* add the hi interrupt */
3450Sstevel@tonic-gate 	if (ddi_add_intr(dip, 0, NULL, (ddi_idevice_cookie_t *)
3460Sstevel@tonic-gate 	    &ds1287_hi_iblock, ds1287_intr, NULL) != DDI_SUCCESS) {
3470Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287_attach: Failed to add high "
3480Sstevel@tonic-gate 		    "interrupt.");
3490Sstevel@tonic-gate 		goto error2;
3500Sstevel@tonic-gate 	}
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 	/*
3530Sstevel@tonic-gate 	 * Combination of instance number and clone number 0 is used for
3540Sstevel@tonic-gate 	 * creating the minor node.
3550Sstevel@tonic-gate 	 */
3560Sstevel@tonic-gate 	if (ddi_create_minor_node(dip, "power_button", S_IFCHR,
3570Sstevel@tonic-gate 	    (instance << 8) + 0, "ddi_power_button", NULL) == DDI_FAILURE) {
3580Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287_attach: Failed to create minor node");
3590Sstevel@tonic-gate 		goto error3;
3600Sstevel@tonic-gate 	}
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 	ddi_report_dev(dip);
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 	return (DDI_SUCCESS);
3650Sstevel@tonic-gate 
3660Sstevel@tonic-gate error3:
3670Sstevel@tonic-gate 	ddi_remove_intr(dip, 0, NULL);
3680Sstevel@tonic-gate error2:
3690Sstevel@tonic-gate 	ddi_remove_softintr(ds1287_softintr_id);
3700Sstevel@tonic-gate error1:
3710Sstevel@tonic-gate 	(void) ddi_prop_remove(DDI_DEV_T_NONE, dip, "interrupt-priorities");
3720Sstevel@tonic-gate error:
3730Sstevel@tonic-gate 	ddi_soft_state_free(ds1287_state, instance);
3740Sstevel@tonic-gate 	return (DDI_FAILURE);
3750Sstevel@tonic-gate }
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate /*ARGSUSED*/
3780Sstevel@tonic-gate static int
ds1287_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3790Sstevel@tonic-gate ds1287_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3800Sstevel@tonic-gate {
3810Sstevel@tonic-gate 	DPRINTF("ds1287_detach\n");
3820Sstevel@tonic-gate 	switch (cmd) {
3830Sstevel@tonic-gate 	case DDI_DETACH:
3840Sstevel@tonic-gate 		/*
3850Sstevel@tonic-gate 		 * Since it needs to always handle the power button, fail
3860Sstevel@tonic-gate 		 * to detach.
3870Sstevel@tonic-gate 		 */
3880Sstevel@tonic-gate 		return (DDI_FAILURE);
3890Sstevel@tonic-gate 	case DDI_SUSPEND:
3900Sstevel@tonic-gate 		return (DDI_SUCCESS);
3910Sstevel@tonic-gate 	default:
3920Sstevel@tonic-gate 		return (DDI_FAILURE);
3930Sstevel@tonic-gate 	}
3940Sstevel@tonic-gate }
3950Sstevel@tonic-gate 
3960Sstevel@tonic-gate /*ARGSUSED1*/
3970Sstevel@tonic-gate static int
ds1287_open(dev_t * devp,int flags,int otyp,cred_t * credp)3980Sstevel@tonic-gate ds1287_open(dev_t *devp, int flags, int otyp, cred_t *credp)
3990Sstevel@tonic-gate {
4000Sstevel@tonic-gate 	struct ds1287 *softsp;
4010Sstevel@tonic-gate 	int clone;
4020Sstevel@tonic-gate 
4030Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
4040Sstevel@tonic-gate 		return (EINVAL);
4050Sstevel@tonic-gate 
4060Sstevel@tonic-gate 	if ((softsp = ddi_get_soft_state(ds1287_state, instance)) ==
4070Sstevel@tonic-gate 	    NULL)
4080Sstevel@tonic-gate 		return (ENXIO);
4090Sstevel@tonic-gate 
4100Sstevel@tonic-gate 	mutex_enter(&softsp->ds1287_mutex);
4110Sstevel@tonic-gate 	for (clone = 1; clone < DS1287_MAX_CLONE; clone++)
4120Sstevel@tonic-gate 		if (!softsp->clones[clone])
4130Sstevel@tonic-gate 			break;
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 	if (clone == DS1287_MAX_CLONE) {
4160Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287_open: No more allocation left "
4170Sstevel@tonic-gate 		    "to clone a minor.");
4180Sstevel@tonic-gate 		mutex_exit(&softsp->ds1287_mutex);
4190Sstevel@tonic-gate 		return (ENXIO);
4200Sstevel@tonic-gate 	}
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate 	*devp = makedevice(getmajor(*devp), (instance << 8) + clone);
4230Sstevel@tonic-gate 	softsp->clones[clone] = 1;
4240Sstevel@tonic-gate 	mutex_exit(&softsp->ds1287_mutex);
4250Sstevel@tonic-gate 
4260Sstevel@tonic-gate 	return (0);
4270Sstevel@tonic-gate }
4280Sstevel@tonic-gate 
4290Sstevel@tonic-gate /*ARGSUSED*/
4300Sstevel@tonic-gate static int
ds1287_close(dev_t dev,int flags,int otyp,cred_t * credp)4310Sstevel@tonic-gate ds1287_close(dev_t dev, int flags, int otyp, cred_t *credp)
4320Sstevel@tonic-gate {
4330Sstevel@tonic-gate 	struct ds1287 *softsp;
4340Sstevel@tonic-gate 	int clone;
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 	if (otyp != OTYP_CHR)
4370Sstevel@tonic-gate 		return (EINVAL);
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate 	if ((softsp = ddi_get_soft_state(ds1287_state, instance)) ==
4400Sstevel@tonic-gate 	    NULL)
4410Sstevel@tonic-gate 		return (ENXIO);
4420Sstevel@tonic-gate 
4430Sstevel@tonic-gate 	clone = DS1287_MINOR_TO_CLONE(getminor(dev));
4440Sstevel@tonic-gate 	mutex_enter(&softsp->ds1287_mutex);
4450Sstevel@tonic-gate 	if (softsp->monitor_on == clone)
4460Sstevel@tonic-gate 		softsp->monitor_on = 0;
4470Sstevel@tonic-gate 	softsp->clones[clone] = 0;
4480Sstevel@tonic-gate 	mutex_exit(&softsp->ds1287_mutex);
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 	return (0);
4510Sstevel@tonic-gate }
4520Sstevel@tonic-gate 
4530Sstevel@tonic-gate /*ARGSUSED4*/
4540Sstevel@tonic-gate static int
ds1287_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)4550Sstevel@tonic-gate ds1287_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
4560Sstevel@tonic-gate 	cred_t *credp, int *rvalp)
4570Sstevel@tonic-gate {
4580Sstevel@tonic-gate 	struct ds1287 *softsp;
4590Sstevel@tonic-gate 	int clone;
4600Sstevel@tonic-gate 
4610Sstevel@tonic-gate 	if ((softsp = ddi_get_soft_state(ds1287_state, instance)) ==
4620Sstevel@tonic-gate 	    NULL)
4630Sstevel@tonic-gate 		return (ENXIO);
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate 	clone = DS1287_MINOR_TO_CLONE(getminor(dev));
4660Sstevel@tonic-gate 	switch (cmd) {
4670Sstevel@tonic-gate 	case PB_BEGIN_MONITOR:
4680Sstevel@tonic-gate 		DPRINTF("ds1287_ioctl: PB_BEGIN_MONITOR is called.\n");
4690Sstevel@tonic-gate 		mutex_enter(&softsp->ds1287_mutex);
4700Sstevel@tonic-gate 		if (softsp->monitor_on) {
4710Sstevel@tonic-gate 			mutex_exit(&softsp->ds1287_mutex);
4720Sstevel@tonic-gate 			return (EBUSY);
4730Sstevel@tonic-gate 		}
4740Sstevel@tonic-gate 		softsp->monitor_on = clone;
4750Sstevel@tonic-gate 		mutex_exit(&softsp->ds1287_mutex);
4760Sstevel@tonic-gate 		return (0);
4770Sstevel@tonic-gate 
4780Sstevel@tonic-gate 	case PB_END_MONITOR:
4790Sstevel@tonic-gate 		DPRINTF("ds1287_ioctl: PB_END_MONITOR is called.\n");
4800Sstevel@tonic-gate 		mutex_enter(&softsp->ds1287_mutex);
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate 		/*
4830Sstevel@tonic-gate 		 * If PB_END_MONITOR is called without first
4840Sstevel@tonic-gate 		 * calling PB_BEGIN_MONITOR, an error will be
4850Sstevel@tonic-gate 		 * returned.
4860Sstevel@tonic-gate 		 */
4870Sstevel@tonic-gate 		if (!softsp->monitor_on) {
4880Sstevel@tonic-gate 			mutex_exit(&softsp->ds1287_mutex);
4890Sstevel@tonic-gate 			return (ENXIO);
4900Sstevel@tonic-gate 		}
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 		/*
4930Sstevel@tonic-gate 		 * This clone is not monitoring the button.
4940Sstevel@tonic-gate 		 */
4950Sstevel@tonic-gate 		if (softsp->monitor_on != clone) {
4960Sstevel@tonic-gate 			mutex_exit(&softsp->ds1287_mutex);
4970Sstevel@tonic-gate 			return (EINVAL);
4980Sstevel@tonic-gate 		}
4990Sstevel@tonic-gate 		softsp->monitor_on = 0;
5000Sstevel@tonic-gate 		mutex_exit(&softsp->ds1287_mutex);
5010Sstevel@tonic-gate 		return (0);
5020Sstevel@tonic-gate 
5030Sstevel@tonic-gate 	case PB_GET_EVENTS:
5040Sstevel@tonic-gate 		DPRINTF("ds1287_ioctl: PB_GET_EVENTS is called.\n");
5050Sstevel@tonic-gate 		mutex_enter(&softsp->ds1287_mutex);
5060Sstevel@tonic-gate 		if (ddi_copyout((void *)&softsp->events, (void *)arg,
5070Sstevel@tonic-gate 		    sizeof (int), mode) != 0) {
5080Sstevel@tonic-gate 			mutex_exit(&softsp->ds1287_mutex);
5090Sstevel@tonic-gate 			return (EFAULT);
5100Sstevel@tonic-gate 		}
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate 		/*
5130Sstevel@tonic-gate 		 * This ioctl returned the events detected since last
5140Sstevel@tonic-gate 		 * call.  Note that any application can get the events
5150Sstevel@tonic-gate 		 * and clear the event register.
5160Sstevel@tonic-gate 		 */
5170Sstevel@tonic-gate 		softsp->events = 0;
5180Sstevel@tonic-gate 		mutex_exit(&softsp->ds1287_mutex);
5190Sstevel@tonic-gate 		return (0);
5200Sstevel@tonic-gate 
5210Sstevel@tonic-gate 	/*
5220Sstevel@tonic-gate 	 * This ioctl is used by the test suite.
5230Sstevel@tonic-gate 	 */
5240Sstevel@tonic-gate 	case PB_CREATE_BUTTON_EVENT:
5250Sstevel@tonic-gate 		DPRINTF("ds1287_ioctl: PB_CREATE_BUTTON_EVENT is called.\n");
5260Sstevel@tonic-gate 		(void) ds1287_intr(NULL);
5270Sstevel@tonic-gate 		return (0);
5280Sstevel@tonic-gate 
5290Sstevel@tonic-gate 	default:
5300Sstevel@tonic-gate 		return (ENOTTY);
5310Sstevel@tonic-gate 	}
5320Sstevel@tonic-gate }
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate /*ARGSUSED*/
5350Sstevel@tonic-gate static int
ds1287_chpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)5360Sstevel@tonic-gate ds1287_chpoll(dev_t dev, short events, int anyyet,
5370Sstevel@tonic-gate     short *reventsp, struct pollhead **phpp)
5380Sstevel@tonic-gate {
5390Sstevel@tonic-gate 	struct ds1287 *softsp;
5400Sstevel@tonic-gate 
5410Sstevel@tonic-gate 	if ((softsp = ddi_get_soft_state(ds1287_state, instance)) == NULL)
5420Sstevel@tonic-gate 		return (ENXIO);
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 	mutex_enter(&softsp->ds1287_mutex);
5450Sstevel@tonic-gate 	*reventsp = 0;
5460Sstevel@tonic-gate 	if (softsp->events)
5470Sstevel@tonic-gate 		*reventsp = POLLRDNORM|POLLIN;
5480Sstevel@tonic-gate 	else {
5490Sstevel@tonic-gate 		if (!anyyet)
5500Sstevel@tonic-gate 			*phpp = &softsp->pollhd;
5510Sstevel@tonic-gate 	}
5520Sstevel@tonic-gate 	mutex_exit(&softsp->ds1287_mutex);
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 	return (0);
5550Sstevel@tonic-gate }
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate static void
ds1287_log_message(void)5580Sstevel@tonic-gate ds1287_log_message(void)
5590Sstevel@tonic-gate {
5600Sstevel@tonic-gate 	struct ds1287 *softsp;
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 	if ((softsp = ddi_get_soft_state(ds1287_state, instance)) == NULL) {
5630Sstevel@tonic-gate 		cmn_err(CE_WARN, "ds1287: Failed to get internal state!");
5640Sstevel@tonic-gate 		return;
5650Sstevel@tonic-gate 	}
5660Sstevel@tonic-gate 
5670Sstevel@tonic-gate 	mutex_enter(&softsp->ds1287_mutex);
5680Sstevel@tonic-gate 	softsp->shutdown_pending = 0;
5690Sstevel@tonic-gate 	cmn_err(CE_WARN, "ds1287: Failed to shut down the system!");
5700Sstevel@tonic-gate 	mutex_exit(&softsp->ds1287_mutex);
5710Sstevel@tonic-gate }
5720Sstevel@tonic-gate 
5730Sstevel@tonic-gate /*
5740Sstevel@tonic-gate  * To facilitate a power button abort, ds1287_intr() now posts
5750Sstevel@tonic-gate  * a softint (calling ds1287_softintr()) for all power button presses and
5760Sstevel@tonic-gate  * counts the number of button presses. An abort is issued if the desired
5770Sstevel@tonic-gate  * number of button presses within the given time interval.
5780Sstevel@tonic-gate  *
5790Sstevel@tonic-gate  * Two variables are used to synchronize between the high level intr;
5800Sstevel@tonic-gate  * the softint handler and timeout handler
5810Sstevel@tonic-gate  *
5820Sstevel@tonic-gate  * power_button_cancel  - Indicates that an abort happened and the number
5830Sstevel@tonic-gate  *                        of outstanding timeouts that have to be cancelled
5840Sstevel@tonic-gate  *
5850Sstevel@tonic-gate  * power_button_pressed - Indicates the number of button presses outstanding
5860Sstevel@tonic-gate  *                        which have not been serviced
5870Sstevel@tonic-gate  */
5880Sstevel@tonic-gate /*ARGSUSED*/
5890Sstevel@tonic-gate static uint_t
ds1287_intr(caddr_t ignore)5900Sstevel@tonic-gate ds1287_intr(caddr_t ignore)
5910Sstevel@tonic-gate {
5920Sstevel@tonic-gate 	hrtime_t tstamp;
5930Sstevel@tonic-gate 	static hrtime_t o_tstamp = 0;
5940Sstevel@tonic-gate 	static hrtime_t power_button_tstamp = 0;
5950Sstevel@tonic-gate 	static int power_button_cnt;
5960Sstevel@tonic-gate 	uint8_t	apcr1;
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 	/*
5990Sstevel@tonic-gate 	 * Stop the Fail-safe timer that starts running
6000Sstevel@tonic-gate 	 * after power button is pressed.  If it is not
6010Sstevel@tonic-gate 	 * stopped in 21 seconds, system powers off.
6020Sstevel@tonic-gate 	 */
6030Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
6040Sstevel@tonic-gate 	select_bank(2);
6050Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_APCR1;
6060Sstevel@tonic-gate 	apcr1 = DS1287_DATA_REG;
6070Sstevel@tonic-gate 	apcr1 |= APC_FSTRC;
6080Sstevel@tonic-gate 	DS1287_DATA_REG = apcr1;
6090Sstevel@tonic-gate 	select_bank(1);
6100Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 	tstamp = gethrtime();
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 	/* need to deal with power button debounce */
6150Sstevel@tonic-gate 	if (o_tstamp && (tstamp - o_tstamp) < power_button_debounce) {
6160Sstevel@tonic-gate 		o_tstamp = tstamp;
6170Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
6180Sstevel@tonic-gate 	}
6190Sstevel@tonic-gate 	o_tstamp = tstamp;
6200Sstevel@tonic-gate 
6210Sstevel@tonic-gate 	power_button_cnt++;
6220Sstevel@tonic-gate 
6230Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
6240Sstevel@tonic-gate 	power_button_pressed++;
6250Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
6260Sstevel@tonic-gate 
6270Sstevel@tonic-gate 	/*
6280Sstevel@tonic-gate 	 * If power button abort is enabled and power button was pressed
6290Sstevel@tonic-gate 	 * power_button_abort_presses times within power_button_abort_interval
6300Sstevel@tonic-gate 	 * then call abort_sequence_enter();
6310Sstevel@tonic-gate 	 */
6320Sstevel@tonic-gate 	if (power_button_abort_enable) {
6330Sstevel@tonic-gate 		if (power_button_abort_presses == 1 ||
6340Sstevel@tonic-gate 		    tstamp < (power_button_tstamp +
6350Sstevel@tonic-gate 		    power_button_abort_interval)) {
6360Sstevel@tonic-gate 			if (power_button_cnt == power_button_abort_presses) {
6370Sstevel@tonic-gate 				mutex_enter(&ds1287_reg_mutex);
6380Sstevel@tonic-gate 				power_button_cancel += power_button_timeouts;
6390Sstevel@tonic-gate 				power_button_pressed = 0;
6400Sstevel@tonic-gate 				mutex_exit(&ds1287_reg_mutex);
6410Sstevel@tonic-gate 				power_button_cnt = 0;
6420Sstevel@tonic-gate 				abort_sequence_enter("Power Button Abort");
6430Sstevel@tonic-gate 				return (DDI_INTR_CLAIMED);
6440Sstevel@tonic-gate 			}
6450Sstevel@tonic-gate 		} else {
6460Sstevel@tonic-gate 			power_button_cnt = 1;
6470Sstevel@tonic-gate 			power_button_tstamp = tstamp;
6480Sstevel@tonic-gate 		}
6490Sstevel@tonic-gate 	}
6500Sstevel@tonic-gate 
6510Sstevel@tonic-gate 	if (!power_button_enable)
6520Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
6530Sstevel@tonic-gate 
6540Sstevel@tonic-gate 	/* post softint to issue timeout for power button action */
6550Sstevel@tonic-gate 	ddi_trigger_softintr(ds1287_softintr_id);
6560Sstevel@tonic-gate 
6570Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
6580Sstevel@tonic-gate }
6590Sstevel@tonic-gate 
6600Sstevel@tonic-gate /*
6610Sstevel@tonic-gate  * Handle the softints....
6620Sstevel@tonic-gate  *
6630Sstevel@tonic-gate  * If only one softint is posted for several button presses, record
6640Sstevel@tonic-gate  * the number of additional presses just incase this was actually not quite
6650Sstevel@tonic-gate  * an Abort sequence so that we can log this event later.
6660Sstevel@tonic-gate  *
6670Sstevel@tonic-gate  * Issue a timeout with a duration being a fraction larger than
6680Sstevel@tonic-gate  * the specified Abort interval inorder to perform a power down if required.
6690Sstevel@tonic-gate  */
6700Sstevel@tonic-gate static uint_t
ds1287_softintr(caddr_t arg)6710Sstevel@tonic-gate ds1287_softintr(caddr_t arg)
6720Sstevel@tonic-gate {
6730Sstevel@tonic-gate 	struct ds1287 *softsp = (struct ds1287 *)arg;
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate 	DPRINTF("ds1287_softintr\n");
6760Sstevel@tonic-gate 
6770Sstevel@tonic-gate 	if (!power_button_abort_enable)
6780Sstevel@tonic-gate 		return (ds1287_issue_shutdown(arg));
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
6810Sstevel@tonic-gate 	if (!power_button_pressed) {
6820Sstevel@tonic-gate 		mutex_exit(&ds1287_reg_mutex);
6830Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
6840Sstevel@tonic-gate 	}
6850Sstevel@tonic-gate 
6860Sstevel@tonic-gate 	/*
6870Sstevel@tonic-gate 	 * Schedule a timeout to do the necessary
6880Sstevel@tonic-gate 	 * work for shutdown, only one timeout for
6890Sstevel@tonic-gate 	 * n presses if power button was pressed
6900Sstevel@tonic-gate 	 * more than once before softint fired
6910Sstevel@tonic-gate 	 */
6920Sstevel@tonic-gate 	if (power_button_pressed > 1)
6930Sstevel@tonic-gate 		additional_presses += power_button_pressed - 1;
6940Sstevel@tonic-gate 
6950Sstevel@tonic-gate 	timeout_cancel = 0;
6960Sstevel@tonic-gate 	power_button_pressed = 0;
6970Sstevel@tonic-gate 	power_button_timeouts++;
6980Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
6990Sstevel@tonic-gate 	(void) timeout((void(*)(void *))ds1287_timeout,
7000Sstevel@tonic-gate 	    softsp, NSEC_TO_TICK(power_button_abort_interval) +
7010Sstevel@tonic-gate 	    ABORT_INCREMENT_DELAY);
7020Sstevel@tonic-gate 
7030Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
7040Sstevel@tonic-gate }
7050Sstevel@tonic-gate 
7060Sstevel@tonic-gate /*
7070Sstevel@tonic-gate  * Upon receiving a timeout the following is determined:
7080Sstevel@tonic-gate  *
7090Sstevel@tonic-gate  * If an  Abort sequence was issued, then we cancel all outstanding timeouts
7100Sstevel@tonic-gate  * and additional presses prior to the Abort sequence.
7110Sstevel@tonic-gate  *
7120Sstevel@tonic-gate  * If we had multiple timeouts issued and the abort sequence was not met,
7130Sstevel@tonic-gate  * then we had more than one button press to power down the machine. We
7140Sstevel@tonic-gate  * were probably trying to issue an abort. So log a message indicating this
7150Sstevel@tonic-gate  * and cancel all outstanding timeouts.
7160Sstevel@tonic-gate  *
7170Sstevel@tonic-gate  * If we had just one timeout and the abort sequence was not met then
7180Sstevel@tonic-gate  * we really did want to power down the machine, so call ds1287_issue_shutdown()
7190Sstevel@tonic-gate  * to do the work and schedule a power down
7200Sstevel@tonic-gate  */
7210Sstevel@tonic-gate static void
ds1287_timeout(caddr_t arg)7220Sstevel@tonic-gate ds1287_timeout(caddr_t arg)
7230Sstevel@tonic-gate {
7240Sstevel@tonic-gate 	static int first = 0;
7250Sstevel@tonic-gate 
7260Sstevel@tonic-gate 	DPRINTF("ds1287_timeout\n");
7270Sstevel@tonic-gate 
7280Sstevel@tonic-gate 	/*
7290Sstevel@tonic-gate 	 * Abort was generated cancel all outstanding power
7300Sstevel@tonic-gate 	 * button timeouts
7310Sstevel@tonic-gate 	 */
7320Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
7330Sstevel@tonic-gate 	if (power_button_cancel) {
7340Sstevel@tonic-gate 		power_button_cancel--;
7350Sstevel@tonic-gate 		power_button_timeouts--;
7360Sstevel@tonic-gate 		if (!first) {
7370Sstevel@tonic-gate 			first++;
7380Sstevel@tonic-gate 			additional_presses = 0;
7390Sstevel@tonic-gate 		}
7400Sstevel@tonic-gate 		mutex_exit(&ds1287_reg_mutex);
7410Sstevel@tonic-gate 		return;
7420Sstevel@tonic-gate 	}
7430Sstevel@tonic-gate 	first = 0;
7440Sstevel@tonic-gate 
7450Sstevel@tonic-gate 	/*
7460Sstevel@tonic-gate 	 * We get here if the timeout(s) have fired and they were
7470Sstevel@tonic-gate 	 * not issued prior to an abort.
7480Sstevel@tonic-gate 	 *
7490Sstevel@tonic-gate 	 * If we had more than one press in the interval we were
7500Sstevel@tonic-gate 	 * probably trying to issue an abort, but didnt press the
7510Sstevel@tonic-gate 	 * required number within the interval. Hence cancel all
7520Sstevel@tonic-gate 	 * timeouts and do not continue towards shutdown.
7530Sstevel@tonic-gate 	 */
7540Sstevel@tonic-gate 	if (!timeout_cancel) {
7550Sstevel@tonic-gate 		timeout_cancel = power_button_timeouts +
7560Sstevel@tonic-gate 		    additional_presses;
7570Sstevel@tonic-gate 
7580Sstevel@tonic-gate 		power_button_timeouts--;
7590Sstevel@tonic-gate 		if (!power_button_timeouts)
7600Sstevel@tonic-gate 			additional_presses = 0;
7610Sstevel@tonic-gate 
7620Sstevel@tonic-gate 		if (timeout_cancel > 1) {
7630Sstevel@tonic-gate 			mutex_exit(&ds1287_reg_mutex);
7640Sstevel@tonic-gate 			cmn_err(CE_NOTE, "Power Button pressed "
7650Sstevel@tonic-gate 			    "%d times, cancelling all requests",
7660Sstevel@tonic-gate 			    timeout_cancel);
7670Sstevel@tonic-gate 			return;
7680Sstevel@tonic-gate 		}
7690Sstevel@tonic-gate 		mutex_exit(&ds1287_reg_mutex);
7700Sstevel@tonic-gate 
7710Sstevel@tonic-gate 		/* Go and do the work to request shutdown */
7720Sstevel@tonic-gate 		(void) ds1287_issue_shutdown(arg);
7730Sstevel@tonic-gate 		return;
7740Sstevel@tonic-gate 	}
7750Sstevel@tonic-gate 
7760Sstevel@tonic-gate 	power_button_timeouts--;
7770Sstevel@tonic-gate 	if (!power_button_timeouts)
7780Sstevel@tonic-gate 		additional_presses = 0;
7790Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
7800Sstevel@tonic-gate }
7810Sstevel@tonic-gate 
7820Sstevel@tonic-gate static uint_t
ds1287_issue_shutdown(caddr_t arg)7830Sstevel@tonic-gate ds1287_issue_shutdown(caddr_t arg)
7840Sstevel@tonic-gate {
7850Sstevel@tonic-gate 	struct ds1287 *softsp = (struct ds1287 *)arg;
7860Sstevel@tonic-gate 
7870Sstevel@tonic-gate 	DPRINTF("ds1287_issue_shutdown\n");
7880Sstevel@tonic-gate 
7890Sstevel@tonic-gate 	mutex_enter(&softsp->ds1287_mutex);
7900Sstevel@tonic-gate 	softsp->events |= PB_BUTTON_PRESS;
7910Sstevel@tonic-gate 	if (softsp->monitor_on != 0) {
7920Sstevel@tonic-gate 		mutex_exit(&softsp->ds1287_mutex);
7930Sstevel@tonic-gate 		pollwakeup(&softsp->pollhd, POLLRDNORM);
7940Sstevel@tonic-gate 		pollwakeup(&softsp->pollhd, POLLIN);
7950Sstevel@tonic-gate 		return (DDI_INTR_CLAIMED);
7960Sstevel@tonic-gate 	}
7970Sstevel@tonic-gate 
7980Sstevel@tonic-gate 	if (!softsp->shutdown_pending) {
7990Sstevel@tonic-gate 		cmn_err(CE_WARN, "Power button is pressed, powering down "
8000Sstevel@tonic-gate 		    "the system!");
8010Sstevel@tonic-gate 		softsp->shutdown_pending = 1;
8020Sstevel@tonic-gate 		do_shutdown();
8030Sstevel@tonic-gate 
8040Sstevel@tonic-gate 		/*
8050Sstevel@tonic-gate 		 * Wait a while for "do_shutdown()" to shut down the system
8060Sstevel@tonic-gate 		 * before logging an error message.
8070Sstevel@tonic-gate 		 */
8080Sstevel@tonic-gate 		(void) timeout((void(*)(void *))ds1287_log_message, NULL,
8090Sstevel@tonic-gate 		    100 * hz);
8100Sstevel@tonic-gate 	}
8110Sstevel@tonic-gate 	mutex_exit(&softsp->ds1287_mutex);
8120Sstevel@tonic-gate 
8130Sstevel@tonic-gate 	return (DDI_INTR_CLAIMED);
8140Sstevel@tonic-gate }
8150Sstevel@tonic-gate 
8160Sstevel@tonic-gate /*
8170Sstevel@tonic-gate  * Read the current time from the clock chip and convert to UNIX form.
8180Sstevel@tonic-gate  * Assumes that the year in the clock chip is valid.
8190Sstevel@tonic-gate  * Must be called with tod_lock held.
8200Sstevel@tonic-gate  */
8210Sstevel@tonic-gate static timestruc_t
todds_get(void)8220Sstevel@tonic-gate todds_get(void)
8230Sstevel@tonic-gate {
8240Sstevel@tonic-gate 	timestruc_t ts;
8250Sstevel@tonic-gate 	todinfo_t tod;
8260Sstevel@tonic-gate 	struct rtc_t rtc;
8270Sstevel@tonic-gate 
8280Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
8290Sstevel@tonic-gate 
8300Sstevel@tonic-gate 	read_rtc(&rtc);
8310Sstevel@tonic-gate 	DPRINTF("todds_get: century=%d year=%d dom=%d hrs=%d\n",
8320Sstevel@tonic-gate 	    rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs);
8330Sstevel@tonic-gate 
8340Sstevel@tonic-gate 	/*
8350Sstevel@tonic-gate 	 * tod_year is base 1900 so this code needs to adjust the true
8360Sstevel@tonic-gate 	 * year retrieved from the rtc's century and year fields.
8370Sstevel@tonic-gate 	 */
8380Sstevel@tonic-gate 	tod.tod_year	= rtc.rtc_year + (rtc.rtc_century * 100) - 1900;
8390Sstevel@tonic-gate 	tod.tod_month	= rtc.rtc_mon;
8400Sstevel@tonic-gate 	tod.tod_day	= rtc.rtc_dom;
8410Sstevel@tonic-gate 	tod.tod_dow	= rtc.rtc_dow;
8420Sstevel@tonic-gate 	tod.tod_hour	= rtc.rtc_hrs;
8430Sstevel@tonic-gate 	tod.tod_min	= rtc.rtc_min;
8440Sstevel@tonic-gate 	tod.tod_sec	= rtc.rtc_sec;
8450Sstevel@tonic-gate 
8460Sstevel@tonic-gate 	ts.tv_sec = tod_to_utc(tod);
8470Sstevel@tonic-gate 	ts.tv_nsec = 0;
8480Sstevel@tonic-gate 
8490Sstevel@tonic-gate 	/* set the hw watchdog timer if it's been activated */
8500Sstevel@tonic-gate 	if (watchdog_activated) {
8510Sstevel@tonic-gate 		int ret = 0;
8520Sstevel@tonic-gate 		ret = tod_ops.tod_set_watchdog_timer(watchdog_timeout_seconds);
8530Sstevel@tonic-gate 		if (ret == 0)
8540Sstevel@tonic-gate 			cmn_err(CE_WARN, "ds1287: failed to set hardware "
8557240Srh87107 			    "watchdog timer.");
8560Sstevel@tonic-gate 	}
8570Sstevel@tonic-gate 
8580Sstevel@tonic-gate 	return (ts);
8590Sstevel@tonic-gate }
8600Sstevel@tonic-gate 
8610Sstevel@tonic-gate void
read_rtc(struct rtc_t * rtc)8620Sstevel@tonic-gate read_rtc(struct rtc_t *rtc)
8630Sstevel@tonic-gate {
8640Sstevel@tonic-gate 	uint8_t regb;
8650Sstevel@tonic-gate 
8660Sstevel@tonic-gate 	/*
8670Sstevel@tonic-gate 	 * Some SuperIO tod devices don't seem to properly initialize
8680Sstevel@tonic-gate 	 * the CADDR register to place the Century register at bank 1
8690Sstevel@tonic-gate 	 * address 0x48.
8700Sstevel@tonic-gate 	 */
8710Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
8720Sstevel@tonic-gate 
8730Sstevel@tonic-gate 	select_bank(2);
8740Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_CADDR;
8750Sstevel@tonic-gate 	regb = DS1287_DATA_REG;
8760Sstevel@tonic-gate 	if (regb != 0xc8) {
8770Sstevel@tonic-gate 		if (!ds1287_caddr_warn) {
8780Sstevel@tonic-gate 			ds1287_caddr_warn = 1;
8790Sstevel@tonic-gate 			cmn_err(CE_WARN, "ds1287: century address register "
8800Sstevel@tonic-gate 			    "incorrect (exp 0xc8, obs %x)", regb);
8810Sstevel@tonic-gate 		}
8820Sstevel@tonic-gate 		DS1287_DATA_REG = 0xc8;
8830Sstevel@tonic-gate 	}
8840Sstevel@tonic-gate 
8850Sstevel@tonic-gate 	select_bank(1);
8860Sstevel@tonic-gate 	/*
8870Sstevel@tonic-gate 	 * Freeze clock update
8880Sstevel@tonic-gate 	 */
8890Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_B;
8900Sstevel@tonic-gate 	regb = DS1287_DATA_REG;
8910Sstevel@tonic-gate 	DS1287_DATA_REG = (regb | RTC_SET);
8920Sstevel@tonic-gate 
8930Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_SEC;
8940Sstevel@tonic-gate 	rtc->rtc_sec = DS1287_DATA_REG;
8950Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_ASEC;
8960Sstevel@tonic-gate 	rtc->rtc_asec = DS1287_DATA_REG;
8970Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_MIN;
8980Sstevel@tonic-gate 	rtc->rtc_min = DS1287_DATA_REG;
8990Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_AMIN;
9000Sstevel@tonic-gate 	rtc->rtc_amin = DS1287_DATA_REG;
9010Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_HRS;
9020Sstevel@tonic-gate 	rtc->rtc_hrs = DS1287_DATA_REG;
9030Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_AHRS;
9040Sstevel@tonic-gate 	rtc->rtc_ahrs = DS1287_DATA_REG;
9050Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_DOW;
9060Sstevel@tonic-gate 	rtc->rtc_dow = DS1287_DATA_REG;
9070Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_DOM;
9080Sstevel@tonic-gate 	rtc->rtc_dom = DS1287_DATA_REG;
9090Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_MON;
9100Sstevel@tonic-gate 	rtc->rtc_mon = DS1287_DATA_REG;
9110Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_YEAR;
9120Sstevel@tonic-gate 	rtc->rtc_year = DS1287_DATA_REG;
9130Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_CENTURY;
9140Sstevel@tonic-gate 	rtc->rtc_century = DS1287_DATA_REG;
9150Sstevel@tonic-gate 
9160Sstevel@tonic-gate 	/* Read date alarm */
9170Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_ADOM;
9180Sstevel@tonic-gate 	rtc->rtc_adom = DS1287_DATA_REG;
9190Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_AMON;
9200Sstevel@tonic-gate 	rtc->rtc_amon = DS1287_DATA_REG;
9210Sstevel@tonic-gate 
9220Sstevel@tonic-gate 	/* Read wakeup data */
9230Sstevel@tonic-gate 	select_bank(2);
9240Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WDWR;
9250Sstevel@tonic-gate 	rtc->apc_wdwr = DS1287_DATA_REG;
9260Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WDMR;
9270Sstevel@tonic-gate 	rtc->apc_wdmr = DS1287_DATA_REG;
9280Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WMR;
9290Sstevel@tonic-gate 	rtc->apc_wmr = DS1287_DATA_REG;
9300Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WYR;
9310Sstevel@tonic-gate 	rtc->apc_wyr = DS1287_DATA_REG;
9320Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WCR;
9330Sstevel@tonic-gate 	rtc->apc_wcr = DS1287_DATA_REG;
9340Sstevel@tonic-gate 
9350Sstevel@tonic-gate 	/*
9360Sstevel@tonic-gate 	 * Unfreeze clock update
9370Sstevel@tonic-gate 	 */
9380Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_B;
9390Sstevel@tonic-gate 	DS1287_DATA_REG = regb;
9400Sstevel@tonic-gate 
9410Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
9420Sstevel@tonic-gate }
9430Sstevel@tonic-gate 
9440Sstevel@tonic-gate /*
9450Sstevel@tonic-gate  * Write the specified time into the clock chip.
9460Sstevel@tonic-gate  * Must be called with tod_lock held.
9470Sstevel@tonic-gate  */
9480Sstevel@tonic-gate static void
todds_set(timestruc_t ts)9490Sstevel@tonic-gate todds_set(timestruc_t ts)
9500Sstevel@tonic-gate {
9510Sstevel@tonic-gate 	struct rtc_t	rtc;
9520Sstevel@tonic-gate 	todinfo_t tod = utc_to_tod(ts.tv_sec);
9530Sstevel@tonic-gate 	int year;
9540Sstevel@tonic-gate 
9550Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
9560Sstevel@tonic-gate 
9570Sstevel@tonic-gate 	/* tod_year is base 1900 so this code needs to adjust */
9580Sstevel@tonic-gate 	year = 1900 + tod.tod_year;
9590Sstevel@tonic-gate 	rtc.rtc_year	= year % 100;
9600Sstevel@tonic-gate 	rtc.rtc_century = year / 100;
9610Sstevel@tonic-gate 	rtc.rtc_mon	= (uint8_t)tod.tod_month;
9620Sstevel@tonic-gate 	rtc.rtc_dom	= (uint8_t)tod.tod_day;
9630Sstevel@tonic-gate 	rtc.rtc_dow	= (uint8_t)tod.tod_dow;
9640Sstevel@tonic-gate 	rtc.rtc_hrs	= (uint8_t)tod.tod_hour;
9650Sstevel@tonic-gate 	rtc.rtc_min	= (uint8_t)tod.tod_min;
9660Sstevel@tonic-gate 	rtc.rtc_sec	= (uint8_t)tod.tod_sec;
9670Sstevel@tonic-gate 	DPRINTF("todds_set: century=%d year=%d dom=%d hrs=%d\n",
9680Sstevel@tonic-gate 	    rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs);
9690Sstevel@tonic-gate 
9700Sstevel@tonic-gate 	write_rtc_time(&rtc);
9710Sstevel@tonic-gate }
9720Sstevel@tonic-gate 
9730Sstevel@tonic-gate void
write_rtc_time(struct rtc_t * rtc)9740Sstevel@tonic-gate write_rtc_time(struct rtc_t *rtc)
9750Sstevel@tonic-gate {
9760Sstevel@tonic-gate 	uint8_t	regb;
9770Sstevel@tonic-gate 
9780Sstevel@tonic-gate 	/*
9790Sstevel@tonic-gate 	 * Some SuperIO tod devices don't seem to properly initialize
9800Sstevel@tonic-gate 	 * the CADDR register to place the Century register at bank 1
9810Sstevel@tonic-gate 	 * address 0x48.
9820Sstevel@tonic-gate 	 */
9830Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
9840Sstevel@tonic-gate 
9850Sstevel@tonic-gate 	select_bank(2);
9860Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_CADDR;
9870Sstevel@tonic-gate 	regb = DS1287_DATA_REG;
9880Sstevel@tonic-gate 	if (regb != 0xc8) {
9890Sstevel@tonic-gate 		if (!ds1287_caddr_warn) {
9900Sstevel@tonic-gate 			ds1287_caddr_warn = 1;
9910Sstevel@tonic-gate 			cmn_err(CE_WARN, "ds1287: century address register "
9920Sstevel@tonic-gate 			    "incorrect (exp 0xc8, obs %x)", regb);
9930Sstevel@tonic-gate 		}
9940Sstevel@tonic-gate 		DS1287_DATA_REG = 0xc8;
9950Sstevel@tonic-gate 	}
9960Sstevel@tonic-gate 
9970Sstevel@tonic-gate 	select_bank(1);
9980Sstevel@tonic-gate 
9990Sstevel@tonic-gate 	/*
10000Sstevel@tonic-gate 	 * Freeze
10010Sstevel@tonic-gate 	 */
10020Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_B;
10030Sstevel@tonic-gate 	regb = DS1287_DATA_REG;
10040Sstevel@tonic-gate 
10050Sstevel@tonic-gate 	DS1287_DATA_REG = (regb | RTC_SET);
10060Sstevel@tonic-gate 
10070Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_SEC;
10080Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_sec;
10090Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_MIN;
10100Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_min;
10110Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_HRS;
10120Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_hrs;
10130Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_DOW;
10140Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_dow;
10150Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_DOM;
10160Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_dom;
10170Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_MON;
10180Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_mon;
10190Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_YEAR;
10200Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_year;
10210Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_CENTURY;
10220Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_century;
10230Sstevel@tonic-gate 
10240Sstevel@tonic-gate 	/*
10250Sstevel@tonic-gate 	 * Unfreeze
10260Sstevel@tonic-gate 	 */
10270Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_B;
10280Sstevel@tonic-gate 	DS1287_DATA_REG = regb;
10290Sstevel@tonic-gate 
10300Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
10310Sstevel@tonic-gate }
10320Sstevel@tonic-gate 
10330Sstevel@tonic-gate void
write_rtc_alarm(struct rtc_t * rtc)10340Sstevel@tonic-gate write_rtc_alarm(struct rtc_t *rtc)
10350Sstevel@tonic-gate {
10360Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
10370Sstevel@tonic-gate 
10380Sstevel@tonic-gate 	select_bank(1);
10390Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_ASEC;
10400Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_asec;
10410Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_AMIN;
10420Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_amin;
10430Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_AHRS;
10440Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_ahrs;
10450Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_ADOM;
10460Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_adom;
10470Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_AMON;
10480Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->rtc_amon;
10490Sstevel@tonic-gate 
10500Sstevel@tonic-gate 	select_bank(2);
10510Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WDWR;
10520Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->apc_wdwr;
10530Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WDMR;
10540Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->apc_wdmr;
10550Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WMR;
10560Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->apc_wmr;
10570Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WYR;
10580Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->apc_wyr;
10590Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_WCR;
10600Sstevel@tonic-gate 	DS1287_DATA_REG = rtc->apc_wcr;
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
10630Sstevel@tonic-gate }
10640Sstevel@tonic-gate 
10650Sstevel@tonic-gate /*
10660Sstevel@tonic-gate  * program the rtc registers for alarm to go off at the specified time
10670Sstevel@tonic-gate  */
10680Sstevel@tonic-gate static void
todds_set_power_alarm(timestruc_t ts)10690Sstevel@tonic-gate todds_set_power_alarm(timestruc_t ts)
10700Sstevel@tonic-gate {
10710Sstevel@tonic-gate 	todinfo_t	tod;
10720Sstevel@tonic-gate 	uint8_t		apcr2;
10730Sstevel@tonic-gate 	struct rtc_t	rtc;
10740Sstevel@tonic-gate 
10750Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
10760Sstevel@tonic-gate 	tod = utc_to_tod(ts.tv_sec);
10770Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
10780Sstevel@tonic-gate 
10790Sstevel@tonic-gate 	/* Clear Time Match Detect */
10800Sstevel@tonic-gate 	select_bank(2);
10810Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_APSR;
10820Sstevel@tonic-gate 	apcr2 = DS1287_DATA_REG;
10830Sstevel@tonic-gate 
10840Sstevel@tonic-gate 	/* Disable Time Match Enable */
10850Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_APCR2;
10860Sstevel@tonic-gate 	apcr2 = DS1287_DATA_REG;
10870Sstevel@tonic-gate 	DS1287_DATA_REG = (apcr2 & (~APC_TME));
10880Sstevel@tonic-gate 
10890Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate 	rtc.rtc_asec = (uint8_t)tod.tod_sec;
10920Sstevel@tonic-gate 	rtc.rtc_amin = (uint8_t)tod.tod_min;
10930Sstevel@tonic-gate 	rtc.rtc_ahrs = (uint8_t)tod.tod_hour;
10940Sstevel@tonic-gate 	rtc.rtc_adom = (uint8_t)tod.tod_day;
10950Sstevel@tonic-gate 	rtc.rtc_amon = (uint8_t)tod.tod_month;
10960Sstevel@tonic-gate 
10970Sstevel@tonic-gate 	rtc.apc_wdwr = (uint8_t)tod.tod_dow;
10980Sstevel@tonic-gate 	rtc.apc_wdmr = (uint8_t)tod.tod_day;
10990Sstevel@tonic-gate 	rtc.apc_wmr = (uint8_t)tod.tod_month;
11000Sstevel@tonic-gate 	rtc.apc_wyr = tod.tod_year % 100;
11010Sstevel@tonic-gate 	rtc.apc_wcr = (tod.tod_year / 100) + 19;
11020Sstevel@tonic-gate 
11030Sstevel@tonic-gate 	write_rtc_alarm(&rtc);
11040Sstevel@tonic-gate 
11050Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
11060Sstevel@tonic-gate 	/* Enable Time Match enable */
11070Sstevel@tonic-gate 	select_bank(2);
11080Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_APCR2;
11090Sstevel@tonic-gate 	DS1287_DATA_REG = (apcr2 | APC_TME);
11100Sstevel@tonic-gate 
11110Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
11120Sstevel@tonic-gate }
11130Sstevel@tonic-gate 
11140Sstevel@tonic-gate /*
11150Sstevel@tonic-gate  * clear alarm interrupt
11160Sstevel@tonic-gate  */
11170Sstevel@tonic-gate static void
todds_clear_power_alarm(void)11180Sstevel@tonic-gate todds_clear_power_alarm(void)
11190Sstevel@tonic-gate {
11200Sstevel@tonic-gate 	uint8_t	apcr2;
11210Sstevel@tonic-gate 
11220Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
11230Sstevel@tonic-gate 
11240Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
11250Sstevel@tonic-gate 
11260Sstevel@tonic-gate 	/* Clear Time Match Detect */
11270Sstevel@tonic-gate 	select_bank(2);
11280Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_APSR;
11290Sstevel@tonic-gate 	apcr2 = DS1287_DATA_REG;
11300Sstevel@tonic-gate 
11310Sstevel@tonic-gate 	/* Disable Time Match Enable */
11320Sstevel@tonic-gate 	DS1287_ADDR_REG = APC_APCR2;
11330Sstevel@tonic-gate 	apcr2 = DS1287_DATA_REG;
11340Sstevel@tonic-gate 	DS1287_DATA_REG = (apcr2 & (~APC_TME));
11350Sstevel@tonic-gate 
11360Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
11370Sstevel@tonic-gate }
11380Sstevel@tonic-gate 
11390Sstevel@tonic-gate /*
11400Sstevel@tonic-gate  * Determine the cpu frequency by watching the TOD chip rollover twice.
11410Sstevel@tonic-gate  * Cpu clock rate is determined by computing the ticks added (in tick register)
11420Sstevel@tonic-gate  * during one second interval on TOD.
11430Sstevel@tonic-gate  */
11440Sstevel@tonic-gate uint64_t
todds_get_cpufrequency(void)11450Sstevel@tonic-gate todds_get_cpufrequency(void)
11460Sstevel@tonic-gate {
11470Sstevel@tonic-gate 	uint64_t cpu_freq;
11480Sstevel@tonic-gate 
11490Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
11500Sstevel@tonic-gate 	mutex_enter(&ds1287_reg_mutex);
11510Sstevel@tonic-gate 
11520Sstevel@tonic-gate 	select_bank(1);
11530Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_SEC;
11540Sstevel@tonic-gate 	cpu_freq = find_cpufrequency(v_rtc_data_reg);
11550Sstevel@tonic-gate 
11560Sstevel@tonic-gate 	mutex_exit(&ds1287_reg_mutex);
11570Sstevel@tonic-gate 	return (cpu_freq);
11580Sstevel@tonic-gate }
11590Sstevel@tonic-gate 
11600Sstevel@tonic-gate static void
select_bank(int bank)11610Sstevel@tonic-gate select_bank(int bank)
11620Sstevel@tonic-gate {
11630Sstevel@tonic-gate 	uint8_t	rega;
11640Sstevel@tonic-gate 	int banksel;
11650Sstevel@tonic-gate 
11660Sstevel@tonic-gate 	/* Select Bank 1 */
11670Sstevel@tonic-gate 	DS1287_ADDR_REG = RTC_A;
11680Sstevel@tonic-gate 	rega = DS1287_DATA_REG;
11690Sstevel@tonic-gate 	rega = rega & ~(RTC_DIV0 | RTC_DIV1 | RTC_DIV2);
11700Sstevel@tonic-gate 	switch (bank) {
11710Sstevel@tonic-gate 	case 0:
11720Sstevel@tonic-gate 		banksel = RTC_DIV1;
11730Sstevel@tonic-gate 		break;
11740Sstevel@tonic-gate 	case 1:
11750Sstevel@tonic-gate 		banksel = RTC_DIV0 | RTC_DIV1;
11760Sstevel@tonic-gate 		break;
11770Sstevel@tonic-gate 	case 2:
11780Sstevel@tonic-gate 		banksel = RTC_DIV2;
11790Sstevel@tonic-gate 		break;
11800Sstevel@tonic-gate 	}
11810Sstevel@tonic-gate 	rega |= banksel;
11820Sstevel@tonic-gate 	DS1287_DATA_REG = rega;
11830Sstevel@tonic-gate }
11840Sstevel@tonic-gate 
11850Sstevel@tonic-gate /*ARGSUSED*/
11860Sstevel@tonic-gate static uint_t
todds_set_watchdog_timer(uint_t timeoutval)11870Sstevel@tonic-gate todds_set_watchdog_timer(uint_t timeoutval)
11880Sstevel@tonic-gate {
11890Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
11900Sstevel@tonic-gate 	return (0);
11910Sstevel@tonic-gate }
11920Sstevel@tonic-gate 
11930Sstevel@tonic-gate static uint_t
todds_clear_watchdog_timer(void)11940Sstevel@tonic-gate todds_clear_watchdog_timer(void)
11950Sstevel@tonic-gate {
11960Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&tod_lock));
11970Sstevel@tonic-gate 	return (0);
11980Sstevel@tonic-gate }
1199