15b2a81f5SEmmanuel Vadot /* 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 35b2a81f5SEmmanuel Vadot * 45b2a81f5SEmmanuel Vadot * Copyright 2021 Emmanuel Vadot <manu@freebsd.org> 55b2a81f5SEmmanuel Vadot * 65b2a81f5SEmmanuel Vadot * Redistribution and use in source and binary forms, with or without 75b2a81f5SEmmanuel Vadot * modification, are permitted provided that the following conditions are 85b2a81f5SEmmanuel Vadot * met: 95b2a81f5SEmmanuel Vadot * 105b2a81f5SEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright 115b2a81f5SEmmanuel Vadot * notice, this list of conditions and the following disclaimer. 125b2a81f5SEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright 135b2a81f5SEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the 145b2a81f5SEmmanuel Vadot * documentation and/or other materials provided with the distribution. 155b2a81f5SEmmanuel Vadot * 165b2a81f5SEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 175b2a81f5SEmmanuel Vadot * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 185b2a81f5SEmmanuel Vadot * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 195b2a81f5SEmmanuel Vadot * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE 205b2a81f5SEmmanuel Vadot * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 215b2a81f5SEmmanuel Vadot * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 225b2a81f5SEmmanuel Vadot * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 235b2a81f5SEmmanuel Vadot * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 245b2a81f5SEmmanuel Vadot * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 255b2a81f5SEmmanuel Vadot * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 265b2a81f5SEmmanuel Vadot * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 275b2a81f5SEmmanuel Vadot */ 285b2a81f5SEmmanuel Vadot 295b2a81f5SEmmanuel Vadot #include <sys/cdefs.h> 305b2a81f5SEmmanuel Vadot __FBSDID("$FreeBSD$"); 315b2a81f5SEmmanuel Vadot 325b2a81f5SEmmanuel Vadot #include <sys/param.h> 335b2a81f5SEmmanuel Vadot #include <sys/bus.h> 345b2a81f5SEmmanuel Vadot #include <sys/kernel.h> 355b2a81f5SEmmanuel Vadot #include <sys/module.h> 365b2a81f5SEmmanuel Vadot #include <sys/gpio.h> 375b2a81f5SEmmanuel Vadot 385b2a81f5SEmmanuel Vadot #include <dev/gpio/gpiobusvar.h> 395b2a81f5SEmmanuel Vadot #include <dev/ofw/ofw_bus.h> 405b2a81f5SEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h> 415b2a81f5SEmmanuel Vadot 425b2a81f5SEmmanuel Vadot #include <dev/extres/clk/clk.h> 435b2a81f5SEmmanuel Vadot 445b2a81f5SEmmanuel Vadot #include "mmc_pwrseq_if.h" 455b2a81f5SEmmanuel Vadot 465b2a81f5SEmmanuel Vadot enum pwrseq_type { 475b2a81f5SEmmanuel Vadot PWRSEQ_SIMPLE = 1, 485b2a81f5SEmmanuel Vadot PWRSEQ_EMMC, 495b2a81f5SEmmanuel Vadot }; 505b2a81f5SEmmanuel Vadot 515b2a81f5SEmmanuel Vadot static struct ofw_compat_data compat_data[] = { 525b2a81f5SEmmanuel Vadot { "mmc-pwrseq-simple", PWRSEQ_SIMPLE }, 535b2a81f5SEmmanuel Vadot { "mmc-pwrseq-emmc", PWRSEQ_EMMC }, 545b2a81f5SEmmanuel Vadot { NULL, 0 } 555b2a81f5SEmmanuel Vadot }; 565b2a81f5SEmmanuel Vadot 575b2a81f5SEmmanuel Vadot struct mmc_pwrseq_softc { 585b2a81f5SEmmanuel Vadot enum pwrseq_type type; 595b2a81f5SEmmanuel Vadot clk_t ext_clock; 605b2a81f5SEmmanuel Vadot struct gpiobus_pin *reset_gpio; 615b2a81f5SEmmanuel Vadot 625b2a81f5SEmmanuel Vadot uint32_t post_power_on_delay_ms; 635b2a81f5SEmmanuel Vadot uint32_t power_off_delay_us; 645b2a81f5SEmmanuel Vadot }; 655b2a81f5SEmmanuel Vadot 665b2a81f5SEmmanuel Vadot static int 675b2a81f5SEmmanuel Vadot mmc_pwrseq_probe(device_t dev) 685b2a81f5SEmmanuel Vadot { 695b2a81f5SEmmanuel Vadot enum pwrseq_type type; 705b2a81f5SEmmanuel Vadot 715b2a81f5SEmmanuel Vadot if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 725b2a81f5SEmmanuel Vadot return (ENXIO); 735b2a81f5SEmmanuel Vadot 745b2a81f5SEmmanuel Vadot type = (enum pwrseq_type)ofw_bus_search_compatible(dev, compat_data)->ocd_data; 755b2a81f5SEmmanuel Vadot switch (type) { 765b2a81f5SEmmanuel Vadot case PWRSEQ_SIMPLE: 775b2a81f5SEmmanuel Vadot device_set_desc(dev, "MMC Simple Power sequence"); 785b2a81f5SEmmanuel Vadot break; 795b2a81f5SEmmanuel Vadot case PWRSEQ_EMMC: 805b2a81f5SEmmanuel Vadot device_set_desc(dev, "MMC eMMC Power sequence"); 815b2a81f5SEmmanuel Vadot break; 825b2a81f5SEmmanuel Vadot } 835b2a81f5SEmmanuel Vadot return (BUS_PROBE_DEFAULT); 845b2a81f5SEmmanuel Vadot } 855b2a81f5SEmmanuel Vadot 865b2a81f5SEmmanuel Vadot static int 875b2a81f5SEmmanuel Vadot mmc_pwrseq_attach(device_t dev) 885b2a81f5SEmmanuel Vadot { 895b2a81f5SEmmanuel Vadot struct mmc_pwrseq_softc *sc; 905b2a81f5SEmmanuel Vadot phandle_t node; 915b2a81f5SEmmanuel Vadot int rv; 925b2a81f5SEmmanuel Vadot 935b2a81f5SEmmanuel Vadot sc = device_get_softc(dev); 945b2a81f5SEmmanuel Vadot sc->type = (enum pwrseq_type)ofw_bus_search_compatible(dev, compat_data)->ocd_data; 955b2a81f5SEmmanuel Vadot node = ofw_bus_get_node(dev); 965b2a81f5SEmmanuel Vadot 975b2a81f5SEmmanuel Vadot if (sc->type == PWRSEQ_SIMPLE) { 985b2a81f5SEmmanuel Vadot if (OF_hasprop(node, "clocks")) { 995b2a81f5SEmmanuel Vadot rv = clk_get_by_ofw_name(dev, 0, "ext_clock", &sc->ext_clock); 1005b2a81f5SEmmanuel Vadot if (rv != 0) { 1015b2a81f5SEmmanuel Vadot device_printf(dev, 1025b2a81f5SEmmanuel Vadot "Node have a clocks property but no clocks named \"ext_clock\"\n"); 1035b2a81f5SEmmanuel Vadot return (ENXIO); 1045b2a81f5SEmmanuel Vadot } 1055b2a81f5SEmmanuel Vadot } 1065b2a81f5SEmmanuel Vadot OF_getencprop(node, "post-power-on-delay-ms", &sc->post_power_on_delay_ms, sizeof(uint32_t)); 1075b2a81f5SEmmanuel Vadot OF_getencprop(node, "power-off-delay-us", &sc->power_off_delay_us, sizeof(uint32_t)); 1085b2a81f5SEmmanuel Vadot } 1095b2a81f5SEmmanuel Vadot 1105b2a81f5SEmmanuel Vadot if (OF_hasprop(node, "reset-gpios")) { 1115b2a81f5SEmmanuel Vadot if (gpio_pin_get_by_ofw_property(dev, node, "reset-gpios", 1125b2a81f5SEmmanuel Vadot &sc->reset_gpio) != 0) { 1135b2a81f5SEmmanuel Vadot device_printf(dev, "Cannot get the reset-gpios\n"); 1145b2a81f5SEmmanuel Vadot return (ENXIO); 1155b2a81f5SEmmanuel Vadot } 1165b2a81f5SEmmanuel Vadot gpio_pin_setflags(sc->reset_gpio, GPIO_PIN_OUTPUT); 1175b2a81f5SEmmanuel Vadot gpio_pin_set_active(sc->reset_gpio, true); 1185b2a81f5SEmmanuel Vadot } 1195b2a81f5SEmmanuel Vadot 1205b2a81f5SEmmanuel Vadot OF_device_register_xref(OF_xref_from_node(node), dev); 1215b2a81f5SEmmanuel Vadot return (0); 1225b2a81f5SEmmanuel Vadot } 1235b2a81f5SEmmanuel Vadot 1245b2a81f5SEmmanuel Vadot static int 1255b2a81f5SEmmanuel Vadot mmc_pwrseq_detach(device_t dev) 1265b2a81f5SEmmanuel Vadot { 1275b2a81f5SEmmanuel Vadot 1285b2a81f5SEmmanuel Vadot return (EBUSY); 1295b2a81f5SEmmanuel Vadot } 1305b2a81f5SEmmanuel Vadot 1315b2a81f5SEmmanuel Vadot static int 1325b2a81f5SEmmanuel Vadot mmv_pwrseq_set_power(device_t dev, bool power_on) 1335b2a81f5SEmmanuel Vadot { 1345b2a81f5SEmmanuel Vadot struct mmc_pwrseq_softc *sc; 1355b2a81f5SEmmanuel Vadot int rv; 1365b2a81f5SEmmanuel Vadot 1375b2a81f5SEmmanuel Vadot sc = device_get_softc(dev); 1385b2a81f5SEmmanuel Vadot 1395b2a81f5SEmmanuel Vadot if (power_on) { 1405b2a81f5SEmmanuel Vadot if (sc->ext_clock) { 1415b2a81f5SEmmanuel Vadot rv = clk_enable(sc->ext_clock); 1425b2a81f5SEmmanuel Vadot if (rv != 0) 1435b2a81f5SEmmanuel Vadot return (rv); 1445b2a81f5SEmmanuel Vadot } 1455b2a81f5SEmmanuel Vadot 1465b2a81f5SEmmanuel Vadot if (sc->reset_gpio) { 1475b2a81f5SEmmanuel Vadot rv = gpio_pin_set_active(sc->reset_gpio, false); 1485b2a81f5SEmmanuel Vadot if (rv != 0) 1495b2a81f5SEmmanuel Vadot return (rv); 1505b2a81f5SEmmanuel Vadot } 1515b2a81f5SEmmanuel Vadot 1525b2a81f5SEmmanuel Vadot if (sc->post_power_on_delay_ms) 1535b2a81f5SEmmanuel Vadot DELAY(sc->post_power_on_delay_ms * 1000); 1545b2a81f5SEmmanuel Vadot } else { 1555b2a81f5SEmmanuel Vadot if (sc->reset_gpio) { 1565b2a81f5SEmmanuel Vadot rv = gpio_pin_set_active(sc->reset_gpio, true); 1575b2a81f5SEmmanuel Vadot if (rv != 0) 1585b2a81f5SEmmanuel Vadot return (rv); 1595b2a81f5SEmmanuel Vadot } 1605b2a81f5SEmmanuel Vadot 1615b2a81f5SEmmanuel Vadot if (sc->ext_clock) { 1625b2a81f5SEmmanuel Vadot rv = clk_stop(sc->ext_clock); 1635b2a81f5SEmmanuel Vadot if (rv != 0) 1645b2a81f5SEmmanuel Vadot return (rv); 1655b2a81f5SEmmanuel Vadot } 1665b2a81f5SEmmanuel Vadot if (sc->power_off_delay_us) 1675b2a81f5SEmmanuel Vadot DELAY(sc->power_off_delay_us); 1685b2a81f5SEmmanuel Vadot } 1695b2a81f5SEmmanuel Vadot 1705b2a81f5SEmmanuel Vadot return (0); 1715b2a81f5SEmmanuel Vadot } 1725b2a81f5SEmmanuel Vadot 1735b2a81f5SEmmanuel Vadot static device_method_t mmc_pwrseq_methods[] = { 1745b2a81f5SEmmanuel Vadot /* Device interface */ 1755b2a81f5SEmmanuel Vadot DEVMETHOD(device_probe, mmc_pwrseq_probe), 1765b2a81f5SEmmanuel Vadot DEVMETHOD(device_attach, mmc_pwrseq_attach), 1775b2a81f5SEmmanuel Vadot DEVMETHOD(device_detach, mmc_pwrseq_detach), 1785b2a81f5SEmmanuel Vadot 1795b2a81f5SEmmanuel Vadot DEVMETHOD(mmc_pwrseq_set_power, mmv_pwrseq_set_power), 1805b2a81f5SEmmanuel Vadot DEVMETHOD_END 1815b2a81f5SEmmanuel Vadot }; 1825b2a81f5SEmmanuel Vadot 1835b2a81f5SEmmanuel Vadot static driver_t mmc_pwrseq_driver = { 1845b2a81f5SEmmanuel Vadot "mmc_pwrseq", 1855b2a81f5SEmmanuel Vadot mmc_pwrseq_methods, 1865b2a81f5SEmmanuel Vadot sizeof(struct mmc_pwrseq_softc), 1875b2a81f5SEmmanuel Vadot }; 1885b2a81f5SEmmanuel Vadot 189b58c5abfSJohn Baldwin EARLY_DRIVER_MODULE(mmc_pwrseq, simplebus, mmc_pwrseq_driver, 0, 0, 1905b2a81f5SEmmanuel Vadot BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_FIRST); 1915b2a81f5SEmmanuel Vadot MODULE_VERSION(mmc_pwrseq, 1); 1925b2a81f5SEmmanuel Vadot SIMPLEBUS_PNP_INFO(compat_data); 193