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