xref: /freebsd/sys/dev/syscon/syscon_power.c (revision 62e8ccc3a489434af379c7f47da71545bc1e14ee)
1*62e8ccc3SEmmanuel Vadot /*-
2*62e8ccc3SEmmanuel Vadot  * SPDX-License-Identifier: BSD-2-Clause
3*62e8ccc3SEmmanuel Vadot  *
4*62e8ccc3SEmmanuel Vadot  * Copyright (c) 2020 Jessica Clarke <jrtc27@FreeBSD.org>
5*62e8ccc3SEmmanuel Vadot  *
6*62e8ccc3SEmmanuel Vadot  * Redistribution and use in source and binary forms, with or without
7*62e8ccc3SEmmanuel Vadot  * modification, are permitted provided that the following conditions
8*62e8ccc3SEmmanuel Vadot  * are met:
9*62e8ccc3SEmmanuel Vadot  * 1. Redistributions of source code must retain the above copyright
10*62e8ccc3SEmmanuel Vadot  *    notice, this list of conditions and the following disclaimer.
11*62e8ccc3SEmmanuel Vadot  * 2. Redistributions in binary form must reproduce the above copyright
12*62e8ccc3SEmmanuel Vadot  *    notice, this list of conditions and the following disclaimer in the
13*62e8ccc3SEmmanuel Vadot  *    documentation and/or other materials provided with the distribution.
14*62e8ccc3SEmmanuel Vadot  *
15*62e8ccc3SEmmanuel Vadot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16*62e8ccc3SEmmanuel Vadot  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*62e8ccc3SEmmanuel Vadot  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*62e8ccc3SEmmanuel Vadot  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19*62e8ccc3SEmmanuel Vadot  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*62e8ccc3SEmmanuel Vadot  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21*62e8ccc3SEmmanuel Vadot  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22*62e8ccc3SEmmanuel Vadot  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23*62e8ccc3SEmmanuel Vadot  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*62e8ccc3SEmmanuel Vadot  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*62e8ccc3SEmmanuel Vadot  * SUCH DAMAGE.
26*62e8ccc3SEmmanuel Vadot  */
27*62e8ccc3SEmmanuel Vadot 
28*62e8ccc3SEmmanuel Vadot /*
29*62e8ccc3SEmmanuel Vadot  * Driver for simple syscon poweroff and reset devices. The device tree
30*62e8ccc3SEmmanuel Vadot  * specifications are fully described at:
31*62e8ccc3SEmmanuel Vadot  *
32*62e8ccc3SEmmanuel Vadot  * https://www.kernel.org/doc/Documentation/devicetree/bindings/power/reset/syscon-poweroff.txt
33*62e8ccc3SEmmanuel Vadot  * https://www.kernel.org/doc/Documentation/devicetree/bindings/power/reset/syscon-reboot.txt
34*62e8ccc3SEmmanuel Vadot  */
35*62e8ccc3SEmmanuel Vadot 
36*62e8ccc3SEmmanuel Vadot #include <sys/param.h>
37*62e8ccc3SEmmanuel Vadot #include <sys/bus.h>
38*62e8ccc3SEmmanuel Vadot #include <sys/types.h>
39*62e8ccc3SEmmanuel Vadot #include <sys/eventhandler.h>
40*62e8ccc3SEmmanuel Vadot #include <sys/kernel.h>
41*62e8ccc3SEmmanuel Vadot #include <sys/module.h>
42*62e8ccc3SEmmanuel Vadot #include <sys/reboot.h>
43*62e8ccc3SEmmanuel Vadot 
44*62e8ccc3SEmmanuel Vadot #include <machine/bus.h>
45*62e8ccc3SEmmanuel Vadot 
46*62e8ccc3SEmmanuel Vadot #include <dev/ofw/ofw_bus.h>
47*62e8ccc3SEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h>
48*62e8ccc3SEmmanuel Vadot #include <dev/ofw/openfirm.h>
49*62e8ccc3SEmmanuel Vadot 
50*62e8ccc3SEmmanuel Vadot #include "syscon_if.h"
51*62e8ccc3SEmmanuel Vadot #include "syscon.h"
52*62e8ccc3SEmmanuel Vadot 
53*62e8ccc3SEmmanuel Vadot struct syscon_power_softc {
54*62e8ccc3SEmmanuel Vadot 	struct syscon		*regmap;
55*62e8ccc3SEmmanuel Vadot 	uint32_t		offset;
56*62e8ccc3SEmmanuel Vadot 	uint32_t		value;
57*62e8ccc3SEmmanuel Vadot 	uint32_t		mask;
58*62e8ccc3SEmmanuel Vadot 	bool			reboot;
59*62e8ccc3SEmmanuel Vadot 	eventhandler_tag	shutdown_tag;
60*62e8ccc3SEmmanuel Vadot };
61*62e8ccc3SEmmanuel Vadot 
62*62e8ccc3SEmmanuel Vadot static void
63*62e8ccc3SEmmanuel Vadot syscon_power_shutdown_final(device_t dev, int howto)
64*62e8ccc3SEmmanuel Vadot {
65*62e8ccc3SEmmanuel Vadot 	struct syscon_power_softc *sc;
66*62e8ccc3SEmmanuel Vadot 	bool write;
67*62e8ccc3SEmmanuel Vadot 
68*62e8ccc3SEmmanuel Vadot 	sc = device_get_softc(dev);
69*62e8ccc3SEmmanuel Vadot 	if (sc->reboot)
70*62e8ccc3SEmmanuel Vadot 		write = (howto & RB_HALT) == 0;
71*62e8ccc3SEmmanuel Vadot 	else
72*62e8ccc3SEmmanuel Vadot 		write = (howto & RB_POWEROFF) != 0;
73*62e8ccc3SEmmanuel Vadot 
74*62e8ccc3SEmmanuel Vadot 	if (write)
75*62e8ccc3SEmmanuel Vadot 		SYSCON_MODIFY_4(sc->regmap, sc->offset, sc->mask,
76*62e8ccc3SEmmanuel Vadot 		    sc->value & sc->mask);
77*62e8ccc3SEmmanuel Vadot }
78*62e8ccc3SEmmanuel Vadot 
79*62e8ccc3SEmmanuel Vadot static int
80*62e8ccc3SEmmanuel Vadot syscon_power_probe(device_t dev)
81*62e8ccc3SEmmanuel Vadot {
82*62e8ccc3SEmmanuel Vadot 
83*62e8ccc3SEmmanuel Vadot 	if (!ofw_bus_status_okay(dev))
84*62e8ccc3SEmmanuel Vadot 		return (ENXIO);
85*62e8ccc3SEmmanuel Vadot 
86*62e8ccc3SEmmanuel Vadot 	if (ofw_bus_is_compatible(dev, "syscon-poweroff")) {
87*62e8ccc3SEmmanuel Vadot 		device_set_desc(dev, "Syscon poweroff");
88*62e8ccc3SEmmanuel Vadot 		return (BUS_PROBE_DEFAULT);
89*62e8ccc3SEmmanuel Vadot 	} else if (ofw_bus_is_compatible(dev, "syscon-reboot")) {
90*62e8ccc3SEmmanuel Vadot 		device_set_desc(dev, "Syscon reboot");
91*62e8ccc3SEmmanuel Vadot 		return (BUS_PROBE_DEFAULT);
92*62e8ccc3SEmmanuel Vadot 	}
93*62e8ccc3SEmmanuel Vadot 
94*62e8ccc3SEmmanuel Vadot 	return (ENXIO);
95*62e8ccc3SEmmanuel Vadot }
96*62e8ccc3SEmmanuel Vadot 
97*62e8ccc3SEmmanuel Vadot static int
98*62e8ccc3SEmmanuel Vadot syscon_power_attach(device_t dev)
99*62e8ccc3SEmmanuel Vadot {
100*62e8ccc3SEmmanuel Vadot 	struct syscon_power_softc *sc;
101*62e8ccc3SEmmanuel Vadot 	phandle_t node;
102*62e8ccc3SEmmanuel Vadot 	int error, len;
103*62e8ccc3SEmmanuel Vadot 	bool has_mask;
104*62e8ccc3SEmmanuel Vadot 
105*62e8ccc3SEmmanuel Vadot 	sc = device_get_softc(dev);
106*62e8ccc3SEmmanuel Vadot 	node = ofw_bus_get_node(dev);
107*62e8ccc3SEmmanuel Vadot 
108*62e8ccc3SEmmanuel Vadot 	if (!OF_hasprop(node, "regmap")) {
109*62e8ccc3SEmmanuel Vadot 		device_printf(dev, "could not find regmap\n");
110*62e8ccc3SEmmanuel Vadot 		return (ENXIO);
111*62e8ccc3SEmmanuel Vadot 	}
112*62e8ccc3SEmmanuel Vadot 
113*62e8ccc3SEmmanuel Vadot 	error = syscon_get_by_ofw_property(dev, node, "regmap", &sc->regmap);
114*62e8ccc3SEmmanuel Vadot 	if (error != 0) {
115*62e8ccc3SEmmanuel Vadot 		device_printf(dev, "could not get syscon\n");
116*62e8ccc3SEmmanuel Vadot 		return (ENXIO);
117*62e8ccc3SEmmanuel Vadot 	}
118*62e8ccc3SEmmanuel Vadot 
119*62e8ccc3SEmmanuel Vadot 	len = OF_getproplen(node, "offset");
120*62e8ccc3SEmmanuel Vadot 	if (len != 4) {
121*62e8ccc3SEmmanuel Vadot 		device_printf(dev, "could not get offset\n");
122*62e8ccc3SEmmanuel Vadot 		return (ENXIO);
123*62e8ccc3SEmmanuel Vadot 	}
124*62e8ccc3SEmmanuel Vadot 
125*62e8ccc3SEmmanuel Vadot 	OF_getencprop(node, "offset", &sc->offset, sizeof(sc->offset));
126*62e8ccc3SEmmanuel Vadot 
127*62e8ccc3SEmmanuel Vadot 	/* Optional mask */
128*62e8ccc3SEmmanuel Vadot 	has_mask = OF_hasprop(node, "mask");
129*62e8ccc3SEmmanuel Vadot 	if (has_mask) {
130*62e8ccc3SEmmanuel Vadot 		len = OF_getproplen(node, "mask");
131*62e8ccc3SEmmanuel Vadot 		if (len != 4) {
132*62e8ccc3SEmmanuel Vadot 			device_printf(dev, "cannot handle mask\n");
133*62e8ccc3SEmmanuel Vadot 			return (ENXIO);
134*62e8ccc3SEmmanuel Vadot 		}
135*62e8ccc3SEmmanuel Vadot 
136*62e8ccc3SEmmanuel Vadot 		OF_getencprop(node, "mask", &sc->mask, sizeof(sc->mask));
137*62e8ccc3SEmmanuel Vadot 	} else {
138*62e8ccc3SEmmanuel Vadot 		sc->mask = 0xffffffff;
139*62e8ccc3SEmmanuel Vadot 	}
140*62e8ccc3SEmmanuel Vadot 
141*62e8ccc3SEmmanuel Vadot 	/*
142*62e8ccc3SEmmanuel Vadot 	 * From the device tree specification:
143*62e8ccc3SEmmanuel Vadot 	 *
144*62e8ccc3SEmmanuel Vadot 	 *   Legacy usage: If a node doesn't contain a value property but
145*62e8ccc3SEmmanuel Vadot 	 *   contains a mask property, the mask property is used as the value.
146*62e8ccc3SEmmanuel Vadot 	 */
147*62e8ccc3SEmmanuel Vadot 	if (!OF_hasprop(node, "value")) {
148*62e8ccc3SEmmanuel Vadot 		if (!has_mask) {
149*62e8ccc3SEmmanuel Vadot 			device_printf(dev, "must have a value or a mask\n");
150*62e8ccc3SEmmanuel Vadot 			return (ENXIO);
151*62e8ccc3SEmmanuel Vadot 		}
152*62e8ccc3SEmmanuel Vadot 
153*62e8ccc3SEmmanuel Vadot 		sc->value = sc->mask;
154*62e8ccc3SEmmanuel Vadot 	} else {
155*62e8ccc3SEmmanuel Vadot 		len = OF_getproplen(node, "value");
156*62e8ccc3SEmmanuel Vadot 		if (len != 4) {
157*62e8ccc3SEmmanuel Vadot 			device_printf(dev, "cannot handle value\n");
158*62e8ccc3SEmmanuel Vadot 			return (ENXIO);
159*62e8ccc3SEmmanuel Vadot 		}
160*62e8ccc3SEmmanuel Vadot 
161*62e8ccc3SEmmanuel Vadot 		OF_getencprop(node, "value", &sc->value, sizeof(sc->value));
162*62e8ccc3SEmmanuel Vadot 	}
163*62e8ccc3SEmmanuel Vadot 
164*62e8ccc3SEmmanuel Vadot 	sc->reboot = ofw_bus_is_compatible(dev, "syscon-reboot");
165*62e8ccc3SEmmanuel Vadot 	sc->shutdown_tag = EVENTHANDLER_REGISTER(shutdown_final,
166*62e8ccc3SEmmanuel Vadot 	    syscon_power_shutdown_final, dev, SHUTDOWN_PRI_LAST);
167*62e8ccc3SEmmanuel Vadot 
168*62e8ccc3SEmmanuel Vadot 	return (0);
169*62e8ccc3SEmmanuel Vadot }
170*62e8ccc3SEmmanuel Vadot 
171*62e8ccc3SEmmanuel Vadot static int
172*62e8ccc3SEmmanuel Vadot syscon_power_detach(device_t dev)
173*62e8ccc3SEmmanuel Vadot {
174*62e8ccc3SEmmanuel Vadot 	struct syscon_power_softc *sc;
175*62e8ccc3SEmmanuel Vadot 
176*62e8ccc3SEmmanuel Vadot 	sc = device_get_softc(dev);
177*62e8ccc3SEmmanuel Vadot 	EVENTHANDLER_DEREGISTER(shutdown_final, sc->shutdown_tag);
178*62e8ccc3SEmmanuel Vadot 
179*62e8ccc3SEmmanuel Vadot 	return (0);
180*62e8ccc3SEmmanuel Vadot }
181*62e8ccc3SEmmanuel Vadot 
182*62e8ccc3SEmmanuel Vadot static device_method_t syscon_power_methods[] = {
183*62e8ccc3SEmmanuel Vadot 	DEVMETHOD(device_probe,		syscon_power_probe),
184*62e8ccc3SEmmanuel Vadot 	DEVMETHOD(device_attach,	syscon_power_attach),
185*62e8ccc3SEmmanuel Vadot 	DEVMETHOD(device_detach,	syscon_power_detach),
186*62e8ccc3SEmmanuel Vadot 
187*62e8ccc3SEmmanuel Vadot 	DEVMETHOD_END
188*62e8ccc3SEmmanuel Vadot };
189*62e8ccc3SEmmanuel Vadot 
190*62e8ccc3SEmmanuel Vadot DEFINE_CLASS_0(syscon_power, syscon_power_driver, syscon_power_methods,
191*62e8ccc3SEmmanuel Vadot     sizeof(struct syscon_power_softc));
192*62e8ccc3SEmmanuel Vadot 
193*62e8ccc3SEmmanuel Vadot DRIVER_MODULE(syscon_power, simplebus, syscon_power_driver, NULL, NULL);
194