1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 2001 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Driver for the Power Management Controller (logical unit 8) of the 31 * PC87317 SuperI/O chip. The PMC contains the hardware watchdog timer. 32 */ 33 34 #include <sys/types.h> 35 #include <sys/time.h> 36 #include <sys/cmn_err.h> 37 #include <sys/param.h> 38 #include <sys/modctl.h> 39 #include <sys/conf.h> 40 #include <sys/stat.h> 41 #include <sys/clock.h> 42 #include <sys/reboot.h> 43 #include <sys/ddi.h> 44 #include <sys/sunddi.h> 45 #include <sys/file.h> 46 #include <sys/note.h> 47 48 #ifdef DEBUG 49 int pmc_debug_flag = 0; 50 #define DPRINTF(ARGLIST) if (pmc_debug_flag) printf ARGLIST; 51 #else 52 #define DPRINTF(ARGLIST) 53 #endif /* DEBUG */ 54 55 /* Driver soft state structure */ 56 typedef struct pmc { 57 dev_info_t *dip; 58 ddi_acc_handle_t pmc_handle; 59 } pmc_t; 60 61 static void *pmc_soft_state; 62 static int instance = -1; 63 64 /* dev_ops and cb_ops entry point function declarations */ 65 static int pmc_attach(dev_info_t *, ddi_attach_cmd_t); 66 static int pmc_detach(dev_info_t *, ddi_detach_cmd_t); 67 static int pmc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 68 69 /* hardware watchdog parameters */ 70 static uint_t pmc_set_watchdog_timer(uint_t); 71 static uint_t pmc_clear_watchdog_timer(void); 72 73 extern volatile uint8_t *v_pmc_addr_reg; 74 extern volatile uint8_t *v_pmc_data_reg; 75 extern int watchdog_enable; 76 extern int watchdog_available; 77 extern int watchdog_activated; 78 extern int boothowto; 79 extern uint_t watchdog_timeout_seconds; 80 81 /* 82 * Power Management Registers and values 83 */ 84 #define PMC_WDTO 0x05 /* Watchdog Time Out */ 85 #define PMC_CLEAR_WDTO 0x00 86 87 struct cb_ops pmc_cb_ops = { 88 nodev, 89 nodev, 90 nodev, 91 nodev, 92 nodev, /* dump */ 93 nodev, 94 nodev, 95 nodev, 96 nodev, /* devmap */ 97 nodev, 98 nodev, 99 nochpoll, 100 ddi_prop_op, 101 NULL, /* for STREAMS drivers */ 102 D_NEW | D_MP, /* driver compatibility flag */ 103 CB_REV, 104 nodev, 105 nodev 106 }; 107 108 static struct dev_ops pmc_dev_ops = { 109 DEVO_REV, /* driver build version */ 110 0, /* device reference count */ 111 pmc_getinfo, 112 nulldev, 113 nulldev, /* probe */ 114 pmc_attach, 115 pmc_detach, 116 nulldev, /* reset */ 117 &pmc_cb_ops, 118 (struct bus_ops *)NULL, 119 nulldev /* power */ 120 }; 121 122 /* module configuration stuff */ 123 extern struct mod_ops mod_driverops; 124 static struct modldrv modldrv = { 125 &mod_driverops, 126 "pmc driver %I%", 127 &pmc_dev_ops 128 }; 129 static struct modlinkage modlinkage = { 130 MODREV_1, 131 &modldrv, 132 0 133 }; 134 135 136 int 137 _init(void) 138 { 139 int e; 140 141 e = ddi_soft_state_init(&pmc_soft_state, sizeof (pmc_t), 1); 142 if (e != 0) { 143 DPRINTF(("_init: ddi_soft_state_init failed\n")); 144 return (e); 145 } 146 147 e = mod_install(&modlinkage); 148 if (e != 0) { 149 DPRINTF(("_init: mod_install failed\n")); 150 ddi_soft_state_fini(&pmc_soft_state); 151 return (e); 152 } 153 154 if (v_pmc_addr_reg != NULL) { 155 tod_ops.tod_set_watchdog_timer = pmc_set_watchdog_timer; 156 tod_ops.tod_clear_watchdog_timer = pmc_clear_watchdog_timer; 157 158 /* 159 * See if the user has enabled the watchdog timer, and if 160 * it's available. 161 */ 162 if (watchdog_enable) { 163 if (!watchdog_available) { 164 cmn_err(CE_WARN, "pmc: Hardware watchdog " 165 "unavailable"); 166 } else if (boothowto & RB_DEBUG) { 167 watchdog_available = 0; 168 cmn_err(CE_WARN, "pmc: kernel debugger " 169 "detected: hardware watchdog disabled"); 170 } 171 } 172 } 173 return (e); 174 } 175 176 int 177 _fini(void) 178 { 179 int e; 180 181 if (v_pmc_addr_reg != NULL) 182 return (DDI_FAILURE); 183 else { 184 e = mod_remove(&modlinkage); 185 if (e != 0) 186 return (e); 187 188 ddi_soft_state_fini(&pmc_soft_state); 189 return (DDI_SUCCESS); 190 } 191 } 192 193 194 int 195 _info(struct modinfo *modinfop) 196 { 197 return (mod_info(&modlinkage, modinfop)); 198 } 199 200 static int 201 pmc_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 202 { 203 _NOTE(ARGUNUSED(dip)) 204 205 pmc_t *pmcp; 206 int instance; 207 208 switch (cmd) { 209 case DDI_INFO_DEVT2DEVINFO: 210 instance = getminor((dev_t)arg); 211 pmcp = (pmc_t *)ddi_get_soft_state(pmc_soft_state, instance); 212 if (pmcp == NULL) { 213 *result = (void *)NULL; 214 return (DDI_FAILURE); 215 } 216 *result = (void *)pmcp->dip; 217 return (DDI_SUCCESS); 218 219 case DDI_INFO_DEVT2INSTANCE: 220 *result = (void *)getminor((dev_t)arg); 221 return (DDI_SUCCESS); 222 223 default: 224 return (DDI_FAILURE); 225 } 226 } 227 228 229 static int 230 pmc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 231 { 232 pmc_t *pmcp; 233 uint_t wd_timout; 234 235 switch (cmd) { 236 case DDI_ATTACH: 237 break; 238 case DDI_RESUME: 239 if (v_pmc_addr_reg != NULL && watchdog_enable) { 240 int ret = 0; 241 wd_timout = watchdog_timeout_seconds; 242 mutex_enter(&tod_lock); 243 ret = tod_ops.tod_set_watchdog_timer(wd_timout); 244 mutex_exit(&tod_lock); 245 if (ret == 0) 246 return (DDI_FAILURE); 247 } 248 return (DDI_SUCCESS); 249 default: 250 return (DDI_FAILURE); 251 } 252 253 if (instance != -1) { 254 DPRINTF(("pmc_attach: Another instance is already attached.")); 255 return (DDI_FAILURE); 256 } 257 258 instance = ddi_get_instance(dip); 259 260 if (ddi_soft_state_zalloc(pmc_soft_state, instance) != DDI_SUCCESS) { 261 DPRINTF(("pmc_attach: Failed to allocate soft state.")); 262 return (DDI_FAILURE); 263 } 264 265 pmcp = (pmc_t *)ddi_get_soft_state(pmc_soft_state, instance); 266 pmcp->dip = dip; 267 268 return (DDI_SUCCESS); 269 } 270 271 static int 272 pmc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 273 { 274 _NOTE(ARGUNUSED(dip)) 275 276 pmc_t *pmcp; 277 278 switch (cmd) { 279 case DDI_DETACH: 280 /* allow detach if no hardware watchdog */ 281 if (v_pmc_addr_reg == NULL || !watchdog_activated) { 282 pmcp = (pmc_t *)ddi_get_soft_state(pmc_soft_state, 283 instance); 284 if (pmcp == NULL) 285 return (ENXIO); 286 ddi_soft_state_free(pmc_soft_state, instance); 287 return (DDI_SUCCESS); 288 } else 289 return (DDI_FAILURE); 290 case DDI_SUSPEND: 291 if (v_pmc_addr_reg != NULL && watchdog_activated) { 292 mutex_enter(&tod_lock); 293 (void) tod_ops.tod_clear_watchdog_timer(); 294 mutex_exit(&tod_lock); 295 } 296 return (DDI_SUCCESS); 297 default: 298 return (DDI_FAILURE); 299 } 300 301 } 302 303 /* 304 * Set the hardware watchdog timer; returning what we set it to. 305 */ 306 static uint_t 307 pmc_set_watchdog_timer(uint_t timeoutval) 308 { 309 uint_t timeoutval_minutes; 310 ASSERT(MUTEX_HELD(&tod_lock)); 311 312 /* sanity checks */ 313 if (watchdog_enable == 0 || watchdog_available == 0 || 314 timeoutval == 0) 315 return (0); 316 317 /* 318 * Historically the timer has been counted out in seconds. 319 * The PC87317 counts the timeout in minutes. The default 320 * timeout is 10 seconds; the least we can do is one minute. 321 */ 322 timeoutval_minutes = (timeoutval + 59) / 60; 323 if (timeoutval_minutes > UINT8_MAX) 324 return (0); 325 326 *v_pmc_addr_reg = (uint8_t)PMC_WDTO; 327 *v_pmc_data_reg = (uint8_t)timeoutval_minutes; 328 watchdog_activated = 1; 329 330 /* we'll still return seconds */ 331 return (timeoutval_minutes * 60); 332 } 333 334 /* 335 * Clear the hardware watchdog timer; returning what it was set to. 336 */ 337 static uint_t 338 pmc_clear_watchdog_timer(void) 339 { 340 uint_t wd_timeout; 341 342 ASSERT(MUTEX_HELD(&tod_lock)); 343 if (watchdog_activated == 0) 344 return (0); 345 346 *v_pmc_addr_reg = (uint8_t)PMC_WDTO; 347 wd_timeout = (uint_t)*v_pmc_data_reg; 348 *v_pmc_data_reg = (uint8_t)PMC_CLEAR_WDTO; 349 watchdog_activated = 0; 350 351 /* return seconds */ 352 return (wd_timeout * 60); 353 } 354