]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/mmc/mmc_pwrseq.c
less: upgrade to v590.
[FreeBSD/FreeBSD.git] / sys / dev / mmc / mmc_pwrseq.c
1 /*
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
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 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/kernel.h>
35 #include <sys/module.h>
36 #include <sys/gpio.h>
37
38 #include <dev/gpio/gpiobusvar.h>
39 #include <dev/ofw/ofw_bus.h>
40 #include <dev/ofw/ofw_bus_subr.h>
41
42 #include <dev/extres/clk/clk.h>
43
44 #include "mmc_pwrseq_if.h"
45
46 enum pwrseq_type {
47         PWRSEQ_SIMPLE = 1,
48         PWRSEQ_EMMC,
49 };
50
51 static struct ofw_compat_data compat_data[] = {
52         { "mmc-pwrseq-simple",  PWRSEQ_SIMPLE },
53         { "mmc-pwrseq-emmc",    PWRSEQ_EMMC },
54         { NULL,                 0 }
55 };
56
57 struct mmc_pwrseq_softc {
58         enum pwrseq_type        type;
59         clk_t                   ext_clock;
60         struct gpiobus_pin      *reset_gpio;
61
62         uint32_t                post_power_on_delay_ms;
63         uint32_t                power_off_delay_us;
64 };
65
66 static int
67 mmc_pwrseq_probe(device_t dev)
68 {
69         enum pwrseq_type type;
70
71         if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
72                 return (ENXIO);
73
74         type = (enum pwrseq_type)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
75         switch (type) {
76         case PWRSEQ_SIMPLE:
77                 device_set_desc(dev, "MMC Simple Power sequence");
78                 break;
79         case PWRSEQ_EMMC:
80                 device_set_desc(dev, "MMC eMMC Power sequence");
81                 break;
82         }
83         return (BUS_PROBE_DEFAULT);
84 }
85
86 static int
87 mmc_pwrseq_attach(device_t dev)
88 {
89         struct mmc_pwrseq_softc *sc;
90         phandle_t node;
91         int rv;
92
93         sc = device_get_softc(dev);
94         sc->type = (enum pwrseq_type)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
95         node = ofw_bus_get_node(dev);
96
97         if (sc->type == PWRSEQ_SIMPLE) {
98                 if (OF_hasprop(node, "clocks")) {
99                         rv = clk_get_by_ofw_name(dev, 0, "ext_clock", &sc->ext_clock);
100                         if (rv != 0) {
101                                 device_printf(dev,
102                                     "Node have a clocks property but no clocks named \"ext_clock\"\n");
103                                 return (ENXIO);
104                         }
105                 }
106                 OF_getencprop(node, "post-power-on-delay-ms", &sc->post_power_on_delay_ms, sizeof(uint32_t));
107                 OF_getencprop(node, "power-off-delay-us", &sc->power_off_delay_us, sizeof(uint32_t));
108         }
109
110         if (OF_hasprop(node, "reset-gpios")) {
111                 if (gpio_pin_get_by_ofw_property(dev, node, "reset-gpios",
112                     &sc->reset_gpio) != 0) {
113                         device_printf(dev, "Cannot get the reset-gpios\n");
114                         return (ENXIO);
115                 }
116                 gpio_pin_setflags(sc->reset_gpio, GPIO_PIN_OUTPUT);
117                 gpio_pin_set_active(sc->reset_gpio, true);
118         }
119
120         OF_device_register_xref(OF_xref_from_node(node), dev);
121         return (0);
122 }
123
124 static int
125 mmc_pwrseq_detach(device_t dev)
126 {
127
128         return (EBUSY);
129 }
130
131 static int
132 mmv_pwrseq_set_power(device_t dev, bool power_on)
133 {
134         struct mmc_pwrseq_softc *sc;
135         int rv;
136
137         sc = device_get_softc(dev);
138
139         if (power_on) {
140                 if (sc->ext_clock) {
141                         rv = clk_enable(sc->ext_clock);
142                         if (rv != 0)
143                                 return (rv);
144                 }
145
146                 if (sc->reset_gpio) {
147                         rv = gpio_pin_set_active(sc->reset_gpio, false);
148                         if (rv != 0)
149                                 return (rv);
150                 }
151
152                 if (sc->post_power_on_delay_ms)
153                         DELAY(sc->post_power_on_delay_ms * 1000);
154         } else {
155                 if (sc->reset_gpio) {
156                         rv = gpio_pin_set_active(sc->reset_gpio, true);
157                         if (rv != 0)
158                                 return (rv);
159                 }
160
161                 if (sc->ext_clock) {
162                         rv = clk_stop(sc->ext_clock);
163                         if (rv != 0)
164                                 return (rv);
165                 }
166                 if (sc->power_off_delay_us)
167                         DELAY(sc->power_off_delay_us);
168         }
169
170         return (0);
171 }
172
173 static device_method_t mmc_pwrseq_methods[] = {
174         /* Device interface */
175         DEVMETHOD(device_probe,         mmc_pwrseq_probe),
176         DEVMETHOD(device_attach,        mmc_pwrseq_attach),
177         DEVMETHOD(device_detach,        mmc_pwrseq_detach),
178
179         DEVMETHOD(mmc_pwrseq_set_power, mmv_pwrseq_set_power),
180         DEVMETHOD_END
181 };
182
183 static driver_t mmc_pwrseq_driver = {
184         "mmc_pwrseq",
185         mmc_pwrseq_methods,
186         sizeof(struct mmc_pwrseq_softc),
187 };
188
189 static devclass_t mmc_pwrseq_devclass;
190
191 EARLY_DRIVER_MODULE(mmc_pwrseq, simplebus, mmc_pwrseq_driver, mmc_pwrseq_devclass, 0, 0,
192         BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_FIRST);
193 MODULE_VERSION(mmc_pwrseq, 1);
194 SIMPLEBUS_PNP_INFO(compat_data);