xref: /netbsd-src/sys/arch/sandpoint/sandpoint/nhpow.c (revision c7fb772b85b2b5d4cfb282f868f454b4701534fd)
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
nhpow_match(device_t parent,cfdata_t cf,void * aux)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
nhpow_attach(device_t parent,device_t self,void * aux)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
nhgpio_pin_write(void * arg,int pin,int value)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
nhgpio_pin_read(void * arg,int pin)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
nhgpio_pin_ctl(void * arg,int pin,int flags)243 nhgpio_pin_ctl(void *arg, int pin, int flags)
244 {
245 
246 	/* nothing to control */
247 }
248 #endif
249 
250 static void
nhpow_reboot(int howto)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
nhpow_sysctl_fan(SYSCTLFN_ARGS)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
hwintr(void * arg)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
guarded_pbutton(void * arg)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
sched_sysmon_pbutton(void * arg)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