166830Sobrien/* $NetBSD: power.c,v 1.3 2019/04/15 20:40:37 skrll Exp $ */ 266830Sobrien 366830Sobrien/* 466830Sobrien * Copyright (c) 2004 Jochen Kunz. 566830Sobrien * All rights reserved. 666830Sobrien * 766830Sobrien * Redistribution and use in source and binary forms, with or without 866830Sobrien * modification, are permitted provided that the following conditions 966830Sobrien * are met: 1066830Sobrien * 1. Redistributions of source code must retain the above copyright 1166830Sobrien * notice, this list of conditions and the following disclaimer. 1266830Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1366830Sobrien * notice, this list of conditions and the following disclaimer in the 1466830Sobrien * documentation and/or other materials provided with the distribution. 1566830Sobrien * 3. The name of Jochen Kunz may not be used to endorse or promote 1666830Sobrien * products derived from this software without specific prior 1766830Sobrien * written permission. 1866830Sobrien * 1966830Sobrien * THIS SOFTWARE IS PROVIDED BY JOCHEN KUNZ 2066830Sobrien * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2166830Sobrien * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2266830Sobrien * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JOCHEN KUNZ 2366830Sobrien * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2466830Sobrien * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25100280Sgordon * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2651231Ssheldonh * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2766830Sobrien * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2866830Sobrien * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2943803Sdillon * POSSIBILITY OF SUCH DAMAGE. 30127345Sbrooks */ 31240336Sobrien 32136224Smtm/* $OpenBSD: power.c,v 1.5 2004/06/11 12:53:09 mickey Exp $ */ 3375931Simp 34127345Sbrooks/* 3576409Sbsd * Copyright (c) 2003 Michael Shalayeff 36127345Sbrooks * All rights reserved. 37174464Sdougb * 3876409Sbsd * Redistribution and use in source and binary forms, with or without 39127345Sbrooks * modification, are permitted provided that the following conditions 4043803Sdillon * are met: 41108191Sdillon * 1. Redistributions of source code must retain the above copyright 42108191Sdillon * notice, this list of conditions and the following disclaimer. 43108191Sdillon * 2. Redistributions in binary form must reproduce the above copyright 44225670Shrs * notice, this list of conditions and the following disclaimer in the 45225670Shrs * documentation and/or other materials provided with the distribution. 46225670Shrs * 47127345Sbrooks * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 48225670Shrs * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 49127345Sbrooks * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 50127345Sbrooks * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, 51170198Sdougb * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 52170198Sdougb * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 53225670Shrs * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 54170198Sdougb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 55225670Shrs * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 56108191Sdillon * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 57108191Sdillon * THE POSSIBILITY OF SUCH DAMAGE. 58 */ 59 60#include <sys/param.h> 61#include <sys/kernel.h> 62#include <sys/systm.h> 63#include <sys/reboot.h> 64#include <sys/device.h> 65#include <sys/sysctl.h> 66#include <sys/kmem.h> 67 68#include <machine/reg.h> 69#include <machine/pdc.h> 70#include <machine/autoconf.h> 71 72#include <hppa/dev/cpudevs.h> 73 74#include <dev/sysmon/sysmon_taskq.h> 75#include <dev/sysmon/sysmonvar.h> 76 77/* Enable / disable control over the power switch. */ 78#define PWR_SW_CTRL_DISABLE 0 79#define PWR_SW_CTRL_ENABLE 1 80#define PWR_SW_CTRL_LOCK 2 81#define PWR_SW_CTRL_MAX PWR_SW_CTRL_LOCK 82 83struct power_softc { 84 device_t sc_dev; 85 bus_space_tag_t sc_bst; 86 bus_space_handle_t sc_bsh; 87 88 void (*sc_kicker)(void *); 89 90 struct callout sc_callout; 91 int sc_timeout; 92 93 int sc_dr_cnt; 94}; 95 96int powermatch(device_t, cfdata_t, void *); 97void powerattach(device_t, device_t, void *); 98 99CFATTACH_DECL_NEW(power, sizeof(struct power_softc), 100 powermatch, powerattach, NULL, NULL); 101 102static struct pdc_power_info pdc_power_info; 103static bool pswitch_on; /* power switch */ 104static int pwr_sw_control; 105static const char *pwr_sw_control_str[] = {"disabled", "enabled", "locked"}; 106static struct sysmon_pswitch *pwr_sw_sysmon; 107 108static int pwr_sw_sysctl_state(SYSCTLFN_PROTO); 109static int pwr_sw_sysctl_ctrl(SYSCTLFN_PROTO); 110static void pwr_sw_sysmon_cb(void *); 111static void pwr_sw_ctrl(int); 112static int pwr_sw_init(struct power_softc *); 113 114void power_thread_dr(void *v); 115void power_thread_reg(void *v); 116void power_cold_hook_reg(int); 117 118int 119powermatch(device_t parent, cfdata_t cf, void *aux) 120{ 121 struct confargs *ca = aux; 122 123 if (cf->cf_unit > 0 && !strcmp(ca->ca_name, "power")) 124 return (0); 125 126 return (1); 127} 128 129void 130powerattach(device_t parent, device_t self, void *aux) 131{ 132 struct power_softc *sc = device_private(self); 133 struct confargs *ca = aux; 134 int err; 135 136 sc->sc_dev = self; 137 sc->sc_kicker = NULL; 138 139 err = pdcproc_soft_power_info(&pdc_power_info); 140 141 if (!err) 142 ca->ca_hpa = pdc_power_info.addr; 143 144 switch (cpu_modelno) { 145 case HPPA_BOARD_HP712_60: 146 case HPPA_BOARD_HP712_80: 147 case HPPA_BOARD_HP712_100: 148 case HPPA_BOARD_HP712_120: 149 sc->sc_kicker = power_thread_dr; 150 151 /* Diag Reg. needs software dampening, poll at 0.2 Hz.*/ 152 sc->sc_timeout = hz / 5; 153 154 aprint_normal(": DR25\n"); 155 break; 156 157 default: 158 if (ca->ca_hpa) { 159 sc->sc_bst = ca->ca_iot; 160 if (bus_space_map(sc->sc_bst, ca->ca_hpa, 4, 0, 161 &sc->sc_bsh) != 0) 162 aprint_error_dev(self, 163 "Can't map power switch status reg.\n"); 164 165 cold_hook = power_cold_hook_reg; 166 sc->sc_kicker = power_thread_reg; 167 168 /* Power Reg. is hardware dampened, poll at 1 Hz. */ 169 sc->sc_timeout = hz; 170 171 aprint_normal("\n"); 172 } else 173 aprint_normal(": not available\n"); 174 break; 175 } 176 177 if (sc->sc_kicker) { 178 if (pwr_sw_init(sc)) 179 return; 180 181 pswitch_on = true; 182 pwr_sw_control = PWR_SW_CTRL_ENABLE; 183 } 184} 185 186/* 187 * If the power switch is turned off we schedule a sysmon task 188 * to register that event for this power switch device. 189 */ 190static void 191check_pwr_state(struct power_softc *sc) 192{ 193 if (pswitch_on == false && pwr_sw_control != PWR_SW_CTRL_LOCK) 194 sysmon_task_queue_sched(0, pwr_sw_sysmon_cb, NULL); 195 else 196 callout_reset(&sc->sc_callout, sc->sc_timeout, 197 sc->sc_kicker, sc); 198} 199 200void 201power_thread_dr(void *v) 202{ 203 struct power_softc *sc = v; 204 uint32_t r; 205 206 /* Get Power Fail status from CPU Diagnose Register 25 */ 207 mfcpu(25, r); 208 209 /* 210 * On power failure, the hardware clears bit DR25_PCXL_POWFAIL 211 * in CPU Diagnose Register 25. 212 */ 213 if (r & (1 << DR25_PCXL_POWFAIL)) 214 sc->sc_dr_cnt = 0; 215 else 216 sc->sc_dr_cnt++; 217 218 /* 219 * the bit is undampened straight wire from the power 220 * switch and thus we have do dampen it ourselves. 221 */ 222 if (sc->sc_dr_cnt == sc->sc_timeout) 223 pswitch_on = false; 224 else 225 pswitch_on = true; 226 227 check_pwr_state(sc); 228} 229 230void 231power_thread_reg(void *v) 232{ 233 struct power_softc *sc = v; 234 uint32_t r; 235 236 r = bus_space_read_4(sc->sc_bst, sc->sc_bsh, 0); 237 238 if (!(r & 1)) 239 pswitch_on = false; 240 else 241 pswitch_on = true; 242 243 check_pwr_state(sc); 244} 245 246void 247power_cold_hook_reg(int on) 248{ 249 int error; 250 251 error = pdcproc_soft_power_enable(on == HPPA_COLD_HOT); 252 if (error) 253 aprint_error("PDC_SOFT_POWER_ENABLE failed (%d)\n", error); 254} 255 256static int 257pwr_sw_init(struct power_softc *sc) 258{ 259 struct sysctllog *sysctl_log = NULL; 260 const struct sysctlnode *pwr_sw_node; 261 const char *errmsg; 262 int error = EINVAL; 263 264 /* 265 * Ensure that we are on a PCX-L / PA7100LC CPU if it is a 266 * 712 style machine. 267 */ 268 if (pdc_power_info.addr == 0 && hppa_cpu_info->hci_cputype != hpcxl) { 269 aprint_error_dev(sc->sc_dev, "No soft power available.\n"); 270 return error; 271 } 272 273 errmsg = "Can't create sysctl machdep.power_switch (or children)\n"; 274 error = sysctl_createv(&sysctl_log, 0, NULL, NULL, 0, 275 CTLTYPE_NODE, "machdep", NULL, NULL, 0, NULL, 0, 276 CTL_MACHDEP, CTL_EOL); 277 278 if (error) 279 goto err_sysctl; 280 281 error = sysctl_createv(&sysctl_log, 0, NULL, &pwr_sw_node, 0, 282 CTLTYPE_NODE, "power_switch", NULL, NULL, 0, NULL, 0, 283 CTL_MACHDEP, CTL_CREATE, CTL_EOL); 284 285 if (error) 286 goto err_sysctl; 287 288 error = sysctl_createv(&sysctl_log, 0, NULL, NULL, 289 CTLFLAG_READONLY, CTLTYPE_STRING, "state", NULL, 290 pwr_sw_sysctl_state, 0, NULL, 16, 291 CTL_MACHDEP, pwr_sw_node->sysctl_num, CTL_CREATE, CTL_EOL); 292 293 if (error) 294 goto err_sysctl; 295 296 error = sysctl_createv(&sysctl_log, 0, NULL, NULL, 297 CTLFLAG_READWRITE, CTLTYPE_STRING, "control", NULL, 298 pwr_sw_sysctl_ctrl, 0, NULL, 16, 299 CTL_MACHDEP, pwr_sw_node->sysctl_num, CTL_CREATE, CTL_EOL); 300 301 if (error) 302 goto err_sysctl; 303 304 errmsg = "Can't alloc sysmon power switch.\n"; 305 pwr_sw_sysmon = kmem_zalloc(sizeof(*pwr_sw_sysmon), KM_SLEEP); 306 errmsg = "Can't register power switch with sysmon.\n"; 307 sysmon_task_queue_init(); 308 pwr_sw_sysmon->smpsw_name = "power switch"; 309 pwr_sw_sysmon->smpsw_type = PSWITCH_TYPE_POWER; 310 error = sysmon_pswitch_register(pwr_sw_sysmon); 311 312 if (error) 313 goto err_sysmon; 314 315 callout_init(&sc->sc_callout, 0); 316 callout_reset(&sc->sc_callout, sc->sc_timeout, sc->sc_kicker, sc); 317 318 return error; 319 320err_sysmon: 321 kmem_free(pwr_sw_sysmon, sizeof(*pwr_sw_sysmon)); 322 323err_sysctl: 324 sysctl_teardown(&sysctl_log); 325 326 aprint_error_dev(sc->sc_dev, errmsg); 327 328 return error; 329} 330 331static void 332pwr_sw_sysmon_cb(void *not_used) 333{ 334 sysmon_pswitch_event(pwr_sw_sysmon, PSWITCH_EVENT_PRESSED); 335} 336 337static void 338pwr_sw_ctrl(int enable) 339{ 340 int on; 341 342#ifdef DEBUG 343 printf("pwr_sw_control=%d enable=%d\n", pwr_sw_control, enable); 344#endif /* DEBUG */ 345 346 if (cold_hook == NULL) 347 return; 348 349 switch(enable) { 350 case PWR_SW_CTRL_DISABLE: 351 on = HPPA_COLD_OFF; 352 break; 353 case PWR_SW_CTRL_ENABLE: 354 case PWR_SW_CTRL_LOCK: 355 on = HPPA_COLD_HOT; 356 break; 357 default: 358 panic("invalid power state in pwr_sw_control: %d", enable); 359 } 360 361 pwr_sw_control = enable; 362 363 if (cold_hook) 364 (*cold_hook)(on); 365} 366 367int 368pwr_sw_sysctl_state(SYSCTLFN_ARGS) 369{ 370 struct sysctlnode node; 371 const char *status; 372 373 if (pswitch_on == true) 374 status = "on"; 375 else 376 status = "off"; 377 378 node = *rnode; 379 node.sysctl_data = __UNCONST(status); 380 return sysctl_lookup(SYSCTLFN_CALL(&node)); 381} 382 383int 384pwr_sw_sysctl_ctrl(SYSCTLFN_ARGS) 385{ 386 struct sysctlnode node; 387 int i, error; 388 char val[16]; 389 390 node = *rnode; 391 strcpy(val, pwr_sw_control_str[pwr_sw_control]); 392 393 node.sysctl_data = val; 394 395 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 396 397 if (error || newp == NULL) 398 return error; 399 400 for (i = 0; i <= PWR_SW_CTRL_MAX; i++) 401 if (strcmp(val, pwr_sw_control_str[i]) == 0) { 402 pwr_sw_ctrl(i); 403 return 0; 404 } 405 406 return EINVAL; 407} 408