xref: /freebsd/sys/dev/mmc/mmc_fdt_helpers.c (revision e63fbd7bb7a2507cd9e2b15174722573e366364e)
1*e63fbd7bSEmmanuel Vadot /*
2*e63fbd7bSEmmanuel Vadot  * Copyright 2019 Emmanuel Vadot <manu@freebsd.org>
3*e63fbd7bSEmmanuel Vadot  * Copyright (c) 2017 Ian Lepore <ian@freebsd.org> All rights reserved.
4*e63fbd7bSEmmanuel Vadot  *
5*e63fbd7bSEmmanuel Vadot  * Redistribution and use in source and binary forms, with or without
6*e63fbd7bSEmmanuel Vadot  * modification, are permitted provided that the following conditions are
7*e63fbd7bSEmmanuel Vadot  * met:
8*e63fbd7bSEmmanuel Vadot  *
9*e63fbd7bSEmmanuel Vadot  *  1. Redistributions of source code must retain the above copyright
10*e63fbd7bSEmmanuel Vadot  *     notice, this list of conditions and the following disclaimer.
11*e63fbd7bSEmmanuel Vadot  *  2. Redistributions in binary form must reproduce the above copyright
12*e63fbd7bSEmmanuel Vadot  *     notice, this list of conditions and the following disclaimer in the
13*e63fbd7bSEmmanuel Vadot  *     documentation and/or other materials provided with the distribution.
14*e63fbd7bSEmmanuel Vadot  *
15*e63fbd7bSEmmanuel Vadot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16*e63fbd7bSEmmanuel Vadot  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*e63fbd7bSEmmanuel Vadot  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18*e63fbd7bSEmmanuel Vadot  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
19*e63fbd7bSEmmanuel Vadot  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20*e63fbd7bSEmmanuel Vadot  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21*e63fbd7bSEmmanuel Vadot  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
22*e63fbd7bSEmmanuel Vadot  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23*e63fbd7bSEmmanuel Vadot  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24*e63fbd7bSEmmanuel Vadot  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25*e63fbd7bSEmmanuel Vadot  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*e63fbd7bSEmmanuel Vadot  */
27*e63fbd7bSEmmanuel Vadot 
28*e63fbd7bSEmmanuel Vadot #include <sys/cdefs.h>
29*e63fbd7bSEmmanuel Vadot __FBSDID("$FreeBSD$");
30*e63fbd7bSEmmanuel Vadot 
31*e63fbd7bSEmmanuel Vadot #include <sys/param.h>
32*e63fbd7bSEmmanuel Vadot #include <sys/bus.h>
33*e63fbd7bSEmmanuel Vadot #include <sys/kernel.h>
34*e63fbd7bSEmmanuel Vadot #include <sys/gpio.h>
35*e63fbd7bSEmmanuel Vadot #include <sys/taskqueue.h>
36*e63fbd7bSEmmanuel Vadot 
37*e63fbd7bSEmmanuel Vadot #include <dev/mmc/bridge.h>
38*e63fbd7bSEmmanuel Vadot #include <dev/mmc/mmc_fdt_helpers.h>
39*e63fbd7bSEmmanuel Vadot 
40*e63fbd7bSEmmanuel Vadot #include <dev/gpio/gpiobusvar.h>
41*e63fbd7bSEmmanuel Vadot #include <dev/ofw/ofw_bus.h>
42*e63fbd7bSEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h>
43*e63fbd7bSEmmanuel Vadot 
44*e63fbd7bSEmmanuel Vadot #ifdef EXT_RESOURCES
45*e63fbd7bSEmmanuel Vadot #include <dev/extres/regulator/regulator.h>
46*e63fbd7bSEmmanuel Vadot #endif
47*e63fbd7bSEmmanuel Vadot 
48*e63fbd7bSEmmanuel Vadot static inline void
49*e63fbd7bSEmmanuel Vadot mmc_fdt_parse_sd_speed(phandle_t node, struct mmc_host *host)
50*e63fbd7bSEmmanuel Vadot {
51*e63fbd7bSEmmanuel Vadot 	bool no_18v = false;
52*e63fbd7bSEmmanuel Vadot 
53*e63fbd7bSEmmanuel Vadot 	/*
54*e63fbd7bSEmmanuel Vadot 	 * Parse SD supported modes
55*e63fbd7bSEmmanuel Vadot 	 * All UHS-I modes requires 1.8V signaling.
56*e63fbd7bSEmmanuel Vadot 	 */
57*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "no1-8-v"))
58*e63fbd7bSEmmanuel Vadot 		no_18v = true;
59*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "cap-sd-highspeed"))
60*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_HSPEED;
61*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "sd-uhs-sdr12") && no_18v == false)
62*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_UHS_SDR12 | MMC_CAP_SIGNALING_180;
63*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "sd-uhs-sdr25") && no_18v == false)
64*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_UHS_SDR25 | MMC_CAP_SIGNALING_180;
65*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "sd-uhs-sdr50") && no_18v == false)
66*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_UHS_SDR50 | MMC_CAP_SIGNALING_180;
67*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "sd-uhs-sdr104") && no_18v == false)
68*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_UHS_SDR104 | MMC_CAP_SIGNALING_180;
69*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "sd-uhs-ddr50") && no_18v == false)
70*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_UHS_DDR50 | MMC_CAP_SIGNALING_180;
71*e63fbd7bSEmmanuel Vadot }
72*e63fbd7bSEmmanuel Vadot 
73*e63fbd7bSEmmanuel Vadot static inline void
74*e63fbd7bSEmmanuel Vadot mmc_fdt_parse_mmc_speed(phandle_t node, struct mmc_host *host)
75*e63fbd7bSEmmanuel Vadot {
76*e63fbd7bSEmmanuel Vadot 
77*e63fbd7bSEmmanuel Vadot 	/* Parse eMMC supported modes */
78*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "cap-mmc-highspeed"))
79*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_HSPEED;
80*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "mmc-ddr-1_2v"))
81*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_MMC_DDR52_120 | MMC_CAP_SIGNALING_120;
82*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "mmc-ddr-1_8v"))
83*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_MMC_DDR52_180 | MMC_CAP_SIGNALING_180;
84*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "mmc-ddr-3_3v"))
85*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_SIGNALING_330;
86*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "mmc-hs200-1_2v"))
87*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_MMC_HS200_120 | MMC_CAP_SIGNALING_120;
88*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "mmc-hs200-1_8v"))
89*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_MMC_HS200_180 | MMC_CAP_SIGNALING_180;
90*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "mmc-hs400-1_2v"))
91*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_MMC_HS400_120 | MMC_CAP_SIGNALING_120;
92*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "mmc-hs400-1_8v"))
93*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_MMC_HS400_180 | MMC_CAP_SIGNALING_180;
94*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "mmc-hs400-enhanced-strobe"))
95*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_MMC_ENH_STROBE;
96*e63fbd7bSEmmanuel Vadot }
97*e63fbd7bSEmmanuel Vadot 
98*e63fbd7bSEmmanuel Vadot int
99*e63fbd7bSEmmanuel Vadot mmc_fdt_parse(device_t dev, phandle_t node, struct mmc_fdt_helper *helper,
100*e63fbd7bSEmmanuel Vadot     struct mmc_host *host)
101*e63fbd7bSEmmanuel Vadot {
102*e63fbd7bSEmmanuel Vadot 	uint32_t bus_width;
103*e63fbd7bSEmmanuel Vadot 
104*e63fbd7bSEmmanuel Vadot 	if (node <= 0)
105*e63fbd7bSEmmanuel Vadot 		node = ofw_bus_get_node(dev);
106*e63fbd7bSEmmanuel Vadot 	if (node <= 0)
107*e63fbd7bSEmmanuel Vadot 		return (ENXIO);
108*e63fbd7bSEmmanuel Vadot 
109*e63fbd7bSEmmanuel Vadot 	if (OF_getencprop(node, "bus-width", &bus_width, sizeof(uint32_t)) <= 0)
110*e63fbd7bSEmmanuel Vadot 		bus_width = 1;
111*e63fbd7bSEmmanuel Vadot 
112*e63fbd7bSEmmanuel Vadot 	if (bus_width >= 4)
113*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_4_BIT_DATA;
114*e63fbd7bSEmmanuel Vadot 	if (bus_width >= 8)
115*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_8_BIT_DATA;
116*e63fbd7bSEmmanuel Vadot 
117*e63fbd7bSEmmanuel Vadot 	/*
118*e63fbd7bSEmmanuel Vadot 	 * max-frequency is optional, drivers should tweak this value
119*e63fbd7bSEmmanuel Vadot 	 * if it's not present based on the clock that the mmc controller
120*e63fbd7bSEmmanuel Vadot 	 * operates on
121*e63fbd7bSEmmanuel Vadot 	 */
122*e63fbd7bSEmmanuel Vadot 	OF_getencprop(node, "max-frequency", &host->f_max, sizeof(uint32_t));
123*e63fbd7bSEmmanuel Vadot 
124*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "broken-cd"))
125*e63fbd7bSEmmanuel Vadot 		helper->props |= MMC_PROP_BROKEN_CD;
126*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "non-removable"))
127*e63fbd7bSEmmanuel Vadot 		helper->props |= MMC_PROP_NON_REMOVABLE;
128*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "wp-inverted"))
129*e63fbd7bSEmmanuel Vadot 		helper->props |= MMC_PROP_WP_INVERTED;
130*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "cd-inverted"))
131*e63fbd7bSEmmanuel Vadot 		helper->props |= MMC_PROP_CD_INVERTED;
132*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "no-sdio"))
133*e63fbd7bSEmmanuel Vadot 		helper->props |= MMC_PROP_NO_SDIO;
134*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "no-sd"))
135*e63fbd7bSEmmanuel Vadot 		helper->props |= MMC_PROP_NO_SD;
136*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "no-mmc"))
137*e63fbd7bSEmmanuel Vadot 		helper->props |= MMC_PROP_NO_MMC;
138*e63fbd7bSEmmanuel Vadot 
139*e63fbd7bSEmmanuel Vadot 	if (!(helper->props & MMC_PROP_NO_SD))
140*e63fbd7bSEmmanuel Vadot 		mmc_fdt_parse_sd_speed(node, host);
141*e63fbd7bSEmmanuel Vadot 
142*e63fbd7bSEmmanuel Vadot 	if (!(helper->props & MMC_PROP_NO_MMC))
143*e63fbd7bSEmmanuel Vadot 		mmc_fdt_parse_mmc_speed(node, host);
144*e63fbd7bSEmmanuel Vadot 
145*e63fbd7bSEmmanuel Vadot #ifdef EXT_RESOURCES
146*e63fbd7bSEmmanuel Vadot 	/*
147*e63fbd7bSEmmanuel Vadot 	 * Get the regulators if they are supported and
148*e63fbd7bSEmmanuel Vadot 	 * clean the non supported modes based on the available voltages.
149*e63fbd7bSEmmanuel Vadot 	 */
150*e63fbd7bSEmmanuel Vadot 	if (regulator_get_by_ofw_property(dev, 0, "vmmc-supply",
151*e63fbd7bSEmmanuel Vadot 	    &helper->vmmc_supply) == 0) {
152*e63fbd7bSEmmanuel Vadot 		if (bootverbose)
153*e63fbd7bSEmmanuel Vadot 			device_printf(dev, "vmmc-supply regulator found\n");
154*e63fbd7bSEmmanuel Vadot 	}
155*e63fbd7bSEmmanuel Vadot 	if (regulator_get_by_ofw_property(dev, 0, "vqmmc-supply",
156*e63fbd7bSEmmanuel Vadot 	    &helper->vqmmc_supply) == 0 && bootverbose) {
157*e63fbd7bSEmmanuel Vadot 		if (bootverbose)
158*e63fbd7bSEmmanuel Vadot 			device_printf(dev, "vqmmc-supply regulator found\n");
159*e63fbd7bSEmmanuel Vadot 	}
160*e63fbd7bSEmmanuel Vadot 
161*e63fbd7bSEmmanuel Vadot 	if (helper->vqmmc_supply != NULL) {
162*e63fbd7bSEmmanuel Vadot 		if (regulator_check_voltage(helper->vqmmc_supply, 1200000) == 0)
163*e63fbd7bSEmmanuel Vadot 			host->caps |= MMC_CAP_SIGNALING_120;
164*e63fbd7bSEmmanuel Vadot 		else
165*e63fbd7bSEmmanuel Vadot 			host->caps &= ~( MMC_CAP_MMC_HS400_120 |
166*e63fbd7bSEmmanuel Vadot 			    MMC_CAP_MMC_HS200_120 |
167*e63fbd7bSEmmanuel Vadot 			    MMC_CAP_MMC_DDR52_120);
168*e63fbd7bSEmmanuel Vadot 		if (regulator_check_voltage(helper->vqmmc_supply, 1800000) == 0)
169*e63fbd7bSEmmanuel Vadot 			host->caps |= MMC_CAP_SIGNALING_180;
170*e63fbd7bSEmmanuel Vadot 		else
171*e63fbd7bSEmmanuel Vadot 			host->caps &= ~(MMC_CAP_MMC_HS400_180 |
172*e63fbd7bSEmmanuel Vadot 			    MMC_CAP_MMC_HS200_180 |
173*e63fbd7bSEmmanuel Vadot 			    MMC_CAP_MMC_DDR52_180 |
174*e63fbd7bSEmmanuel Vadot 			    MMC_CAP_UHS_DDR50 |
175*e63fbd7bSEmmanuel Vadot 			    MMC_CAP_UHS_SDR104 |
176*e63fbd7bSEmmanuel Vadot 			    MMC_CAP_UHS_SDR50 |
177*e63fbd7bSEmmanuel Vadot 			    MMC_CAP_UHS_SDR25);
178*e63fbd7bSEmmanuel Vadot 		if (regulator_check_voltage(helper->vqmmc_supply, 3300000) == 0)
179*e63fbd7bSEmmanuel Vadot 			host->caps |= MMC_CAP_SIGNALING_330;
180*e63fbd7bSEmmanuel Vadot 	} else
181*e63fbd7bSEmmanuel Vadot 		host->caps |= MMC_CAP_SIGNALING_330;
182*e63fbd7bSEmmanuel Vadot #endif
183*e63fbd7bSEmmanuel Vadot 
184*e63fbd7bSEmmanuel Vadot 	return (0);
185*e63fbd7bSEmmanuel Vadot }
186*e63fbd7bSEmmanuel Vadot 
187*e63fbd7bSEmmanuel Vadot /*
188*e63fbd7bSEmmanuel Vadot  * Card detect interrupt handler.
189*e63fbd7bSEmmanuel Vadot  */
190*e63fbd7bSEmmanuel Vadot static void
191*e63fbd7bSEmmanuel Vadot cd_intr(void *arg)
192*e63fbd7bSEmmanuel Vadot {
193*e63fbd7bSEmmanuel Vadot 	struct mmc_fdt_helper *helper = arg;
194*e63fbd7bSEmmanuel Vadot 
195*e63fbd7bSEmmanuel Vadot 	taskqueue_enqueue_timeout(taskqueue_swi_giant,
196*e63fbd7bSEmmanuel Vadot 	    &helper->cd_delayed_task, -(hz / 2));
197*e63fbd7bSEmmanuel Vadot }
198*e63fbd7bSEmmanuel Vadot 
199*e63fbd7bSEmmanuel Vadot static void
200*e63fbd7bSEmmanuel Vadot cd_card_task(void *arg, int pending __unused)
201*e63fbd7bSEmmanuel Vadot {
202*e63fbd7bSEmmanuel Vadot 	struct mmc_fdt_helper *helper = arg;
203*e63fbd7bSEmmanuel Vadot 	bool cd_present;
204*e63fbd7bSEmmanuel Vadot 
205*e63fbd7bSEmmanuel Vadot 	cd_present = mmc_fdt_gpio_get_present(helper);
206*e63fbd7bSEmmanuel Vadot 	if(helper->cd_handler && cd_present != helper->cd_present)
207*e63fbd7bSEmmanuel Vadot 		helper->cd_handler(helper->dev,
208*e63fbd7bSEmmanuel Vadot 		    cd_present);
209*e63fbd7bSEmmanuel Vadot 	helper->cd_present = cd_present;
210*e63fbd7bSEmmanuel Vadot 
211*e63fbd7bSEmmanuel Vadot 	/* If we're polling re-schedule the task */
212*e63fbd7bSEmmanuel Vadot 	if (helper->cd_ihandler == NULL)
213*e63fbd7bSEmmanuel Vadot 		taskqueue_enqueue_timeout_sbt(taskqueue_swi_giant,
214*e63fbd7bSEmmanuel Vadot 		    &helper->cd_delayed_task, mstosbt(500), 0, C_PREL(2));
215*e63fbd7bSEmmanuel Vadot }
216*e63fbd7bSEmmanuel Vadot 
217*e63fbd7bSEmmanuel Vadot /*
218*e63fbd7bSEmmanuel Vadot  * Card detect setup.
219*e63fbd7bSEmmanuel Vadot  */
220*e63fbd7bSEmmanuel Vadot static void
221*e63fbd7bSEmmanuel Vadot cd_setup(struct mmc_fdt_helper *helper, phandle_t node)
222*e63fbd7bSEmmanuel Vadot {
223*e63fbd7bSEmmanuel Vadot 	int pincaps;
224*e63fbd7bSEmmanuel Vadot 	device_t dev;
225*e63fbd7bSEmmanuel Vadot 	const char *cd_mode_str;
226*e63fbd7bSEmmanuel Vadot 
227*e63fbd7bSEmmanuel Vadot 	dev = helper->dev;
228*e63fbd7bSEmmanuel Vadot 	/*
229*e63fbd7bSEmmanuel Vadot 	 * If the device is flagged as non-removable, set that slot option, and
230*e63fbd7bSEmmanuel Vadot 	 * set a flag to make sdhci_fdt_gpio_get_present() always return true.
231*e63fbd7bSEmmanuel Vadot 	 */
232*e63fbd7bSEmmanuel Vadot 	if (helper->props & MMC_PROP_NON_REMOVABLE) {
233*e63fbd7bSEmmanuel Vadot 		helper->cd_disabled = true;
234*e63fbd7bSEmmanuel Vadot 		if (bootverbose)
235*e63fbd7bSEmmanuel Vadot 			device_printf(dev, "Non-removable media\n");
236*e63fbd7bSEmmanuel Vadot 		return;
237*e63fbd7bSEmmanuel Vadot 	}
238*e63fbd7bSEmmanuel Vadot 
239*e63fbd7bSEmmanuel Vadot 	/*
240*e63fbd7bSEmmanuel Vadot 	 * If there is no cd-gpios property, then presumably the hardware
241*e63fbd7bSEmmanuel Vadot 	 * PRESENT_STATE register and interrupts will reflect card state
242*e63fbd7bSEmmanuel Vadot 	 * properly, and there's nothing more for us to do.  Our get_present()
243*e63fbd7bSEmmanuel Vadot 	 * will return sdhci_generic_get_card_present() because cd_pin is NULL.
244*e63fbd7bSEmmanuel Vadot 	 *
245*e63fbd7bSEmmanuel Vadot 	 * If there is a property, make sure we can read the pin.
246*e63fbd7bSEmmanuel Vadot 	 */
247*e63fbd7bSEmmanuel Vadot 	if (gpio_pin_get_by_ofw_property(dev, node, "cd-gpios",
248*e63fbd7bSEmmanuel Vadot 	    &helper->cd_pin))
249*e63fbd7bSEmmanuel Vadot 		return;
250*e63fbd7bSEmmanuel Vadot 
251*e63fbd7bSEmmanuel Vadot 	if (gpio_pin_getcaps(helper->cd_pin, &pincaps) != 0 ||
252*e63fbd7bSEmmanuel Vadot 	    !(pincaps & GPIO_PIN_INPUT)) {
253*e63fbd7bSEmmanuel Vadot 		device_printf(dev, "Cannot read card-detect gpio pin; "
254*e63fbd7bSEmmanuel Vadot 		    "setting card-always-present flag.\n");
255*e63fbd7bSEmmanuel Vadot 		helper->cd_disabled = true;
256*e63fbd7bSEmmanuel Vadot 		return;
257*e63fbd7bSEmmanuel Vadot 	}
258*e63fbd7bSEmmanuel Vadot 
259*e63fbd7bSEmmanuel Vadot 	/*
260*e63fbd7bSEmmanuel Vadot 	 * If the pin can trigger an interrupt on both rising and falling edges,
261*e63fbd7bSEmmanuel Vadot 	 * we can use it to detect card presence changes.  If not, we'll request
262*e63fbd7bSEmmanuel Vadot 	 * card presence polling instead of using interrupts.
263*e63fbd7bSEmmanuel Vadot 	 */
264*e63fbd7bSEmmanuel Vadot 	if (!(pincaps & GPIO_INTR_EDGE_BOTH)) {
265*e63fbd7bSEmmanuel Vadot 		if (bootverbose)
266*e63fbd7bSEmmanuel Vadot 			device_printf(dev, "Cannot configure "
267*e63fbd7bSEmmanuel Vadot 			    "GPIO_INTR_EDGE_BOTH for card detect\n");
268*e63fbd7bSEmmanuel Vadot 		goto without_interrupts;
269*e63fbd7bSEmmanuel Vadot 	}
270*e63fbd7bSEmmanuel Vadot 
271*e63fbd7bSEmmanuel Vadot 	if (helper->cd_handler == NULL) {
272*e63fbd7bSEmmanuel Vadot 		if (bootverbose)
273*e63fbd7bSEmmanuel Vadot 			device_printf(dev, "Cannot configure "
274*e63fbd7bSEmmanuel Vadot 			    "interrupts as no cd_handler is set\n");
275*e63fbd7bSEmmanuel Vadot 		goto without_interrupts;
276*e63fbd7bSEmmanuel Vadot 	}
277*e63fbd7bSEmmanuel Vadot 
278*e63fbd7bSEmmanuel Vadot 	/*
279*e63fbd7bSEmmanuel Vadot 	 * Create an interrupt resource from the pin and set up the interrupt.
280*e63fbd7bSEmmanuel Vadot 	 */
281*e63fbd7bSEmmanuel Vadot 	if ((helper->cd_ires = gpio_alloc_intr_resource(dev, &helper->cd_irid,
282*e63fbd7bSEmmanuel Vadot 	    RF_ACTIVE, helper->cd_pin, GPIO_INTR_EDGE_BOTH)) == NULL) {
283*e63fbd7bSEmmanuel Vadot 		if (bootverbose)
284*e63fbd7bSEmmanuel Vadot 			device_printf(dev, "Cannot allocate an IRQ for card "
285*e63fbd7bSEmmanuel Vadot 			    "detect GPIO\n");
286*e63fbd7bSEmmanuel Vadot 		goto without_interrupts;
287*e63fbd7bSEmmanuel Vadot 	}
288*e63fbd7bSEmmanuel Vadot 
289*e63fbd7bSEmmanuel Vadot 	if (bus_setup_intr(dev, helper->cd_ires, INTR_TYPE_BIO | INTR_MPSAFE,
290*e63fbd7bSEmmanuel Vadot 	    NULL, cd_intr, helper, &helper->cd_ihandler) != 0) {
291*e63fbd7bSEmmanuel Vadot 		device_printf(dev, "Unable to setup card-detect irq handler\n");
292*e63fbd7bSEmmanuel Vadot 		helper->cd_ihandler = NULL;
293*e63fbd7bSEmmanuel Vadot 		goto without_interrupts;
294*e63fbd7bSEmmanuel Vadot 	}
295*e63fbd7bSEmmanuel Vadot 
296*e63fbd7bSEmmanuel Vadot without_interrupts:
297*e63fbd7bSEmmanuel Vadot 	TIMEOUT_TASK_INIT(taskqueue_swi_giant, &helper->cd_delayed_task, 0,
298*e63fbd7bSEmmanuel Vadot 	    cd_card_task, helper);
299*e63fbd7bSEmmanuel Vadot 
300*e63fbd7bSEmmanuel Vadot 	/*
301*e63fbd7bSEmmanuel Vadot 	 * If we have a readable gpio pin, but didn't successfully configure
302*e63fbd7bSEmmanuel Vadot 	 * gpio interrupts, setup a timeout task to poll the pin
303*e63fbd7bSEmmanuel Vadot 	 */
304*e63fbd7bSEmmanuel Vadot 	if (helper->cd_ihandler == NULL) {
305*e63fbd7bSEmmanuel Vadot 		cd_mode_str = "polling";
306*e63fbd7bSEmmanuel Vadot 	} else {
307*e63fbd7bSEmmanuel Vadot 		cd_mode_str = "interrupts";
308*e63fbd7bSEmmanuel Vadot 	}
309*e63fbd7bSEmmanuel Vadot 
310*e63fbd7bSEmmanuel Vadot 	if (bootverbose) {
311*e63fbd7bSEmmanuel Vadot 		device_printf(dev, "Card presence detect on %s pin %u, "
312*e63fbd7bSEmmanuel Vadot 		    "configured for %s.\n",
313*e63fbd7bSEmmanuel Vadot 		    device_get_nameunit(helper->cd_pin->dev), helper->cd_pin->pin,
314*e63fbd7bSEmmanuel Vadot 		    cd_mode_str);
315*e63fbd7bSEmmanuel Vadot 	}
316*e63fbd7bSEmmanuel Vadot }
317*e63fbd7bSEmmanuel Vadot 
318*e63fbd7bSEmmanuel Vadot /*
319*e63fbd7bSEmmanuel Vadot  * Write protect setup.
320*e63fbd7bSEmmanuel Vadot  */
321*e63fbd7bSEmmanuel Vadot static void
322*e63fbd7bSEmmanuel Vadot wp_setup(struct mmc_fdt_helper *helper, phandle_t node)
323*e63fbd7bSEmmanuel Vadot {
324*e63fbd7bSEmmanuel Vadot 	device_t dev;
325*e63fbd7bSEmmanuel Vadot 
326*e63fbd7bSEmmanuel Vadot 	dev = helper->dev;
327*e63fbd7bSEmmanuel Vadot 
328*e63fbd7bSEmmanuel Vadot 	if (OF_hasprop(node, "disable-wp")) {
329*e63fbd7bSEmmanuel Vadot 		helper->wp_disabled = true;
330*e63fbd7bSEmmanuel Vadot 		if (bootverbose)
331*e63fbd7bSEmmanuel Vadot 			device_printf(dev, "Write protect disabled\n");
332*e63fbd7bSEmmanuel Vadot 		return;
333*e63fbd7bSEmmanuel Vadot 	}
334*e63fbd7bSEmmanuel Vadot 
335*e63fbd7bSEmmanuel Vadot 	if (gpio_pin_get_by_ofw_property(dev, node, "wp-gpios", &helper->wp_pin))
336*e63fbd7bSEmmanuel Vadot 		return;
337*e63fbd7bSEmmanuel Vadot 
338*e63fbd7bSEmmanuel Vadot 	if (bootverbose)
339*e63fbd7bSEmmanuel Vadot 		device_printf(dev, "Write protect switch on %s pin %u\n",
340*e63fbd7bSEmmanuel Vadot 		    device_get_nameunit(helper->wp_pin->dev), helper->wp_pin->pin);
341*e63fbd7bSEmmanuel Vadot }
342*e63fbd7bSEmmanuel Vadot 
343*e63fbd7bSEmmanuel Vadot int
344*e63fbd7bSEmmanuel Vadot mmc_fdt_gpio_setup(device_t dev, phandle_t node, struct mmc_fdt_helper *helper,
345*e63fbd7bSEmmanuel Vadot     mmc_fdt_cd_handler handler)
346*e63fbd7bSEmmanuel Vadot {
347*e63fbd7bSEmmanuel Vadot 
348*e63fbd7bSEmmanuel Vadot 	if (node <= 0)
349*e63fbd7bSEmmanuel Vadot 		node = ofw_bus_get_node(dev);
350*e63fbd7bSEmmanuel Vadot 	if (node <= 0) {
351*e63fbd7bSEmmanuel Vadot 		device_printf(dev, "Cannot get node for device\n");
352*e63fbd7bSEmmanuel Vadot 		return (ENXIO);
353*e63fbd7bSEmmanuel Vadot 	}
354*e63fbd7bSEmmanuel Vadot 
355*e63fbd7bSEmmanuel Vadot 	helper->dev = dev;
356*e63fbd7bSEmmanuel Vadot 	helper->cd_handler = handler;
357*e63fbd7bSEmmanuel Vadot 	cd_setup(helper, node);
358*e63fbd7bSEmmanuel Vadot 	wp_setup(helper, node);
359*e63fbd7bSEmmanuel Vadot 
360*e63fbd7bSEmmanuel Vadot 	/*
361*e63fbd7bSEmmanuel Vadot 	 * Schedule a card detection
362*e63fbd7bSEmmanuel Vadot 	 */
363*e63fbd7bSEmmanuel Vadot 	taskqueue_enqueue_timeout_sbt(taskqueue_swi_giant,
364*e63fbd7bSEmmanuel Vadot 	    &helper->cd_delayed_task, mstosbt(500), 0, C_PREL(2));
365*e63fbd7bSEmmanuel Vadot 	return (0);
366*e63fbd7bSEmmanuel Vadot }
367*e63fbd7bSEmmanuel Vadot 
368*e63fbd7bSEmmanuel Vadot void
369*e63fbd7bSEmmanuel Vadot mmc_fdt_gpio_teardown(struct mmc_fdt_helper *helper)
370*e63fbd7bSEmmanuel Vadot {
371*e63fbd7bSEmmanuel Vadot 
372*e63fbd7bSEmmanuel Vadot 	if (helper == NULL)
373*e63fbd7bSEmmanuel Vadot 		return;
374*e63fbd7bSEmmanuel Vadot 
375*e63fbd7bSEmmanuel Vadot 	if (helper->cd_ihandler != NULL)
376*e63fbd7bSEmmanuel Vadot 		bus_teardown_intr(helper->dev, helper->cd_ires, helper->cd_ihandler);
377*e63fbd7bSEmmanuel Vadot 	if (helper->wp_pin != NULL)
378*e63fbd7bSEmmanuel Vadot 		gpio_pin_release(helper->wp_pin);
379*e63fbd7bSEmmanuel Vadot 	if (helper->cd_pin != NULL)
380*e63fbd7bSEmmanuel Vadot 		gpio_pin_release(helper->cd_pin);
381*e63fbd7bSEmmanuel Vadot 	if (helper->cd_ires != NULL)
382*e63fbd7bSEmmanuel Vadot 		bus_release_resource(helper->dev, SYS_RES_IRQ, 0, helper->cd_ires);
383*e63fbd7bSEmmanuel Vadot }
384*e63fbd7bSEmmanuel Vadot 
385*e63fbd7bSEmmanuel Vadot bool
386*e63fbd7bSEmmanuel Vadot mmc_fdt_gpio_get_present(struct mmc_fdt_helper *helper)
387*e63fbd7bSEmmanuel Vadot {
388*e63fbd7bSEmmanuel Vadot 	bool pinstate;
389*e63fbd7bSEmmanuel Vadot 
390*e63fbd7bSEmmanuel Vadot 	if (helper->cd_disabled)
391*e63fbd7bSEmmanuel Vadot 		return (true);
392*e63fbd7bSEmmanuel Vadot 	if (helper->cd_pin == NULL)
393*e63fbd7bSEmmanuel Vadot 		return (false);
394*e63fbd7bSEmmanuel Vadot 
395*e63fbd7bSEmmanuel Vadot 	gpio_pin_is_active(helper->cd_pin, &pinstate);
396*e63fbd7bSEmmanuel Vadot 
397*e63fbd7bSEmmanuel Vadot 	return (pinstate ^ (helper->props & MMC_PROP_CD_INVERTED));
398*e63fbd7bSEmmanuel Vadot }
399*e63fbd7bSEmmanuel Vadot 
400*e63fbd7bSEmmanuel Vadot bool
401*e63fbd7bSEmmanuel Vadot mmc_fdt_gpio_get_readonly(struct mmc_fdt_helper *helper)
402*e63fbd7bSEmmanuel Vadot {
403*e63fbd7bSEmmanuel Vadot 	bool pinstate;
404*e63fbd7bSEmmanuel Vadot 
405*e63fbd7bSEmmanuel Vadot 	if (helper->wp_disabled)
406*e63fbd7bSEmmanuel Vadot 		return (false);
407*e63fbd7bSEmmanuel Vadot 
408*e63fbd7bSEmmanuel Vadot 	if (helper->wp_pin == NULL)
409*e63fbd7bSEmmanuel Vadot 		return (false);
410*e63fbd7bSEmmanuel Vadot 
411*e63fbd7bSEmmanuel Vadot 	gpio_pin_is_active(helper->wp_pin, &pinstate);
412*e63fbd7bSEmmanuel Vadot 
413*e63fbd7bSEmmanuel Vadot 	return (pinstate ^ (helper->props & MMC_PROP_WP_INVERTED));
414*e63fbd7bSEmmanuel Vadot }
415