xref: /freebsd/sys/dev/gpio/gpiopower.c (revision 320e4beb97618c6964380bfa404a3e96c5de7663)
1099a0e1bSJustin Hibbits /*-
2099a0e1bSJustin Hibbits  * Copyright (c) 2016 Justin Hibbits
3099a0e1bSJustin Hibbits  * All rights reserved.
4099a0e1bSJustin Hibbits  *
5099a0e1bSJustin Hibbits  * Redistribution and use in source and binary forms, with or without
6099a0e1bSJustin Hibbits  * modification, are permitted provided that the following conditions
7099a0e1bSJustin Hibbits  * are met:
8099a0e1bSJustin Hibbits  * 1. Redistributions of source code must retain the above copyright
9099a0e1bSJustin Hibbits  *    notice, this list of conditions and the following disclaimer.
10099a0e1bSJustin Hibbits  * 2. Redistributions in binary form must reproduce the above copyright
11099a0e1bSJustin Hibbits  *    notice, this list of conditions and the following disclaimer in the
12099a0e1bSJustin Hibbits  *    documentation and/or other materials provided with the distribution.
13099a0e1bSJustin Hibbits  *
14099a0e1bSJustin Hibbits  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15099a0e1bSJustin Hibbits  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16099a0e1bSJustin Hibbits  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17099a0e1bSJustin Hibbits  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18099a0e1bSJustin Hibbits  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19099a0e1bSJustin Hibbits  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20099a0e1bSJustin Hibbits  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21099a0e1bSJustin Hibbits  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22099a0e1bSJustin Hibbits  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23099a0e1bSJustin Hibbits  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24099a0e1bSJustin Hibbits  * SUCH DAMAGE.
25099a0e1bSJustin Hibbits  */
26099a0e1bSJustin Hibbits 
27099a0e1bSJustin Hibbits 
28099a0e1bSJustin Hibbits #include <sys/param.h>
29099a0e1bSJustin Hibbits #include <sys/systm.h>
30099a0e1bSJustin Hibbits #include <sys/bus.h>
31099a0e1bSJustin Hibbits #include <sys/eventhandler.h>
32099a0e1bSJustin Hibbits #include <sys/gpio.h>
33099a0e1bSJustin Hibbits #include <sys/kernel.h>
34099a0e1bSJustin Hibbits #include <sys/module.h>
35099a0e1bSJustin Hibbits #include <sys/reboot.h>
36099a0e1bSJustin Hibbits 
37*320e4bebSAndriy Gapon #include <dev/fdt/fdt_common.h>
38099a0e1bSJustin Hibbits #include <dev/ofw/ofw_bus.h>
39099a0e1bSJustin Hibbits 
40099a0e1bSJustin Hibbits #include <dev/gpio/gpiobusvar.h>
41099a0e1bSJustin Hibbits 
42099a0e1bSJustin Hibbits struct gpiopower_softc {
43099a0e1bSJustin Hibbits 	gpio_pin_t	sc_pin;
44099a0e1bSJustin Hibbits 	int		sc_rbmask;
45*320e4bebSAndriy Gapon 	int		sc_hi_period;
46*320e4bebSAndriy Gapon 	int		sc_lo_period;
47*320e4bebSAndriy Gapon 	int		sc_timeout;
48099a0e1bSJustin Hibbits };
49099a0e1bSJustin Hibbits 
50099a0e1bSJustin Hibbits static void gpiopower_assert(device_t dev, int howto);
51099a0e1bSJustin Hibbits 
52099a0e1bSJustin Hibbits static int
gpiopower_probe(device_t dev)53099a0e1bSJustin Hibbits gpiopower_probe(device_t dev)
54099a0e1bSJustin Hibbits {
55099a0e1bSJustin Hibbits 
56099a0e1bSJustin Hibbits 	if (ofw_bus_is_compatible(dev, "gpio-poweroff")) {
57099a0e1bSJustin Hibbits 		device_set_desc(dev, "GPIO poweroff control");
58099a0e1bSJustin Hibbits 		return (0);
59099a0e1bSJustin Hibbits 	} else if (ofw_bus_is_compatible(dev, "gpio-restart")) {
60099a0e1bSJustin Hibbits 		device_set_desc(dev, "GPIO restart control");
61099a0e1bSJustin Hibbits 		return (0);
62099a0e1bSJustin Hibbits 	}
63099a0e1bSJustin Hibbits 
64099a0e1bSJustin Hibbits 	return (ENXIO);
65099a0e1bSJustin Hibbits }
66099a0e1bSJustin Hibbits 
67099a0e1bSJustin Hibbits static int
gpiopower_attach(device_t dev)68099a0e1bSJustin Hibbits gpiopower_attach(device_t dev)
69099a0e1bSJustin Hibbits {
70099a0e1bSJustin Hibbits 	struct gpiopower_softc *sc;
71099a0e1bSJustin Hibbits 	phandle_t node;
72*320e4bebSAndriy Gapon 	uint32_t prop;
73099a0e1bSJustin Hibbits 
74099a0e1bSJustin Hibbits 	sc = device_get_softc(dev);
75099a0e1bSJustin Hibbits 
76099a0e1bSJustin Hibbits 	if ((node = ofw_bus_get_node(dev)) == -1)
77099a0e1bSJustin Hibbits 		return (ENXIO);
78099a0e1bSJustin Hibbits 
79de6353adSJustin Hibbits 	if (ofw_gpiobus_parse_gpios(dev, "gpios", &sc->sc_pin) <= 0) {
80099a0e1bSJustin Hibbits 		device_printf(dev, "failed to map GPIO pin\n");
81099a0e1bSJustin Hibbits 		return (ENXIO);
82099a0e1bSJustin Hibbits 	}
83099a0e1bSJustin Hibbits 
84099a0e1bSJustin Hibbits 	if (ofw_bus_is_compatible(dev, "gpio-poweroff"))
85099a0e1bSJustin Hibbits 		sc->sc_rbmask = RB_HALT | RB_POWEROFF;
86099a0e1bSJustin Hibbits 	else
87099a0e1bSJustin Hibbits 		sc->sc_rbmask = 0;
88*320e4bebSAndriy Gapon 
89*320e4bebSAndriy Gapon 	sc->sc_hi_period = 100000;
90*320e4bebSAndriy Gapon 	sc->sc_lo_period = 100000;
91*320e4bebSAndriy Gapon 	sc->sc_timeout = 1000000;
92*320e4bebSAndriy Gapon 
93*320e4bebSAndriy Gapon 	if ((OF_getprop(node, "active-delay-ms", &prop, sizeof(prop))) > 0)
94*320e4bebSAndriy Gapon 		sc->sc_hi_period = fdt32_to_cpu(prop) * 1000;
95*320e4bebSAndriy Gapon 	if ((OF_getprop(node, "inactive-delay-ms", &prop, sizeof(prop))) > 0)
96*320e4bebSAndriy Gapon 		sc->sc_lo_period = fdt32_to_cpu(prop) * 1000;
97*320e4bebSAndriy Gapon 	if ((OF_getprop(node, "timeout-ms", &prop, sizeof(prop))) > 0)
98*320e4bebSAndriy Gapon 		sc->sc_timeout = fdt32_to_cpu(prop) * 1000;
99*320e4bebSAndriy Gapon 
100099a0e1bSJustin Hibbits 	EVENTHANDLER_REGISTER(shutdown_final, gpiopower_assert, dev,
101099a0e1bSJustin Hibbits 	    SHUTDOWN_PRI_LAST);
102099a0e1bSJustin Hibbits 
103099a0e1bSJustin Hibbits 	return (0);
104099a0e1bSJustin Hibbits }
105099a0e1bSJustin Hibbits 
106099a0e1bSJustin Hibbits static void
gpiopower_assert(device_t dev,int howto)107099a0e1bSJustin Hibbits gpiopower_assert(device_t dev, int howto)
108099a0e1bSJustin Hibbits {
109099a0e1bSJustin Hibbits 	struct gpiopower_softc *sc;
110099a0e1bSJustin Hibbits 	int do_assert;
111099a0e1bSJustin Hibbits 
112099a0e1bSJustin Hibbits 	sc = device_get_softc(dev);
113099a0e1bSJustin Hibbits 	do_assert = sc->sc_rbmask ? (sc->sc_rbmask & howto) :
114099a0e1bSJustin Hibbits 	    ((howto & RB_HALT) == 0);
115099a0e1bSJustin Hibbits 
116099a0e1bSJustin Hibbits 	if (!do_assert)
117099a0e1bSJustin Hibbits 		return;
118099a0e1bSJustin Hibbits 
119099a0e1bSJustin Hibbits 	if (howto & RB_POWEROFF)
120099a0e1bSJustin Hibbits 		device_printf(dev, "powering system off\n");
121099a0e1bSJustin Hibbits 	else if ((howto & RB_HALT) == 0)
122099a0e1bSJustin Hibbits 		device_printf(dev, "resetting system\n");
123099a0e1bSJustin Hibbits 	else
124099a0e1bSJustin Hibbits 		return;
125099a0e1bSJustin Hibbits 
126*320e4bebSAndriy Gapon 	gpio_pin_setflags(sc->sc_pin, GPIO_PIN_OUTPUT);
127099a0e1bSJustin Hibbits 	gpio_pin_set_active(sc->sc_pin, true);
128*320e4bebSAndriy Gapon 	DELAY(sc->sc_hi_period);
129*320e4bebSAndriy Gapon 	gpio_pin_set_active(sc->sc_pin, false);
130*320e4bebSAndriy Gapon 	DELAY(sc->sc_lo_period);
131*320e4bebSAndriy Gapon 	gpio_pin_set_active(sc->sc_pin, true);
132*320e4bebSAndriy Gapon 	DELAY(sc->sc_timeout);
133099a0e1bSJustin Hibbits 
134*320e4bebSAndriy Gapon 	device_printf(dev, "%s failed\n",
135*320e4bebSAndriy Gapon 	    (howto & RB_POWEROFF) != 0 ? "power off" : "reset");
136099a0e1bSJustin Hibbits }
137099a0e1bSJustin Hibbits 
138099a0e1bSJustin Hibbits static device_method_t gpiopower_methods[] = {
139099a0e1bSJustin Hibbits 	/* Device interface */
140099a0e1bSJustin Hibbits 	DEVMETHOD(device_probe,		gpiopower_probe),
141099a0e1bSJustin Hibbits 	DEVMETHOD(device_attach,	gpiopower_attach),
142099a0e1bSJustin Hibbits 
143099a0e1bSJustin Hibbits 	DEVMETHOD_END
144099a0e1bSJustin Hibbits };
145099a0e1bSJustin Hibbits 
146099a0e1bSJustin Hibbits static driver_t gpiopower_driver = {
147099a0e1bSJustin Hibbits 	"gpiopower",
148099a0e1bSJustin Hibbits 	gpiopower_methods,
149099a0e1bSJustin Hibbits 	sizeof(struct gpiopower_softc)
150099a0e1bSJustin Hibbits };
151099a0e1bSJustin Hibbits 
15284c5f982SJohn Baldwin DRIVER_MODULE(gpiopower, simplebus, gpiopower_driver, 0, 0);
153099a0e1bSJustin Hibbits MODULE_DEPEND(gpiopower, gpiobus, 1, 1, 1);
154