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