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