Home | History | Annotate | Line # | Download | only in dev
      1 /*	$NetBSD: power.c,v 1.3 2019/04/15 20:40:37 skrll Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2004 Jochen Kunz.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. The name of Jochen Kunz may not be used to endorse or promote
     16  *    products derived from this software without specific prior
     17  *    written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY JOCHEN KUNZ
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL JOCHEN KUNZ
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*	$OpenBSD: power.c,v 1.5 2004/06/11 12:53:09 mickey Exp $	*/
     33 
     34 /*
     35  * Copyright (c) 2003 Michael Shalayeff
     36  * All rights reserved.
     37  *
     38  * Redistribution and use in source and binary forms, with or without
     39  * modification, are permitted provided that the following conditions
     40  * are met:
     41  * 1. Redistributions of source code must retain the above copyright
     42  *    notice, this list of conditions and the following disclaimer.
     43  * 2. Redistributions in binary form must reproduce the above copyright
     44  *    notice, this list of conditions and the following disclaimer in the
     45  *    documentation and/or other materials provided with the distribution.
     46  *
     47  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     48  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     49  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     50  * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
     51  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     52  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     53  * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     54  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     55  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
     56  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     57  * 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 
     83 struct 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 
     96 int	powermatch(device_t, cfdata_t, void *);
     97 void	powerattach(device_t, device_t, void *);
     98 
     99 CFATTACH_DECL_NEW(power, sizeof(struct power_softc),
    100     powermatch, powerattach, NULL, NULL);
    101 
    102 static struct pdc_power_info pdc_power_info;
    103 static bool pswitch_on;			/* power switch */
    104 static int pwr_sw_control;
    105 static const char *pwr_sw_control_str[] = {"disabled", "enabled", "locked"};
    106 static struct sysmon_pswitch *pwr_sw_sysmon;
    107 
    108 static int pwr_sw_sysctl_state(SYSCTLFN_PROTO);
    109 static int pwr_sw_sysctl_ctrl(SYSCTLFN_PROTO);
    110 static void pwr_sw_sysmon_cb(void *);
    111 static void pwr_sw_ctrl(int);
    112 static int pwr_sw_init(struct power_softc *);
    113 
    114 void power_thread_dr(void *v);
    115 void power_thread_reg(void *v);
    116 void power_cold_hook_reg(int);
    117 
    118 int
    119 powermatch(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 
    129 void
    130 powerattach(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  */
    190 static void
    191 check_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 
    200 void
    201 power_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 
    230 void
    231 power_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 
    246 void
    247 power_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 
    256 static int
    257 pwr_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 
    320 err_sysmon:
    321 	kmem_free(pwr_sw_sysmon, sizeof(*pwr_sw_sysmon));
    322 
    323 err_sysctl:
    324 	sysctl_teardown(&sysctl_log);
    325 
    326 	aprint_error_dev(sc->sc_dev, errmsg);
    327 
    328 	return error;
    329 }
    330 
    331 static void
    332 pwr_sw_sysmon_cb(void *not_used)
    333 {
    334 	sysmon_pswitch_event(pwr_sw_sysmon, PSWITCH_EVENT_PRESSED);
    335 }
    336 
    337 static void
    338 pwr_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 
    367 int
    368 pwr_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 
    383 int
    384 pwr_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