1*5b2a81f5SEmmanuel Vadot /* 2*5b2a81f5SEmmanuel Vadot * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*5b2a81f5SEmmanuel Vadot * 4*5b2a81f5SEmmanuel Vadot * Copyright 2021 Emmanuel Vadot <manu@freebsd.org> 5*5b2a81f5SEmmanuel Vadot * 6*5b2a81f5SEmmanuel Vadot * Redistribution and use in source and binary forms, with or without 7*5b2a81f5SEmmanuel Vadot * modification, are permitted provided that the following conditions are 8*5b2a81f5SEmmanuel Vadot * met: 9*5b2a81f5SEmmanuel Vadot * 10*5b2a81f5SEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright 11*5b2a81f5SEmmanuel Vadot * notice, this list of conditions and the following disclaimer. 12*5b2a81f5SEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright 13*5b2a81f5SEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the 14*5b2a81f5SEmmanuel Vadot * documentation and/or other materials provided with the distribution. 15*5b2a81f5SEmmanuel Vadot * 16*5b2a81f5SEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17*5b2a81f5SEmmanuel Vadot * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18*5b2a81f5SEmmanuel Vadot * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19*5b2a81f5SEmmanuel Vadot * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE 20*5b2a81f5SEmmanuel Vadot * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21*5b2a81f5SEmmanuel Vadot * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22*5b2a81f5SEmmanuel Vadot * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23*5b2a81f5SEmmanuel Vadot * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24*5b2a81f5SEmmanuel Vadot * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25*5b2a81f5SEmmanuel Vadot * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26*5b2a81f5SEmmanuel Vadot * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27*5b2a81f5SEmmanuel Vadot */ 28*5b2a81f5SEmmanuel Vadot 29*5b2a81f5SEmmanuel Vadot #include <sys/cdefs.h> 30*5b2a81f5SEmmanuel Vadot __FBSDID("$FreeBSD$"); 31*5b2a81f5SEmmanuel Vadot 32*5b2a81f5SEmmanuel Vadot #include <sys/param.h> 33*5b2a81f5SEmmanuel Vadot #include <sys/bus.h> 34*5b2a81f5SEmmanuel Vadot #include <sys/kernel.h> 35*5b2a81f5SEmmanuel Vadot #include <sys/module.h> 36*5b2a81f5SEmmanuel Vadot #include <sys/gpio.h> 37*5b2a81f5SEmmanuel Vadot 38*5b2a81f5SEmmanuel Vadot #include <dev/gpio/gpiobusvar.h> 39*5b2a81f5SEmmanuel Vadot #include <dev/ofw/ofw_bus.h> 40*5b2a81f5SEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h> 41*5b2a81f5SEmmanuel Vadot 42*5b2a81f5SEmmanuel Vadot #include <dev/extres/clk/clk.h> 43*5b2a81f5SEmmanuel Vadot 44*5b2a81f5SEmmanuel Vadot #include "mmc_pwrseq_if.h" 45*5b2a81f5SEmmanuel Vadot 46*5b2a81f5SEmmanuel Vadot enum pwrseq_type { 47*5b2a81f5SEmmanuel Vadot PWRSEQ_SIMPLE = 1, 48*5b2a81f5SEmmanuel Vadot PWRSEQ_EMMC, 49*5b2a81f5SEmmanuel Vadot }; 50*5b2a81f5SEmmanuel Vadot 51*5b2a81f5SEmmanuel Vadot static struct ofw_compat_data compat_data[] = { 52*5b2a81f5SEmmanuel Vadot { "mmc-pwrseq-simple", PWRSEQ_SIMPLE }, 53*5b2a81f5SEmmanuel Vadot { "mmc-pwrseq-emmc", PWRSEQ_EMMC }, 54*5b2a81f5SEmmanuel Vadot { NULL, 0 } 55*5b2a81f5SEmmanuel Vadot }; 56*5b2a81f5SEmmanuel Vadot 57*5b2a81f5SEmmanuel Vadot struct mmc_pwrseq_softc { 58*5b2a81f5SEmmanuel Vadot enum pwrseq_type type; 59*5b2a81f5SEmmanuel Vadot clk_t ext_clock; 60*5b2a81f5SEmmanuel Vadot struct gpiobus_pin *reset_gpio; 61*5b2a81f5SEmmanuel Vadot 62*5b2a81f5SEmmanuel Vadot uint32_t post_power_on_delay_ms; 63*5b2a81f5SEmmanuel Vadot uint32_t power_off_delay_us; 64*5b2a81f5SEmmanuel Vadot }; 65*5b2a81f5SEmmanuel Vadot 66*5b2a81f5SEmmanuel Vadot static int 67*5b2a81f5SEmmanuel Vadot mmc_pwrseq_probe(device_t dev) 68*5b2a81f5SEmmanuel Vadot { 69*5b2a81f5SEmmanuel Vadot enum pwrseq_type type; 70*5b2a81f5SEmmanuel Vadot 71*5b2a81f5SEmmanuel Vadot if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 72*5b2a81f5SEmmanuel Vadot return (ENXIO); 73*5b2a81f5SEmmanuel Vadot 74*5b2a81f5SEmmanuel Vadot type = (enum pwrseq_type)ofw_bus_search_compatible(dev, compat_data)->ocd_data; 75*5b2a81f5SEmmanuel Vadot switch (type) { 76*5b2a81f5SEmmanuel Vadot case PWRSEQ_SIMPLE: 77*5b2a81f5SEmmanuel Vadot device_set_desc(dev, "MMC Simple Power sequence"); 78*5b2a81f5SEmmanuel Vadot break; 79*5b2a81f5SEmmanuel Vadot case PWRSEQ_EMMC: 80*5b2a81f5SEmmanuel Vadot device_set_desc(dev, "MMC eMMC Power sequence"); 81*5b2a81f5SEmmanuel Vadot break; 82*5b2a81f5SEmmanuel Vadot } 83*5b2a81f5SEmmanuel Vadot return (BUS_PROBE_DEFAULT); 84*5b2a81f5SEmmanuel Vadot } 85*5b2a81f5SEmmanuel Vadot 86*5b2a81f5SEmmanuel Vadot static int 87*5b2a81f5SEmmanuel Vadot mmc_pwrseq_attach(device_t dev) 88*5b2a81f5SEmmanuel Vadot { 89*5b2a81f5SEmmanuel Vadot struct mmc_pwrseq_softc *sc; 90*5b2a81f5SEmmanuel Vadot phandle_t node; 91*5b2a81f5SEmmanuel Vadot int rv; 92*5b2a81f5SEmmanuel Vadot 93*5b2a81f5SEmmanuel Vadot sc = device_get_softc(dev); 94*5b2a81f5SEmmanuel Vadot sc->type = (enum pwrseq_type)ofw_bus_search_compatible(dev, compat_data)->ocd_data; 95*5b2a81f5SEmmanuel Vadot node = ofw_bus_get_node(dev); 96*5b2a81f5SEmmanuel Vadot 97*5b2a81f5SEmmanuel Vadot if (sc->type == PWRSEQ_SIMPLE) { 98*5b2a81f5SEmmanuel Vadot if (OF_hasprop(node, "clocks")) { 99*5b2a81f5SEmmanuel Vadot rv = clk_get_by_ofw_name(dev, 0, "ext_clock", &sc->ext_clock); 100*5b2a81f5SEmmanuel Vadot if (rv != 0) { 101*5b2a81f5SEmmanuel Vadot device_printf(dev, 102*5b2a81f5SEmmanuel Vadot "Node have a clocks property but no clocks named \"ext_clock\"\n"); 103*5b2a81f5SEmmanuel Vadot return (ENXIO); 104*5b2a81f5SEmmanuel Vadot } 105*5b2a81f5SEmmanuel Vadot } 106*5b2a81f5SEmmanuel Vadot OF_getencprop(node, "post-power-on-delay-ms", &sc->post_power_on_delay_ms, sizeof(uint32_t)); 107*5b2a81f5SEmmanuel Vadot OF_getencprop(node, "power-off-delay-us", &sc->power_off_delay_us, sizeof(uint32_t)); 108*5b2a81f5SEmmanuel Vadot } 109*5b2a81f5SEmmanuel Vadot 110*5b2a81f5SEmmanuel Vadot if (OF_hasprop(node, "reset-gpios")) { 111*5b2a81f5SEmmanuel Vadot if (gpio_pin_get_by_ofw_property(dev, node, "reset-gpios", 112*5b2a81f5SEmmanuel Vadot &sc->reset_gpio) != 0) { 113*5b2a81f5SEmmanuel Vadot device_printf(dev, "Cannot get the reset-gpios\n"); 114*5b2a81f5SEmmanuel Vadot return (ENXIO); 115*5b2a81f5SEmmanuel Vadot } 116*5b2a81f5SEmmanuel Vadot gpio_pin_setflags(sc->reset_gpio, GPIO_PIN_OUTPUT); 117*5b2a81f5SEmmanuel Vadot gpio_pin_set_active(sc->reset_gpio, true); 118*5b2a81f5SEmmanuel Vadot } 119*5b2a81f5SEmmanuel Vadot 120*5b2a81f5SEmmanuel Vadot OF_device_register_xref(OF_xref_from_node(node), dev); 121*5b2a81f5SEmmanuel Vadot return (0); 122*5b2a81f5SEmmanuel Vadot } 123*5b2a81f5SEmmanuel Vadot 124*5b2a81f5SEmmanuel Vadot static int 125*5b2a81f5SEmmanuel Vadot mmc_pwrseq_detach(device_t dev) 126*5b2a81f5SEmmanuel Vadot { 127*5b2a81f5SEmmanuel Vadot 128*5b2a81f5SEmmanuel Vadot return (EBUSY); 129*5b2a81f5SEmmanuel Vadot } 130*5b2a81f5SEmmanuel Vadot 131*5b2a81f5SEmmanuel Vadot static int 132*5b2a81f5SEmmanuel Vadot mmv_pwrseq_set_power(device_t dev, bool power_on) 133*5b2a81f5SEmmanuel Vadot { 134*5b2a81f5SEmmanuel Vadot struct mmc_pwrseq_softc *sc; 135*5b2a81f5SEmmanuel Vadot int rv; 136*5b2a81f5SEmmanuel Vadot 137*5b2a81f5SEmmanuel Vadot sc = device_get_softc(dev); 138*5b2a81f5SEmmanuel Vadot 139*5b2a81f5SEmmanuel Vadot if (power_on) { 140*5b2a81f5SEmmanuel Vadot if (sc->ext_clock) { 141*5b2a81f5SEmmanuel Vadot rv = clk_enable(sc->ext_clock); 142*5b2a81f5SEmmanuel Vadot if (rv != 0) 143*5b2a81f5SEmmanuel Vadot return (rv); 144*5b2a81f5SEmmanuel Vadot } 145*5b2a81f5SEmmanuel Vadot 146*5b2a81f5SEmmanuel Vadot if (sc->reset_gpio) { 147*5b2a81f5SEmmanuel Vadot rv = gpio_pin_set_active(sc->reset_gpio, false); 148*5b2a81f5SEmmanuel Vadot if (rv != 0) 149*5b2a81f5SEmmanuel Vadot return (rv); 150*5b2a81f5SEmmanuel Vadot } 151*5b2a81f5SEmmanuel Vadot 152*5b2a81f5SEmmanuel Vadot if (sc->post_power_on_delay_ms) 153*5b2a81f5SEmmanuel Vadot DELAY(sc->post_power_on_delay_ms * 1000); 154*5b2a81f5SEmmanuel Vadot } else { 155*5b2a81f5SEmmanuel Vadot if (sc->reset_gpio) { 156*5b2a81f5SEmmanuel Vadot rv = gpio_pin_set_active(sc->reset_gpio, true); 157*5b2a81f5SEmmanuel Vadot if (rv != 0) 158*5b2a81f5SEmmanuel Vadot return (rv); 159*5b2a81f5SEmmanuel Vadot } 160*5b2a81f5SEmmanuel Vadot 161*5b2a81f5SEmmanuel Vadot if (sc->ext_clock) { 162*5b2a81f5SEmmanuel Vadot rv = clk_stop(sc->ext_clock); 163*5b2a81f5SEmmanuel Vadot if (rv != 0) 164*5b2a81f5SEmmanuel Vadot return (rv); 165*5b2a81f5SEmmanuel Vadot } 166*5b2a81f5SEmmanuel Vadot if (sc->power_off_delay_us) 167*5b2a81f5SEmmanuel Vadot DELAY(sc->power_off_delay_us); 168*5b2a81f5SEmmanuel Vadot } 169*5b2a81f5SEmmanuel Vadot 170*5b2a81f5SEmmanuel Vadot return (0); 171*5b2a81f5SEmmanuel Vadot } 172*5b2a81f5SEmmanuel Vadot 173*5b2a81f5SEmmanuel Vadot static device_method_t mmc_pwrseq_methods[] = { 174*5b2a81f5SEmmanuel Vadot /* Device interface */ 175*5b2a81f5SEmmanuel Vadot DEVMETHOD(device_probe, mmc_pwrseq_probe), 176*5b2a81f5SEmmanuel Vadot DEVMETHOD(device_attach, mmc_pwrseq_attach), 177*5b2a81f5SEmmanuel Vadot DEVMETHOD(device_detach, mmc_pwrseq_detach), 178*5b2a81f5SEmmanuel Vadot 179*5b2a81f5SEmmanuel Vadot DEVMETHOD(mmc_pwrseq_set_power, mmv_pwrseq_set_power), 180*5b2a81f5SEmmanuel Vadot DEVMETHOD_END 181*5b2a81f5SEmmanuel Vadot }; 182*5b2a81f5SEmmanuel Vadot 183*5b2a81f5SEmmanuel Vadot static driver_t mmc_pwrseq_driver = { 184*5b2a81f5SEmmanuel Vadot "mmc_pwrseq", 185*5b2a81f5SEmmanuel Vadot mmc_pwrseq_methods, 186*5b2a81f5SEmmanuel Vadot sizeof(struct mmc_pwrseq_softc), 187*5b2a81f5SEmmanuel Vadot }; 188*5b2a81f5SEmmanuel Vadot 189*5b2a81f5SEmmanuel Vadot static devclass_t mmc_pwrseq_devclass; 190*5b2a81f5SEmmanuel Vadot 191*5b2a81f5SEmmanuel Vadot EARLY_DRIVER_MODULE(mmc_pwrseq, simplebus, mmc_pwrseq_driver, mmc_pwrseq_devclass, 0, 0, 192*5b2a81f5SEmmanuel Vadot BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_FIRST); 193*5b2a81f5SEmmanuel Vadot MODULE_VERSION(mmc_pwrseq, 1); 194*5b2a81f5SEmmanuel Vadot SIMPLEBUS_PNP_INFO(compat_data); 195