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 57static int nhpow_match(device_t, cfdata_t, void *); 58static void nhpow_attach(device_t, device_t, void *); 59static void nhpow_reboot(int); 60static int nhpow_sysctl_fan(SYSCTLFN_PROTO); 61static int hwintr(void *); 62static void guarded_pbutton(void *); 63static void sched_sysmon_pbutton(void *); 64 65static void nhgpio_pin_write(void *, int, int); 66#if NGPIO > 0 67static int nhgpio_pin_read(void *, int); 68static 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 89struct 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 103CFATTACH_DECL_NEW(nhpow, sizeof(struct nhpow_softc), 104 nhpow_match, nhpow_attach, NULL, NULL); 105 106static int found = 0; 107 108extern struct cfdriver nhpow_cd; 109extern void (*md_reboot)(int); 110 111static int 112nhpow_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 122static void 123nhpow_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 215static void 216nhgpio_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 231static int 232nhgpio_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 242static void 243nhgpio_pin_ctl(void *arg, int pin, int flags) 244{ 245 246 /* nothing to control */ 247} 248#endif 249 250static void 251nhpow_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 264static int 265nhpow_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 288static int 289hwintr(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 308static void 309guarded_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 318static void 319sched_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