1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * CDDL HEADER START 3*7c478bd9Sstevel@tonic-gate * 4*7c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*7c478bd9Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*7c478bd9Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*7c478bd9Sstevel@tonic-gate * with the License. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*7c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*7c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 12*7c478bd9Sstevel@tonic-gate * and limitations under the License. 13*7c478bd9Sstevel@tonic-gate * 14*7c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*7c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*7c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*7c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*7c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*7c478bd9Sstevel@tonic-gate * 20*7c478bd9Sstevel@tonic-gate * CDDL HEADER END 21*7c478bd9Sstevel@tonic-gate */ 22*7c478bd9Sstevel@tonic-gate /* 23*7c478bd9Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*7c478bd9Sstevel@tonic-gate * Use is subject to license terms. 25*7c478bd9Sstevel@tonic-gate */ 26*7c478bd9Sstevel@tonic-gate 27*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*7c478bd9Sstevel@tonic-gate 29*7c478bd9Sstevel@tonic-gate /* 30*7c478bd9Sstevel@tonic-gate * The "todds1287" module has implementation for both tod 31*7c478bd9Sstevel@tonic-gate * and power button (pbio) interfaces. This driver controls 32*7c478bd9Sstevel@tonic-gate * RTC & APC units of National Semiconductor's 87317 SuperI/O 33*7c478bd9Sstevel@tonic-gate * chip. The tod interface accesses the RTC unit and pbio 34*7c478bd9Sstevel@tonic-gate * interface accesses the APC unit of SuperI/O. Since both 35*7c478bd9Sstevel@tonic-gate * units are implemented in the same Logical Device, registers 36*7c478bd9Sstevel@tonic-gate * for both units are accessible through a common set of index 37*7c478bd9Sstevel@tonic-gate * address & data registers. That is why both interfaces are 38*7c478bd9Sstevel@tonic-gate * implemented in a same driver. 39*7c478bd9Sstevel@tonic-gate * 40*7c478bd9Sstevel@tonic-gate * The APC unit is used to implement the power button. When the 41*7c478bd9Sstevel@tonic-gate * button momentarily is pressed, an interrupt is generated and 42*7c478bd9Sstevel@tonic-gate * at the same time a Fail-safe timer starts to run. If the 43*7c478bd9Sstevel@tonic-gate * timer is not stopped in 21 seconds, the power to system is 44*7c478bd9Sstevel@tonic-gate * turned off. So the first task in the interrupt handler is to 45*7c478bd9Sstevel@tonic-gate * reset the Fail-safe timer. Note that OBP is not clearing 46*7c478bd9Sstevel@tonic-gate * the Fail-safe timer due to limitation in handling interrupts, 47*7c478bd9Sstevel@tonic-gate * so when OBP is running, the power button should be pressed 48*7c478bd9Sstevel@tonic-gate * and held for 4 seconds for the power to go off, otherwise 49*7c478bd9Sstevel@tonic-gate * a momentarily press will delay the power-off for 21 seconds. 50*7c478bd9Sstevel@tonic-gate * 51*7c478bd9Sstevel@tonic-gate * PSARC/1999/393 describes the pbio(7I) interface. 52*7c478bd9Sstevel@tonic-gate */ 53*7c478bd9Sstevel@tonic-gate 54*7c478bd9Sstevel@tonic-gate #include <sys/types.h> 55*7c478bd9Sstevel@tonic-gate #include <sys/conf.h> 56*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h> 57*7c478bd9Sstevel@tonic-gate #include <sys/open.h> 58*7c478bd9Sstevel@tonic-gate #include <sys/ddi.h> 59*7c478bd9Sstevel@tonic-gate #include <sys/sunddi.h> 60*7c478bd9Sstevel@tonic-gate 61*7c478bd9Sstevel@tonic-gate #include <sys/todds1287.h> 62*7c478bd9Sstevel@tonic-gate #include <sys/modctl.h> 63*7c478bd9Sstevel@tonic-gate #include <sys/stat.h> 64*7c478bd9Sstevel@tonic-gate #include <sys/clock.h> 65*7c478bd9Sstevel@tonic-gate #include <sys/reboot.h> 66*7c478bd9Sstevel@tonic-gate #include <sys/machsystm.h> 67*7c478bd9Sstevel@tonic-gate #include <sys/poll.h> 68*7c478bd9Sstevel@tonic-gate #include <sys/pbio.h> 69*7c478bd9Sstevel@tonic-gate 70*7c478bd9Sstevel@tonic-gate #define ABORT_INCREMENT_DELAY 10 71*7c478bd9Sstevel@tonic-gate 72*7c478bd9Sstevel@tonic-gate static timestruc_t todds_get(void); 73*7c478bd9Sstevel@tonic-gate static void todds_set(timestruc_t); 74*7c478bd9Sstevel@tonic-gate static uint_t todds_set_watchdog_timer(uint_t); 75*7c478bd9Sstevel@tonic-gate static uint_t todds_clear_watchdog_timer(void); 76*7c478bd9Sstevel@tonic-gate static void todds_set_power_alarm(timestruc_t); 77*7c478bd9Sstevel@tonic-gate static void todds_clear_power_alarm(void); 78*7c478bd9Sstevel@tonic-gate static uint64_t todds_get_cpufrequency(void); 79*7c478bd9Sstevel@tonic-gate 80*7c478bd9Sstevel@tonic-gate extern uint64_t find_cpufrequency(volatile uint8_t *); 81*7c478bd9Sstevel@tonic-gate 82*7c478bd9Sstevel@tonic-gate /* 83*7c478bd9Sstevel@tonic-gate * External variables 84*7c478bd9Sstevel@tonic-gate */ 85*7c478bd9Sstevel@tonic-gate extern int watchdog_activated; 86*7c478bd9Sstevel@tonic-gate extern uint_t watchdog_timeout_seconds; 87*7c478bd9Sstevel@tonic-gate extern volatile uint8_t *v_pmc_addr_reg; 88*7c478bd9Sstevel@tonic-gate 89*7c478bd9Sstevel@tonic-gate /* 90*7c478bd9Sstevel@tonic-gate * Global variables 91*7c478bd9Sstevel@tonic-gate */ 92*7c478bd9Sstevel@tonic-gate int ds1287_debug_flags; 93*7c478bd9Sstevel@tonic-gate int ds1287_caddr_warn; 94*7c478bd9Sstevel@tonic-gate 95*7c478bd9Sstevel@tonic-gate /* 96*7c478bd9Sstevel@tonic-gate * cb ops 97*7c478bd9Sstevel@tonic-gate */ 98*7c478bd9Sstevel@tonic-gate static int ds1287_open(dev_t *, int, int, cred_t *); 99*7c478bd9Sstevel@tonic-gate static int ds1287_close(dev_t, int, int, cred_t *); 100*7c478bd9Sstevel@tonic-gate static int ds1287_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 101*7c478bd9Sstevel@tonic-gate static int ds1287_chpoll(dev_t, short, int, short *, struct pollhead **); 102*7c478bd9Sstevel@tonic-gate 103*7c478bd9Sstevel@tonic-gate static void read_rtc(struct rtc_t *); 104*7c478bd9Sstevel@tonic-gate static void write_rtc_time(struct rtc_t *); 105*7c478bd9Sstevel@tonic-gate static void write_rtc_alarm(struct rtc_t *); 106*7c478bd9Sstevel@tonic-gate static void select_bank(int bank); 107*7c478bd9Sstevel@tonic-gate static uint_t ds1287_intr(caddr_t); 108*7c478bd9Sstevel@tonic-gate static uint_t ds1287_softintr(caddr_t); 109*7c478bd9Sstevel@tonic-gate static void ds1287_timeout(caddr_t); 110*7c478bd9Sstevel@tonic-gate static uint_t ds1287_issue_shutdown(caddr_t); 111*7c478bd9Sstevel@tonic-gate static void ds1287_log_message(void); 112*7c478bd9Sstevel@tonic-gate 113*7c478bd9Sstevel@tonic-gate static struct cb_ops ds1287_cbops = { 114*7c478bd9Sstevel@tonic-gate ds1287_open, /* open */ 115*7c478bd9Sstevel@tonic-gate ds1287_close, /* close */ 116*7c478bd9Sstevel@tonic-gate nodev, /* strategy */ 117*7c478bd9Sstevel@tonic-gate nodev, /* print */ 118*7c478bd9Sstevel@tonic-gate nodev, /* dump */ 119*7c478bd9Sstevel@tonic-gate nodev, /* read */ 120*7c478bd9Sstevel@tonic-gate nodev, /* write */ 121*7c478bd9Sstevel@tonic-gate ds1287_ioctl, /* ioctl */ 122*7c478bd9Sstevel@tonic-gate nodev, /* devmap */ 123*7c478bd9Sstevel@tonic-gate nodev, /* mmap */ 124*7c478bd9Sstevel@tonic-gate nodev, /* segmap */ 125*7c478bd9Sstevel@tonic-gate ds1287_chpoll, /* poll */ 126*7c478bd9Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */ 127*7c478bd9Sstevel@tonic-gate NULL, /* streamtab */ 128*7c478bd9Sstevel@tonic-gate D_NEW | D_MP, /* Driver compatibility flag */ 129*7c478bd9Sstevel@tonic-gate CB_REV, /* rev */ 130*7c478bd9Sstevel@tonic-gate nodev, /* int (*cb_aread)() */ 131*7c478bd9Sstevel@tonic-gate nodev /* int (*cb_awrite)() */ 132*7c478bd9Sstevel@tonic-gate }; 133*7c478bd9Sstevel@tonic-gate 134*7c478bd9Sstevel@tonic-gate /* 135*7c478bd9Sstevel@tonic-gate * dev ops 136*7c478bd9Sstevel@tonic-gate */ 137*7c478bd9Sstevel@tonic-gate static int ds1287_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 138*7c478bd9Sstevel@tonic-gate static int ds1287_attach(dev_info_t *, ddi_attach_cmd_t); 139*7c478bd9Sstevel@tonic-gate static int ds1287_detach(dev_info_t *, ddi_detach_cmd_t); 140*7c478bd9Sstevel@tonic-gate 141*7c478bd9Sstevel@tonic-gate static struct dev_ops ds1287_ops = { 142*7c478bd9Sstevel@tonic-gate DEVO_REV, /* devo_rev */ 143*7c478bd9Sstevel@tonic-gate 0, /* refcnt */ 144*7c478bd9Sstevel@tonic-gate ds1287_getinfo, /* getinfo */ 145*7c478bd9Sstevel@tonic-gate nulldev, /* identify */ 146*7c478bd9Sstevel@tonic-gate nulldev, /* probe */ 147*7c478bd9Sstevel@tonic-gate ds1287_attach, /* attach */ 148*7c478bd9Sstevel@tonic-gate ds1287_detach, /* detach */ 149*7c478bd9Sstevel@tonic-gate nodev, /* reset */ 150*7c478bd9Sstevel@tonic-gate &ds1287_cbops, /* cb_ops */ 151*7c478bd9Sstevel@tonic-gate (struct bus_ops *)NULL, /* bus_ops */ 152*7c478bd9Sstevel@tonic-gate NULL /* power */ 153*7c478bd9Sstevel@tonic-gate }; 154*7c478bd9Sstevel@tonic-gate 155*7c478bd9Sstevel@tonic-gate 156*7c478bd9Sstevel@tonic-gate static void *ds1287_state; 157*7c478bd9Sstevel@tonic-gate static int instance = -1; 158*7c478bd9Sstevel@tonic-gate 159*7c478bd9Sstevel@tonic-gate /* Driver Tunables */ 160*7c478bd9Sstevel@tonic-gate static int ds1287_interrupt_priority = 15; 161*7c478bd9Sstevel@tonic-gate static int ds1287_softint_priority = 2; 162*7c478bd9Sstevel@tonic-gate static hrtime_t power_button_debounce = NANOSEC/MILLISEC*10; 163*7c478bd9Sstevel@tonic-gate static hrtime_t power_button_abort_interval = 1.5 * NANOSEC; 164*7c478bd9Sstevel@tonic-gate static int power_button_abort_presses = 3; 165*7c478bd9Sstevel@tonic-gate static int power_button_abort_enable = 1; 166*7c478bd9Sstevel@tonic-gate static int power_button_enable = 1; 167*7c478bd9Sstevel@tonic-gate 168*7c478bd9Sstevel@tonic-gate static int power_button_pressed = 0; 169*7c478bd9Sstevel@tonic-gate static int power_button_cancel = 0; 170*7c478bd9Sstevel@tonic-gate static int power_button_timeouts = 0; 171*7c478bd9Sstevel@tonic-gate static int timeout_cancel = 0; 172*7c478bd9Sstevel@tonic-gate static int additional_presses = 0; 173*7c478bd9Sstevel@tonic-gate 174*7c478bd9Sstevel@tonic-gate static ddi_iblock_cookie_t ds1287_lo_iblock; 175*7c478bd9Sstevel@tonic-gate static ddi_iblock_cookie_t ds1287_hi_iblock; 176*7c478bd9Sstevel@tonic-gate static ddi_softintr_t ds1287_softintr_id; 177*7c478bd9Sstevel@tonic-gate static kmutex_t ds1287_reg_mutex; /* Protects ds1287 Registers */ 178*7c478bd9Sstevel@tonic-gate 179*7c478bd9Sstevel@tonic-gate static struct modldrv modldrv = { 180*7c478bd9Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */ 181*7c478bd9Sstevel@tonic-gate "ds1287 clock driver %I%", /* Name of the module. */ 182*7c478bd9Sstevel@tonic-gate &ds1287_ops, /* driver ops */ 183*7c478bd9Sstevel@tonic-gate }; 184*7c478bd9Sstevel@tonic-gate 185*7c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = { 186*7c478bd9Sstevel@tonic-gate MODREV_1, &modldrv, NULL 187*7c478bd9Sstevel@tonic-gate }; 188*7c478bd9Sstevel@tonic-gate 189*7c478bd9Sstevel@tonic-gate 190*7c478bd9Sstevel@tonic-gate int 191*7c478bd9Sstevel@tonic-gate _init(void) 192*7c478bd9Sstevel@tonic-gate { 193*7c478bd9Sstevel@tonic-gate int status; 194*7c478bd9Sstevel@tonic-gate 195*7c478bd9Sstevel@tonic-gate status = ddi_soft_state_init(&ds1287_state, sizeof (struct ds1287), 0); 196*7c478bd9Sstevel@tonic-gate if (status != 0) { 197*7c478bd9Sstevel@tonic-gate return (status); 198*7c478bd9Sstevel@tonic-gate } 199*7c478bd9Sstevel@tonic-gate 200*7c478bd9Sstevel@tonic-gate if ((status = mod_install(&modlinkage)) != 0) { 201*7c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&ds1287_state); 202*7c478bd9Sstevel@tonic-gate return (status); 203*7c478bd9Sstevel@tonic-gate } 204*7c478bd9Sstevel@tonic-gate 205*7c478bd9Sstevel@tonic-gate 206*7c478bd9Sstevel@tonic-gate ds1287_hi_iblock = (ddi_iblock_cookie_t) 207*7c478bd9Sstevel@tonic-gate ipltospl(ds1287_interrupt_priority); 208*7c478bd9Sstevel@tonic-gate mutex_init(&ds1287_reg_mutex, NULL, MUTEX_DRIVER, ds1287_hi_iblock); 209*7c478bd9Sstevel@tonic-gate 210*7c478bd9Sstevel@tonic-gate mutex_enter(&ds1287_reg_mutex); 211*7c478bd9Sstevel@tonic-gate /* Select Bank 1 */ 212*7c478bd9Sstevel@tonic-gate select_bank(1); 213*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_B; 214*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = (RTC_DM | RTC_HM); 215*7c478bd9Sstevel@tonic-gate mutex_exit(&ds1287_reg_mutex); 216*7c478bd9Sstevel@tonic-gate 217*7c478bd9Sstevel@tonic-gate tod_ops.tod_get = todds_get; 218*7c478bd9Sstevel@tonic-gate tod_ops.tod_set = todds_set; 219*7c478bd9Sstevel@tonic-gate 220*7c478bd9Sstevel@tonic-gate /* 221*7c478bd9Sstevel@tonic-gate * If v_pmc_addr_reg isn't set, it's because it wasn't set in 222*7c478bd9Sstevel@tonic-gate * sun4u/os/fillsysinfo.c:have_pmc(). This means the real (pmc) 223*7c478bd9Sstevel@tonic-gate * watchdog routines (sun4u/io/pmc.c) will not be used. If the 224*7c478bd9Sstevel@tonic-gate * user were to set watchdog_enable in /etc/system, we'll need to 225*7c478bd9Sstevel@tonic-gate * use our own NOP routines. 226*7c478bd9Sstevel@tonic-gate */ 227*7c478bd9Sstevel@tonic-gate if (v_pmc_addr_reg == NULL) { 228*7c478bd9Sstevel@tonic-gate tod_ops.tod_set_watchdog_timer = todds_set_watchdog_timer; 229*7c478bd9Sstevel@tonic-gate tod_ops.tod_clear_watchdog_timer = todds_clear_watchdog_timer; 230*7c478bd9Sstevel@tonic-gate } 231*7c478bd9Sstevel@tonic-gate tod_ops.tod_set_power_alarm = todds_set_power_alarm; 232*7c478bd9Sstevel@tonic-gate tod_ops.tod_clear_power_alarm = todds_clear_power_alarm; 233*7c478bd9Sstevel@tonic-gate tod_ops.tod_get_cpufrequency = todds_get_cpufrequency; 234*7c478bd9Sstevel@tonic-gate 235*7c478bd9Sstevel@tonic-gate return (status); 236*7c478bd9Sstevel@tonic-gate } 237*7c478bd9Sstevel@tonic-gate 238*7c478bd9Sstevel@tonic-gate int 239*7c478bd9Sstevel@tonic-gate _fini(void) 240*7c478bd9Sstevel@tonic-gate { 241*7c478bd9Sstevel@tonic-gate if (strcmp(tod_module_name, "todds1287") == 0) 242*7c478bd9Sstevel@tonic-gate return (EBUSY); 243*7c478bd9Sstevel@tonic-gate 244*7c478bd9Sstevel@tonic-gate return (mod_remove(&modlinkage)); 245*7c478bd9Sstevel@tonic-gate } 246*7c478bd9Sstevel@tonic-gate 247*7c478bd9Sstevel@tonic-gate /* 248*7c478bd9Sstevel@tonic-gate * The loadable-module _info(9E) entry point 249*7c478bd9Sstevel@tonic-gate */ 250*7c478bd9Sstevel@tonic-gate int 251*7c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop) 252*7c478bd9Sstevel@tonic-gate { 253*7c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 254*7c478bd9Sstevel@tonic-gate } 255*7c478bd9Sstevel@tonic-gate 256*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 257*7c478bd9Sstevel@tonic-gate static int 258*7c478bd9Sstevel@tonic-gate ds1287_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 259*7c478bd9Sstevel@tonic-gate void **result) 260*7c478bd9Sstevel@tonic-gate { 261*7c478bd9Sstevel@tonic-gate struct ds1287 *softsp; 262*7c478bd9Sstevel@tonic-gate 263*7c478bd9Sstevel@tonic-gate if (instance == -1) 264*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 265*7c478bd9Sstevel@tonic-gate 266*7c478bd9Sstevel@tonic-gate switch (infocmd) { 267*7c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO: 268*7c478bd9Sstevel@tonic-gate if ((softsp = ddi_get_soft_state(ds1287_state, instance)) 269*7c478bd9Sstevel@tonic-gate == NULL) 270*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 271*7c478bd9Sstevel@tonic-gate *result = (void *)softsp->dip; 272*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 273*7c478bd9Sstevel@tonic-gate 274*7c478bd9Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE: 275*7c478bd9Sstevel@tonic-gate *result = (void *)instance; 276*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 277*7c478bd9Sstevel@tonic-gate 278*7c478bd9Sstevel@tonic-gate default: 279*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 280*7c478bd9Sstevel@tonic-gate } 281*7c478bd9Sstevel@tonic-gate } 282*7c478bd9Sstevel@tonic-gate 283*7c478bd9Sstevel@tonic-gate static int 284*7c478bd9Sstevel@tonic-gate ds1287_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 285*7c478bd9Sstevel@tonic-gate { 286*7c478bd9Sstevel@tonic-gate struct ds1287 *softsp; 287*7c478bd9Sstevel@tonic-gate 288*7c478bd9Sstevel@tonic-gate DPRINTF("ds1287_attach\n"); 289*7c478bd9Sstevel@tonic-gate switch (cmd) { 290*7c478bd9Sstevel@tonic-gate case DDI_ATTACH: 291*7c478bd9Sstevel@tonic-gate break; 292*7c478bd9Sstevel@tonic-gate case DDI_RESUME: 293*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 294*7c478bd9Sstevel@tonic-gate default: 295*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 296*7c478bd9Sstevel@tonic-gate } 297*7c478bd9Sstevel@tonic-gate 298*7c478bd9Sstevel@tonic-gate if (instance != -1) { 299*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "ds1287_attach: Another instance is already " 300*7c478bd9Sstevel@tonic-gate "attached."); 301*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 302*7c478bd9Sstevel@tonic-gate } 303*7c478bd9Sstevel@tonic-gate 304*7c478bd9Sstevel@tonic-gate instance = ddi_get_instance(dip); 305*7c478bd9Sstevel@tonic-gate 306*7c478bd9Sstevel@tonic-gate if (v_rtc_addr_reg == NULL) { 307*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "ds1287_attach: v_rtc_addr_reg is NULL"); 308*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 309*7c478bd9Sstevel@tonic-gate } 310*7c478bd9Sstevel@tonic-gate 311*7c478bd9Sstevel@tonic-gate /* 312*7c478bd9Sstevel@tonic-gate * Allocate softc information. 313*7c478bd9Sstevel@tonic-gate */ 314*7c478bd9Sstevel@tonic-gate if (ddi_soft_state_zalloc(ds1287_state, instance) != DDI_SUCCESS) { 315*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "ds1287_attach: Failed to allocate " 316*7c478bd9Sstevel@tonic-gate "soft states."); 317*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 318*7c478bd9Sstevel@tonic-gate } 319*7c478bd9Sstevel@tonic-gate 320*7c478bd9Sstevel@tonic-gate softsp = ddi_get_soft_state(ds1287_state, instance); 321*7c478bd9Sstevel@tonic-gate DPRINTF("ds1287_attach: instance=%d softsp=0x%x\n", instance, softsp); 322*7c478bd9Sstevel@tonic-gate 323*7c478bd9Sstevel@tonic-gate softsp->dip = dip; 324*7c478bd9Sstevel@tonic-gate 325*7c478bd9Sstevel@tonic-gate if (ddi_prop_create(DDI_DEV_T_NONE, dip, DDI_PROP_CANSLEEP, 326*7c478bd9Sstevel@tonic-gate "interrupt-priorities", (caddr_t)&ds1287_interrupt_priority, 327*7c478bd9Sstevel@tonic-gate sizeof (int)) != DDI_PROP_SUCCESS) { 328*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "ds1287_attach: Failed to create \"" 329*7c478bd9Sstevel@tonic-gate "interrupt-priorities\" property."); 330*7c478bd9Sstevel@tonic-gate goto error; 331*7c478bd9Sstevel@tonic-gate } 332*7c478bd9Sstevel@tonic-gate 333*7c478bd9Sstevel@tonic-gate /* add the softint */ 334*7c478bd9Sstevel@tonic-gate ds1287_lo_iblock = (ddi_iblock_cookie_t) 335*7c478bd9Sstevel@tonic-gate ipltospl(ds1287_softint_priority); 336*7c478bd9Sstevel@tonic-gate 337*7c478bd9Sstevel@tonic-gate if (ddi_add_softintr(dip, DDI_SOFTINT_FIXED, &ds1287_softintr_id, 338*7c478bd9Sstevel@tonic-gate &ds1287_lo_iblock, NULL, ds1287_softintr, (caddr_t)softsp) != 339*7c478bd9Sstevel@tonic-gate DDI_SUCCESS) { 340*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "ds1287_attach: Failed to add low interrupt."); 341*7c478bd9Sstevel@tonic-gate goto error1; 342*7c478bd9Sstevel@tonic-gate } 343*7c478bd9Sstevel@tonic-gate 344*7c478bd9Sstevel@tonic-gate /* add the hi interrupt */ 345*7c478bd9Sstevel@tonic-gate if (ddi_add_intr(dip, 0, NULL, (ddi_idevice_cookie_t *) 346*7c478bd9Sstevel@tonic-gate &ds1287_hi_iblock, ds1287_intr, NULL) != DDI_SUCCESS) { 347*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "ds1287_attach: Failed to add high " 348*7c478bd9Sstevel@tonic-gate "interrupt."); 349*7c478bd9Sstevel@tonic-gate goto error2; 350*7c478bd9Sstevel@tonic-gate } 351*7c478bd9Sstevel@tonic-gate 352*7c478bd9Sstevel@tonic-gate /* 353*7c478bd9Sstevel@tonic-gate * Combination of instance number and clone number 0 is used for 354*7c478bd9Sstevel@tonic-gate * creating the minor node. 355*7c478bd9Sstevel@tonic-gate */ 356*7c478bd9Sstevel@tonic-gate if (ddi_create_minor_node(dip, "power_button", S_IFCHR, 357*7c478bd9Sstevel@tonic-gate (instance << 8) + 0, "ddi_power_button", NULL) == DDI_FAILURE) { 358*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "ds1287_attach: Failed to create minor node"); 359*7c478bd9Sstevel@tonic-gate goto error3; 360*7c478bd9Sstevel@tonic-gate } 361*7c478bd9Sstevel@tonic-gate 362*7c478bd9Sstevel@tonic-gate ddi_report_dev(dip); 363*7c478bd9Sstevel@tonic-gate 364*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 365*7c478bd9Sstevel@tonic-gate 366*7c478bd9Sstevel@tonic-gate error3: 367*7c478bd9Sstevel@tonic-gate ddi_remove_intr(dip, 0, NULL); 368*7c478bd9Sstevel@tonic-gate error2: 369*7c478bd9Sstevel@tonic-gate ddi_remove_softintr(ds1287_softintr_id); 370*7c478bd9Sstevel@tonic-gate error1: 371*7c478bd9Sstevel@tonic-gate (void) ddi_prop_remove(DDI_DEV_T_NONE, dip, "interrupt-priorities"); 372*7c478bd9Sstevel@tonic-gate error: 373*7c478bd9Sstevel@tonic-gate ddi_soft_state_free(ds1287_state, instance); 374*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 375*7c478bd9Sstevel@tonic-gate } 376*7c478bd9Sstevel@tonic-gate 377*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 378*7c478bd9Sstevel@tonic-gate static int 379*7c478bd9Sstevel@tonic-gate ds1287_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 380*7c478bd9Sstevel@tonic-gate { 381*7c478bd9Sstevel@tonic-gate DPRINTF("ds1287_detach\n"); 382*7c478bd9Sstevel@tonic-gate switch (cmd) { 383*7c478bd9Sstevel@tonic-gate case DDI_DETACH: 384*7c478bd9Sstevel@tonic-gate /* 385*7c478bd9Sstevel@tonic-gate * Since it needs to always handle the power button, fail 386*7c478bd9Sstevel@tonic-gate * to detach. 387*7c478bd9Sstevel@tonic-gate */ 388*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 389*7c478bd9Sstevel@tonic-gate case DDI_SUSPEND: 390*7c478bd9Sstevel@tonic-gate return (DDI_SUCCESS); 391*7c478bd9Sstevel@tonic-gate default: 392*7c478bd9Sstevel@tonic-gate return (DDI_FAILURE); 393*7c478bd9Sstevel@tonic-gate } 394*7c478bd9Sstevel@tonic-gate } 395*7c478bd9Sstevel@tonic-gate 396*7c478bd9Sstevel@tonic-gate /*ARGSUSED1*/ 397*7c478bd9Sstevel@tonic-gate static int 398*7c478bd9Sstevel@tonic-gate ds1287_open(dev_t *devp, int flags, int otyp, cred_t *credp) 399*7c478bd9Sstevel@tonic-gate { 400*7c478bd9Sstevel@tonic-gate struct ds1287 *softsp; 401*7c478bd9Sstevel@tonic-gate int clone; 402*7c478bd9Sstevel@tonic-gate 403*7c478bd9Sstevel@tonic-gate if (otyp != OTYP_CHR) 404*7c478bd9Sstevel@tonic-gate return (EINVAL); 405*7c478bd9Sstevel@tonic-gate 406*7c478bd9Sstevel@tonic-gate if ((softsp = ddi_get_soft_state(ds1287_state, instance)) == 407*7c478bd9Sstevel@tonic-gate NULL) 408*7c478bd9Sstevel@tonic-gate return (ENXIO); 409*7c478bd9Sstevel@tonic-gate 410*7c478bd9Sstevel@tonic-gate mutex_enter(&softsp->ds1287_mutex); 411*7c478bd9Sstevel@tonic-gate for (clone = 1; clone < DS1287_MAX_CLONE; clone++) 412*7c478bd9Sstevel@tonic-gate if (!softsp->clones[clone]) 413*7c478bd9Sstevel@tonic-gate break; 414*7c478bd9Sstevel@tonic-gate 415*7c478bd9Sstevel@tonic-gate if (clone == DS1287_MAX_CLONE) { 416*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "ds1287_open: No more allocation left " 417*7c478bd9Sstevel@tonic-gate "to clone a minor."); 418*7c478bd9Sstevel@tonic-gate mutex_exit(&softsp->ds1287_mutex); 419*7c478bd9Sstevel@tonic-gate return (ENXIO); 420*7c478bd9Sstevel@tonic-gate } 421*7c478bd9Sstevel@tonic-gate 422*7c478bd9Sstevel@tonic-gate *devp = makedevice(getmajor(*devp), (instance << 8) + clone); 423*7c478bd9Sstevel@tonic-gate softsp->clones[clone] = 1; 424*7c478bd9Sstevel@tonic-gate mutex_exit(&softsp->ds1287_mutex); 425*7c478bd9Sstevel@tonic-gate 426*7c478bd9Sstevel@tonic-gate return (0); 427*7c478bd9Sstevel@tonic-gate } 428*7c478bd9Sstevel@tonic-gate 429*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 430*7c478bd9Sstevel@tonic-gate static int 431*7c478bd9Sstevel@tonic-gate ds1287_close(dev_t dev, int flags, int otyp, cred_t *credp) 432*7c478bd9Sstevel@tonic-gate { 433*7c478bd9Sstevel@tonic-gate struct ds1287 *softsp; 434*7c478bd9Sstevel@tonic-gate int clone; 435*7c478bd9Sstevel@tonic-gate 436*7c478bd9Sstevel@tonic-gate if (otyp != OTYP_CHR) 437*7c478bd9Sstevel@tonic-gate return (EINVAL); 438*7c478bd9Sstevel@tonic-gate 439*7c478bd9Sstevel@tonic-gate if ((softsp = ddi_get_soft_state(ds1287_state, instance)) == 440*7c478bd9Sstevel@tonic-gate NULL) 441*7c478bd9Sstevel@tonic-gate return (ENXIO); 442*7c478bd9Sstevel@tonic-gate 443*7c478bd9Sstevel@tonic-gate clone = DS1287_MINOR_TO_CLONE(getminor(dev)); 444*7c478bd9Sstevel@tonic-gate mutex_enter(&softsp->ds1287_mutex); 445*7c478bd9Sstevel@tonic-gate if (softsp->monitor_on == clone) 446*7c478bd9Sstevel@tonic-gate softsp->monitor_on = 0; 447*7c478bd9Sstevel@tonic-gate softsp->clones[clone] = 0; 448*7c478bd9Sstevel@tonic-gate mutex_exit(&softsp->ds1287_mutex); 449*7c478bd9Sstevel@tonic-gate 450*7c478bd9Sstevel@tonic-gate return (0); 451*7c478bd9Sstevel@tonic-gate } 452*7c478bd9Sstevel@tonic-gate 453*7c478bd9Sstevel@tonic-gate /*ARGSUSED4*/ 454*7c478bd9Sstevel@tonic-gate static int 455*7c478bd9Sstevel@tonic-gate ds1287_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 456*7c478bd9Sstevel@tonic-gate cred_t *credp, int *rvalp) 457*7c478bd9Sstevel@tonic-gate { 458*7c478bd9Sstevel@tonic-gate struct ds1287 *softsp; 459*7c478bd9Sstevel@tonic-gate int clone; 460*7c478bd9Sstevel@tonic-gate 461*7c478bd9Sstevel@tonic-gate if ((softsp = ddi_get_soft_state(ds1287_state, instance)) == 462*7c478bd9Sstevel@tonic-gate NULL) 463*7c478bd9Sstevel@tonic-gate return (ENXIO); 464*7c478bd9Sstevel@tonic-gate 465*7c478bd9Sstevel@tonic-gate clone = DS1287_MINOR_TO_CLONE(getminor(dev)); 466*7c478bd9Sstevel@tonic-gate switch (cmd) { 467*7c478bd9Sstevel@tonic-gate case PB_BEGIN_MONITOR: 468*7c478bd9Sstevel@tonic-gate DPRINTF("ds1287_ioctl: PB_BEGIN_MONITOR is called.\n"); 469*7c478bd9Sstevel@tonic-gate mutex_enter(&softsp->ds1287_mutex); 470*7c478bd9Sstevel@tonic-gate if (softsp->monitor_on) { 471*7c478bd9Sstevel@tonic-gate mutex_exit(&softsp->ds1287_mutex); 472*7c478bd9Sstevel@tonic-gate return (EBUSY); 473*7c478bd9Sstevel@tonic-gate } 474*7c478bd9Sstevel@tonic-gate softsp->monitor_on = clone; 475*7c478bd9Sstevel@tonic-gate mutex_exit(&softsp->ds1287_mutex); 476*7c478bd9Sstevel@tonic-gate return (0); 477*7c478bd9Sstevel@tonic-gate 478*7c478bd9Sstevel@tonic-gate case PB_END_MONITOR: 479*7c478bd9Sstevel@tonic-gate DPRINTF("ds1287_ioctl: PB_END_MONITOR is called.\n"); 480*7c478bd9Sstevel@tonic-gate mutex_enter(&softsp->ds1287_mutex); 481*7c478bd9Sstevel@tonic-gate 482*7c478bd9Sstevel@tonic-gate /* 483*7c478bd9Sstevel@tonic-gate * If PB_END_MONITOR is called without first 484*7c478bd9Sstevel@tonic-gate * calling PB_BEGIN_MONITOR, an error will be 485*7c478bd9Sstevel@tonic-gate * returned. 486*7c478bd9Sstevel@tonic-gate */ 487*7c478bd9Sstevel@tonic-gate if (!softsp->monitor_on) { 488*7c478bd9Sstevel@tonic-gate mutex_exit(&softsp->ds1287_mutex); 489*7c478bd9Sstevel@tonic-gate return (ENXIO); 490*7c478bd9Sstevel@tonic-gate } 491*7c478bd9Sstevel@tonic-gate 492*7c478bd9Sstevel@tonic-gate /* 493*7c478bd9Sstevel@tonic-gate * This clone is not monitoring the button. 494*7c478bd9Sstevel@tonic-gate */ 495*7c478bd9Sstevel@tonic-gate if (softsp->monitor_on != clone) { 496*7c478bd9Sstevel@tonic-gate mutex_exit(&softsp->ds1287_mutex); 497*7c478bd9Sstevel@tonic-gate return (EINVAL); 498*7c478bd9Sstevel@tonic-gate } 499*7c478bd9Sstevel@tonic-gate softsp->monitor_on = 0; 500*7c478bd9Sstevel@tonic-gate mutex_exit(&softsp->ds1287_mutex); 501*7c478bd9Sstevel@tonic-gate return (0); 502*7c478bd9Sstevel@tonic-gate 503*7c478bd9Sstevel@tonic-gate case PB_GET_EVENTS: 504*7c478bd9Sstevel@tonic-gate DPRINTF("ds1287_ioctl: PB_GET_EVENTS is called.\n"); 505*7c478bd9Sstevel@tonic-gate mutex_enter(&softsp->ds1287_mutex); 506*7c478bd9Sstevel@tonic-gate if (ddi_copyout((void *)&softsp->events, (void *)arg, 507*7c478bd9Sstevel@tonic-gate sizeof (int), mode) != 0) { 508*7c478bd9Sstevel@tonic-gate mutex_exit(&softsp->ds1287_mutex); 509*7c478bd9Sstevel@tonic-gate return (EFAULT); 510*7c478bd9Sstevel@tonic-gate } 511*7c478bd9Sstevel@tonic-gate 512*7c478bd9Sstevel@tonic-gate /* 513*7c478bd9Sstevel@tonic-gate * This ioctl returned the events detected since last 514*7c478bd9Sstevel@tonic-gate * call. Note that any application can get the events 515*7c478bd9Sstevel@tonic-gate * and clear the event register. 516*7c478bd9Sstevel@tonic-gate */ 517*7c478bd9Sstevel@tonic-gate softsp->events = 0; 518*7c478bd9Sstevel@tonic-gate mutex_exit(&softsp->ds1287_mutex); 519*7c478bd9Sstevel@tonic-gate return (0); 520*7c478bd9Sstevel@tonic-gate 521*7c478bd9Sstevel@tonic-gate /* 522*7c478bd9Sstevel@tonic-gate * This ioctl is used by the test suite. 523*7c478bd9Sstevel@tonic-gate */ 524*7c478bd9Sstevel@tonic-gate case PB_CREATE_BUTTON_EVENT: 525*7c478bd9Sstevel@tonic-gate DPRINTF("ds1287_ioctl: PB_CREATE_BUTTON_EVENT is called.\n"); 526*7c478bd9Sstevel@tonic-gate (void) ds1287_intr(NULL); 527*7c478bd9Sstevel@tonic-gate return (0); 528*7c478bd9Sstevel@tonic-gate 529*7c478bd9Sstevel@tonic-gate default: 530*7c478bd9Sstevel@tonic-gate return (ENOTTY); 531*7c478bd9Sstevel@tonic-gate } 532*7c478bd9Sstevel@tonic-gate } 533*7c478bd9Sstevel@tonic-gate 534*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 535*7c478bd9Sstevel@tonic-gate static int 536*7c478bd9Sstevel@tonic-gate ds1287_chpoll(dev_t dev, short events, int anyyet, 537*7c478bd9Sstevel@tonic-gate short *reventsp, struct pollhead **phpp) 538*7c478bd9Sstevel@tonic-gate { 539*7c478bd9Sstevel@tonic-gate struct ds1287 *softsp; 540*7c478bd9Sstevel@tonic-gate 541*7c478bd9Sstevel@tonic-gate if ((softsp = ddi_get_soft_state(ds1287_state, instance)) == NULL) 542*7c478bd9Sstevel@tonic-gate return (ENXIO); 543*7c478bd9Sstevel@tonic-gate 544*7c478bd9Sstevel@tonic-gate mutex_enter(&softsp->ds1287_mutex); 545*7c478bd9Sstevel@tonic-gate *reventsp = 0; 546*7c478bd9Sstevel@tonic-gate if (softsp->events) 547*7c478bd9Sstevel@tonic-gate *reventsp = POLLRDNORM|POLLIN; 548*7c478bd9Sstevel@tonic-gate else { 549*7c478bd9Sstevel@tonic-gate if (!anyyet) 550*7c478bd9Sstevel@tonic-gate *phpp = &softsp->pollhd; 551*7c478bd9Sstevel@tonic-gate } 552*7c478bd9Sstevel@tonic-gate mutex_exit(&softsp->ds1287_mutex); 553*7c478bd9Sstevel@tonic-gate 554*7c478bd9Sstevel@tonic-gate return (0); 555*7c478bd9Sstevel@tonic-gate } 556*7c478bd9Sstevel@tonic-gate 557*7c478bd9Sstevel@tonic-gate static void 558*7c478bd9Sstevel@tonic-gate ds1287_log_message(void) 559*7c478bd9Sstevel@tonic-gate { 560*7c478bd9Sstevel@tonic-gate struct ds1287 *softsp; 561*7c478bd9Sstevel@tonic-gate 562*7c478bd9Sstevel@tonic-gate if ((softsp = ddi_get_soft_state(ds1287_state, instance)) == NULL) { 563*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "ds1287: Failed to get internal state!"); 564*7c478bd9Sstevel@tonic-gate return; 565*7c478bd9Sstevel@tonic-gate } 566*7c478bd9Sstevel@tonic-gate 567*7c478bd9Sstevel@tonic-gate mutex_enter(&softsp->ds1287_mutex); 568*7c478bd9Sstevel@tonic-gate softsp->shutdown_pending = 0; 569*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "ds1287: Failed to shut down the system!"); 570*7c478bd9Sstevel@tonic-gate mutex_exit(&softsp->ds1287_mutex); 571*7c478bd9Sstevel@tonic-gate } 572*7c478bd9Sstevel@tonic-gate 573*7c478bd9Sstevel@tonic-gate /* 574*7c478bd9Sstevel@tonic-gate * To facilitate a power button abort, ds1287_intr() now posts 575*7c478bd9Sstevel@tonic-gate * a softint (calling ds1287_softintr()) for all power button presses and 576*7c478bd9Sstevel@tonic-gate * counts the number of button presses. An abort is issued if the desired 577*7c478bd9Sstevel@tonic-gate * number of button presses within the given time interval. 578*7c478bd9Sstevel@tonic-gate * 579*7c478bd9Sstevel@tonic-gate * Two variables are used to synchronize between the high level intr; 580*7c478bd9Sstevel@tonic-gate * the softint handler and timeout handler 581*7c478bd9Sstevel@tonic-gate * 582*7c478bd9Sstevel@tonic-gate * power_button_cancel - Indicates that an abort happened and the number 583*7c478bd9Sstevel@tonic-gate * of outstanding timeouts that have to be cancelled 584*7c478bd9Sstevel@tonic-gate * 585*7c478bd9Sstevel@tonic-gate * power_button_pressed - Indicates the number of button presses outstanding 586*7c478bd9Sstevel@tonic-gate * which have not been serviced 587*7c478bd9Sstevel@tonic-gate */ 588*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 589*7c478bd9Sstevel@tonic-gate static uint_t 590*7c478bd9Sstevel@tonic-gate ds1287_intr(caddr_t ignore) 591*7c478bd9Sstevel@tonic-gate { 592*7c478bd9Sstevel@tonic-gate hrtime_t tstamp; 593*7c478bd9Sstevel@tonic-gate static hrtime_t o_tstamp = 0; 594*7c478bd9Sstevel@tonic-gate static hrtime_t power_button_tstamp = 0; 595*7c478bd9Sstevel@tonic-gate static int power_button_cnt; 596*7c478bd9Sstevel@tonic-gate uint8_t apcr1; 597*7c478bd9Sstevel@tonic-gate 598*7c478bd9Sstevel@tonic-gate /* 599*7c478bd9Sstevel@tonic-gate * Stop the Fail-safe timer that starts running 600*7c478bd9Sstevel@tonic-gate * after power button is pressed. If it is not 601*7c478bd9Sstevel@tonic-gate * stopped in 21 seconds, system powers off. 602*7c478bd9Sstevel@tonic-gate */ 603*7c478bd9Sstevel@tonic-gate mutex_enter(&ds1287_reg_mutex); 604*7c478bd9Sstevel@tonic-gate select_bank(2); 605*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = APC_APCR1; 606*7c478bd9Sstevel@tonic-gate apcr1 = DS1287_DATA_REG; 607*7c478bd9Sstevel@tonic-gate apcr1 |= APC_FSTRC; 608*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = apcr1; 609*7c478bd9Sstevel@tonic-gate select_bank(1); 610*7c478bd9Sstevel@tonic-gate mutex_exit(&ds1287_reg_mutex); 611*7c478bd9Sstevel@tonic-gate 612*7c478bd9Sstevel@tonic-gate tstamp = gethrtime(); 613*7c478bd9Sstevel@tonic-gate 614*7c478bd9Sstevel@tonic-gate /* need to deal with power button debounce */ 615*7c478bd9Sstevel@tonic-gate if (o_tstamp && (tstamp - o_tstamp) < power_button_debounce) { 616*7c478bd9Sstevel@tonic-gate o_tstamp = tstamp; 617*7c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 618*7c478bd9Sstevel@tonic-gate } 619*7c478bd9Sstevel@tonic-gate o_tstamp = tstamp; 620*7c478bd9Sstevel@tonic-gate 621*7c478bd9Sstevel@tonic-gate power_button_cnt++; 622*7c478bd9Sstevel@tonic-gate 623*7c478bd9Sstevel@tonic-gate mutex_enter(&ds1287_reg_mutex); 624*7c478bd9Sstevel@tonic-gate power_button_pressed++; 625*7c478bd9Sstevel@tonic-gate mutex_exit(&ds1287_reg_mutex); 626*7c478bd9Sstevel@tonic-gate 627*7c478bd9Sstevel@tonic-gate /* 628*7c478bd9Sstevel@tonic-gate * If power button abort is enabled and power button was pressed 629*7c478bd9Sstevel@tonic-gate * power_button_abort_presses times within power_button_abort_interval 630*7c478bd9Sstevel@tonic-gate * then call abort_sequence_enter(); 631*7c478bd9Sstevel@tonic-gate */ 632*7c478bd9Sstevel@tonic-gate if (power_button_abort_enable) { 633*7c478bd9Sstevel@tonic-gate if (power_button_abort_presses == 1 || 634*7c478bd9Sstevel@tonic-gate tstamp < (power_button_tstamp + 635*7c478bd9Sstevel@tonic-gate power_button_abort_interval)) { 636*7c478bd9Sstevel@tonic-gate if (power_button_cnt == power_button_abort_presses) { 637*7c478bd9Sstevel@tonic-gate mutex_enter(&ds1287_reg_mutex); 638*7c478bd9Sstevel@tonic-gate power_button_cancel += power_button_timeouts; 639*7c478bd9Sstevel@tonic-gate power_button_pressed = 0; 640*7c478bd9Sstevel@tonic-gate mutex_exit(&ds1287_reg_mutex); 641*7c478bd9Sstevel@tonic-gate power_button_cnt = 0; 642*7c478bd9Sstevel@tonic-gate abort_sequence_enter("Power Button Abort"); 643*7c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 644*7c478bd9Sstevel@tonic-gate } 645*7c478bd9Sstevel@tonic-gate } else { 646*7c478bd9Sstevel@tonic-gate power_button_cnt = 1; 647*7c478bd9Sstevel@tonic-gate power_button_tstamp = tstamp; 648*7c478bd9Sstevel@tonic-gate } 649*7c478bd9Sstevel@tonic-gate } 650*7c478bd9Sstevel@tonic-gate 651*7c478bd9Sstevel@tonic-gate if (!power_button_enable) 652*7c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 653*7c478bd9Sstevel@tonic-gate 654*7c478bd9Sstevel@tonic-gate /* post softint to issue timeout for power button action */ 655*7c478bd9Sstevel@tonic-gate ddi_trigger_softintr(ds1287_softintr_id); 656*7c478bd9Sstevel@tonic-gate 657*7c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 658*7c478bd9Sstevel@tonic-gate } 659*7c478bd9Sstevel@tonic-gate 660*7c478bd9Sstevel@tonic-gate /* 661*7c478bd9Sstevel@tonic-gate * Handle the softints.... 662*7c478bd9Sstevel@tonic-gate * 663*7c478bd9Sstevel@tonic-gate * If only one softint is posted for several button presses, record 664*7c478bd9Sstevel@tonic-gate * the number of additional presses just incase this was actually not quite 665*7c478bd9Sstevel@tonic-gate * an Abort sequence so that we can log this event later. 666*7c478bd9Sstevel@tonic-gate * 667*7c478bd9Sstevel@tonic-gate * Issue a timeout with a duration being a fraction larger than 668*7c478bd9Sstevel@tonic-gate * the specified Abort interval inorder to perform a power down if required. 669*7c478bd9Sstevel@tonic-gate */ 670*7c478bd9Sstevel@tonic-gate static uint_t 671*7c478bd9Sstevel@tonic-gate ds1287_softintr(caddr_t arg) 672*7c478bd9Sstevel@tonic-gate { 673*7c478bd9Sstevel@tonic-gate struct ds1287 *softsp = (struct ds1287 *)arg; 674*7c478bd9Sstevel@tonic-gate 675*7c478bd9Sstevel@tonic-gate DPRINTF("ds1287_softintr\n"); 676*7c478bd9Sstevel@tonic-gate 677*7c478bd9Sstevel@tonic-gate if (!power_button_abort_enable) 678*7c478bd9Sstevel@tonic-gate return (ds1287_issue_shutdown(arg)); 679*7c478bd9Sstevel@tonic-gate 680*7c478bd9Sstevel@tonic-gate mutex_enter(&ds1287_reg_mutex); 681*7c478bd9Sstevel@tonic-gate if (!power_button_pressed) { 682*7c478bd9Sstevel@tonic-gate mutex_exit(&ds1287_reg_mutex); 683*7c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 684*7c478bd9Sstevel@tonic-gate } 685*7c478bd9Sstevel@tonic-gate 686*7c478bd9Sstevel@tonic-gate /* 687*7c478bd9Sstevel@tonic-gate * Schedule a timeout to do the necessary 688*7c478bd9Sstevel@tonic-gate * work for shutdown, only one timeout for 689*7c478bd9Sstevel@tonic-gate * n presses if power button was pressed 690*7c478bd9Sstevel@tonic-gate * more than once before softint fired 691*7c478bd9Sstevel@tonic-gate */ 692*7c478bd9Sstevel@tonic-gate if (power_button_pressed > 1) 693*7c478bd9Sstevel@tonic-gate additional_presses += power_button_pressed - 1; 694*7c478bd9Sstevel@tonic-gate 695*7c478bd9Sstevel@tonic-gate timeout_cancel = 0; 696*7c478bd9Sstevel@tonic-gate power_button_pressed = 0; 697*7c478bd9Sstevel@tonic-gate power_button_timeouts++; 698*7c478bd9Sstevel@tonic-gate mutex_exit(&ds1287_reg_mutex); 699*7c478bd9Sstevel@tonic-gate (void) timeout((void(*)(void *))ds1287_timeout, 700*7c478bd9Sstevel@tonic-gate softsp, NSEC_TO_TICK(power_button_abort_interval) + 701*7c478bd9Sstevel@tonic-gate ABORT_INCREMENT_DELAY); 702*7c478bd9Sstevel@tonic-gate 703*7c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 704*7c478bd9Sstevel@tonic-gate } 705*7c478bd9Sstevel@tonic-gate 706*7c478bd9Sstevel@tonic-gate /* 707*7c478bd9Sstevel@tonic-gate * Upon receiving a timeout the following is determined: 708*7c478bd9Sstevel@tonic-gate * 709*7c478bd9Sstevel@tonic-gate * If an Abort sequence was issued, then we cancel all outstanding timeouts 710*7c478bd9Sstevel@tonic-gate * and additional presses prior to the Abort sequence. 711*7c478bd9Sstevel@tonic-gate * 712*7c478bd9Sstevel@tonic-gate * If we had multiple timeouts issued and the abort sequence was not met, 713*7c478bd9Sstevel@tonic-gate * then we had more than one button press to power down the machine. We 714*7c478bd9Sstevel@tonic-gate * were probably trying to issue an abort. So log a message indicating this 715*7c478bd9Sstevel@tonic-gate * and cancel all outstanding timeouts. 716*7c478bd9Sstevel@tonic-gate * 717*7c478bd9Sstevel@tonic-gate * If we had just one timeout and the abort sequence was not met then 718*7c478bd9Sstevel@tonic-gate * we really did want to power down the machine, so call ds1287_issue_shutdown() 719*7c478bd9Sstevel@tonic-gate * to do the work and schedule a power down 720*7c478bd9Sstevel@tonic-gate */ 721*7c478bd9Sstevel@tonic-gate static void 722*7c478bd9Sstevel@tonic-gate ds1287_timeout(caddr_t arg) 723*7c478bd9Sstevel@tonic-gate { 724*7c478bd9Sstevel@tonic-gate static int first = 0; 725*7c478bd9Sstevel@tonic-gate 726*7c478bd9Sstevel@tonic-gate DPRINTF("ds1287_timeout\n"); 727*7c478bd9Sstevel@tonic-gate 728*7c478bd9Sstevel@tonic-gate /* 729*7c478bd9Sstevel@tonic-gate * Abort was generated cancel all outstanding power 730*7c478bd9Sstevel@tonic-gate * button timeouts 731*7c478bd9Sstevel@tonic-gate */ 732*7c478bd9Sstevel@tonic-gate mutex_enter(&ds1287_reg_mutex); 733*7c478bd9Sstevel@tonic-gate if (power_button_cancel) { 734*7c478bd9Sstevel@tonic-gate power_button_cancel--; 735*7c478bd9Sstevel@tonic-gate power_button_timeouts--; 736*7c478bd9Sstevel@tonic-gate if (!first) { 737*7c478bd9Sstevel@tonic-gate first++; 738*7c478bd9Sstevel@tonic-gate additional_presses = 0; 739*7c478bd9Sstevel@tonic-gate } 740*7c478bd9Sstevel@tonic-gate mutex_exit(&ds1287_reg_mutex); 741*7c478bd9Sstevel@tonic-gate return; 742*7c478bd9Sstevel@tonic-gate } 743*7c478bd9Sstevel@tonic-gate first = 0; 744*7c478bd9Sstevel@tonic-gate 745*7c478bd9Sstevel@tonic-gate /* 746*7c478bd9Sstevel@tonic-gate * We get here if the timeout(s) have fired and they were 747*7c478bd9Sstevel@tonic-gate * not issued prior to an abort. 748*7c478bd9Sstevel@tonic-gate * 749*7c478bd9Sstevel@tonic-gate * If we had more than one press in the interval we were 750*7c478bd9Sstevel@tonic-gate * probably trying to issue an abort, but didnt press the 751*7c478bd9Sstevel@tonic-gate * required number within the interval. Hence cancel all 752*7c478bd9Sstevel@tonic-gate * timeouts and do not continue towards shutdown. 753*7c478bd9Sstevel@tonic-gate */ 754*7c478bd9Sstevel@tonic-gate if (!timeout_cancel) { 755*7c478bd9Sstevel@tonic-gate timeout_cancel = power_button_timeouts + 756*7c478bd9Sstevel@tonic-gate additional_presses; 757*7c478bd9Sstevel@tonic-gate 758*7c478bd9Sstevel@tonic-gate power_button_timeouts--; 759*7c478bd9Sstevel@tonic-gate if (!power_button_timeouts) 760*7c478bd9Sstevel@tonic-gate additional_presses = 0; 761*7c478bd9Sstevel@tonic-gate 762*7c478bd9Sstevel@tonic-gate if (timeout_cancel > 1) { 763*7c478bd9Sstevel@tonic-gate mutex_exit(&ds1287_reg_mutex); 764*7c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "Power Button pressed " 765*7c478bd9Sstevel@tonic-gate "%d times, cancelling all requests", 766*7c478bd9Sstevel@tonic-gate timeout_cancel); 767*7c478bd9Sstevel@tonic-gate return; 768*7c478bd9Sstevel@tonic-gate } 769*7c478bd9Sstevel@tonic-gate mutex_exit(&ds1287_reg_mutex); 770*7c478bd9Sstevel@tonic-gate 771*7c478bd9Sstevel@tonic-gate /* Go and do the work to request shutdown */ 772*7c478bd9Sstevel@tonic-gate (void) ds1287_issue_shutdown(arg); 773*7c478bd9Sstevel@tonic-gate return; 774*7c478bd9Sstevel@tonic-gate } 775*7c478bd9Sstevel@tonic-gate 776*7c478bd9Sstevel@tonic-gate power_button_timeouts--; 777*7c478bd9Sstevel@tonic-gate if (!power_button_timeouts) 778*7c478bd9Sstevel@tonic-gate additional_presses = 0; 779*7c478bd9Sstevel@tonic-gate mutex_exit(&ds1287_reg_mutex); 780*7c478bd9Sstevel@tonic-gate } 781*7c478bd9Sstevel@tonic-gate 782*7c478bd9Sstevel@tonic-gate static uint_t 783*7c478bd9Sstevel@tonic-gate ds1287_issue_shutdown(caddr_t arg) 784*7c478bd9Sstevel@tonic-gate { 785*7c478bd9Sstevel@tonic-gate struct ds1287 *softsp = (struct ds1287 *)arg; 786*7c478bd9Sstevel@tonic-gate 787*7c478bd9Sstevel@tonic-gate DPRINTF("ds1287_issue_shutdown\n"); 788*7c478bd9Sstevel@tonic-gate 789*7c478bd9Sstevel@tonic-gate mutex_enter(&softsp->ds1287_mutex); 790*7c478bd9Sstevel@tonic-gate softsp->events |= PB_BUTTON_PRESS; 791*7c478bd9Sstevel@tonic-gate if (softsp->monitor_on != 0) { 792*7c478bd9Sstevel@tonic-gate mutex_exit(&softsp->ds1287_mutex); 793*7c478bd9Sstevel@tonic-gate pollwakeup(&softsp->pollhd, POLLRDNORM); 794*7c478bd9Sstevel@tonic-gate pollwakeup(&softsp->pollhd, POLLIN); 795*7c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 796*7c478bd9Sstevel@tonic-gate } 797*7c478bd9Sstevel@tonic-gate 798*7c478bd9Sstevel@tonic-gate if (!softsp->shutdown_pending) { 799*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "Power button is pressed, powering down " 800*7c478bd9Sstevel@tonic-gate "the system!"); 801*7c478bd9Sstevel@tonic-gate softsp->shutdown_pending = 1; 802*7c478bd9Sstevel@tonic-gate do_shutdown(); 803*7c478bd9Sstevel@tonic-gate 804*7c478bd9Sstevel@tonic-gate /* 805*7c478bd9Sstevel@tonic-gate * Wait a while for "do_shutdown()" to shut down the system 806*7c478bd9Sstevel@tonic-gate * before logging an error message. 807*7c478bd9Sstevel@tonic-gate */ 808*7c478bd9Sstevel@tonic-gate (void) timeout((void(*)(void *))ds1287_log_message, NULL, 809*7c478bd9Sstevel@tonic-gate 100 * hz); 810*7c478bd9Sstevel@tonic-gate } 811*7c478bd9Sstevel@tonic-gate mutex_exit(&softsp->ds1287_mutex); 812*7c478bd9Sstevel@tonic-gate 813*7c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED); 814*7c478bd9Sstevel@tonic-gate } 815*7c478bd9Sstevel@tonic-gate 816*7c478bd9Sstevel@tonic-gate /* 817*7c478bd9Sstevel@tonic-gate * Read the current time from the clock chip and convert to UNIX form. 818*7c478bd9Sstevel@tonic-gate * Assumes that the year in the clock chip is valid. 819*7c478bd9Sstevel@tonic-gate * Must be called with tod_lock held. 820*7c478bd9Sstevel@tonic-gate */ 821*7c478bd9Sstevel@tonic-gate static timestruc_t 822*7c478bd9Sstevel@tonic-gate todds_get(void) 823*7c478bd9Sstevel@tonic-gate { 824*7c478bd9Sstevel@tonic-gate timestruc_t ts; 825*7c478bd9Sstevel@tonic-gate todinfo_t tod; 826*7c478bd9Sstevel@tonic-gate struct rtc_t rtc; 827*7c478bd9Sstevel@tonic-gate 828*7c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&tod_lock)); 829*7c478bd9Sstevel@tonic-gate 830*7c478bd9Sstevel@tonic-gate read_rtc(&rtc); 831*7c478bd9Sstevel@tonic-gate DPRINTF("todds_get: century=%d year=%d dom=%d hrs=%d\n", 832*7c478bd9Sstevel@tonic-gate rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs); 833*7c478bd9Sstevel@tonic-gate 834*7c478bd9Sstevel@tonic-gate /* 835*7c478bd9Sstevel@tonic-gate * tod_year is base 1900 so this code needs to adjust the true 836*7c478bd9Sstevel@tonic-gate * year retrieved from the rtc's century and year fields. 837*7c478bd9Sstevel@tonic-gate */ 838*7c478bd9Sstevel@tonic-gate tod.tod_year = rtc.rtc_year + (rtc.rtc_century * 100) - 1900; 839*7c478bd9Sstevel@tonic-gate tod.tod_month = rtc.rtc_mon; 840*7c478bd9Sstevel@tonic-gate tod.tod_day = rtc.rtc_dom; 841*7c478bd9Sstevel@tonic-gate tod.tod_dow = rtc.rtc_dow; 842*7c478bd9Sstevel@tonic-gate tod.tod_hour = rtc.rtc_hrs; 843*7c478bd9Sstevel@tonic-gate tod.tod_min = rtc.rtc_min; 844*7c478bd9Sstevel@tonic-gate tod.tod_sec = rtc.rtc_sec; 845*7c478bd9Sstevel@tonic-gate 846*7c478bd9Sstevel@tonic-gate ts.tv_sec = tod_to_utc(tod); 847*7c478bd9Sstevel@tonic-gate ts.tv_nsec = 0; 848*7c478bd9Sstevel@tonic-gate 849*7c478bd9Sstevel@tonic-gate /* set the hw watchdog timer if it's been activated */ 850*7c478bd9Sstevel@tonic-gate if (watchdog_activated) { 851*7c478bd9Sstevel@tonic-gate int ret = 0; 852*7c478bd9Sstevel@tonic-gate ret = tod_ops.tod_set_watchdog_timer(watchdog_timeout_seconds); 853*7c478bd9Sstevel@tonic-gate if (ret == 0) 854*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "ds1287: failed to set hardware " 855*7c478bd9Sstevel@tonic-gate "watchdog timer."); 856*7c478bd9Sstevel@tonic-gate } 857*7c478bd9Sstevel@tonic-gate 858*7c478bd9Sstevel@tonic-gate return (ts); 859*7c478bd9Sstevel@tonic-gate } 860*7c478bd9Sstevel@tonic-gate 861*7c478bd9Sstevel@tonic-gate void 862*7c478bd9Sstevel@tonic-gate read_rtc(struct rtc_t *rtc) 863*7c478bd9Sstevel@tonic-gate { 864*7c478bd9Sstevel@tonic-gate uint8_t regb; 865*7c478bd9Sstevel@tonic-gate 866*7c478bd9Sstevel@tonic-gate /* 867*7c478bd9Sstevel@tonic-gate * Some SuperIO tod devices don't seem to properly initialize 868*7c478bd9Sstevel@tonic-gate * the CADDR register to place the Century register at bank 1 869*7c478bd9Sstevel@tonic-gate * address 0x48. 870*7c478bd9Sstevel@tonic-gate */ 871*7c478bd9Sstevel@tonic-gate mutex_enter(&ds1287_reg_mutex); 872*7c478bd9Sstevel@tonic-gate 873*7c478bd9Sstevel@tonic-gate select_bank(2); 874*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_CADDR; 875*7c478bd9Sstevel@tonic-gate regb = DS1287_DATA_REG; 876*7c478bd9Sstevel@tonic-gate if (regb != 0xc8) { 877*7c478bd9Sstevel@tonic-gate if (!ds1287_caddr_warn) { 878*7c478bd9Sstevel@tonic-gate ds1287_caddr_warn = 1; 879*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "ds1287: century address register " 880*7c478bd9Sstevel@tonic-gate "incorrect (exp 0xc8, obs %x)", regb); 881*7c478bd9Sstevel@tonic-gate } 882*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = 0xc8; 883*7c478bd9Sstevel@tonic-gate } 884*7c478bd9Sstevel@tonic-gate 885*7c478bd9Sstevel@tonic-gate select_bank(1); 886*7c478bd9Sstevel@tonic-gate /* 887*7c478bd9Sstevel@tonic-gate * Freeze clock update 888*7c478bd9Sstevel@tonic-gate */ 889*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_B; 890*7c478bd9Sstevel@tonic-gate regb = DS1287_DATA_REG; 891*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = (regb | RTC_SET); 892*7c478bd9Sstevel@tonic-gate 893*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_SEC; 894*7c478bd9Sstevel@tonic-gate rtc->rtc_sec = DS1287_DATA_REG; 895*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_ASEC; 896*7c478bd9Sstevel@tonic-gate rtc->rtc_asec = DS1287_DATA_REG; 897*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_MIN; 898*7c478bd9Sstevel@tonic-gate rtc->rtc_min = DS1287_DATA_REG; 899*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_AMIN; 900*7c478bd9Sstevel@tonic-gate rtc->rtc_amin = DS1287_DATA_REG; 901*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_HRS; 902*7c478bd9Sstevel@tonic-gate rtc->rtc_hrs = DS1287_DATA_REG; 903*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_AHRS; 904*7c478bd9Sstevel@tonic-gate rtc->rtc_ahrs = DS1287_DATA_REG; 905*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_DOW; 906*7c478bd9Sstevel@tonic-gate rtc->rtc_dow = DS1287_DATA_REG; 907*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_DOM; 908*7c478bd9Sstevel@tonic-gate rtc->rtc_dom = DS1287_DATA_REG; 909*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_MON; 910*7c478bd9Sstevel@tonic-gate rtc->rtc_mon = DS1287_DATA_REG; 911*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_YEAR; 912*7c478bd9Sstevel@tonic-gate rtc->rtc_year = DS1287_DATA_REG; 913*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_CENTURY; 914*7c478bd9Sstevel@tonic-gate rtc->rtc_century = DS1287_DATA_REG; 915*7c478bd9Sstevel@tonic-gate 916*7c478bd9Sstevel@tonic-gate /* Read date alarm */ 917*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_ADOM; 918*7c478bd9Sstevel@tonic-gate rtc->rtc_adom = DS1287_DATA_REG; 919*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_AMON; 920*7c478bd9Sstevel@tonic-gate rtc->rtc_amon = DS1287_DATA_REG; 921*7c478bd9Sstevel@tonic-gate 922*7c478bd9Sstevel@tonic-gate /* Read wakeup data */ 923*7c478bd9Sstevel@tonic-gate select_bank(2); 924*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = APC_WDWR; 925*7c478bd9Sstevel@tonic-gate rtc->apc_wdwr = DS1287_DATA_REG; 926*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = APC_WDMR; 927*7c478bd9Sstevel@tonic-gate rtc->apc_wdmr = DS1287_DATA_REG; 928*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = APC_WMR; 929*7c478bd9Sstevel@tonic-gate rtc->apc_wmr = DS1287_DATA_REG; 930*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = APC_WYR; 931*7c478bd9Sstevel@tonic-gate rtc->apc_wyr = DS1287_DATA_REG; 932*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = APC_WCR; 933*7c478bd9Sstevel@tonic-gate rtc->apc_wcr = DS1287_DATA_REG; 934*7c478bd9Sstevel@tonic-gate 935*7c478bd9Sstevel@tonic-gate /* 936*7c478bd9Sstevel@tonic-gate * Unfreeze clock update 937*7c478bd9Sstevel@tonic-gate */ 938*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_B; 939*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = regb; 940*7c478bd9Sstevel@tonic-gate 941*7c478bd9Sstevel@tonic-gate mutex_exit(&ds1287_reg_mutex); 942*7c478bd9Sstevel@tonic-gate } 943*7c478bd9Sstevel@tonic-gate 944*7c478bd9Sstevel@tonic-gate /* 945*7c478bd9Sstevel@tonic-gate * Write the specified time into the clock chip. 946*7c478bd9Sstevel@tonic-gate * Must be called with tod_lock held. 947*7c478bd9Sstevel@tonic-gate */ 948*7c478bd9Sstevel@tonic-gate static void 949*7c478bd9Sstevel@tonic-gate todds_set(timestruc_t ts) 950*7c478bd9Sstevel@tonic-gate { 951*7c478bd9Sstevel@tonic-gate struct rtc_t rtc; 952*7c478bd9Sstevel@tonic-gate todinfo_t tod = utc_to_tod(ts.tv_sec); 953*7c478bd9Sstevel@tonic-gate int year; 954*7c478bd9Sstevel@tonic-gate 955*7c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&tod_lock)); 956*7c478bd9Sstevel@tonic-gate 957*7c478bd9Sstevel@tonic-gate /* tod_year is base 1900 so this code needs to adjust */ 958*7c478bd9Sstevel@tonic-gate year = 1900 + tod.tod_year; 959*7c478bd9Sstevel@tonic-gate rtc.rtc_year = year % 100; 960*7c478bd9Sstevel@tonic-gate rtc.rtc_century = year / 100; 961*7c478bd9Sstevel@tonic-gate rtc.rtc_mon = (uint8_t)tod.tod_month; 962*7c478bd9Sstevel@tonic-gate rtc.rtc_dom = (uint8_t)tod.tod_day; 963*7c478bd9Sstevel@tonic-gate rtc.rtc_dow = (uint8_t)tod.tod_dow; 964*7c478bd9Sstevel@tonic-gate rtc.rtc_hrs = (uint8_t)tod.tod_hour; 965*7c478bd9Sstevel@tonic-gate rtc.rtc_min = (uint8_t)tod.tod_min; 966*7c478bd9Sstevel@tonic-gate rtc.rtc_sec = (uint8_t)tod.tod_sec; 967*7c478bd9Sstevel@tonic-gate DPRINTF("todds_set: century=%d year=%d dom=%d hrs=%d\n", 968*7c478bd9Sstevel@tonic-gate rtc.rtc_century, rtc.rtc_year, rtc.rtc_dom, rtc.rtc_hrs); 969*7c478bd9Sstevel@tonic-gate 970*7c478bd9Sstevel@tonic-gate write_rtc_time(&rtc); 971*7c478bd9Sstevel@tonic-gate } 972*7c478bd9Sstevel@tonic-gate 973*7c478bd9Sstevel@tonic-gate void 974*7c478bd9Sstevel@tonic-gate write_rtc_time(struct rtc_t *rtc) 975*7c478bd9Sstevel@tonic-gate { 976*7c478bd9Sstevel@tonic-gate uint8_t regb; 977*7c478bd9Sstevel@tonic-gate 978*7c478bd9Sstevel@tonic-gate /* 979*7c478bd9Sstevel@tonic-gate * Some SuperIO tod devices don't seem to properly initialize 980*7c478bd9Sstevel@tonic-gate * the CADDR register to place the Century register at bank 1 981*7c478bd9Sstevel@tonic-gate * address 0x48. 982*7c478bd9Sstevel@tonic-gate */ 983*7c478bd9Sstevel@tonic-gate mutex_enter(&ds1287_reg_mutex); 984*7c478bd9Sstevel@tonic-gate 985*7c478bd9Sstevel@tonic-gate select_bank(2); 986*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_CADDR; 987*7c478bd9Sstevel@tonic-gate regb = DS1287_DATA_REG; 988*7c478bd9Sstevel@tonic-gate if (regb != 0xc8) { 989*7c478bd9Sstevel@tonic-gate if (!ds1287_caddr_warn) { 990*7c478bd9Sstevel@tonic-gate ds1287_caddr_warn = 1; 991*7c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "ds1287: century address register " 992*7c478bd9Sstevel@tonic-gate "incorrect (exp 0xc8, obs %x)", regb); 993*7c478bd9Sstevel@tonic-gate } 994*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = 0xc8; 995*7c478bd9Sstevel@tonic-gate } 996*7c478bd9Sstevel@tonic-gate 997*7c478bd9Sstevel@tonic-gate select_bank(1); 998*7c478bd9Sstevel@tonic-gate 999*7c478bd9Sstevel@tonic-gate /* 1000*7c478bd9Sstevel@tonic-gate * Freeze 1001*7c478bd9Sstevel@tonic-gate */ 1002*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_B; 1003*7c478bd9Sstevel@tonic-gate regb = DS1287_DATA_REG; 1004*7c478bd9Sstevel@tonic-gate 1005*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = (regb | RTC_SET); 1006*7c478bd9Sstevel@tonic-gate 1007*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_SEC; 1008*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->rtc_sec; 1009*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_MIN; 1010*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->rtc_min; 1011*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_HRS; 1012*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->rtc_hrs; 1013*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_DOW; 1014*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->rtc_dow; 1015*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_DOM; 1016*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->rtc_dom; 1017*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_MON; 1018*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->rtc_mon; 1019*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_YEAR; 1020*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->rtc_year; 1021*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_CENTURY; 1022*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->rtc_century; 1023*7c478bd9Sstevel@tonic-gate 1024*7c478bd9Sstevel@tonic-gate /* 1025*7c478bd9Sstevel@tonic-gate * Unfreeze 1026*7c478bd9Sstevel@tonic-gate */ 1027*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_B; 1028*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = regb; 1029*7c478bd9Sstevel@tonic-gate 1030*7c478bd9Sstevel@tonic-gate mutex_exit(&ds1287_reg_mutex); 1031*7c478bd9Sstevel@tonic-gate } 1032*7c478bd9Sstevel@tonic-gate 1033*7c478bd9Sstevel@tonic-gate void 1034*7c478bd9Sstevel@tonic-gate write_rtc_alarm(struct rtc_t *rtc) 1035*7c478bd9Sstevel@tonic-gate { 1036*7c478bd9Sstevel@tonic-gate mutex_enter(&ds1287_reg_mutex); 1037*7c478bd9Sstevel@tonic-gate 1038*7c478bd9Sstevel@tonic-gate select_bank(1); 1039*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_ASEC; 1040*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->rtc_asec; 1041*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_AMIN; 1042*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->rtc_amin; 1043*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_AHRS; 1044*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->rtc_ahrs; 1045*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_ADOM; 1046*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->rtc_adom; 1047*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_AMON; 1048*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->rtc_amon; 1049*7c478bd9Sstevel@tonic-gate 1050*7c478bd9Sstevel@tonic-gate select_bank(2); 1051*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = APC_WDWR; 1052*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->apc_wdwr; 1053*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = APC_WDMR; 1054*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->apc_wdmr; 1055*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = APC_WMR; 1056*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->apc_wmr; 1057*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = APC_WYR; 1058*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->apc_wyr; 1059*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = APC_WCR; 1060*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rtc->apc_wcr; 1061*7c478bd9Sstevel@tonic-gate 1062*7c478bd9Sstevel@tonic-gate mutex_exit(&ds1287_reg_mutex); 1063*7c478bd9Sstevel@tonic-gate } 1064*7c478bd9Sstevel@tonic-gate 1065*7c478bd9Sstevel@tonic-gate /* 1066*7c478bd9Sstevel@tonic-gate * program the rtc registers for alarm to go off at the specified time 1067*7c478bd9Sstevel@tonic-gate */ 1068*7c478bd9Sstevel@tonic-gate static void 1069*7c478bd9Sstevel@tonic-gate todds_set_power_alarm(timestruc_t ts) 1070*7c478bd9Sstevel@tonic-gate { 1071*7c478bd9Sstevel@tonic-gate todinfo_t tod; 1072*7c478bd9Sstevel@tonic-gate uint8_t apcr2; 1073*7c478bd9Sstevel@tonic-gate struct rtc_t rtc; 1074*7c478bd9Sstevel@tonic-gate 1075*7c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&tod_lock)); 1076*7c478bd9Sstevel@tonic-gate tod = utc_to_tod(ts.tv_sec); 1077*7c478bd9Sstevel@tonic-gate mutex_enter(&ds1287_reg_mutex); 1078*7c478bd9Sstevel@tonic-gate 1079*7c478bd9Sstevel@tonic-gate /* Clear Time Match Detect */ 1080*7c478bd9Sstevel@tonic-gate select_bank(2); 1081*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = APC_APSR; 1082*7c478bd9Sstevel@tonic-gate apcr2 = DS1287_DATA_REG; 1083*7c478bd9Sstevel@tonic-gate 1084*7c478bd9Sstevel@tonic-gate /* Disable Time Match Enable */ 1085*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = APC_APCR2; 1086*7c478bd9Sstevel@tonic-gate apcr2 = DS1287_DATA_REG; 1087*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = (apcr2 & (~APC_TME)); 1088*7c478bd9Sstevel@tonic-gate 1089*7c478bd9Sstevel@tonic-gate mutex_exit(&ds1287_reg_mutex); 1090*7c478bd9Sstevel@tonic-gate 1091*7c478bd9Sstevel@tonic-gate rtc.rtc_asec = (uint8_t)tod.tod_sec; 1092*7c478bd9Sstevel@tonic-gate rtc.rtc_amin = (uint8_t)tod.tod_min; 1093*7c478bd9Sstevel@tonic-gate rtc.rtc_ahrs = (uint8_t)tod.tod_hour; 1094*7c478bd9Sstevel@tonic-gate rtc.rtc_adom = (uint8_t)tod.tod_day; 1095*7c478bd9Sstevel@tonic-gate rtc.rtc_amon = (uint8_t)tod.tod_month; 1096*7c478bd9Sstevel@tonic-gate 1097*7c478bd9Sstevel@tonic-gate rtc.apc_wdwr = (uint8_t)tod.tod_dow; 1098*7c478bd9Sstevel@tonic-gate rtc.apc_wdmr = (uint8_t)tod.tod_day; 1099*7c478bd9Sstevel@tonic-gate rtc.apc_wmr = (uint8_t)tod.tod_month; 1100*7c478bd9Sstevel@tonic-gate rtc.apc_wyr = tod.tod_year % 100; 1101*7c478bd9Sstevel@tonic-gate rtc.apc_wcr = (tod.tod_year / 100) + 19; 1102*7c478bd9Sstevel@tonic-gate 1103*7c478bd9Sstevel@tonic-gate write_rtc_alarm(&rtc); 1104*7c478bd9Sstevel@tonic-gate 1105*7c478bd9Sstevel@tonic-gate mutex_enter(&ds1287_reg_mutex); 1106*7c478bd9Sstevel@tonic-gate /* Enable Time Match enable */ 1107*7c478bd9Sstevel@tonic-gate select_bank(2); 1108*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = APC_APCR2; 1109*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = (apcr2 | APC_TME); 1110*7c478bd9Sstevel@tonic-gate 1111*7c478bd9Sstevel@tonic-gate mutex_exit(&ds1287_reg_mutex); 1112*7c478bd9Sstevel@tonic-gate } 1113*7c478bd9Sstevel@tonic-gate 1114*7c478bd9Sstevel@tonic-gate /* 1115*7c478bd9Sstevel@tonic-gate * clear alarm interrupt 1116*7c478bd9Sstevel@tonic-gate */ 1117*7c478bd9Sstevel@tonic-gate static void 1118*7c478bd9Sstevel@tonic-gate todds_clear_power_alarm(void) 1119*7c478bd9Sstevel@tonic-gate { 1120*7c478bd9Sstevel@tonic-gate uint8_t apcr2; 1121*7c478bd9Sstevel@tonic-gate 1122*7c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&tod_lock)); 1123*7c478bd9Sstevel@tonic-gate 1124*7c478bd9Sstevel@tonic-gate mutex_enter(&ds1287_reg_mutex); 1125*7c478bd9Sstevel@tonic-gate 1126*7c478bd9Sstevel@tonic-gate /* Clear Time Match Detect */ 1127*7c478bd9Sstevel@tonic-gate select_bank(2); 1128*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = APC_APSR; 1129*7c478bd9Sstevel@tonic-gate apcr2 = DS1287_DATA_REG; 1130*7c478bd9Sstevel@tonic-gate 1131*7c478bd9Sstevel@tonic-gate /* Disable Time Match Enable */ 1132*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = APC_APCR2; 1133*7c478bd9Sstevel@tonic-gate apcr2 = DS1287_DATA_REG; 1134*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = (apcr2 & (~APC_TME)); 1135*7c478bd9Sstevel@tonic-gate 1136*7c478bd9Sstevel@tonic-gate mutex_exit(&ds1287_reg_mutex); 1137*7c478bd9Sstevel@tonic-gate } 1138*7c478bd9Sstevel@tonic-gate 1139*7c478bd9Sstevel@tonic-gate /* 1140*7c478bd9Sstevel@tonic-gate * Determine the cpu frequency by watching the TOD chip rollover twice. 1141*7c478bd9Sstevel@tonic-gate * Cpu clock rate is determined by computing the ticks added (in tick register) 1142*7c478bd9Sstevel@tonic-gate * during one second interval on TOD. 1143*7c478bd9Sstevel@tonic-gate */ 1144*7c478bd9Sstevel@tonic-gate uint64_t 1145*7c478bd9Sstevel@tonic-gate todds_get_cpufrequency(void) 1146*7c478bd9Sstevel@tonic-gate { 1147*7c478bd9Sstevel@tonic-gate uint64_t cpu_freq; 1148*7c478bd9Sstevel@tonic-gate 1149*7c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&tod_lock)); 1150*7c478bd9Sstevel@tonic-gate mutex_enter(&ds1287_reg_mutex); 1151*7c478bd9Sstevel@tonic-gate 1152*7c478bd9Sstevel@tonic-gate select_bank(1); 1153*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_SEC; 1154*7c478bd9Sstevel@tonic-gate cpu_freq = find_cpufrequency(v_rtc_data_reg); 1155*7c478bd9Sstevel@tonic-gate 1156*7c478bd9Sstevel@tonic-gate mutex_exit(&ds1287_reg_mutex); 1157*7c478bd9Sstevel@tonic-gate return (cpu_freq); 1158*7c478bd9Sstevel@tonic-gate } 1159*7c478bd9Sstevel@tonic-gate 1160*7c478bd9Sstevel@tonic-gate static void 1161*7c478bd9Sstevel@tonic-gate select_bank(int bank) 1162*7c478bd9Sstevel@tonic-gate { 1163*7c478bd9Sstevel@tonic-gate uint8_t rega; 1164*7c478bd9Sstevel@tonic-gate int banksel; 1165*7c478bd9Sstevel@tonic-gate 1166*7c478bd9Sstevel@tonic-gate /* Select Bank 1 */ 1167*7c478bd9Sstevel@tonic-gate DS1287_ADDR_REG = RTC_A; 1168*7c478bd9Sstevel@tonic-gate rega = DS1287_DATA_REG; 1169*7c478bd9Sstevel@tonic-gate rega = rega & ~(RTC_DIV0 | RTC_DIV1 | RTC_DIV2); 1170*7c478bd9Sstevel@tonic-gate switch (bank) { 1171*7c478bd9Sstevel@tonic-gate case 0: 1172*7c478bd9Sstevel@tonic-gate banksel = RTC_DIV1; 1173*7c478bd9Sstevel@tonic-gate break; 1174*7c478bd9Sstevel@tonic-gate case 1: 1175*7c478bd9Sstevel@tonic-gate banksel = RTC_DIV0 | RTC_DIV1; 1176*7c478bd9Sstevel@tonic-gate break; 1177*7c478bd9Sstevel@tonic-gate case 2: 1178*7c478bd9Sstevel@tonic-gate banksel = RTC_DIV2; 1179*7c478bd9Sstevel@tonic-gate break; 1180*7c478bd9Sstevel@tonic-gate } 1181*7c478bd9Sstevel@tonic-gate rega |= banksel; 1182*7c478bd9Sstevel@tonic-gate DS1287_DATA_REG = rega; 1183*7c478bd9Sstevel@tonic-gate } 1184*7c478bd9Sstevel@tonic-gate 1185*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/ 1186*7c478bd9Sstevel@tonic-gate static uint_t 1187*7c478bd9Sstevel@tonic-gate todds_set_watchdog_timer(uint_t timeoutval) 1188*7c478bd9Sstevel@tonic-gate { 1189*7c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&tod_lock)); 1190*7c478bd9Sstevel@tonic-gate return (0); 1191*7c478bd9Sstevel@tonic-gate } 1192*7c478bd9Sstevel@tonic-gate 1193*7c478bd9Sstevel@tonic-gate static uint_t 1194*7c478bd9Sstevel@tonic-gate todds_clear_watchdog_timer(void) 1195*7c478bd9Sstevel@tonic-gate { 1196*7c478bd9Sstevel@tonic-gate ASSERT(MUTEX_HELD(&tod_lock)); 1197*7c478bd9Sstevel@tonic-gate return (0); 1198*7c478bd9Sstevel@tonic-gate } 1199