Home | History | Annotate | Line # | Download | only in sandpoint
      1 /* $NetBSD: nhpow.c,v 1.4 2021/08/07 16:19:04 thorpej Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2012 Frank Wille.
      5  * All rights reserved.
      6  *
      7  * Written by Frank Wille for The NetBSD Project.
      8  *
      9  * Redistribution and use in source and binary forms, with or without
     10  * modification, are permitted provided that the following conditions
     11  * are met:
     12  * 1. Redistributions of source code must retain the above copyright
     13  *    notice, this list of conditions and the following disclaimer.
     14  * 2. Redistributions in binary form must reproduce the above copyright
     15  *    notice, this list of conditions and the following disclaimer in the
     16  *    documentation and/or other materials provided with the distribution.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     20  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     22  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     28  * POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 /*
     32  * NH230/231 power and LED control, button handling
     33  */
     34 #include <sys/cdefs.h>
     35 __KERNEL_RCSID(0, "$NetBSD: nhpow.c,v 1.4 2021/08/07 16:19:04 thorpej Exp $");
     36 #include "gpio.h"
     37 
     38 #include <sys/param.h>
     39 #include <sys/device.h>
     40 #if NGPIO > 0
     41 #include <sys/gpio.h>
     42 #endif
     43 #include <sys/callout.h>
     44 #include <sys/kernel.h>
     45 #include <sys/proc.h>
     46 #include <sys/reboot.h>
     47 #include <sys/sysctl.h>
     48 
     49 #if NGPIO > 0
     50 #include <dev/gpio/gpiovar.h>
     51 #endif
     52 #include <dev/sysmon/sysmonvar.h>
     53 #include <dev/sysmon/sysmon_taskq.h>
     54 
     55 #include <machine/autoconf.h>
     56 
     57 static int  nhpow_match(device_t, cfdata_t, void *);
     58 static void nhpow_attach(device_t, device_t, void *);
     59 static void nhpow_reboot(int);
     60 static int nhpow_sysctl_fan(SYSCTLFN_PROTO);
     61 static int hwintr(void *);
     62 static void guarded_pbutton(void *);
     63 static void sched_sysmon_pbutton(void *);
     64 
     65 static void nhgpio_pin_write(void *, int, int);
     66 #if NGPIO > 0
     67 static int nhgpio_pin_read(void *, int);
     68 static void nhgpio_pin_ctl(void *, int, int);
     69 #endif
     70 
     71 #define NHGPIO_PINS		8
     72 
     73 /* write gpio */
     74 #define NHGPIO_WRITE(sc,x)	bus_space_write_1(sc->sc_iot, sc->sc_ioh, 0, x)
     75 #define NHGPIO_POWEROFF		0x01
     76 #define NHGPIO_RESET		0x02
     77 #define NHGPIO_STATUS_LED_OFF	0x04
     78 #define NHGPIO_FAN_HIGH		0x08
     79 #define NHGPIO_USB1_LED_OFF	0x40
     80 #define NHGPIO_USB2_LED_OFF	0x80
     81 
     82 /* read gpio */
     83 #define NHGPIO_READ(sc)		bus_space_read_1(sc->sc_iot, sc->sc_ioh, 0)
     84 #define NHGPIO_POWERBUTTON	0x01
     85 #define NHGPIO_RESETBUTTON	0x02
     86 #define NHGPIO_VERSIONMASK	0xf0
     87 #define NHGPIO_VERSIONSHIFT	4
     88 
     89 struct nhpow_softc {
     90 	device_t		sc_dev;
     91 	bus_space_tag_t		sc_iot;
     92 	bus_space_handle_t	sc_ioh;
     93 	callout_t		sc_ch_pbutton;
     94 	struct sysmon_pswitch	sc_sm_pbutton;
     95 	int			sc_sysctl_fan;
     96 #if NGPIO > 0
     97 	struct gpio_chipset_tag	sc_gpio_gc;
     98 	gpio_pin_t		sc_gpio_pins[8];
     99 #endif
    100 	uint8_t			sc_gpio_wrstate;
    101 };
    102 
    103 CFATTACH_DECL_NEW(nhpow, sizeof(struct nhpow_softc),
    104     nhpow_match, nhpow_attach, NULL, NULL);
    105 
    106 static int found = 0;
    107 
    108 extern struct cfdriver nhpow_cd;
    109 extern void (*md_reboot)(int);
    110 
    111 static int
    112 nhpow_match(device_t parent, cfdata_t cf, void *aux)
    113 {
    114 	struct mainbus_attach_args *ma = aux;
    115 
    116 	if (found != 0 || strcmp(ma->ma_name, nhpow_cd.cd_name) != 0)
    117 		return 0;
    118 
    119 	return 1;
    120 }
    121 
    122 static void
    123 nhpow_attach(device_t parent, device_t self, void *aux)
    124 {
    125 	struct mainbus_attach_args *ma = aux;
    126 #if NGPIO > 0
    127 	struct gpiobus_attach_args gba;
    128 #endif
    129 	const struct sysctlnode *rnode;
    130 	struct sysctllog *clog;
    131 	struct nhpow_softc *sc;
    132 	int i;
    133 
    134 	found = 1;
    135 	sc = device_private(self);
    136 	sc->sc_dev = self;
    137 
    138 	/* map the first byte for GPIO */
    139 	KASSERT(ma->ma_bst != NULL);
    140 	sc->sc_iot = ma->ma_bst;
    141 	i = bus_space_map(sc->sc_iot, 0, 1, 0, &sc->sc_ioh);
    142 	if (i != 0) {
    143 		aprint_error(": could not map error %d\n", i);
    144 		return;
    145 	}
    146 
    147 	aprint_naive(": Power Button Manager\n");
    148 	aprint_normal(": NH230/231 gpio board control, version %u\n",
    149 	    (NHGPIO_READ(sc) & NHGPIO_VERSIONMASK) >> NHGPIO_VERSIONSHIFT);
    150 
    151 	md_reboot = nhpow_reboot;		/* cpu_reboot() hook */
    152 	callout_init(&sc->sc_ch_pbutton, 0);	/* power-button callout */
    153 
    154 	/* establish button interrupt handler */
    155 	intr_establish_xname(I8259_ICU + 4, IST_EDGE_RISING, IPL_SCHED, hwintr,
    156 	    sc, device_xname(self));
    157 	aprint_normal_dev(self, "interrupting at irq %d\n", I8259_ICU + 4);
    158 
    159 	/* register power button with sysmon */
    160 	sysmon_task_queue_init();
    161 	memset(&sc->sc_sm_pbutton, 0, sizeof(struct sysmon_pswitch));
    162 	sc->sc_sm_pbutton.smpsw_name = device_xname(sc->sc_dev);
    163 	sc->sc_sm_pbutton.smpsw_type = PSWITCH_TYPE_POWER;
    164 	if (sysmon_pswitch_register(&sc->sc_sm_pbutton) != 0)
    165 		aprint_error_dev(sc->sc_dev,
    166 		    "unable to register power button with sysmon\n");
    167 
    168 	/* create machdep.nhpow subtree for fan control */
    169 	clog = NULL;
    170 	sysctl_createv(&clog, 0, NULL, &rnode,
    171 	    CTLFLAG_PERMANENT,
    172 	    CTLTYPE_NODE, "machdep", NULL,
    173 	    NULL, 0, NULL, 0,
    174 	    CTL_MACHDEP, CTL_EOL);
    175 	sysctl_createv(&clog, 0, &rnode, &rnode,
    176 	    CTLFLAG_PERMANENT,
    177 	    CTLTYPE_NODE, "nhpow", NULL,
    178 	    NULL, 0, NULL, 0,
    179 	    CTL_CREATE, CTL_EOL);
    180 	sysctl_createv(&clog, 0, &rnode, NULL,
    181 	    CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
    182 	    CTLTYPE_INT, "fan",
    183 	    SYSCTL_DESCR("Toggle high(1)/low(0) fan speed"),
    184 	    nhpow_sysctl_fan, 0, NULL, 0,
    185 	    CTL_CREATE, CTL_EOL);
    186 
    187 	/* define initial output state */
    188 	sc->sc_sysctl_fan = 0;
    189 	sc->sc_gpio_wrstate = NHGPIO_USB1_LED_OFF | NHGPIO_USB2_LED_OFF;
    190 	NHGPIO_WRITE(sc, sc->sc_gpio_wrstate);
    191 
    192 #if NGPIO > 0
    193 	/* initialize gpio pin array */
    194 	for (i = 0; i < NHGPIO_PINS; i++) {
    195 		sc->sc_gpio_pins[i].pin_num = i;
    196 		sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INOUT;
    197 		sc->sc_gpio_pins[i].pin_flags = GPIO_PIN_INOUT;
    198 		sc->sc_gpio_pins[i].pin_state =
    199 		    (sc->sc_gpio_wrstate & (1 << i)) ?
    200 		    GPIO_PIN_HIGH : GPIO_PIN_LOW;
    201 	}
    202 
    203 	/* create controller tag and attach GPIO framework */
    204 	sc->sc_gpio_gc.gp_cookie = sc;
    205 	sc->sc_gpio_gc.gp_pin_read = nhgpio_pin_read;
    206 	sc->sc_gpio_gc.gp_pin_write = nhgpio_pin_write;
    207 	sc->sc_gpio_gc.gp_pin_ctl = nhgpio_pin_ctl;
    208 	gba.gba_gc = &sc->sc_gpio_gc;
    209 	gba.gba_pins = sc->sc_gpio_pins;
    210 	gba.gba_npins = NHGPIO_PINS;
    211 	config_found(self, &gba, gpiobus_print, CFARGS_NONE);
    212 #endif
    213 }
    214 
    215 static void
    216 nhgpio_pin_write(void *arg, int pin, int value)
    217 {
    218 	struct nhpow_softc *sc = arg;
    219 	int p;
    220 
    221 	KASSERT(sc != NULL);
    222 	p = pin % NHGPIO_PINS;
    223 	if (value)
    224 		sc->sc_gpio_wrstate |= (1 << p);
    225 	else
    226 		sc->sc_gpio_wrstate &= ~(1 << p);
    227 	NHGPIO_WRITE(sc, sc->sc_gpio_wrstate);
    228 }
    229 
    230 #if NGPIO > 0
    231 static int
    232 nhgpio_pin_read(void *arg, int pin)
    233 {
    234 	struct nhpow_softc *sc = arg;
    235 	int p;
    236 
    237 	KASSERT(sc != NULL);
    238 	p = pin % NHGPIO_PINS;
    239 	return (NHGPIO_READ(sc) >> p) & 1;
    240 }
    241 
    242 static void
    243 nhgpio_pin_ctl(void *arg, int pin, int flags)
    244 {
    245 
    246 	/* nothing to control */
    247 }
    248 #endif
    249 
    250 static void
    251 nhpow_reboot(int howto)
    252 {
    253 	struct nhpow_softc *sc = device_lookup_private(&nhpow_cd, 0);
    254 
    255 	if ((howto & RB_POWERDOWN) == RB_AUTOBOOT)
    256 		NHGPIO_WRITE(sc, NHGPIO_STATUS_LED_OFF | NHGPIO_RESET);
    257 	else
    258 		NHGPIO_WRITE(sc, NHGPIO_STATUS_LED_OFF | NHGPIO_POWEROFF);
    259 
    260 	tsleep(nhpow_reboot, PWAIT, "reboot", 0);
    261 	/*NOTREACHED*/
    262 }
    263 
    264 static int
    265 nhpow_sysctl_fan(SYSCTLFN_ARGS)
    266 {
    267 	struct sysctlnode node;
    268 	struct nhpow_softc *sc;
    269 	int error, t;
    270 
    271 	sc = device_lookup_private(&nhpow_cd, 0);
    272 	node = *rnode;
    273 	t = sc->sc_sysctl_fan;
    274 	node.sysctl_data = &t;
    275 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
    276 	if (error || newp == NULL)
    277 		return error;
    278 	if (t < 0 || t > 1)
    279 		return EINVAL;
    280 
    281 	if (sc->sc_sysctl_fan != t) {
    282 		sc->sc_sysctl_fan = t;
    283 		nhgpio_pin_write(sc, 3, t);	/* set new fan speed */
    284 	}
    285 	return 0;
    286 }
    287 
    288 static int
    289 hwintr(void *arg)
    290 {
    291 	struct nhpow_softc *sc = arg;
    292 	uint8_t buttons;
    293 
    294 	callout_stop(&sc->sc_ch_pbutton);
    295 
    296 	buttons = NHGPIO_READ(sc);
    297 	if (!(buttons & NHGPIO_POWERBUTTON)) {
    298 		/* power button, schedule 3 seconds poweroff guard time */
    299 		callout_reset(&sc->sc_ch_pbutton, 3 * hz, guarded_pbutton, sc);
    300 	}
    301 	if (!(buttons & NHGPIO_RESETBUTTON)) {
    302 		/* reset/setup button */
    303 	}
    304 
    305 	return 1;
    306 }
    307 
    308 static void
    309 guarded_pbutton(void *arg)
    310 {
    311 	struct nhpow_softc *sc = arg;
    312 
    313 	/* we're now in callout(9) context */
    314 	if (!(NHGPIO_READ(sc) & NHGPIO_POWERBUTTON))
    315 		sysmon_task_queue_sched(0, sched_sysmon_pbutton, sc);
    316 }
    317 
    318 static void
    319 sched_sysmon_pbutton(void *arg)
    320 {
    321 	struct nhpow_softc *sc = arg;
    322 
    323 	/* we're now in kthread(9) context */
    324 	sysmon_pswitch_event(&sc->sc_sm_pbutton, PSWITCH_EVENT_PRESSED);
    325 }
    326