xref: /freebsd/sys/dev/mmc/mmc_pwrseq.c (revision be82b3a0)
15b2a81f5SEmmanuel Vadot /*
24d846d26SWarner 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/param.h>
305b2a81f5SEmmanuel Vadot #include <sys/bus.h>
315b2a81f5SEmmanuel Vadot #include <sys/kernel.h>
325b2a81f5SEmmanuel Vadot #include <sys/module.h>
335b2a81f5SEmmanuel Vadot #include <sys/gpio.h>
345b2a81f5SEmmanuel Vadot 
355b2a81f5SEmmanuel Vadot #include <dev/gpio/gpiobusvar.h>
365b2a81f5SEmmanuel Vadot #include <dev/ofw/ofw_bus.h>
375b2a81f5SEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h>
385b2a81f5SEmmanuel Vadot 
39be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
405b2a81f5SEmmanuel Vadot 
415b2a81f5SEmmanuel Vadot #include "mmc_pwrseq_if.h"
425b2a81f5SEmmanuel Vadot 
435b2a81f5SEmmanuel Vadot enum pwrseq_type {
445b2a81f5SEmmanuel Vadot 	PWRSEQ_SIMPLE = 1,
455b2a81f5SEmmanuel Vadot 	PWRSEQ_EMMC,
465b2a81f5SEmmanuel Vadot };
475b2a81f5SEmmanuel Vadot 
485b2a81f5SEmmanuel Vadot static struct ofw_compat_data compat_data[] = {
495b2a81f5SEmmanuel Vadot 	{ "mmc-pwrseq-simple",	PWRSEQ_SIMPLE },
505b2a81f5SEmmanuel Vadot 	{ "mmc-pwrseq-emmc",	PWRSEQ_EMMC },
515b2a81f5SEmmanuel Vadot 	{ NULL,			0 }
525b2a81f5SEmmanuel Vadot };
535b2a81f5SEmmanuel Vadot 
545b2a81f5SEmmanuel Vadot struct mmc_pwrseq_softc {
555b2a81f5SEmmanuel Vadot 	enum pwrseq_type	type;
565b2a81f5SEmmanuel Vadot 	clk_t			ext_clock;
575b2a81f5SEmmanuel Vadot 	struct gpiobus_pin	*reset_gpio;
585b2a81f5SEmmanuel Vadot 
595b2a81f5SEmmanuel Vadot 	uint32_t		post_power_on_delay_ms;
605b2a81f5SEmmanuel Vadot 	uint32_t		power_off_delay_us;
615b2a81f5SEmmanuel Vadot };
625b2a81f5SEmmanuel Vadot 
635b2a81f5SEmmanuel Vadot static int
mmc_pwrseq_probe(device_t dev)645b2a81f5SEmmanuel Vadot mmc_pwrseq_probe(device_t dev)
655b2a81f5SEmmanuel Vadot {
665b2a81f5SEmmanuel Vadot 	enum pwrseq_type type;
675b2a81f5SEmmanuel Vadot 
685b2a81f5SEmmanuel Vadot 	if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
695b2a81f5SEmmanuel Vadot 		return (ENXIO);
705b2a81f5SEmmanuel Vadot 
715b2a81f5SEmmanuel Vadot 	type = (enum pwrseq_type)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
725b2a81f5SEmmanuel Vadot 	switch (type) {
735b2a81f5SEmmanuel Vadot 	case PWRSEQ_SIMPLE:
745b2a81f5SEmmanuel Vadot 		device_set_desc(dev, "MMC Simple Power sequence");
755b2a81f5SEmmanuel Vadot 		break;
765b2a81f5SEmmanuel Vadot 	case PWRSEQ_EMMC:
775b2a81f5SEmmanuel Vadot 		device_set_desc(dev, "MMC eMMC Power sequence");
785b2a81f5SEmmanuel Vadot 		break;
795b2a81f5SEmmanuel Vadot 	}
805b2a81f5SEmmanuel Vadot 	return (BUS_PROBE_DEFAULT);
815b2a81f5SEmmanuel Vadot }
825b2a81f5SEmmanuel Vadot 
835b2a81f5SEmmanuel Vadot static int
mmc_pwrseq_attach(device_t dev)845b2a81f5SEmmanuel Vadot mmc_pwrseq_attach(device_t dev)
855b2a81f5SEmmanuel Vadot {
865b2a81f5SEmmanuel Vadot 	struct mmc_pwrseq_softc *sc;
875b2a81f5SEmmanuel Vadot 	phandle_t node;
885b2a81f5SEmmanuel Vadot 	int rv;
895b2a81f5SEmmanuel Vadot 
905b2a81f5SEmmanuel Vadot 	sc = device_get_softc(dev);
915b2a81f5SEmmanuel Vadot 	sc->type = (enum pwrseq_type)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
925b2a81f5SEmmanuel Vadot 	node = ofw_bus_get_node(dev);
935b2a81f5SEmmanuel Vadot 
945b2a81f5SEmmanuel Vadot 	if (sc->type == PWRSEQ_SIMPLE) {
955b2a81f5SEmmanuel Vadot 		if (OF_hasprop(node, "clocks")) {
965b2a81f5SEmmanuel Vadot 			rv = clk_get_by_ofw_name(dev, 0, "ext_clock", &sc->ext_clock);
975b2a81f5SEmmanuel Vadot 			if (rv != 0) {
985b2a81f5SEmmanuel Vadot 				device_printf(dev,
995b2a81f5SEmmanuel Vadot 				    "Node have a clocks property but no clocks named \"ext_clock\"\n");
1005b2a81f5SEmmanuel Vadot 				return (ENXIO);
1015b2a81f5SEmmanuel Vadot 			}
1025b2a81f5SEmmanuel Vadot 		}
1035b2a81f5SEmmanuel Vadot 		OF_getencprop(node, "post-power-on-delay-ms", &sc->post_power_on_delay_ms, sizeof(uint32_t));
1045b2a81f5SEmmanuel Vadot 		OF_getencprop(node, "power-off-delay-us", &sc->power_off_delay_us, sizeof(uint32_t));
1055b2a81f5SEmmanuel Vadot 	}
1065b2a81f5SEmmanuel Vadot 
1075b2a81f5SEmmanuel Vadot 	if (OF_hasprop(node, "reset-gpios")) {
1085b2a81f5SEmmanuel Vadot 		if (gpio_pin_get_by_ofw_property(dev, node, "reset-gpios",
1095b2a81f5SEmmanuel Vadot 		    &sc->reset_gpio) != 0) {
1105b2a81f5SEmmanuel Vadot 			device_printf(dev, "Cannot get the reset-gpios\n");
1115b2a81f5SEmmanuel Vadot 			return (ENXIO);
1125b2a81f5SEmmanuel Vadot 		}
1135b2a81f5SEmmanuel Vadot 		gpio_pin_setflags(sc->reset_gpio, GPIO_PIN_OUTPUT);
1145b2a81f5SEmmanuel Vadot 		gpio_pin_set_active(sc->reset_gpio, true);
1155b2a81f5SEmmanuel Vadot 	}
1165b2a81f5SEmmanuel Vadot 
1175b2a81f5SEmmanuel Vadot 	OF_device_register_xref(OF_xref_from_node(node), dev);
1185b2a81f5SEmmanuel Vadot 	return (0);
1195b2a81f5SEmmanuel Vadot }
1205b2a81f5SEmmanuel Vadot 
1215b2a81f5SEmmanuel Vadot static int
mmc_pwrseq_detach(device_t dev)1225b2a81f5SEmmanuel Vadot mmc_pwrseq_detach(device_t dev)
1235b2a81f5SEmmanuel Vadot {
1245b2a81f5SEmmanuel Vadot 
1255b2a81f5SEmmanuel Vadot 	return (EBUSY);
1265b2a81f5SEmmanuel Vadot }
1275b2a81f5SEmmanuel Vadot 
1285b2a81f5SEmmanuel Vadot static int
mmv_pwrseq_set_power(device_t dev,bool power_on)1295b2a81f5SEmmanuel Vadot mmv_pwrseq_set_power(device_t dev, bool power_on)
1305b2a81f5SEmmanuel Vadot {
1315b2a81f5SEmmanuel Vadot 	struct mmc_pwrseq_softc *sc;
1325b2a81f5SEmmanuel Vadot 	int rv;
1335b2a81f5SEmmanuel Vadot 
1345b2a81f5SEmmanuel Vadot 	sc = device_get_softc(dev);
1355b2a81f5SEmmanuel Vadot 
1365b2a81f5SEmmanuel Vadot 	if (power_on) {
1375b2a81f5SEmmanuel Vadot 		if (sc->ext_clock) {
1385b2a81f5SEmmanuel Vadot 			rv = clk_enable(sc->ext_clock);
1395b2a81f5SEmmanuel Vadot 			if (rv != 0)
1405b2a81f5SEmmanuel Vadot 				return (rv);
1415b2a81f5SEmmanuel Vadot 		}
1425b2a81f5SEmmanuel Vadot 
1435b2a81f5SEmmanuel Vadot 		if (sc->reset_gpio) {
1445b2a81f5SEmmanuel Vadot 			rv = gpio_pin_set_active(sc->reset_gpio, false);
1455b2a81f5SEmmanuel Vadot 			if (rv != 0)
1465b2a81f5SEmmanuel Vadot 				return (rv);
1475b2a81f5SEmmanuel Vadot 		}
1485b2a81f5SEmmanuel Vadot 
1495b2a81f5SEmmanuel Vadot 		if (sc->post_power_on_delay_ms)
1505b2a81f5SEmmanuel Vadot 			DELAY(sc->post_power_on_delay_ms * 1000);
1515b2a81f5SEmmanuel Vadot 	} else {
1525b2a81f5SEmmanuel Vadot 		if (sc->reset_gpio) {
1535b2a81f5SEmmanuel Vadot 			rv = gpio_pin_set_active(sc->reset_gpio, true);
1545b2a81f5SEmmanuel Vadot 			if (rv != 0)
1555b2a81f5SEmmanuel Vadot 				return (rv);
1565b2a81f5SEmmanuel Vadot 		}
1575b2a81f5SEmmanuel Vadot 
1585b2a81f5SEmmanuel Vadot 		if (sc->ext_clock) {
1595b2a81f5SEmmanuel Vadot 			rv = clk_stop(sc->ext_clock);
1605b2a81f5SEmmanuel Vadot 			if (rv != 0)
1615b2a81f5SEmmanuel Vadot 				return (rv);
1625b2a81f5SEmmanuel Vadot 		}
1635b2a81f5SEmmanuel Vadot 		if (sc->power_off_delay_us)
1645b2a81f5SEmmanuel Vadot 			DELAY(sc->power_off_delay_us);
1655b2a81f5SEmmanuel Vadot 	}
1665b2a81f5SEmmanuel Vadot 
1675b2a81f5SEmmanuel Vadot 	return (0);
1685b2a81f5SEmmanuel Vadot }
1695b2a81f5SEmmanuel Vadot 
1705b2a81f5SEmmanuel Vadot static device_method_t mmc_pwrseq_methods[] = {
1715b2a81f5SEmmanuel Vadot 	/* Device interface */
1725b2a81f5SEmmanuel Vadot 	DEVMETHOD(device_probe,		mmc_pwrseq_probe),
1735b2a81f5SEmmanuel Vadot 	DEVMETHOD(device_attach,	mmc_pwrseq_attach),
1745b2a81f5SEmmanuel Vadot 	DEVMETHOD(device_detach,	mmc_pwrseq_detach),
1755b2a81f5SEmmanuel Vadot 
1765b2a81f5SEmmanuel Vadot 	DEVMETHOD(mmc_pwrseq_set_power,	mmv_pwrseq_set_power),
1775b2a81f5SEmmanuel Vadot 	DEVMETHOD_END
1785b2a81f5SEmmanuel Vadot };
1795b2a81f5SEmmanuel Vadot 
1805b2a81f5SEmmanuel Vadot static driver_t mmc_pwrseq_driver = {
1815b2a81f5SEmmanuel Vadot 	"mmc_pwrseq",
1825b2a81f5SEmmanuel Vadot 	mmc_pwrseq_methods,
1835b2a81f5SEmmanuel Vadot 	sizeof(struct mmc_pwrseq_softc),
1845b2a81f5SEmmanuel Vadot };
1855b2a81f5SEmmanuel Vadot 
186b58c5abfSJohn Baldwin EARLY_DRIVER_MODULE(mmc_pwrseq, simplebus, mmc_pwrseq_driver, 0, 0,
1875b2a81f5SEmmanuel Vadot 	BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_FIRST);
1885b2a81f5SEmmanuel Vadot MODULE_VERSION(mmc_pwrseq, 1);
1895b2a81f5SEmmanuel Vadot SIMPLEBUS_PNP_INFO(compat_data);
190