xref: /freebsd/sys/dev/mmc/mmc_pwrseq.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
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