Index: dev/mmc_pwrseq_simple/mmc_pwrseq_simple.c =================================================================== --- /dev/null +++ dev/mmc_pwrseq_simple/mmc_pwrseq_simple.c @@ -0,0 +1,241 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2020 Bjoern A. Zeeb + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#ifdef EXT_RESOURCES +#include +#endif + +struct mmc_pwrseq_simple_softc { + struct mmc_pwrseq pwrseq; /* Keep first! node, (*f)() */ + device_t dev; + gpio_pin_t reset_pin; + clk_t clk; +}; + +static struct ofw_compat_data compat_data[] = { + { "mmc-pwrseq-simple", 0 }, + + { NULL, 0 } +}; + +static int +mmc_pwrseq_simple_off(struct mmc_pwrseq *pwrseq) +{ + struct mmc_pwrseq_simple_softc *sc; + + sc = (struct mmc_pwrseq_simple_softc *)pwrseq; +#ifdef EXT_RESOURCES + /* Enable clock. */ + if (sc->clk != NULL) { + int rv; + + rv = clk_disable(sc->clk); + if (rv != 0) { + device_printf(sc->dev, "Failed to enable 'ext_clock': " + "%d\n", rv); + return (rv); + } + } +#endif + + return (0); +} + +static int +mmc_pwrseq_simple_up(struct mmc_pwrseq *pwrseq) +{ + struct mmc_pwrseq_simple_softc *sc; + device_t dev; + int rv; + + sc = (struct mmc_pwrseq_simple_softc *)pwrseq; + dev = sc->dev; + +#ifdef EXT_RESOURCES + /* Enable clock. */ + if (sc->clk != NULL) { + rv = clk_enable(sc->clk); + if (rv != 0) { + device_printf(dev, "Failed to enable 'ext_clock': " + "%d\n", rv); + return (rv); + } + if (bootverbose) { + int64_t freq; + + clk_get_freq(sc->clk, &freq); + device_printf(dev, "mmc-pwrseq-simple clock(%s) freq: " + "%jd\n", clk_get_name(sc->clk), (intmax_t)freq); + } + } +#endif + + /* GPIO/Pin. */ + rv = gpio_pin_set_active(sc->reset_pin, true); + if (rv != 0) { + device_printf(dev, "Cannot set 'reset-gpios': %d\n", rv); + gpio_pin_release(sc->reset_pin); + return (rv); + } + + return (0); +} + +static int +mmc_pwrseq_simple_on(struct mmc_pwrseq *pwrseq) +{ + struct mmc_pwrseq_simple_softc *sc; + device_t dev; + int rv; + + sc = (struct mmc_pwrseq_simple_softc *)pwrseq; + dev = sc->dev; + + /* GPIO/Pin. */ + rv = gpio_pin_set_active(sc->reset_pin, false); + if (rv != 0) { + device_printf(dev, "Cannot clear 'reset-gpios': %d\n", rv); + gpio_pin_release(sc->reset_pin); + return (rv); + } + + return (0); +} + +static int +mmc_pwrseq_simple_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (ofw_bus_search_compatible(dev, compat_data)->ocd_str == NULL) + return (ENXIO); + + device_set_desc(dev, "Simple MMC power sequence handler"); + return (BUS_PROBE_DEFAULT); +} + +static int +mmc_pwrseq_simple_attach(device_t dev) +{ + struct mmc_pwrseq_simple_softc *sc; + int rv; + + sc = device_get_softc(dev); + sc->dev = dev; + sc->pwrseq.node = ofw_bus_get_node(dev); + + /* Find the 'reset-gpios' pin first. */ + rv = gpio_pin_get_by_ofw_property(dev, sc->pwrseq.node, "reset-gpios", + &sc->reset_pin); + if (rv != 0 && rv != ENOENT) { + device_printf(dev, "Cannot get 'reset-gpios' gpio: %d\n", rv); + return (ENXIO); + } + if (sc->reset_pin == NULL) { + device_printf(dev, "No 'reset-gpios' pin.\n"); + return (ENXIO); + } + +#ifdef EXT_RESOURCES + /* Find "clocks". */ + if (clk_get_by_ofw_name(dev, 0, "ext_clock", &sc->clk) != 0) { + device_printf(dev, "Could not find clock 'ext_clock'\n"); + gpio_pin_release(sc->reset_pin); + return (ENXIO); + } +#endif + + sc->pwrseq.power_off = mmc_pwrseq_simple_off; + sc->pwrseq.power_up = mmc_pwrseq_simple_up; + sc->pwrseq.power_on = mmc_pwrseq_simple_on; + + return (mmc_fdt_pwrseq_enqueue(&sc->pwrseq)); +} + +static int +mmc_pwrseq_simple_detach(device_t dev) +{ + struct mmc_pwrseq_simple_softc *sc; + int error; + + sc = device_get_softc(dev); + + error = mmc_fdt_pwrseq_dequeue(&sc->pwrseq); + /* Do not release resources if dequeu failed; should never happen. */ + if (error != 0) + return (error); + + sc->pwrseq.power_off = NULL; + sc->pwrseq.power_up = NULL; + sc->pwrseq.power_on = NULL; + + if (sc->reset_pin != NULL) + gpio_pin_release(sc->reset_pin); + if (sc->clk != NULL) + clk_release(sc->clk); + + return (0); +} + +static device_method_t mmc_pwrseq_simple_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, mmc_pwrseq_simple_probe), + DEVMETHOD(device_attach, mmc_pwrseq_simple_attach), + DEVMETHOD(device_detach, mmc_pwrseq_simple_detach), + + DEVMETHOD_END +}; + +static driver_t mmc_pwrseq_simple_driver = { + "mmc-pwrseq-simple", + mmc_pwrseq_simple_methods, + sizeof(struct mmc_pwrseq_simple_softc), +}; + +static devclass_t mmc_pwrseq_simple_devclass; + +EARLY_DRIVER_MODULE(mmc_pwrseq_simple, ofwbus, mmc_pwrseq_simple_driver, + mmc_pwrseq_simple_devclass, NULL, 0, BUS_PASS_TIMER); +EARLY_DRIVER_MODULE(mmc_pwrseq_simple, simplebus, mmc_pwrseq_simple_driver, + mmc_pwrseq_simple_devclass, NULL, 0, BUS_PASS_TIMER); Index: sys/conf/files =================================================================== --- sys/conf/files +++ sys/conf/files @@ -2455,6 +2455,7 @@ dev/mmc/mmcsd.c optional mmcsd !mmccam dev/mmc/mmc_fdt_helpers.c optional mmc fdt | mmccam fdt dev/mmc/mmc_fdt_pwrseq.c optional mmc fdt | mmccam fdt +dev/mmc_pwrseq_simple/mmc_pwrseq_simple.c optional mmccam fdt dev/mmcnull/mmcnull.c optional mmcnull dev/mn/if_mn.c optional mn pci dev/mpr/mpr.c optional mpr