1 /* $NetBSD: nhpow.c,v 1.1 2012/01/14 19:39:25 phx 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.1 2012/01/14 19:39:25 phx 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(I8259_ICU + 4, IST_EDGE_RISING, IPL_SCHED, hwintr, sc); 156 aprint_normal_dev(self, "interrupting at irq %d\n", I8259_ICU + 4); 157 158 /* register power button with sysmon */ 159 sysmon_task_queue_init(); 160 memset(&sc->sc_sm_pbutton, 0, sizeof(struct sysmon_pswitch)); 161 sc->sc_sm_pbutton.smpsw_name = device_xname(sc->sc_dev); 162 sc->sc_sm_pbutton.smpsw_type = PSWITCH_TYPE_POWER; 163 if (sysmon_pswitch_register(&sc->sc_sm_pbutton) != 0) 164 aprint_error_dev(sc->sc_dev, 165 "unable to register power button with sysmon\n"); 166 167 /* create machdep.nhpow subtree for fan control */ 168 clog = NULL; 169 sysctl_createv(&clog, 0, NULL, &rnode, 170 CTLFLAG_PERMANENT, 171 CTLTYPE_NODE, "machdep", NULL, 172 NULL, 0, NULL, 0, 173 CTL_MACHDEP, CTL_EOL); 174 sysctl_createv(&clog, 0, &rnode, &rnode, 175 CTLFLAG_PERMANENT, 176 CTLTYPE_NODE, "nhpow", NULL, 177 NULL, 0, NULL, 0, 178 CTL_CREATE, CTL_EOL); 179 sysctl_createv(&clog, 0, &rnode, NULL, 180 CTLFLAG_PERMANENT|CTLFLAG_READWRITE, 181 CTLTYPE_INT, "fan", 182 SYSCTL_DESCR("Toggle high(1)/low(0) fan speed"), 183 nhpow_sysctl_fan, 0, NULL, 0, 184 CTL_CREATE, CTL_EOL); 185 186 /* define initial output state */ 187 sc->sc_sysctl_fan = 0; 188 sc->sc_gpio_wrstate = NHGPIO_USB1_LED_OFF | NHGPIO_USB2_LED_OFF; 189 NHGPIO_WRITE(sc, sc->sc_gpio_wrstate); 190 191 #if NGPIO > 0 192 /* initialize gpio pin array */ 193 for (i = 0; i < NHGPIO_PINS; i++) { 194 sc->sc_gpio_pins[i].pin_num = i; 195 sc->sc_gpio_pins[i].pin_caps = GPIO_PIN_INOUT; 196 sc->sc_gpio_pins[i].pin_flags = GPIO_PIN_INOUT; 197 sc->sc_gpio_pins[i].pin_state = 198 (sc->sc_gpio_wrstate & (1 << i)) ? 199 GPIO_PIN_HIGH : GPIO_PIN_LOW; 200 } 201 202 /* create controller tag and attach GPIO framework */ 203 sc->sc_gpio_gc.gp_cookie = sc; 204 sc->sc_gpio_gc.gp_pin_read = nhgpio_pin_read; 205 sc->sc_gpio_gc.gp_pin_write = nhgpio_pin_write; 206 sc->sc_gpio_gc.gp_pin_ctl = nhgpio_pin_ctl; 207 gba.gba_gc = &sc->sc_gpio_gc; 208 gba.gba_pins = sc->sc_gpio_pins; 209 gba.gba_npins = NHGPIO_PINS; 210 config_found(self, &gba, gpiobus_print); 211 #endif 212 } 213 214 static void 215 nhgpio_pin_write(void *arg, int pin, int value) 216 { 217 struct nhpow_softc *sc = arg; 218 int p; 219 220 KASSERT(sc != NULL); 221 p = pin % NHGPIO_PINS; 222 if (value) 223 sc->sc_gpio_wrstate |= (1 << p); 224 else 225 sc->sc_gpio_wrstate &= ~(1 << p); 226 NHGPIO_WRITE(sc, sc->sc_gpio_wrstate); 227 } 228 229 #if NGPIO > 0 230 static int 231 nhgpio_pin_read(void *arg, int pin) 232 { 233 struct nhpow_softc *sc = arg; 234 int p; 235 236 KASSERT(sc != NULL); 237 p = pin % NHGPIO_PINS; 238 return (NHGPIO_READ(sc) >> p) & 1; 239 } 240 241 static void 242 nhgpio_pin_ctl(void *arg, int pin, int flags) 243 { 244 245 /* nothing to control */ 246 } 247 #endif 248 249 static void 250 nhpow_reboot(int howto) 251 { 252 struct nhpow_softc *sc = device_lookup_private(&nhpow_cd, 0); 253 254 if ((howto & RB_POWERDOWN) == RB_AUTOBOOT) 255 NHGPIO_WRITE(sc, NHGPIO_STATUS_LED_OFF | NHGPIO_RESET); 256 else 257 NHGPIO_WRITE(sc, NHGPIO_STATUS_LED_OFF | NHGPIO_POWEROFF); 258 259 tsleep(nhpow_reboot, PWAIT, "reboot", 0); 260 /*NOTREACHED*/ 261 } 262 263 static int 264 nhpow_sysctl_fan(SYSCTLFN_ARGS) 265 { 266 struct sysctlnode node; 267 struct nhpow_softc *sc; 268 int error, t; 269 270 sc = device_lookup_private(&nhpow_cd, 0); 271 node = *rnode; 272 t = sc->sc_sysctl_fan; 273 node.sysctl_data = &t; 274 error = sysctl_lookup(SYSCTLFN_CALL(&node)); 275 if (error || newp == NULL) 276 return error; 277 if (t < 0 || t > 1) 278 return EINVAL; 279 280 if (sc->sc_sysctl_fan != t) { 281 sc->sc_sysctl_fan = t; 282 nhgpio_pin_write(sc, 3, t); /* set new fan speed */ 283 } 284 return 0; 285 } 286 287 static int 288 hwintr(void *arg) 289 { 290 struct nhpow_softc *sc = arg; 291 uint8_t buttons; 292 293 callout_stop(&sc->sc_ch_pbutton); 294 295 buttons = NHGPIO_READ(sc); 296 if (!(buttons & NHGPIO_POWERBUTTON)) { 297 /* power button, schedule 3 seconds poweroff guard time */ 298 callout_reset(&sc->sc_ch_pbutton, 3 * hz, guarded_pbutton, sc); 299 } 300 if (!(buttons & NHGPIO_RESETBUTTON)) { 301 /* reset/setup button */ 302 } 303 304 return 1; 305 } 306 307 static void 308 guarded_pbutton(void *arg) 309 { 310 struct nhpow_softc *sc = arg; 311 312 /* we're now in callout(9) context */ 313 if (!(NHGPIO_READ(sc) & NHGPIO_POWERBUTTON)) 314 sysmon_task_queue_sched(0, sched_sysmon_pbutton, sc); 315 } 316 317 static void 318 sched_sysmon_pbutton(void *arg) 319 { 320 struct nhpow_softc *sc = arg; 321 322 /* we're now in kthread(9) context */ 323 sysmon_pswitch_event(&sc->sc_sm_pbutton, PSWITCH_EVENT_PRESSED); 324 } 325