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