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