1ef2ee5d0SMichal Meloun /*-
2ef2ee5d0SMichal Meloun * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3ef2ee5d0SMichal Meloun * All rights reserved.
4ef2ee5d0SMichal Meloun *
5ef2ee5d0SMichal Meloun * Redistribution and use in source and binary forms, with or without
6ef2ee5d0SMichal Meloun * modification, are permitted provided that the following conditions
7ef2ee5d0SMichal Meloun * are met:
8ef2ee5d0SMichal Meloun * 1. Redistributions of source code must retain the above copyright
9ef2ee5d0SMichal Meloun * notice, this list of conditions and the following disclaimer.
10ef2ee5d0SMichal Meloun * 2. Redistributions in binary form must reproduce the above copyright
11ef2ee5d0SMichal Meloun * notice, this list of conditions and the following disclaimer in the
12ef2ee5d0SMichal Meloun * documentation and/or other materials provided with the distribution.
13ef2ee5d0SMichal Meloun *
14ef2ee5d0SMichal Meloun * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15ef2ee5d0SMichal Meloun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16ef2ee5d0SMichal Meloun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17ef2ee5d0SMichal Meloun * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18ef2ee5d0SMichal Meloun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19ef2ee5d0SMichal Meloun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20ef2ee5d0SMichal Meloun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21ef2ee5d0SMichal Meloun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22ef2ee5d0SMichal Meloun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23ef2ee5d0SMichal Meloun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24ef2ee5d0SMichal Meloun * SUCH DAMAGE.
25ef2ee5d0SMichal Meloun */
26ef2ee5d0SMichal Meloun
27ef2ee5d0SMichal Meloun #include <sys/cdefs.h>
28ef2ee5d0SMichal Meloun /*
29ef2ee5d0SMichal Meloun * SDHCI driver glue for NVIDIA Tegra family
30ef2ee5d0SMichal Meloun *
31ef2ee5d0SMichal Meloun */
32ef2ee5d0SMichal Meloun #include <sys/param.h>
33ef2ee5d0SMichal Meloun #include <sys/systm.h>
34ef2ee5d0SMichal Meloun #include <sys/types.h>
35ef2ee5d0SMichal Meloun #include <sys/bus.h>
36d457839bSMichal Meloun #include <sys/gpio.h>
37ef2ee5d0SMichal Meloun #include <sys/kernel.h>
38ef2ee5d0SMichal Meloun #include <sys/lock.h>
39ef2ee5d0SMichal Meloun #include <sys/malloc.h>
40ef2ee5d0SMichal Meloun #include <sys/module.h>
41ef2ee5d0SMichal Meloun #include <sys/mutex.h>
42ef2ee5d0SMichal Meloun #include <sys/resource.h>
43ef2ee5d0SMichal Meloun #include <sys/rman.h>
44ef2ee5d0SMichal Meloun #include <sys/sysctl.h>
45ef2ee5d0SMichal Meloun #include <sys/taskqueue.h>
46ef2ee5d0SMichal Meloun
47ef2ee5d0SMichal Meloun #include <machine/bus.h>
48ef2ee5d0SMichal Meloun #include <machine/resource.h>
49ef2ee5d0SMichal Meloun #include <machine/intr.h>
50ef2ee5d0SMichal Meloun
51be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
521f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h>
53ef2ee5d0SMichal Meloun #include <dev/gpio/gpiobusvar.h>
54ef2ee5d0SMichal Meloun #include <dev/mmc/bridge.h>
55ef2ee5d0SMichal Meloun #include <dev/mmc/mmcbrvar.h>
56ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus.h>
57ef2ee5d0SMichal Meloun #include <dev/ofw/ofw_bus_subr.h>
58ef2ee5d0SMichal Meloun #include <dev/sdhci/sdhci.h>
59d457839bSMichal Meloun #include <dev/sdhci/sdhci_fdt_gpio.h>
60ef2ee5d0SMichal Meloun
61ef2ee5d0SMichal Meloun #include "sdhci_if.h"
62ef2ee5d0SMichal Meloun
63d597cfd5SEmmanuel Vadot #include "opt_mmccam.h"
64d597cfd5SEmmanuel Vadot
65ef2ee5d0SMichal Meloun /* Tegra SDHOST controller vendor register definitions */
66ef2ee5d0SMichal Meloun #define SDMMC_VENDOR_CLOCK_CNTRL 0x100
67ef2ee5d0SMichal Meloun #define VENDOR_CLOCK_CNTRL_CLK_SHIFT 8
68ef2ee5d0SMichal Meloun #define VENDOR_CLOCK_CNTRL_CLK_MASK 0xFF
69ef2ee5d0SMichal Meloun #define SDMMC_VENDOR_SYS_SW_CNTRL 0x104
70ef2ee5d0SMichal Meloun #define SDMMC_VENDOR_CAP_OVERRIDES 0x10C
71ef2ee5d0SMichal Meloun #define SDMMC_VENDOR_BOOT_CNTRL 0x110
72ef2ee5d0SMichal Meloun #define SDMMC_VENDOR_BOOT_ACK_TIMEOUT 0x114
73ef2ee5d0SMichal Meloun #define SDMMC_VENDOR_BOOT_DAT_TIMEOUT 0x118
74ef2ee5d0SMichal Meloun #define SDMMC_VENDOR_DEBOUNCE_COUNT 0x11C
75ef2ee5d0SMichal Meloun #define SDMMC_VENDOR_MISC_CNTRL 0x120
76ef2ee5d0SMichal Meloun #define VENDOR_MISC_CTRL_ENABLE_SDR104 0x8
77ef2ee5d0SMichal Meloun #define VENDOR_MISC_CTRL_ENABLE_SDR50 0x10
78ef2ee5d0SMichal Meloun #define VENDOR_MISC_CTRL_ENABLE_SDHCI_SPEC_300 0x20
79ef2ee5d0SMichal Meloun #define VENDOR_MISC_CTRL_ENABLE_DDR50 0x200
80ef2ee5d0SMichal Meloun #define SDMMC_MAX_CURRENT_OVERRIDE 0x124
81ef2ee5d0SMichal Meloun #define SDMMC_MAX_CURRENT_OVERRIDE_HI 0x128
82ef2ee5d0SMichal Meloun #define SDMMC_VENDOR_CLK_GATE_HYSTERESIS_COUNT 0x1D0
83ef2ee5d0SMichal Meloun #define SDMMC_VENDOR_PHWRESET_VAL0 0x1D4
84ef2ee5d0SMichal Meloun #define SDMMC_VENDOR_PHWRESET_VAL1 0x1D8
85ef2ee5d0SMichal Meloun #define SDMMC_VENDOR_PHWRESET_VAL2 0x1DC
86ef2ee5d0SMichal Meloun #define SDMMC_SDMEMCOMPPADCTRL_0 0x1E0
87ef2ee5d0SMichal Meloun #define SDMMC_AUTO_CAL_CONFIG 0x1E4
88ef2ee5d0SMichal Meloun #define SDMMC_AUTO_CAL_INTERVAL 0x1E8
89ef2ee5d0SMichal Meloun #define SDMMC_AUTO_CAL_STATUS 0x1EC
90ef2ee5d0SMichal Meloun #define SDMMC_SDMMC_MCCIF_FIFOCTRL 0x1F4
91ef2ee5d0SMichal Meloun #define SDMMC_TIMEOUT_WCOAL_SDMMC 0x1F8
92ef2ee5d0SMichal Meloun
93ef2ee5d0SMichal Meloun /* Compatible devices. */
94ef2ee5d0SMichal Meloun static struct ofw_compat_data compat_data[] = {
95ef2ee5d0SMichal Meloun {"nvidia,tegra124-sdhci", 1},
96b9cbd68dSMichal Meloun {"nvidia,tegra210-sdhci", 1},
97ef2ee5d0SMichal Meloun {NULL, 0},
98ef2ee5d0SMichal Meloun };
99ef2ee5d0SMichal Meloun
100ef2ee5d0SMichal Meloun struct tegra_sdhci_softc {
101ef2ee5d0SMichal Meloun device_t dev;
102ef2ee5d0SMichal Meloun struct resource * mem_res;
103ef2ee5d0SMichal Meloun struct resource * irq_res;
104ef2ee5d0SMichal Meloun void * intr_cookie;
105ef2ee5d0SMichal Meloun u_int quirks; /* Chip specific quirks */
106ef2ee5d0SMichal Meloun u_int caps; /* If we override SDHCI_CAPABILITIES */
107ef2ee5d0SMichal Meloun uint32_t max_clk; /* Max possible freq */
108ef2ee5d0SMichal Meloun clk_t clk;
109ef2ee5d0SMichal Meloun hwreset_t reset;
110ef2ee5d0SMichal Meloun gpio_pin_t gpio_power;
111d457839bSMichal Meloun struct sdhci_fdt_gpio *gpio;
112ef2ee5d0SMichal Meloun
113ef2ee5d0SMichal Meloun int force_card_present;
114ef2ee5d0SMichal Meloun struct sdhci_slot slot;
115ef2ee5d0SMichal Meloun
116ef2ee5d0SMichal Meloun };
117ef2ee5d0SMichal Meloun
118ef2ee5d0SMichal Meloun static inline uint32_t
RD4(struct tegra_sdhci_softc * sc,bus_size_t off)119ef2ee5d0SMichal Meloun RD4(struct tegra_sdhci_softc *sc, bus_size_t off)
120ef2ee5d0SMichal Meloun {
121ef2ee5d0SMichal Meloun
122ef2ee5d0SMichal Meloun return (bus_read_4(sc->mem_res, off));
123ef2ee5d0SMichal Meloun }
124ef2ee5d0SMichal Meloun
125ef2ee5d0SMichal Meloun static uint8_t
tegra_sdhci_read_1(device_t dev,struct sdhci_slot * slot,bus_size_t off)126ef2ee5d0SMichal Meloun tegra_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off)
127ef2ee5d0SMichal Meloun {
128ef2ee5d0SMichal Meloun struct tegra_sdhci_softc *sc;
129ef2ee5d0SMichal Meloun
130ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
131ef2ee5d0SMichal Meloun return (bus_read_1(sc->mem_res, off));
132ef2ee5d0SMichal Meloun }
133ef2ee5d0SMichal Meloun
134ef2ee5d0SMichal Meloun static uint16_t
tegra_sdhci_read_2(device_t dev,struct sdhci_slot * slot,bus_size_t off)135ef2ee5d0SMichal Meloun tegra_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
136ef2ee5d0SMichal Meloun {
137ef2ee5d0SMichal Meloun struct tegra_sdhci_softc *sc;
138ef2ee5d0SMichal Meloun
139ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
140ef2ee5d0SMichal Meloun return (bus_read_2(sc->mem_res, off));
141ef2ee5d0SMichal Meloun }
142ef2ee5d0SMichal Meloun
143ef2ee5d0SMichal Meloun static uint32_t
tegra_sdhci_read_4(device_t dev,struct sdhci_slot * slot,bus_size_t off)144ef2ee5d0SMichal Meloun tegra_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
145ef2ee5d0SMichal Meloun {
146ef2ee5d0SMichal Meloun struct tegra_sdhci_softc *sc;
147ef2ee5d0SMichal Meloun uint32_t val32;
148ef2ee5d0SMichal Meloun
149ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
150ef2ee5d0SMichal Meloun val32 = bus_read_4(sc->mem_res, off);
151ef2ee5d0SMichal Meloun /* Force the card-present state if necessary. */
152ef2ee5d0SMichal Meloun if (off == SDHCI_PRESENT_STATE && sc->force_card_present)
153ef2ee5d0SMichal Meloun val32 |= SDHCI_CARD_PRESENT;
154ef2ee5d0SMichal Meloun return (val32);
155ef2ee5d0SMichal Meloun }
156ef2ee5d0SMichal Meloun
157ef2ee5d0SMichal Meloun static void
tegra_sdhci_read_multi_4(device_t dev,struct sdhci_slot * slot,bus_size_t off,uint32_t * data,bus_size_t count)158ef2ee5d0SMichal Meloun tegra_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
159ef2ee5d0SMichal Meloun uint32_t *data, bus_size_t count)
160ef2ee5d0SMichal Meloun {
161ef2ee5d0SMichal Meloun struct tegra_sdhci_softc *sc;
162ef2ee5d0SMichal Meloun
163ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
164ef2ee5d0SMichal Meloun bus_read_multi_4(sc->mem_res, off, data, count);
165ef2ee5d0SMichal Meloun }
166ef2ee5d0SMichal Meloun
167ef2ee5d0SMichal Meloun static void
tegra_sdhci_write_1(device_t dev,struct sdhci_slot * slot,bus_size_t off,uint8_t val)168ef2ee5d0SMichal Meloun tegra_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off,
169ef2ee5d0SMichal Meloun uint8_t val)
170ef2ee5d0SMichal Meloun {
171ef2ee5d0SMichal Meloun struct tegra_sdhci_softc *sc;
172ef2ee5d0SMichal Meloun
173ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
174ef2ee5d0SMichal Meloun bus_write_1(sc->mem_res, off, val);
175ef2ee5d0SMichal Meloun }
176ef2ee5d0SMichal Meloun
177ef2ee5d0SMichal Meloun static void
tegra_sdhci_write_2(device_t dev,struct sdhci_slot * slot,bus_size_t off,uint16_t val)178ef2ee5d0SMichal Meloun tegra_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off,
179ef2ee5d0SMichal Meloun uint16_t val)
180ef2ee5d0SMichal Meloun {
181ef2ee5d0SMichal Meloun struct tegra_sdhci_softc *sc;
182ef2ee5d0SMichal Meloun
183ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
184ef2ee5d0SMichal Meloun bus_write_2(sc->mem_res, off, val);
185ef2ee5d0SMichal Meloun }
186ef2ee5d0SMichal Meloun
187ef2ee5d0SMichal Meloun static void
tegra_sdhci_write_4(device_t dev,struct sdhci_slot * slot,bus_size_t off,uint32_t val)188ef2ee5d0SMichal Meloun tegra_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
189ef2ee5d0SMichal Meloun uint32_t val)
190ef2ee5d0SMichal Meloun {
191ef2ee5d0SMichal Meloun struct tegra_sdhci_softc *sc;
192ef2ee5d0SMichal Meloun
193ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
194ef2ee5d0SMichal Meloun bus_write_4(sc->mem_res, off, val);
195ef2ee5d0SMichal Meloun }
196ef2ee5d0SMichal Meloun
197ef2ee5d0SMichal Meloun static void
tegra_sdhci_write_multi_4(device_t dev,struct sdhci_slot * slot,bus_size_t off,uint32_t * data,bus_size_t count)198ef2ee5d0SMichal Meloun tegra_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off,
199ef2ee5d0SMichal Meloun uint32_t *data, bus_size_t count)
200ef2ee5d0SMichal Meloun {
201ef2ee5d0SMichal Meloun struct tegra_sdhci_softc *sc;
202ef2ee5d0SMichal Meloun
203ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
204ef2ee5d0SMichal Meloun bus_write_multi_4(sc->mem_res, off, data, count);
205ef2ee5d0SMichal Meloun }
206ef2ee5d0SMichal Meloun
207ef2ee5d0SMichal Meloun static void
tegra_sdhci_intr(void * arg)208ef2ee5d0SMichal Meloun tegra_sdhci_intr(void *arg)
209ef2ee5d0SMichal Meloun {
210ef2ee5d0SMichal Meloun struct tegra_sdhci_softc *sc = arg;
211ef2ee5d0SMichal Meloun
212ef2ee5d0SMichal Meloun sdhci_generic_intr(&sc->slot);
213ef2ee5d0SMichal Meloun RD4(sc, SDHCI_INT_STATUS);
214ef2ee5d0SMichal Meloun }
215ef2ee5d0SMichal Meloun
216ef2ee5d0SMichal Meloun static int
tegra_sdhci_get_ro(device_t brdev,device_t reqdev)217d457839bSMichal Meloun tegra_sdhci_get_ro(device_t brdev, device_t reqdev)
218ef2ee5d0SMichal Meloun {
219d457839bSMichal Meloun struct tegra_sdhci_softc *sc = device_get_softc(brdev);
220ef2ee5d0SMichal Meloun
221d457839bSMichal Meloun return (sdhci_fdt_gpio_get_readonly(sc->gpio));
222d457839bSMichal Meloun }
223d457839bSMichal Meloun
224d457839bSMichal Meloun static bool
tegra_sdhci_get_card_present(device_t dev,struct sdhci_slot * slot)225d457839bSMichal Meloun tegra_sdhci_get_card_present(device_t dev, struct sdhci_slot *slot)
226d457839bSMichal Meloun {
227d457839bSMichal Meloun struct tegra_sdhci_softc *sc = device_get_softc(dev);
228d457839bSMichal Meloun
229d457839bSMichal Meloun return (sdhci_fdt_gpio_get_present(sc->gpio));
230ef2ee5d0SMichal Meloun }
231ef2ee5d0SMichal Meloun
232ef2ee5d0SMichal Meloun static int
tegra_sdhci_probe(device_t dev)233ef2ee5d0SMichal Meloun tegra_sdhci_probe(device_t dev)
234ef2ee5d0SMichal Meloun {
235ef2ee5d0SMichal Meloun struct tegra_sdhci_softc *sc;
236ef2ee5d0SMichal Meloun phandle_t node;
237ef2ee5d0SMichal Meloun pcell_t cid;
238ef2ee5d0SMichal Meloun const struct ofw_compat_data *cd;
239ef2ee5d0SMichal Meloun
240ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
241ef2ee5d0SMichal Meloun if (!ofw_bus_status_okay(dev))
242ef2ee5d0SMichal Meloun return (ENXIO);
243ef2ee5d0SMichal Meloun
244ef2ee5d0SMichal Meloun cd = ofw_bus_search_compatible(dev, compat_data);
245ef2ee5d0SMichal Meloun if (cd->ocd_data == 0)
246ef2ee5d0SMichal Meloun return (ENXIO);
247ef2ee5d0SMichal Meloun
248ef2ee5d0SMichal Meloun node = ofw_bus_get_node(dev);
249b9cbd68dSMichal Meloun device_set_desc(dev, "Tegra SDHCI controller");
250ef2ee5d0SMichal Meloun
251ef2ee5d0SMichal Meloun /* Allow dts to patch quirks, slots, and max-frequency. */
252ef2ee5d0SMichal Meloun if ((OF_getencprop(node, "quirks", &cid, sizeof(cid))) > 0)
253ef2ee5d0SMichal Meloun sc->quirks = cid;
254ef2ee5d0SMichal Meloun if ((OF_getencprop(node, "max-frequency", &cid, sizeof(cid))) > 0)
255ef2ee5d0SMichal Meloun sc->max_clk = cid;
256ef2ee5d0SMichal Meloun
257ef2ee5d0SMichal Meloun return (BUS_PROBE_DEFAULT);
258ef2ee5d0SMichal Meloun }
259ef2ee5d0SMichal Meloun
260ef2ee5d0SMichal Meloun static int
tegra_sdhci_attach(device_t dev)261ef2ee5d0SMichal Meloun tegra_sdhci_attach(device_t dev)
262ef2ee5d0SMichal Meloun {
263ef2ee5d0SMichal Meloun struct tegra_sdhci_softc *sc;
264ef2ee5d0SMichal Meloun int rid, rv;
265ef2ee5d0SMichal Meloun uint64_t freq;
266ef2ee5d0SMichal Meloun phandle_t node, prop;
267ef2ee5d0SMichal Meloun
268ef2ee5d0SMichal Meloun sc = device_get_softc(dev);
269ef2ee5d0SMichal Meloun sc->dev = dev;
270ef2ee5d0SMichal Meloun node = ofw_bus_get_node(dev);
271ef2ee5d0SMichal Meloun
272ef2ee5d0SMichal Meloun rid = 0;
273ef2ee5d0SMichal Meloun sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
274ef2ee5d0SMichal Meloun RF_ACTIVE);
275ef2ee5d0SMichal Meloun if (!sc->mem_res) {
276ef2ee5d0SMichal Meloun device_printf(dev, "cannot allocate memory window\n");
277ef2ee5d0SMichal Meloun rv = ENXIO;
278ef2ee5d0SMichal Meloun goto fail;
279ef2ee5d0SMichal Meloun }
280ef2ee5d0SMichal Meloun
281ef2ee5d0SMichal Meloun rid = 0;
282ef2ee5d0SMichal Meloun sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
283ef2ee5d0SMichal Meloun RF_ACTIVE);
284ef2ee5d0SMichal Meloun if (!sc->irq_res) {
285ef2ee5d0SMichal Meloun device_printf(dev, "cannot allocate interrupt\n");
286ef2ee5d0SMichal Meloun rv = ENXIO;
287ef2ee5d0SMichal Meloun goto fail;
288ef2ee5d0SMichal Meloun }
289ef2ee5d0SMichal Meloun
290dac93553SMichal Meloun rv = hwreset_get_by_ofw_name(sc->dev, 0, "sdhci", &sc->reset);
291ef2ee5d0SMichal Meloun if (rv != 0) {
292ef2ee5d0SMichal Meloun device_printf(sc->dev, "Cannot get 'sdhci' reset\n");
293ef2ee5d0SMichal Meloun goto fail;
294ef2ee5d0SMichal Meloun }
295b9cbd68dSMichal Meloun rv = hwreset_assert(sc->reset);
296ef2ee5d0SMichal Meloun if (rv != 0) {
297b9cbd68dSMichal Meloun device_printf(dev, "Cannot reset 'sdhci' reset\n");
298ef2ee5d0SMichal Meloun goto fail;
299ef2ee5d0SMichal Meloun }
300ef2ee5d0SMichal Meloun
301b9cbd68dSMichal Meloun gpio_pin_get_by_ofw_property(sc->dev, node, "power-gpios",
302b9cbd68dSMichal Meloun &sc->gpio_power);
303b9cbd68dSMichal Meloun
304b9cbd68dSMichal Meloun if (OF_hasprop(node, "assigned-clocks")) {
305b9cbd68dSMichal Meloun rv = clk_set_assigned(sc->dev, node);
306b9cbd68dSMichal Meloun if (rv != 0) {
307b9cbd68dSMichal Meloun device_printf(dev, "Cannot set assigned clocks\n");
308b9cbd68dSMichal Meloun goto fail;
309b9cbd68dSMichal Meloun }
310b9cbd68dSMichal Meloun }
311ef2ee5d0SMichal Meloun
312dac93553SMichal Meloun rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
313ef2ee5d0SMichal Meloun if (rv != 0) {
314ef2ee5d0SMichal Meloun device_printf(dev, "Cannot get clock\n");
315ef2ee5d0SMichal Meloun goto fail;
316ef2ee5d0SMichal Meloun }
317ef2ee5d0SMichal Meloun rv = clk_enable(sc->clk);
318ef2ee5d0SMichal Meloun if (rv != 0) {
319ef2ee5d0SMichal Meloun device_printf(dev, "Cannot enable clock\n");
320ef2ee5d0SMichal Meloun goto fail;
321ef2ee5d0SMichal Meloun }
322ef2ee5d0SMichal Meloun rv = clk_set_freq(sc->clk, 48000000, CLK_SET_ROUND_DOWN);
323ef2ee5d0SMichal Meloun if (rv != 0) {
324ef2ee5d0SMichal Meloun device_printf(dev, "Cannot set clock\n");
325ef2ee5d0SMichal Meloun }
326ef2ee5d0SMichal Meloun rv = clk_get_freq(sc->clk, &freq);
327ef2ee5d0SMichal Meloun if (rv != 0) {
328ef2ee5d0SMichal Meloun device_printf(dev, "Cannot get clock frequency\n");
329ef2ee5d0SMichal Meloun goto fail;
330ef2ee5d0SMichal Meloun }
331b9cbd68dSMichal Meloun DELAY(4000);
332b9cbd68dSMichal Meloun rv = hwreset_deassert(sc->reset);
333b9cbd68dSMichal Meloun if (rv != 0) {
334b9cbd68dSMichal Meloun device_printf(dev, "Cannot unreset 'sdhci' reset\n");
335b9cbd68dSMichal Meloun goto fail;
336b9cbd68dSMichal Meloun }
337ef2ee5d0SMichal Meloun if (bootverbose)
338b9cbd68dSMichal Meloun device_printf(dev, " Base MMC clock: %jd\n", (uintmax_t)freq);
339ef2ee5d0SMichal Meloun
340ef2ee5d0SMichal Meloun /* Fill slot information. */
341ef2ee5d0SMichal Meloun sc->max_clk = (int)freq;
342ef2ee5d0SMichal Meloun sc->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL |
343ef2ee5d0SMichal Meloun SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
344ef2ee5d0SMichal Meloun SDHCI_QUIRK_MISSING_CAPS;
345ef2ee5d0SMichal Meloun
346ef2ee5d0SMichal Meloun /* Limit real slot capabilities. */
347ef2ee5d0SMichal Meloun sc->caps = RD4(sc, SDHCI_CAPABILITIES);
348ef2ee5d0SMichal Meloun if (OF_getencprop(node, "bus-width", &prop, sizeof(prop)) > 0) {
349ef2ee5d0SMichal Meloun sc->caps &= ~(MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA);
350ef2ee5d0SMichal Meloun switch (prop) {
351ef2ee5d0SMichal Meloun case 8:
352ef2ee5d0SMichal Meloun sc->caps |= MMC_CAP_8_BIT_DATA;
353ef2ee5d0SMichal Meloun /* FALLTHROUGH */
354ef2ee5d0SMichal Meloun case 4:
355ef2ee5d0SMichal Meloun sc->caps |= MMC_CAP_4_BIT_DATA;
356ef2ee5d0SMichal Meloun break;
357ef2ee5d0SMichal Meloun case 1:
358ef2ee5d0SMichal Meloun break;
359ef2ee5d0SMichal Meloun default:
360ef2ee5d0SMichal Meloun device_printf(dev, "Bad bus-width value %u\n", prop);
361ef2ee5d0SMichal Meloun break;
362ef2ee5d0SMichal Meloun }
363ef2ee5d0SMichal Meloun }
364ef2ee5d0SMichal Meloun if (OF_hasprop(node, "non-removable"))
365ef2ee5d0SMichal Meloun sc->force_card_present = 1;
366ef2ee5d0SMichal Meloun /*
367ef2ee5d0SMichal Meloun * Clear clock field, so SDHCI driver uses supplied frequency.
368ef2ee5d0SMichal Meloun * in sc->slot.max_clk
369ef2ee5d0SMichal Meloun */
370ef2ee5d0SMichal Meloun sc->caps &= ~SDHCI_CLOCK_V3_BASE_MASK;
371ef2ee5d0SMichal Meloun
372ef2ee5d0SMichal Meloun sc->slot.quirks = sc->quirks;
373ef2ee5d0SMichal Meloun sc->slot.max_clk = sc->max_clk;
374ef2ee5d0SMichal Meloun sc->slot.caps = sc->caps;
375ef2ee5d0SMichal Meloun
376b9cbd68dSMichal Meloun if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_BIO | INTR_MPSAFE,
377b9cbd68dSMichal Meloun NULL, tegra_sdhci_intr, sc, &sc->intr_cookie)) {
378b9cbd68dSMichal Meloun device_printf(dev, "cannot setup interrupt handler\n");
379b9cbd68dSMichal Meloun rv = ENXIO;
380b9cbd68dSMichal Meloun goto fail;
381b9cbd68dSMichal Meloun }
382ef2ee5d0SMichal Meloun rv = sdhci_init_slot(dev, &sc->slot, 0);
383ef2ee5d0SMichal Meloun if (rv != 0) {
384ef2ee5d0SMichal Meloun goto fail;
385ef2ee5d0SMichal Meloun }
386ef2ee5d0SMichal Meloun
387d457839bSMichal Meloun sc->gpio = sdhci_fdt_gpio_setup(sc->dev, &sc->slot);
388d457839bSMichal Meloun
389723da5d9SJohn Baldwin bus_identify_children(dev);
39018250ec6SJohn Baldwin bus_attach_children(dev);
391ef2ee5d0SMichal Meloun
392ef2ee5d0SMichal Meloun sdhci_start_slot(&sc->slot);
393ef2ee5d0SMichal Meloun
394ef2ee5d0SMichal Meloun return (0);
395ef2ee5d0SMichal Meloun
396ef2ee5d0SMichal Meloun fail:
397d457839bSMichal Meloun if (sc->gpio != NULL)
398d457839bSMichal Meloun sdhci_fdt_gpio_teardown(sc->gpio);
399d457839bSMichal Meloun if (sc->intr_cookie != NULL)
400d457839bSMichal Meloun bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie);
401ef2ee5d0SMichal Meloun if (sc->gpio_power != NULL)
402ef2ee5d0SMichal Meloun gpio_pin_release(sc->gpio_power);
403ef2ee5d0SMichal Meloun if (sc->clk != NULL)
404ef2ee5d0SMichal Meloun clk_release(sc->clk);
405ef2ee5d0SMichal Meloun if (sc->reset != NULL)
406ef2ee5d0SMichal Meloun hwreset_release(sc->reset);
407ef2ee5d0SMichal Meloun if (sc->irq_res != NULL)
408ef2ee5d0SMichal Meloun bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
409ef2ee5d0SMichal Meloun if (sc->mem_res != NULL)
410ef2ee5d0SMichal Meloun bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
411ef2ee5d0SMichal Meloun
412ef2ee5d0SMichal Meloun return (rv);
413ef2ee5d0SMichal Meloun }
414ef2ee5d0SMichal Meloun
415ef2ee5d0SMichal Meloun static int
tegra_sdhci_detach(device_t dev)416ef2ee5d0SMichal Meloun tegra_sdhci_detach(device_t dev)
417ef2ee5d0SMichal Meloun {
418ef2ee5d0SMichal Meloun struct tegra_sdhci_softc *sc = device_get_softc(dev);
419ef2ee5d0SMichal Meloun struct sdhci_slot *slot = &sc->slot;
420*64d1a02eSJohn Baldwin int error;
421ef2ee5d0SMichal Meloun
422*64d1a02eSJohn Baldwin error = bus_detach_children(dev);
423*64d1a02eSJohn Baldwin if (error != 0)
424*64d1a02eSJohn Baldwin return (error);
425*64d1a02eSJohn Baldwin
426d457839bSMichal Meloun sdhci_fdt_gpio_teardown(sc->gpio);
427ef2ee5d0SMichal Meloun clk_release(sc->clk);
428ef2ee5d0SMichal Meloun bus_teardown_intr(dev, sc->irq_res, sc->intr_cookie);
429ef2ee5d0SMichal Meloun bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq_res),
430ef2ee5d0SMichal Meloun sc->irq_res);
431ef2ee5d0SMichal Meloun
432ef2ee5d0SMichal Meloun sdhci_cleanup_slot(slot);
433ef2ee5d0SMichal Meloun bus_release_resource(dev, SYS_RES_MEMORY,
434ef2ee5d0SMichal Meloun rman_get_rid(sc->mem_res),
435ef2ee5d0SMichal Meloun sc->mem_res);
436ef2ee5d0SMichal Meloun return (0);
437ef2ee5d0SMichal Meloun }
438ef2ee5d0SMichal Meloun
439ef2ee5d0SMichal Meloun static device_method_t tegra_sdhci_methods[] = {
440ef2ee5d0SMichal Meloun /* Device interface */
441ef2ee5d0SMichal Meloun DEVMETHOD(device_probe, tegra_sdhci_probe),
442ef2ee5d0SMichal Meloun DEVMETHOD(device_attach, tegra_sdhci_attach),
443ef2ee5d0SMichal Meloun DEVMETHOD(device_detach, tegra_sdhci_detach),
444ef2ee5d0SMichal Meloun
445ef2ee5d0SMichal Meloun /* Bus interface */
446ef2ee5d0SMichal Meloun DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar),
447ef2ee5d0SMichal Meloun DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar),
448ef2ee5d0SMichal Meloun
449ef2ee5d0SMichal Meloun /* MMC bridge interface */
450ef2ee5d0SMichal Meloun DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios),
451ef2ee5d0SMichal Meloun DEVMETHOD(mmcbr_request, sdhci_generic_request),
452d457839bSMichal Meloun DEVMETHOD(mmcbr_get_ro, tegra_sdhci_get_ro),
453ef2ee5d0SMichal Meloun DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host),
454ef2ee5d0SMichal Meloun DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host),
455ef2ee5d0SMichal Meloun
456ef2ee5d0SMichal Meloun /* SDHCI registers accessors */
457ef2ee5d0SMichal Meloun DEVMETHOD(sdhci_read_1, tegra_sdhci_read_1),
458ef2ee5d0SMichal Meloun DEVMETHOD(sdhci_read_2, tegra_sdhci_read_2),
459ef2ee5d0SMichal Meloun DEVMETHOD(sdhci_read_4, tegra_sdhci_read_4),
460ef2ee5d0SMichal Meloun DEVMETHOD(sdhci_read_multi_4, tegra_sdhci_read_multi_4),
461ef2ee5d0SMichal Meloun DEVMETHOD(sdhci_write_1, tegra_sdhci_write_1),
462ef2ee5d0SMichal Meloun DEVMETHOD(sdhci_write_2, tegra_sdhci_write_2),
463ef2ee5d0SMichal Meloun DEVMETHOD(sdhci_write_4, tegra_sdhci_write_4),
464ef2ee5d0SMichal Meloun DEVMETHOD(sdhci_write_multi_4, tegra_sdhci_write_multi_4),
465d457839bSMichal Meloun DEVMETHOD(sdhci_get_card_present, tegra_sdhci_get_card_present),
466ef2ee5d0SMichal Meloun
4674bda238aSMichal Meloun DEVMETHOD_END
468ef2ee5d0SMichal Meloun };
469ef2ee5d0SMichal Meloun
470693f8b36SMichal Meloun static DEFINE_CLASS_0(sdhci, tegra_sdhci_driver, tegra_sdhci_methods,
4714bda238aSMichal Meloun sizeof(struct tegra_sdhci_softc));
472289f133bSJohn Baldwin DRIVER_MODULE(sdhci_tegra, simplebus, tegra_sdhci_driver, NULL, NULL);
473ab00a509SMarius Strobl SDHCI_DEPEND(sdhci_tegra);
474b83d1009SEmmanuel Vadot #ifndef MMCCAM
475693f8b36SMichal Meloun MMC_DECLARE_BRIDGE(sdhci);
476d597cfd5SEmmanuel Vadot #endif
477