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/cdefs.h> 30 #include <sys/param.h> 31 #include <sys/bus.h> 32 #include <sys/kernel.h> 33 #include <sys/module.h> 34 #include <sys/gpio.h> 35 36 #include <dev/gpio/gpiobusvar.h> 37 #include <dev/ofw/ofw_bus.h> 38 #include <dev/ofw/ofw_bus_subr.h> 39 40 #include <dev/extres/clk/clk.h> 41 42 #include "mmc_pwrseq_if.h" 43 44 enum pwrseq_type { 45 PWRSEQ_SIMPLE = 1, 46 PWRSEQ_EMMC, 47 }; 48 49 static struct ofw_compat_data compat_data[] = { 50 { "mmc-pwrseq-simple", PWRSEQ_SIMPLE }, 51 { "mmc-pwrseq-emmc", PWRSEQ_EMMC }, 52 { NULL, 0 } 53 }; 54 55 struct mmc_pwrseq_softc { 56 enum pwrseq_type type; 57 clk_t ext_clock; 58 struct gpiobus_pin *reset_gpio; 59 60 uint32_t post_power_on_delay_ms; 61 uint32_t power_off_delay_us; 62 }; 63 64 static int 65 mmc_pwrseq_probe(device_t dev) 66 { 67 enum pwrseq_type type; 68 69 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 70 return (ENXIO); 71 72 type = (enum pwrseq_type)ofw_bus_search_compatible(dev, compat_data)->ocd_data; 73 switch (type) { 74 case PWRSEQ_SIMPLE: 75 device_set_desc(dev, "MMC Simple Power sequence"); 76 break; 77 case PWRSEQ_EMMC: 78 device_set_desc(dev, "MMC eMMC Power sequence"); 79 break; 80 } 81 return (BUS_PROBE_DEFAULT); 82 } 83 84 static int 85 mmc_pwrseq_attach(device_t dev) 86 { 87 struct mmc_pwrseq_softc *sc; 88 phandle_t node; 89 int rv; 90 91 sc = device_get_softc(dev); 92 sc->type = (enum pwrseq_type)ofw_bus_search_compatible(dev, compat_data)->ocd_data; 93 node = ofw_bus_get_node(dev); 94 95 if (sc->type == PWRSEQ_SIMPLE) { 96 if (OF_hasprop(node, "clocks")) { 97 rv = clk_get_by_ofw_name(dev, 0, "ext_clock", &sc->ext_clock); 98 if (rv != 0) { 99 device_printf(dev, 100 "Node have a clocks property but no clocks named \"ext_clock\"\n"); 101 return (ENXIO); 102 } 103 } 104 OF_getencprop(node, "post-power-on-delay-ms", &sc->post_power_on_delay_ms, sizeof(uint32_t)); 105 OF_getencprop(node, "power-off-delay-us", &sc->power_off_delay_us, sizeof(uint32_t)); 106 } 107 108 if (OF_hasprop(node, "reset-gpios")) { 109 if (gpio_pin_get_by_ofw_property(dev, node, "reset-gpios", 110 &sc->reset_gpio) != 0) { 111 device_printf(dev, "Cannot get the reset-gpios\n"); 112 return (ENXIO); 113 } 114 gpio_pin_setflags(sc->reset_gpio, GPIO_PIN_OUTPUT); 115 gpio_pin_set_active(sc->reset_gpio, true); 116 } 117 118 OF_device_register_xref(OF_xref_from_node(node), dev); 119 return (0); 120 } 121 122 static int 123 mmc_pwrseq_detach(device_t dev) 124 { 125 126 return (EBUSY); 127 } 128 129 static int 130 mmv_pwrseq_set_power(device_t dev, bool power_on) 131 { 132 struct mmc_pwrseq_softc *sc; 133 int rv; 134 135 sc = device_get_softc(dev); 136 137 if (power_on) { 138 if (sc->ext_clock) { 139 rv = clk_enable(sc->ext_clock); 140 if (rv != 0) 141 return (rv); 142 } 143 144 if (sc->reset_gpio) { 145 rv = gpio_pin_set_active(sc->reset_gpio, false); 146 if (rv != 0) 147 return (rv); 148 } 149 150 if (sc->post_power_on_delay_ms) 151 DELAY(sc->post_power_on_delay_ms * 1000); 152 } else { 153 if (sc->reset_gpio) { 154 rv = gpio_pin_set_active(sc->reset_gpio, true); 155 if (rv != 0) 156 return (rv); 157 } 158 159 if (sc->ext_clock) { 160 rv = clk_stop(sc->ext_clock); 161 if (rv != 0) 162 return (rv); 163 } 164 if (sc->power_off_delay_us) 165 DELAY(sc->power_off_delay_us); 166 } 167 168 return (0); 169 } 170 171 static device_method_t mmc_pwrseq_methods[] = { 172 /* Device interface */ 173 DEVMETHOD(device_probe, mmc_pwrseq_probe), 174 DEVMETHOD(device_attach, mmc_pwrseq_attach), 175 DEVMETHOD(device_detach, mmc_pwrseq_detach), 176 177 DEVMETHOD(mmc_pwrseq_set_power, mmv_pwrseq_set_power), 178 DEVMETHOD_END 179 }; 180 181 static driver_t mmc_pwrseq_driver = { 182 "mmc_pwrseq", 183 mmc_pwrseq_methods, 184 sizeof(struct mmc_pwrseq_softc), 185 }; 186 187 EARLY_DRIVER_MODULE(mmc_pwrseq, simplebus, mmc_pwrseq_driver, 0, 0, 188 BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_FIRST); 189 MODULE_VERSION(mmc_pwrseq, 1); 190 SIMPLEBUS_PNP_INFO(compat_data); 191