power.c revision 1.1
11.1Sskrll/*	$NetBSD: power.c,v 1.1 2014/02/24 07:23:42 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.1Sskrll
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	if (pwr_sw_sysmon == NULL) {
3071.1Sskrll		error = ENOMEM;
3081.1Sskrll		goto err_kmem;
3091.1Sskrll	}
3101.1Sskrll
3111.1Sskrll	errmsg = "Can't register power switch with sysmon.\n";
3121.1Sskrll	sysmon_task_queue_init();
3131.1Sskrll	pwr_sw_sysmon->smpsw_name = "power switch";
3141.1Sskrll	pwr_sw_sysmon->smpsw_type = PSWITCH_TYPE_POWER;
3151.1Sskrll	error = sysmon_pswitch_register(pwr_sw_sysmon);
3161.1Sskrll
3171.1Sskrll	if (error)
3181.1Sskrll		goto err_sysmon;
3191.1Sskrll
3201.1Sskrll	callout_init(&sc->sc_callout, 0);
3211.1Sskrll	callout_reset(&sc->sc_callout, sc->sc_timeout, sc->sc_kicker, sc);
3221.1Sskrll
3231.1Sskrll	return error;
3241.1Sskrll
3251.1Sskrllerr_sysmon:
3261.1Sskrll	/* Nothing to do */
3271.1Sskrll
3281.1Sskrllerr_kmem:
3291.1Sskrll	kmem_free(pwr_sw_sysmon, sizeof(*pwr_sw_sysmon));
3301.1Sskrll
3311.1Sskrllerr_sysctl:
3321.1Sskrll	sysctl_teardown(&sysctl_log);
3331.1Sskrll
3341.1Sskrll	aprint_error_dev(sc->sc_dev, errmsg);
3351.1Sskrll
3361.1Sskrll	return error;
3371.1Sskrll}
3381.1Sskrll
3391.1Sskrllstatic void
3401.1Sskrllpwr_sw_sysmon_cb(void *not_used)
3411.1Sskrll{
3421.1Sskrll	sysmon_pswitch_event(pwr_sw_sysmon, PSWITCH_EVENT_PRESSED);
3431.1Sskrll}
3441.1Sskrll
3451.1Sskrllstatic void
3461.1Sskrllpwr_sw_ctrl(int enable)
3471.1Sskrll{
3481.1Sskrll	int on;
3491.1Sskrll
3501.1Sskrll#ifdef DEBUG
3511.1Sskrll	printf("pwr_sw_control=%d enable=%d\n", pwr_sw_control, enable);
3521.1Sskrll#endif /* DEBUG */
3531.1Sskrll
3541.1Sskrll	if (cold_hook == NULL)
3551.1Sskrll		return;
3561.1Sskrll
3571.1Sskrll	switch(enable) {
3581.1Sskrll	case PWR_SW_CTRL_DISABLE:
3591.1Sskrll		on = HPPA_COLD_OFF;
3601.1Sskrll		break;
3611.1Sskrll	case PWR_SW_CTRL_ENABLE:
3621.1Sskrll	case PWR_SW_CTRL_LOCK:
3631.1Sskrll		on = HPPA_COLD_HOT;
3641.1Sskrll		break;
3651.1Sskrll	default:
3661.1Sskrll		panic("invalid power state in pwr_sw_control: %d", enable);
3671.1Sskrll	}
3681.1Sskrll
3691.1Sskrll	pwr_sw_control = enable;
3701.1Sskrll
3711.1Sskrll	if (cold_hook)
3721.1Sskrll		(*cold_hook)(on);
3731.1Sskrll}
3741.1Sskrll
3751.1Sskrllint
3761.1Sskrllpwr_sw_sysctl_state(SYSCTLFN_ARGS)
3771.1Sskrll{
3781.1Sskrll	struct sysctlnode node;
3791.1Sskrll	const char *status;
3801.1Sskrll
3811.1Sskrll	if (pswitch_on == true)
3821.1Sskrll		status = "on";
3831.1Sskrll	else
3841.1Sskrll		status = "off";
3851.1Sskrll
3861.1Sskrll	node = *rnode;
3871.1Sskrll	node.sysctl_data = __UNCONST(status);
3881.1Sskrll	return sysctl_lookup(SYSCTLFN_CALL(&node));
3891.1Sskrll}
3901.1Sskrll
3911.1Sskrllint
3921.1Sskrllpwr_sw_sysctl_ctrl(SYSCTLFN_ARGS)
3931.1Sskrll{
3941.1Sskrll	struct sysctlnode node;
3951.1Sskrll	int i, error;
3961.1Sskrll	char val[16];
3971.1Sskrll
3981.1Sskrll	node = *rnode;
3991.1Sskrll	strcpy(val, pwr_sw_control_str[pwr_sw_control]);
4001.1Sskrll
4011.1Sskrll	node.sysctl_data = val;
4021.1Sskrll
4031.1Sskrll	error = sysctl_lookup(SYSCTLFN_CALL(&node));
4041.1Sskrll
4051.1Sskrll	if (error || newp == NULL)
4061.1Sskrll		return error;
4071.1Sskrll
4081.1Sskrll	for (i = 0; i <= PWR_SW_CTRL_MAX; i++)
4091.1Sskrll		if (strcmp(val, pwr_sw_control_str[i]) == 0) {
4101.1Sskrll			pwr_sw_ctrl(i);
4111.1Sskrll			return 0;
4121.1Sskrll		}
4131.1Sskrll
4141.1Sskrll	return EINVAL;
4151.1Sskrll}
416