xref: /freebsd/sys/arm/nvidia/tegra_sdhci.c (revision 64d1a02e4e1a5f561d03b2bbc9c20dad255e79e8)
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