power.c revision 1.3
11.3Sskrll/* $NetBSD: power.c,v 1.3 2019/04/15 20:40:37 skrll Exp $ */ 21.1Sskrll 31.1Sskrll/* 41.1Sskrll * Copyright (c) 2004 Jochen Kunz. 51.1Sskrll * All rights reserved. 61.1Sskrll * 71.1Sskrll * Redistribution and use in source and binary forms, with or without 81.1Sskrll * modification, are permitted provided that the following conditions 91.1Sskrll * are met: 101.1Sskrll * 1. Redistributions of source code must retain the above copyright 111.1Sskrll * notice, this list of conditions and the following disclaimer. 121.1Sskrll * 2. Redistributions in binary form must reproduce the above copyright 131.1Sskrll * notice, this list of conditions and the following disclaimer in the 141.1Sskrll * documentation and/or other materials provided with the distribution. 151.1Sskrll * 3. The name of Jochen Kunz may not be used to endorse or promote 161.1Sskrll * products derived from this software without specific prior 171.1Sskrll * written permission. 181.1Sskrll * 191.1Sskrll * THIS SOFTWARE IS PROVIDED BY JOCHEN KUNZ 201.1Sskrll * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 211.1Sskrll * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 221.1Sskrll * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JOCHEN KUNZ 231.1Sskrll * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 241.1Sskrll * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 251.1Sskrll * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 261.1Sskrll * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 271.1Sskrll * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 281.1Sskrll * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 291.1Sskrll * POSSIBILITY OF SUCH DAMAGE. 301.1Sskrll */ 311.1Sskrll 321.1Sskrll/* $OpenBSD: power.c,v 1.5 2004/06/11 12:53:09 mickey Exp $ */ 331.1Sskrll 341.1Sskrll/* 351.1Sskrll * Copyright (c) 2003 Michael Shalayeff 361.1Sskrll * All rights reserved. 371.1Sskrll * 381.1Sskrll * Redistribution and use in source and binary forms, with or without 391.1Sskrll * modification, are permitted provided that the following conditions 401.1Sskrll * are met: 411.1Sskrll * 1. Redistributions of source code must retain the above copyright 421.1Sskrll * notice, this list of conditions and the following disclaimer. 431.1Sskrll * 2. Redistributions in binary form must reproduce the above copyright 441.1Sskrll * notice, this list of conditions and the following disclaimer in the 451.1Sskrll * documentation and/or other materials provided with the distribution. 461.1Sskrll * 471.1Sskrll * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 481.1Sskrll * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 491.1Sskrll * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 501.1Sskrll * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT, 511.1Sskrll * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 521.1Sskrll * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 531.1Sskrll * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 541.1Sskrll * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 551.1Sskrll * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 561.1Sskrll * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 571.1Sskrll * THE POSSIBILITY OF SUCH DAMAGE. 581.1Sskrll */ 591.1Sskrll 601.1Sskrll#include <sys/param.h> 611.1Sskrll#include <sys/kernel.h> 621.1Sskrll#include <sys/systm.h> 631.1Sskrll#include <sys/reboot.h> 641.1Sskrll#include <sys/device.h> 651.1Sskrll#include <sys/sysctl.h> 661.1Sskrll#include <sys/kmem.h> 671.1Sskrll 681.1Sskrll#include <machine/reg.h> 691.1Sskrll#include <machine/pdc.h> 701.1Sskrll#include <machine/autoconf.h> 711.1Sskrll 721.1Sskrll#include <hppa/dev/cpudevs.h> 731.1Sskrll 741.1Sskrll#include <dev/sysmon/sysmon_taskq.h> 751.1Sskrll#include <dev/sysmon/sysmonvar.h> 761.1Sskrll 771.1Sskrll/* Enable / disable control over the power switch. */ 781.1Sskrll#define PWR_SW_CTRL_DISABLE 0 791.1Sskrll#define PWR_SW_CTRL_ENABLE 1 801.1Sskrll#define PWR_SW_CTRL_LOCK 2 811.1Sskrll#define PWR_SW_CTRL_MAX PWR_SW_CTRL_LOCK 821.1Sskrll 831.1Sskrllstruct power_softc { 841.1Sskrll device_t sc_dev; 851.1Sskrll bus_space_tag_t sc_bst; 861.1Sskrll bus_space_handle_t sc_bsh; 871.1Sskrll 881.1Sskrll void (*sc_kicker)(void *); 891.1Sskrll 901.1Sskrll struct callout sc_callout; 911.1Sskrll int sc_timeout; 921.1Sskrll 931.1Sskrll int sc_dr_cnt; 941.1Sskrll}; 951.1Sskrll 961.1Sskrllint powermatch(device_t, cfdata_t, void *); 971.1Sskrllvoid powerattach(device_t, device_t, void *); 981.1Sskrll 991.1SskrllCFATTACH_DECL_NEW(power, sizeof(struct power_softc), 1001.1Sskrll powermatch, powerattach, NULL, NULL); 1011.1Sskrll 1021.1Sskrllstatic struct pdc_power_info pdc_power_info; 1031.1Sskrllstatic bool pswitch_on; /* power switch */ 1041.1Sskrllstatic int pwr_sw_control; 1051.1Sskrllstatic const char *pwr_sw_control_str[] = {"disabled", "enabled", "locked"}; 1061.1Sskrllstatic struct sysmon_pswitch *pwr_sw_sysmon; 1071.1Sskrll 1081.1Sskrllstatic int pwr_sw_sysctl_state(SYSCTLFN_PROTO); 1091.1Sskrllstatic int pwr_sw_sysctl_ctrl(SYSCTLFN_PROTO); 1101.1Sskrllstatic void pwr_sw_sysmon_cb(void *); 1111.1Sskrllstatic void pwr_sw_ctrl(int); 1121.1Sskrllstatic int pwr_sw_init(struct power_softc *); 1131.1Sskrll 1141.1Sskrllvoid power_thread_dr(void *v); 1151.1Sskrllvoid power_thread_reg(void *v); 1161.1Sskrllvoid power_cold_hook_reg(int); 1171.1Sskrll 1181.1Sskrllint 1191.1Sskrllpowermatch(device_t parent, cfdata_t cf, void *aux) 1201.1Sskrll{ 1211.1Sskrll struct confargs *ca = aux; 1221.1Sskrll 1231.1Sskrll if (cf->cf_unit > 0 && !strcmp(ca->ca_name, "power")) 1241.1Sskrll return (0); 1251.1Sskrll 1261.1Sskrll return (1); 1271.1Sskrll} 1281.1Sskrll 1291.1Sskrllvoid 1301.1Sskrllpowerattach(device_t parent, device_t self, void *aux) 1311.1Sskrll{ 1321.1Sskrll struct power_softc *sc = device_private(self); 1331.1Sskrll struct confargs *ca = aux; 1341.1Sskrll int err; 1351.1Sskrll 1361.1Sskrll sc->sc_dev = self; 1371.1Sskrll sc->sc_kicker = NULL; 1381.1Sskrll 1391.1Sskrll err = pdcproc_soft_power_info(&pdc_power_info); 1401.1Sskrll 1411.1Sskrll if (!err) 1421.1Sskrll ca->ca_hpa = pdc_power_info.addr; 1431.1Sskrll 1441.1Sskrll switch (cpu_modelno) { 1451.1Sskrll case HPPA_BOARD_HP712_60: 1461.1Sskrll case HPPA_BOARD_HP712_80: 1471.1Sskrll case HPPA_BOARD_HP712_100: 1481.1Sskrll case HPPA_BOARD_HP712_120: 1491.1Sskrll sc->sc_kicker = power_thread_dr; 1501.1Sskrll 1511.1Sskrll /* Diag Reg. needs software dampening, poll at 0.2 Hz.*/ 1521.1Sskrll sc->sc_timeout = hz / 5; 1531.1Sskrll 1541.1Sskrll aprint_normal(": DR25\n"); 1551.1Sskrll break; 1561.1Sskrll 1571.1Sskrll default: 1581.1Sskrll if (ca->ca_hpa) { 1591.1Sskrll sc->sc_bst = ca->ca_iot; 1601.1Sskrll if (bus_space_map(sc->sc_bst, ca->ca_hpa, 4, 0, 1611.1Sskrll &sc->sc_bsh) != 0) 1621.1Sskrll aprint_error_dev(self, 1631.1Sskrll "Can't map power switch status reg.\n"); 1641.1Sskrll 1651.1Sskrll cold_hook = power_cold_hook_reg; 1661.1Sskrll sc->sc_kicker = power_thread_reg; 1671.1Sskrll 1681.1Sskrll /* Power Reg. is hardware dampened, poll at 1 Hz. */ 1691.1Sskrll sc->sc_timeout = hz; 1701.1Sskrll 1711.1Sskrll aprint_normal("\n"); 1721.1Sskrll } else 1731.1Sskrll aprint_normal(": not available\n"); 1741.1Sskrll break; 1751.1Sskrll } 1761.1Sskrll 1771.1Sskrll if (sc->sc_kicker) { 1781.1Sskrll if (pwr_sw_init(sc)) 1791.1Sskrll return; 1801.1Sskrll 1811.1Sskrll pswitch_on = true; 1821.1Sskrll pwr_sw_control = PWR_SW_CTRL_ENABLE; 1831.1Sskrll } 1841.1Sskrll} 1851.1Sskrll 1861.1Sskrll/* 1871.1Sskrll * If the power switch is turned off we schedule a sysmon task 1881.1Sskrll * to register that event for this power switch device. 1891.1Sskrll */ 1901.1Sskrllstatic void 1911.1Sskrllcheck_pwr_state(struct power_softc *sc) 1921.1Sskrll{ 1931.1Sskrll if (pswitch_on == false && pwr_sw_control != PWR_SW_CTRL_LOCK) 1941.1Sskrll sysmon_task_queue_sched(0, pwr_sw_sysmon_cb, NULL); 1951.1Sskrll else 1961.1Sskrll callout_reset(&sc->sc_callout, sc->sc_timeout, 1971.1Sskrll sc->sc_kicker, sc); 1981.1Sskrll} 1991.1Sskrll 2001.1Sskrllvoid 2011.1Sskrllpower_thread_dr(void *v) 2021.1Sskrll{ 2031.1Sskrll struct power_softc *sc = v; 2041.1Sskrll uint32_t r; 2051.1Sskrll 2061.1Sskrll /* Get Power Fail status from CPU Diagnose Register 25 */ 2071.1Sskrll mfcpu(25, r); 2081.1Sskrll 2091.1Sskrll /* 2101.1Sskrll * On power failure, the hardware clears bit DR25_PCXL_POWFAIL 2111.1Sskrll * in CPU Diagnose Register 25. 2121.1Sskrll */ 2131.1Sskrll if (r & (1 << DR25_PCXL_POWFAIL)) 2141.1Sskrll sc->sc_dr_cnt = 0; 2151.1Sskrll else 2161.1Sskrll sc->sc_dr_cnt++; 2171.1Sskrll 2181.1Sskrll /* 2191.1Sskrll * the bit is undampened straight wire from the power 2201.1Sskrll * switch and thus we have do dampen it ourselves. 2211.1Sskrll */ 2221.1Sskrll if (sc->sc_dr_cnt == sc->sc_timeout) 2231.1Sskrll pswitch_on = false; 2241.1Sskrll else 2251.1Sskrll pswitch_on = true; 2261.1Sskrll 2271.1Sskrll check_pwr_state(sc); 2281.1Sskrll} 2291.1Sskrll 2301.1Sskrllvoid 2311.1Sskrllpower_thread_reg(void *v) 2321.1Sskrll{ 2331.1Sskrll struct power_softc *sc = v; 2341.1Sskrll uint32_t r; 2351.1Sskrll 2361.1Sskrll r = bus_space_read_4(sc->sc_bst, sc->sc_bsh, 0); 2371.1Sskrll 2381.1Sskrll if (!(r & 1)) 2391.1Sskrll pswitch_on = false; 2401.1Sskrll else 2411.1Sskrll pswitch_on = true; 2421.1Sskrll 2431.1Sskrll check_pwr_state(sc); 2441.1Sskrll} 2451.1Sskrll 2461.1Sskrllvoid 2471.1Sskrllpower_cold_hook_reg(int on) 2481.1Sskrll{ 2491.1Sskrll int error; 2501.1Sskrll 2511.1Sskrll error = pdcproc_soft_power_enable(on == HPPA_COLD_HOT); 2521.1Sskrll if (error) 2531.1Sskrll aprint_error("PDC_SOFT_POWER_ENABLE failed (%d)\n", error); 2541.1Sskrll} 2551.1Sskrll 2561.1Sskrllstatic int 2571.1Sskrllpwr_sw_init(struct power_softc *sc) 2581.1Sskrll{ 2591.1Sskrll struct sysctllog *sysctl_log = NULL; 2601.1Sskrll const struct sysctlnode *pwr_sw_node; 2611.1Sskrll const char *errmsg; 2621.1Sskrll int error = EINVAL; 2631.1Sskrll 2641.1Sskrll /* 2651.1Sskrll * Ensure that we are on a PCX-L / PA7100LC CPU if it is a 2661.1Sskrll * 712 style machine. 2671.1Sskrll */ 2681.1Sskrll if (pdc_power_info.addr == 0 && hppa_cpu_info->hci_cputype != hpcxl) { 2691.1Sskrll aprint_error_dev(sc->sc_dev, "No soft power available.\n"); 2701.1Sskrll return error; 2711.1Sskrll } 2721.1Sskrll 2731.1Sskrll errmsg = "Can't create sysctl machdep.power_switch (or children)\n"; 2741.1Sskrll error = sysctl_createv(&sysctl_log, 0, NULL, NULL, 0, 2751.1Sskrll CTLTYPE_NODE, "machdep", NULL, NULL, 0, NULL, 0, 2761.1Sskrll CTL_MACHDEP, CTL_EOL); 2771.1Sskrll 2781.1Sskrll if (error) 2791.1Sskrll goto err_sysctl; 2801.3Sskrll 2811.1Sskrll error = sysctl_createv(&sysctl_log, 0, NULL, &pwr_sw_node, 0, 2821.1Sskrll CTLTYPE_NODE, "power_switch", NULL, NULL, 0, NULL, 0, 2831.1Sskrll CTL_MACHDEP, CTL_CREATE, CTL_EOL); 2841.1Sskrll 2851.1Sskrll if (error) 2861.1Sskrll goto err_sysctl; 2871.1Sskrll 2881.1Sskrll error = sysctl_createv(&sysctl_log, 0, NULL, NULL, 2891.1Sskrll CTLFLAG_READONLY, CTLTYPE_STRING, "state", NULL, 2901.1Sskrll pwr_sw_sysctl_state, 0, NULL, 16, 2911.1Sskrll CTL_MACHDEP, pwr_sw_node->sysctl_num, CTL_CREATE, CTL_EOL); 2921.1Sskrll 2931.1Sskrll if (error) 2941.1Sskrll goto err_sysctl; 2951.1Sskrll 2961.1Sskrll error = sysctl_createv(&sysctl_log, 0, NULL, NULL, 2971.1Sskrll CTLFLAG_READWRITE, CTLTYPE_STRING, "control", NULL, 2981.1Sskrll pwr_sw_sysctl_ctrl, 0, NULL, 16, 2991.1Sskrll CTL_MACHDEP, pwr_sw_node->sysctl_num, CTL_CREATE, CTL_EOL); 3001.1Sskrll 3011.1Sskrll if (error) 3021.1Sskrll goto err_sysctl; 3031.1Sskrll 3041.1Sskrll errmsg = "Can't alloc sysmon power switch.\n"; 3051.1Sskrll pwr_sw_sysmon = kmem_zalloc(sizeof(*pwr_sw_sysmon), KM_SLEEP); 3061.1Sskrll errmsg = "Can't register power switch with sysmon.\n"; 3071.1Sskrll sysmon_task_queue_init(); 3081.1Sskrll pwr_sw_sysmon->smpsw_name = "power switch"; 3091.1Sskrll pwr_sw_sysmon->smpsw_type = PSWITCH_TYPE_POWER; 3101.1Sskrll error = sysmon_pswitch_register(pwr_sw_sysmon); 3111.1Sskrll 3121.1Sskrll if (error) 3131.1Sskrll goto err_sysmon; 3141.1Sskrll 3151.1Sskrll callout_init(&sc->sc_callout, 0); 3161.1Sskrll callout_reset(&sc->sc_callout, sc->sc_timeout, sc->sc_kicker, sc); 3171.1Sskrll 3181.1Sskrll return error; 3191.1Sskrll 3201.1Sskrllerr_sysmon: 3211.1Sskrll kmem_free(pwr_sw_sysmon, sizeof(*pwr_sw_sysmon)); 3221.1Sskrll 3231.1Sskrllerr_sysctl: 3241.1Sskrll sysctl_teardown(&sysctl_log); 3251.1Sskrll 3261.1Sskrll aprint_error_dev(sc->sc_dev, errmsg); 3271.1Sskrll 3281.1Sskrll return error; 3291.1Sskrll} 3301.1Sskrll 3311.1Sskrllstatic void 3321.1Sskrllpwr_sw_sysmon_cb(void *not_used) 3331.1Sskrll{ 3341.1Sskrll sysmon_pswitch_event(pwr_sw_sysmon, PSWITCH_EVENT_PRESSED); 3351.1Sskrll} 3361.1Sskrll 3371.1Sskrllstatic void 3381.1Sskrllpwr_sw_ctrl(int enable) 3391.1Sskrll{ 3401.1Sskrll int on; 3411.1Sskrll 3421.1Sskrll#ifdef DEBUG 3431.1Sskrll printf("pwr_sw_control=%d enable=%d\n", pwr_sw_control, enable); 3441.1Sskrll#endif /* DEBUG */ 3451.1Sskrll 3461.1Sskrll if (cold_hook == NULL) 3471.1Sskrll return; 3481.1Sskrll 3491.1Sskrll switch(enable) { 3501.1Sskrll case PWR_SW_CTRL_DISABLE: 3511.1Sskrll on = HPPA_COLD_OFF; 3521.1Sskrll break; 3531.1Sskrll case PWR_SW_CTRL_ENABLE: 3541.1Sskrll case PWR_SW_CTRL_LOCK: 3551.1Sskrll on = HPPA_COLD_HOT; 3561.1Sskrll break; 3571.1Sskrll default: 3581.1Sskrll panic("invalid power state in pwr_sw_control: %d", enable); 3591.1Sskrll } 3601.1Sskrll 3611.1Sskrll pwr_sw_control = enable; 3621.1Sskrll 3631.1Sskrll if (cold_hook) 3641.1Sskrll (*cold_hook)(on); 3651.1Sskrll} 3661.1Sskrll 3671.1Sskrllint 3681.1Sskrllpwr_sw_sysctl_state(SYSCTLFN_ARGS) 3691.1Sskrll{ 3701.1Sskrll struct sysctlnode node; 3711.1Sskrll const char *status; 3721.1Sskrll 3731.1Sskrll if (pswitch_on == true) 3741.1Sskrll status = "on"; 3751.1Sskrll else 3761.1Sskrll status = "off"; 3771.1Sskrll 3781.1Sskrll node = *rnode; 3791.1Sskrll node.sysctl_data = __UNCONST(status); 3801.1Sskrll return sysctl_lookup(SYSCTLFN_CALL(&node)); 3811.1Sskrll} 3821.1Sskrll 3831.1Sskrllint 3841.1Sskrllpwr_sw_sysctl_ctrl(SYSCTLFN_ARGS) 3851.1Sskrll{ 3861.1Sskrll struct sysctlnode node; 3871.1Sskrll int i, error; 3881.1Sskrll char val[16]; 3891.1Sskrll 3901.1Sskrll node = *rnode; 3911.1Sskrll strcpy(val, pwr_sw_control_str[pwr_sw_control]); 3921.1Sskrll 3931.1Sskrll node.sysctl_data = val; 3941.1Sskrll 3951.1Sskrll error = sysctl_lookup(SYSCTLFN_CALL(&node)); 3961.1Sskrll 3971.1Sskrll if (error || newp == NULL) 3981.1Sskrll return error; 3991.1Sskrll 4001.1Sskrll for (i = 0; i <= PWR_SW_CTRL_MAX; i++) 4011.1Sskrll if (strcmp(val, pwr_sw_control_str[i]) == 0) { 4021.1Sskrll pwr_sw_ctrl(i); 4031.1Sskrll return 0; 4041.1Sskrll } 4051.1Sskrll 4061.1Sskrll return EINVAL; 4071.1Sskrll} 408